Android-使用Firebase Auth UI輕鬆實現第三方登入(程式篇)

Vincent Zheng
新手工程師的程式教室
10 min readMar 3, 2019

--

現在許多App都支援第三方登入,也就是不直接向App提供商註冊帳號。但若在開發時一一撰寫各個平台的登入程式碼,例如Facebook寫一次,Google又寫一次,整個程式專案可能會很複雜。

如果想簡化實作第三方登入的的過程,甚至連登入畫面都不想花太多時間製作,本文介紹的Firebase Auth UI是個好選擇。使用的是Kotlin語言。

二、實作登入功能(使用Kotlin)

開啟會被導向登入畫面的Activity檔案(如MainActivity.kt)。接下來需要撰寫的程式並不多,練習時直接寫在該類別的onCreate方法中也無妨。

定義「AuthUI.IdpConfig」清單,將App支援的身份提供商組態(identity provider config)加入List。此處示範加入Facebook、Google組態。

val authProvider: List<AuthUI.IdpConfig> = listOf(
AuthUI.IdpConfig.FacebookBuilder().build(),
AuthUI.IdpConfig.GoogleBuilder().build()
)

設置「身份驗證監聽器(FirebaseAuth.AuthStateListener)」,讓Activity啟動後隨時偵測使用者是否已登入,並做對應的處理。

val authListener: FirebaseAuth.AuthStateListener = 
FirebaseAuth.AuthStateListener { auth: FirebaseAuth ->
val user: FirebaseUser? = auth.currentUser
if (user == null) {
val intent = AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(authProvider)
.setAlwaysShowSignInMethodScreen(true)
.setIsSmartLockEnabled(false)
.build()
startActivityForResult(intent, this.RC_SIGN_IN)
} else {
this.firebaseUser = user
displayInfo()
}
}
FirebaseAuth.getInstance().addAuthStateListener(authListener)

這段程式會讓App啟動時,檢查使用者的驗證狀態。在此我們取得FirebaseUser物件,每個透過Firebase Authentication註冊的會員都是一個FirebaseUser。若使用者尚未登入,則其值為null,需要引導至登入畫面。否則就顯示該使用者的資料。

前往登入畫面的Intent物件,必須透過Firebase Auth UI函式庫所提供的方法建立,且需要提供身份提供商清單(List<AuthUI.IdpConfig>)。最後再啟動Activity即可。

三、登入後的處理

一旦使用者離開登入畫面(可能登入成功、失敗或取消登入)回到MainActivity,首先Android系統會自動呼叫onActivityResult方法。

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)

if (requestCode == this.RC_SIGN_IN) {
if (resultCode != Activity.RESULT_OK) {
val response = IdpResponse.fromResultIntent(data)
Toast.makeText(applicationContext, response?.error?.errorCode.toString(), Toast.LENGTH_SHORT).show()
}
}
}

這裡藉由判斷啟動Activity時的RequestCode,來辨認剛剛是從登入畫面回來的。若登入未完成,可以取得IdpResponse,透過錯誤碼(errorCode)來進行另外的處理。若登入成功,則會觸發FirebaseAuth.AuthStateListener。

一般來說,有兩種狀況會讓firebaseUser不是null值。第一種是剛登入成功;第二種是登入過的使用者退出App後又重新開啟。兩者都是由FirebaseAuth.AuthStateListener賦予值,此時應該將使用者以「已登入」的身份看待。這邊的範例是顯示使用者的資料,如下:

private fun displayInfo() {
val txtUserName = findViewById<TextView>(R.id.txtUserName)
txtUserName.text = firebaseUser?.displayName
}

最後是登出的功能,它與前往登入畫面一樣,都需要透過AuthUI提供的方法執行。

private fun signOut() {
AuthUI.getInstance()
.signOut(this)
.addOnSuccessListener {
Toast.makeText(applicationContext, "已登出", Toast.LENGTH_SHORT).show()
}
}

四、實機測試

程式撰寫完畢後,便可安裝到手機上試用看看。一開始尚未登入,所以出現登入的按鈕。

Firebase Auth UI登入畫面

按下登入後,App會讀取使用者在該平台的基本資料,並透過Firebase Authentication註冊。隨後MainActivity出現,執行了displayInfo方法,顯示使用者的姓名。

登入後顯示使用者姓名的範例

同時我們也可以在Firebase Authentication的使用者頁面,找到剛剛按下登入而註冊的帳號。

Authentication使用者頁面

五、登入畫面客製化

上述範例的登入畫面顯得單調,但它其實是可以做一些客製化的。

想要設置Logo圖片或服務條款連結,可以在建立登入畫面Intent物件時一併添加。

val intent = AuthUI.getInstance()
.createSignInIntentBuilder()
...
.setLogo(R.drawable.logo)
.setTosAndPrivacyPolicyUrls(
"https://www.google.com/服務條款",
"https://www.youtube.com/隱私權政策"
)

.build()
添加Logo與服務條款連結

想要設置背景圖片或其他樣式,可以先在樣式資源檔(style.xml)定義一組樣式,並繼承自「FirebaseUI」。

<style name="LoginTheme" parent="FirebaseUI">
<!--設置背景圖片-->
<item name="android:windowBackground">@drawable/bg_login</item>
<!--移除標題列-->
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<!--其他視需要添加-->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="colorButtonNormal">@color/colorAccent</item>
<item name="colorControlNormal">@android:color/white</item>
<item name="colorControlActivated">@android:color/white</item>
<item name="colorControlHighlight">@android:color/white</item>
<item name="android:textColor">@android:color/white</item>
<item name="android:textColorHint">@android:color/white</item>
<item name="android:textColorPrimary">@android:color/white</item>
<item name="android:textColorSecondary">@android:color/white</item>
</style>

之後一樣在建立Intent時添加上去即可。

val intent = AuthUI.getInstance()
.createSignInIntentBuilder()
...
.setTheme(R.style.LoginTheme)
.build()
套用樣式後的登入畫面

--

--

Vincent Zheng
新手工程師的程式教室

我是Vincent,是個來自資管系的後端軟體工程師。當初因為學校作業,才踏出寫部落格的第一步。這裡提供程式教學文章,包含自學和工作上用到的經驗,希望能讓讀者學到東西。我的部落已搬家至 https://chikuwa-tech-study.blogspot.com/