Creating an App on Twitter
Go to https://developer.twitter.com/apps, log in with your Twitter account, and press Create an app
On the new page, fill all the required fields.
As a Callback URL, add the name of your app (in lowercase) with :// , for example, twittersigninexample://
Also, add the Terms of Service and Privacy policy URLs of your app if you want to get the user’s email too.
After you created the app successfully, go to the Permissions tab.
In the Permissions tab, select Read-only, for Access permissions, and check the box Request email address from users if you want to get the user’s email.
Then press Save
Now go to Keys and tokens tab and copy the API key and API secret key somewhere, we’re going to need them later.
Creating the Twitter Login Button
Go to your build.gradle
of your app and add the following dependencies
We use Coroutines to do the asynchronous tasks and twitter4j for the Twitter API.
dependencies {
// ...
implementation 'org.twitter4j:twitter4j-core:4.0.7'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5'
// ...
}
Code language: Kotlin (kotlin)
In your AndroidManifest.xml
(manifests > AndroidManifest.xml) add the following permission:
<uses-permission android:name="android.permission.INTERNET" />
Code language: HTML, XML (xml)
Go to your layout XML and add a button
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary"
tools:context=".MainActivity">
<Button
android:id="@+id/twitter_login_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@drawable/rounded_corners"
android:drawableStart="@drawable/twitter_icon"
android:drawablePadding="8dp"
android:padding="8dp"
android:text="Log in with Twitter"
android:textAllCaps="false"
android:textColor="@android:color/white" />
</RelativeLayout>
Code language: HTML, XML (xml)
In your project create a new Kotlin object by right-clicking on your project’s package name and then press New > Kotlin File/Class
Give the name TwitterConstants and select Object
Inside the file, paste the following code:
object TwitterConstants {
var CONSUMER_KEY = "MY_CONSUMER_KEY"
var CONSUMER_SECRET = "MY_CONSUMER_SECRET"
var CALLBACK_URL = "MY_CALLBACK_URL"
}
Code language: Kotlin (kotlin)
Replace the MY_CONSUMER_KEY
with your API key, the MY_CONSUMER_SECRET
with your API secret key, and the MY_CALLBACK_URL
with your callback URL you added before on the Twitter dashboard.
Go to your Activity (or Fragment) file and create a click listener for your button that calls the getRequestToken() method.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
twitter_login_btn.setOnClickListener {
getRequestToken()
}
}
}
Code language: Kotlin (kotlin)
Before we ask the user to permit us to use their data, we have to get a request token from Twitter that we’ll use later to do the authorization.
lateinit var twitter: Twitter
private fun getRequestToken() {
GlobalScope.launch(Dispatchers.Default) {
val builder = ConfigurationBuilder()
.setDebugEnabled(true)
.setOAuthConsumerKey(TwitterConstants.CONSUMER_KEY)
.setOAuthConsumerSecret(TwitterConstants.CONSUMER_SECRET)
.setIncludeEmailEnabled(true)
val config = builder.build()
val factory = TwitterFactory(config)
twitter = factory.instance
try {
val requestToken = twitter.oAuthRequestToken
withContext(Dispatchers.Main) {
setupTwitterWebviewDialog(requestToken.authorizationURL)
}
} catch (e: IllegalStateException) {
Log.e("ERROR: ", e.toString())
}
}
}
Code language: Kotlin (kotlin)
After you successfully got the request token, we have to open a Dialog with a WebView in it to ask them if they want our app to get their profile data (authorize).
If they accept it, we get back a URL of the form:
MY_CALLBACK_URL?oauth_token=XXXXXXXXXXXXXXX&oauth_verifier=XXXXXXXXXXXXXXXXXXXXXXXXXXXX
Code language: Swift (swift)
From that URL we take the oauth_verifier, and we use it to get the access token
lateinit var twitterDialog: Dialog
var accToken: AccessToken? = null
// Show twitter login page in a dialog
@SuppressLint("SetJavaScriptEnabled")
fun setupTwitterWebviewDialog(url: String) {
twitterDialog = Dialog(this)
val webView = WebView(this)
webView.isVerticalScrollBarEnabled = false
webView.isHorizontalScrollBarEnabled = false
webView.webViewClient = TwitterWebViewClient()
webView.settings.javaScriptEnabled = true
webView.loadUrl(url)
twitterDialog.setContentView(webView)
twitterDialog.show()
}
// A client to know about WebView navigations
// For API 21 and above
@Suppress("OverridingDeprecatedMember")
inner class TwitterWebViewClient : WebViewClient() {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
override fun shouldOverrideUrlLoading(
view: WebView?,
request: WebResourceRequest?
): Boolean {
if (request?.url.toString().startsWith(TwitterConstants.CALLBACK_URL)) {
Log.d("Authorization URL: ", request?.url.toString())
handleUrl(request?.url.toString())
// Close the dialog after getting the oauth_verifier
if (request?.url.toString().contains(TwitterConstants.CALLBACK_URL)) {
twitterDialog.dismiss()
}
return true
}
return false
}
// For API 19 and below
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
if (url.startsWith(TwitterConstants.CALLBACK_URL)) {
Log.d("Authorization URL: ", url)
handleUrl(url)
// Close the dialog after getting the oauth_verifier
if (url.contains(TwitterConstants.CALLBACK_URL)) {
twitterDialog.dismiss()
}
return true
}
return false
}
// Get the oauth_verifier
private fun handleUrl(url: String) {
val uri = Uri.parse(url)
val oauthVerifier = uri.getQueryParameter("oauth_verifier") ?: ""
GlobalScope.launch(Dispatchers.Main) {
accToken = withContext(Dispatchers.IO) { twitter.getOAuthAccessToken(oauthVerifier) }
getUserProfile()
}
}
}
Code language: Kotlin (kotlin)
Getting User’s Profile Info
To get the user’s data, we’re using the .verifyCredentials()
from the twitter4j
suspend fun getUserProfile() {
val usr = withContext(Dispatchers.IO) { twitter.verifyCredentials() }
//Twitter Id
val twitterId = usr.id.toString()
Log.d("Twitter Id: ", twitterId)
//Twitter Handle
val twitterHandle = usr.screenName
Log.d("Twitter Handle: ", twitterHandle)
//Twitter Name
val twitterName = usr.name
Log.d("Twitter Name: ", twitterName)
//Twitter Email
val twitterEmail = usr.email
Log.d("Twitter Email: ", twitterEmail ?: "'Request email address from users' on the Twitter dashboard is disabled")
// Twitter Profile Pic URL
val twitterProfilePic = usr.profileImageURLHttps.replace("_normal", "")
Log.d("Twitter Profile URL: ", twitterProfilePic)
// Twitter Access Token
Log.d("Twitter Access Token", accToken?.token ?: "")
}
Code language: Kotlin (kotlin)
Checking User’s Logging state
To check the user’s logging state, we use the .verifyCredentials()
.
Using the .verifyCredentials()
, we verify if the access token is valid or not.
Twitter’s access tokens never expire (only in some cases), they just become invalidate when the user revokes your app from their Twitter account (Settings and privacy > Account > Apps and sessions)
To do the .verifyCredentials()
, we need the oauth token and the oauth token secret.
These two can be saved on your app, using SharedPreferences
, when you are getting the user’s profile data.
suspend fun getUserProfile() {
// ...Getting Profile Data
// Save the Access Token (accToken.token) and Access Token Secret (accToken.tokenSecret) using SharedPreferences
// This will allow us to check user's logging state every time they open the app after cold start.
val sharedPref = this.getPreferences(Context.MODE_PRIVATE)
sharedPref.edit().putString("oauth_token",accToken?.token ?: "").apply()
sharedPref.edit().putString("oauth_token_secret",accToken?.tokenSecret ?: "").apply()
}
Code language: Kotlin (kotlin)
So every time the user opens the app, we check if the access token is valid, and we send them to the right Activity (or Fragment).
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
GlobalScope.launch {
val results = GlobalScope.async { isLoggedIn() }
val result = results.await()
if (result) {
// Show the Activity with the logged in user
Log.d("LoggedIn?: ", "YES")
} else {
// Show the Home Activity
Log.d("LoggedIn?: ", "NO")
}
}
// ...
}
suspend fun isLoggedIn(): Boolean {
val sharedPref = this.getPreferences(Context.MODE_PRIVATE)
val accessToken = sharedPref.getString("oauth_token","")
val accessTokenSecret = sharedPref.getString("oauth_token_secret", "")
val builder = ConfigurationBuilder()
builder.setOAuthConsumerKey(TwitterConstants.CONSUMER_KEY)
.setOAuthConsumerSecret(TwitterConstants.CONSUMER_SECRET)
.setOAuthAccessToken(accessToken)
.setOAuthAccessTokenSecret(accessTokenSecret)
val config = builder.build()
val factory = TwitterFactory(config)
val twitter = factory.instance
try {
withContext(Dispatchers.IO) { twitter.verifyCredentials() }
return true
} catch (e: Exception) {
return false
}
}
}
Code language: Kotlin (kotlin)
You can find the final project here
If you have any questions, please feel free to leave a comment below