### 介紹與目的 * 使用 Android Credential Manager 改善使用者名稱與密碼的驗證流程 * 儲存使用者憑證至 Google 帳戶以便跨裝置與網站使用 * 提供簡化登入體驗與自動填入憑證功能 ### 實作預覽與 UX 示意 * 建立包含註冊與登入功能的表單畫面 * 註冊成功後顯示憑證儲存提示視窗 * App 重啟後可自動帶出憑證提示登入 * 可選擇在使用者點擊登入前即觸發憑證提示以改善體驗 ### Credential Manager 支援方式 * 支援使用者名稱與密碼驗證 * 支援 Passkey 指紋等生物辨識驗證(需後端支援) * 教學示範使用者名稱與密碼方式為主 ### 專案初始化與依賴設定 * 使用 `androidx.credentials:credentials` 套件 * 支援 Android 13 以下需加上 `credentials-compat` * 使用 Jetpack Compose、Navigation Compose、Kotlin Serialization * 加入 `kotlin("plugin.serialization")` 至 build.gradle ### 建立核心類別:AccountManager * 管理憑證儲存與登入流程 * 接收 `Activity` 作為參數以啟動憑證介面 * 使用 `CredentialManager.create(context)` 建立實例 ### 註冊流程與錯誤處理 * 使用 suspend 函式 signUp 傳入使用者名稱與密碼 * 包裝在 try-catch 中以處理例外狀況 * 定義 SignUpResult 介面包括 Success、Canceled、Failure * 成功時回傳 Success,取消或失敗時分別回傳對應結果 ### 畫面架構與導覽設計 * 使用 NavController 與 NavHost 管理畫面切換 * 定義 LoginRoute 與 LoggedInRoute(username) 作為導航節點 * 登入成功導向 LoggedInRoute 並顯示使用者名稱 ### 登入畫面資料結構與初始化狀態 * 定義 LoginState 包含使用者名稱、密碼、錯誤訊息與登入模式狀態 * 預設為登入模式,使用者名稱與密碼欄位可預填以方便測試 ### 定義 LoginAction 介面 * 使用 sealed interface 定義所有登入畫面中的使用者操作 * 包含操作項目如:輸入使用者名稱、密碼、切換註冊模式、點擊註冊等 * 提供資料類別 `OnSignUp` 傳入 SignUpResult * 提供 `OnUsernameChange` 與 `OnPasswordChange` 傳入新值 * 提供 `OnToggleIsRegister` 切換註冊/登入狀態 ### 建立 LoginScreen 畫面 * Composable 函式接受 `LoginState` 與 `onAction` Lambda * 使用 `rememberCoroutineScope` 建立 coroutine scope * 使用 `LocalContext.current` 取得 context 並轉型為 `ComponentActivity` * 使用 `remember` 建立 `AccountManager` 實例 * 僅在 UI 使用 AccountManager,避免記憶體洩漏 ### 畫面 UI 元件配置 * 使用 Column 放置輸入欄與按鈕 * TextField 綁定使用者名稱與密碼輸入 * Row 放置註冊文字與切換開關 * Switch 控制是否為註冊模式並觸發對應 action * Button 顯示「Register」或「Login」,依註冊模式決定 ### 註冊流程觸發與結果處理 * 點擊按鈕時若處於註冊模式則呼叫 `AccountManager.signUp` * 使用 coroutine 執行 suspend 函式 * 根據 SignUpResult 執行對應處理並觸發 `onAction(OnSignUp)` ### 建立 LoginViewModel * 擴充自 ViewModel,持有 mutableStateOf 的 LoginState * 將 state 設為 private,僅允許內部更新 * 實作 `onAction` 處理所有 UI 傳遞事件 ### 處理各種登入操作 * `OnUsernameChange` 與 `OnPasswordChange` 更新輸入內容 * `OnToggleIsRegister` 切換註冊狀態 * `OnSignUp` 根據 SignUpResult 更新錯誤訊息或設定登入使用者 ### 導入 NavigationRoute 並連接 ViewModel * 在 `LoginRoute` 中初始化 ViewModel 並傳入 LoginScreen * 將 viewModel.state 作為畫面狀態參數 * 使用 named parameter 傳入 `onAction = viewModel::onAction` ### MainActivity 中設定畫面內容 * 在 Scaffold 中呼叫 NavigationRoute * 使用 Surface 包裝並處理內部 padding * 測試前需在真實裝置或已登入 Google 帳號的模擬器中執行 ### 畫面互動測試確認 * 可輸入自訂使用者名稱與密碼 * 切換註冊模式開關動作正常 * 點擊註冊按鈕會跳出 Credential Manager 儲存提示視窗 * 點擊儲存後目前尚未導向登入成功畫面(邏輯尚未實作) ### 新增登入功能至 AccountManager * 建立 `signIn()` 函式用於從 Credential Manager 取得已儲存的帳號密碼 * 不需要傳入帳號密碼參數,從 Credential Manager 自動取得 * 建立新的 sealed interface `SignInResult`,多一個 `NoCredentials` 狀態 * 使用 try-catch 處理例外,包括取消、無憑證、一般錯誤 * 成功時從 `PasswordCredential` 中取得帳號與密碼,回傳 `SignInResult.Success` * 若實作後端,需以此憑證發送 API 登入請求驗證帳號密碼 ### 擴充 LoginAction 與 ViewModel 邏輯 * `LoginAction` 新增 `OnSignIn(SignInResult)` * 在 ViewModel 中擴充 `onAction`,處理 `SignInResult` 狀態 * 根據不同結果更新錯誤訊息或設定 `loggedInUser` * 處理 `NoCredentials` 狀態顯示提示訊息 ### 登入畫面自動偵測已儲存憑證 * 在 `LoginScreen` 中加入 `LaunchedEffect(true)` 檢查憑證 * 呼叫 `accountManager.signIn()` 並根據結果觸發 `onAction` * 另加一個 `LaunchedEffect` 監聽 `loggedInUser` 是否變化 * 當使用者成功登入時觸發 `onLoggedIn(username)` Lambda ### 顯示錯誤訊息於畫面 * 在 UI 中檢查 `state.errorMessage` 是否為 null * 若不為 null,顯示錯誤文字並使用錯誤配色 ### 在 NavigationRoute 中處理登入成功導覽 * 傳入 `onLoggedIn` Lambda 至 `LoginScreen` * 成功登入後導向 `LoggedInRoute(username)` * 使用 `popUpTo` 將 `LoginRoute` 從 back stack 移除 ### 實際測試與登入流程確認 * 重新啟動 App 時會立即觸發 Credential Manager 登入提示 * 若成功登入會導向顯示 Hello 使用者名稱的畫面 * 登出後仍會保留憑證,下一次可再次自動登入 * 可切換帳號並儲存新憑證以進行測試 ### 網站與 App 共用憑證的支援 * 若需讓網站與 App 共用登入憑證,需設置 Digital Asset Links * 建立 `assetlinks.json` 並上傳至網站 `.well-known/assetlinks.json` 路徑 * Json 內容需包含 App 的 package name 與 SHA-256 簽章指紋 * Google 會驗證 Json 檔案是否正確對應至裝置上已安裝的 App * 實作後可實現網站與 App 互通憑證登入功能 ### 注意事項與補充 * `AccountManager` 因含 `Activity` 引用不可放在 ViewModel 中避免記憶體洩漏 * Credential Manager 僅提供憑證,真正登入驗證仍需透過後端處理 * 測試需在已登入 Google 帳戶的實體裝置或模擬器上進行 ### Terminology * **Credential Manager**:Android 提供的 API,用於儲存與取得用戶的登入憑證如帳號密碼或 Passkey。 * **Username/Password Authentication**:傳統的帳密驗證方式,使用者輸入帳號密碼以登入系統。 * **Passkey**:以公開金鑰為基礎的新型態認證方式,可透過指紋、臉部辨識等方式快速登入,提升安全性與便利性。 * **Composable**:Jetpack Compose 中的函式,用來建構 UI 元件。 * **NavHost**:Jetpack Compose Navigation 元件,負責管理與顯示不同的導航畫面。 * **NavController**:用於控制 Jetpack Compose Navigation 的導航流程。 * **RememberNavController**:Jetpack Compose 中用於記住與管理 NavController 狀態的函式。 * **Serializable**:標示一個物件可序列化,常用於 Compose Navigation 傳遞參數。 * **CreatePasswordRequest**:Credential Manager 提供的 API 用於建立帳密型憑證的請求。 * **CreateCredentialRequest**:用來建立與儲存憑證的請求資料。 * **Credential**:通用憑證資料結構,包含帳號、密碼、Passkey 等形式。 * **CredentialManager.createCredential()**:Credential Manager API,用於儲存使用者的登入憑證。 * **Exception Handling**:處理例外狀況以避免應用程式閃退或出錯。 * **CreateCredentialException**:Credential 建立失敗時丟出的例外。 * **CreateCredentialCancellationException**:用戶取消憑證儲存時丟出的例外。 * **Try-Catch Block**:Kotlin 語法結構,用來捕捉並處理例外。 * **Sealed Interface**:限制型介面,可定義一組明確的子類別,適用於結果分類。 * **Data Class**:Kotlin 中專為儲存資料設計的類別,自動產生常用函式如 equals/hashCode。 * **Coroutine**:Kotlin 的非同步程式處理機制,透過 suspend 函式配合使用。 * **Suspend Function**:可掛起執行、用於非同步邏輯的 Kotlin 函式。 * **Activity Context**:Android 元件提供的上下文,用來操作 UI 或存取系統服務。 * **Composable Navigation**:Jetpack Compose 提供的類型安全導覽架構。 * **ViewModel**:用來管理 UI 狀態與邏輯,與 UI 元件解耦。 * **State Management**:管理應用程式內部資料狀態的方式。 * **MutableState**:Compose 中可觀察的狀態變數,用於 UI 重組。 * **LiveData**:舊版 Android 架構元件中的可觀察資料容器,現多改用 State。 * **Jetpack Compose**:Google 提供的現代化 Android UI 開發工具。 * **Gradle Plugin**:用於擴充 Gradle 功能的模組,如 Kotlin 序列化插件。 * **build.gradle**:Gradle 組態檔案,定義專案相依性與設定。 * **Dependency Block**:Gradle 中宣告第三方函式庫相依性的區塊。 * **Credential Core Dependency**:提供 Credential Manager 功能的核心函式庫。 * **Compat Dependency**:用於支援舊版 Android 裝置的相容函式庫。 * **ContextCompat**:Android 支援函式庫,用於存取兼容性的系統資源。 * **Kotlin Serialization**:Kotlin 官方提供的序列化工具,用於資料格式轉換。 * **Modifier.fillMaxSize()**:Compose 中設計元件填滿整個容器的方法。 * **Alignment.Center**:Compose 中 UI 元件對齊設定,讓內容置中顯示。 * **Text()**:Jetpack Compose 中顯示文字的元件。 * **TextFieldValue**:Compose 中用來表示文字輸入欄位的資料結構。 * **Boolean Flag**:用來控制條件分支與邏輯切換的布林值變數。 * **UI State**:用來描述 UI 當前的狀態資料。 * **Composable Function Parameter**:Composable 函式的參數,用於傳遞資料與事件。 * **Navigation Graph**:定義所有可能導航目的地與邏輯的結構圖。 * **BackstackEntry**:導航返回堆疊中的一個項目,包含當前目的地的資訊。 * **Type-safe Navigation**:透過 Kotlin 資料類別與序列化建立的安全導航邏輯。 * **Bundle Argument**:傳遞給 Fragment 或導航目的地的資料包。 * **Route**:在 Compose Navigation 中的畫面識別符號。 * **Composable Route Mapping**:將特定 route 對應到 Composable 畫面。 * **Login State**:表示登入畫面目前的狀態資訊。 * **Error Message**:顯示於 UI 上的錯誤提示訊息。 * **Login/Register Toggle**:用戶介面中控制登入與註冊模式的開關。 * **Safe Credential Handling**:以安全方式處理使用者憑證,避免明文儲存或洩漏。 * **Cross-device Credential Sync**:憑證跨裝置同步的能力,如 Google 帳戶提供。 * **Auto Fill Prompt**:由 Credential Manager 彈出的自動填入視窗。 * **Credential Retrieval**:從 Credential Manager 中讀取已儲存的使用者憑證。 * **Fingerprint Authentication**:生物辨識登入方式之一,整合於 Credential Manager。 * **LoginAction**:在 MVI 架構中代表使用者行為的密封介面,每個使用者操作對應一個 Action。 * **Sealed Interface**:Kotlin 限制型介面,可定義固定集合的子型別,常用於狀態或事件表示。 * **OnSignUp**:封裝使用者按下註冊按鈕的操作,並附帶註冊結果的資料類別。 * **OnUsernameChange**:表示使用者輸入或修改使用者名稱的行為。 * **OnPasswordChange**:表示使用者輸入或修改密碼的行為。 * **OnToggleIsRegister**:代表切換登入與註冊模式的操作。 * **MVI(Model-View-Intent)**:一種 UI 架構,強調單向資料流與不可變狀態。 * **LoginScreen**:Compose 的畫面函式,用來呈現登入與註冊 UI。 * **LoginState**:儲存登入畫面的所有 UI 狀態資料的資料類別。 * **onAction Lambda**:UI 將使用者操作傳遞給 ViewModel 的方法。 * **rememberCoroutineScope**:取得 Compose 中作用域的記憶函式,用於啟動 Coroutine。 * **LocalContext.current**:Compose 提供的 API,用來取得當前 Context。 * **ComponentActivity**:Android 中可用於 Jetpack Compose 的基本 Activity 類型。 * **Memory Leak**:記憶體洩漏問題,通常因未釋放資源造成。 * **Activity Lifecycle**:Android Activity 的生命週期階段,如 onCreate/onDestroy。 * **Garbage Collector (GC)**:JVM 自動記憶體管理機制,負責清除無參考的物件。 * **Column**:Compose 中用來垂直排列子元件的佈局容器。 * **Modifier.fillMaxSize()**:將元件擴展至填滿父容器的大小。 * **padding(dp)**:設定元件內部的邊距。 * **verticalArrangement.spacedBy()**:設定子項垂直間距。 * **TextField**:用來輸入文字的 Compose 元件。 * **TextFieldValue**:表示文字輸入欄位內容的資料結構。 * **Spacer**:在 Compose 中用來插入間距的空白元件。 * **Switch**:布林開關元件,用於代表二元選擇(如註冊/登入模式)。 * **Button**:可點擊的 Compose 按鈕元件。 * **CoroutineScope.launch**:啟動一個 Coroutine 協程來執行非同步作業。 * **if (isRegister)**:根據註冊模式切換 UI 顯示或行為的條件判斷。 * **SignUpResult**:代表註冊操作結果的密封介面,有成功、失敗、取消三種狀態。 * **ViewModel**:MVVM 架構中負責處理邏輯與儲存狀態的類別。 * **mutableStateOf**:建立可觀察的狀態變數,當其改變會重新組合 UI。 * **private set**:Kotlin 屬性限制寫入權限,只允許在類別內部修改。 * **copy()**:Kotlin 資料類別內建的方法,用來複製物件並更新部分欄位。 * **when 表達式**:Kotlin 的條件控制語法,用於取代大量的 if-else。 * **Composable Navigation Host**:Compose 的導覽容器,負責根據狀態切換畫面。 * **navigationRoute**:集中處理畫面導航與狀態分派的函式。 * **MainActivity**:Android 應用的主要 Activity,承載整個 UI。 * **Surface**:Compose 中提供材質與樣式外觀的容器元件。 * **Scaffold**:Compose 中的佈局框架,提供標題欄、底部欄與內容區域。 * **innerPadding**:Scaffold 提供的內容內距,用來處理系統邊界。 * **Alt + Enter**:Android Studio 的快速修正快捷鍵。 * **Device with Google Account**:Credential Manager 運作所需,裝置需登入 Google 帳號。 * **Login Success Navigation**:登入成功後切換畫面的邏輯處理。 * **Activity Context 使用限制**:僅應在 UI 層級中使用,不應傳入 ViewModel 以避免記憶體洩漏。 * **State Lifting**:將狀態往上傳遞以便共用,常見於 Compose 架構。 * **LoginViewModel.onAction()**:ViewModel 接收來自 UI 的行為並更新狀態的主函式。 * **Result Handling**:處理封裝結果型別的方式,通常與 when 表達式結合使用。 * **Composable Injection**:在 Composable 中使用 remember 等方法注入依賴。 * **UI Recomposition**:Compose 中 UI 會根據 State 變化自動重新繪製的機制。 * **LoggedInUser**:表示成功登入後的使用者名稱資料,用於登入後畫面顯示。 * **isRegister Boolean**:控制 UI 是登入還是註冊模式的狀態變數。 * **CredentialManager SignUp**:使用者註冊時將憑證儲存至 Credential Manager 的呼叫。 * **Context.toActivity()**:將 Context 轉型為 Activity 以存取特定功能。 * **Navigation Controller Integration**:將 ViewModel 狀態變化與導覽邏輯結合的過程。 * **SignInResult**:密封介面,表示登入操作的各種結果,如成功、取消、失敗或無憑證。 * **NoCredentialException**:當 Credential Manager 無可用憑證時拋出的例外。 * **GetCredentialException**:通用的憑證擷取錯誤,通常是網路錯誤或格式錯誤造成。 * **CredentialManager.getCredential()**:Credential Manager API,用於取得已儲存的使用者憑證。 * **GetCredentialRequest**:構造用於查詢憑證的請求類別。 * **GetPasswordOption**:指定憑證查詢類型為密碼的選項。 * **CredentialResponse**:從 Credential Manager 取得的回應物件,內含憑證。 * **PasswordCredential**:代表已儲存帳密資訊的資料類別。 * **username/password combination**:使用者的帳號與密碼配對,常見於登入流程中。 * **API Call**:與後端伺服器的通訊呼叫,用以驗證登入資訊。 * **Backend Verification**:由伺服器負責驗證帳密是否正確的邏輯。 * **OnSignIn**:使用者執行登入操作時所產生的 Action。 * **LoginViewModel SignIn Handling**:ViewModel 接收登入結果並更新狀態的邏輯處理。 * **LaunchEffect(true)**:Compose 的副作用函式,當組合出現時立即執行。 * **Auto Sign-In Prompt**:應用啟動時,自動檢查並顯示登入提示。 * **LoggedInUser State Change**:登入使用者變更時觸發的狀態更新與 UI 流程。 * **OnLoggedIn Lambda**:登入成功後要執行的邏輯,例如跳轉畫面。 * **ErrorMessage UI**:在畫面上顯示登入/註冊錯誤訊息的區塊。 * **MaterialTheme.colorScheme.error**:Material Design 樣式下的錯誤訊息顏色。 * **PopUpToInclusive**:Compose Navigation 中將先前路由從返回堆疊中移除的設定。 * **Cross-App Install Credential Sync**:憑證在不同安裝之間仍可共用的特性。 * **App Uninstall Retention**:應用刪除後仍保留憑證的能力。 * **Multiple Account Handling**:Credential Manager 支援多帳戶的儲存與選擇。 * **Register With Different Account**:支援使用新帳號再次註冊與儲存憑證的流程。 * **Backstack Navigation Cleanup**:導航至新畫面後清除歷史紀錄以防返回登入頁。 * **Digital Asset Links**:將網站與 Android 應用程式連結的安全機制。 * **assetlinks.json**:放置於網站特定路徑的 JSON 檔案,用於數位資產連結驗證。 * **SHA-256 Fingerprint**:Android 應用的簽章指紋,用於驗證應用與網站的關聯性。 * **well-known Path**:Web 標準中用來存放標準設定檔的目錄路徑。 * **Google Smart Lock**:提供密碼管理與自動登入功能的 Google 服務。 * **Sign-In Prompt Dialog**:Credential Manager 彈出的使用者登入選擇視窗。 * **Secure Credential Handling**:確保憑證在本地儲存與傳輸過程中的安全性。 * **App and Website Credential Sync**:網站與應用間同步使用憑證的能力。 * **Cross-Device Credential Sharing**:在多個裝置間共用登入資訊的功能。 * **CredentialManager Integration**:將 Credential Manager 與應用登入流程整合。 * **PasswordCredential Casting**:將取得的憑證轉型為密碼憑證以存取帳密。 * **onLoggedIn Flow Control**:根據登入成功與否進行流程導引的機制。 * **Authentication State Update**:登入或註冊結果變更 UI 狀態的邏輯。 * **Compose Navigation Arguments**:用來傳遞登入使用者資訊至下一個畫面的資料。 * **Nullable Error Handling**:處理可為空錯誤訊息的 UI 顯示邏輯。 * **Text Error Feedback**:在畫面顯示操作失敗或錯誤的即時文字提示。 * **Package Name Linking**:在數位資產 JSON 檔案中定義對應的 Android 應用套件名稱。 * **Credential Selection UX**:使用者體驗上的憑證選擇流程設計。 * **AccountManager UI Safety**:僅於 UI 層操作具有 Context 的元件以避免記憶體洩漏。 * **Credential Storage Lifecycle**:憑證的儲存生命週期與存取條件。 * **Secure Sign-In UX**:安全且流暢的登入使用者體驗設計原則。 * **Recomposable Flow Trigger**:當狀態更新時觸發的 Compose UI 重組行為。 * **Well-Defined Login Entry Point**:登入流程的明確啟動邏輯與條件判斷。 * **Stateless Sign-In Request**:登入請求不保存狀態,憑據來自 Credential Manager。 * **Fallback to Manual Input**:若無憑證時允許使用者自行輸入帳密作為備案。