Android 上的 Permissions API

使用任何 Android 版 Home API 之前,應用程式必須取得存取使用者住家裝置的權限,在 API 中稱為住家結構體。使用者可以透過 Permissions API,使用 Google 帳戶授予 Home API 應用程式存取家中裝置的權限。

使用者可以透過權限流程建立住家結構體,並指派街道地址 (如果尚未設定),不必使用 Google Home app (GHA)。

整合 Permissions API

繼續操作前,請務必按照「在 Android 上初始化住家」一節的指示操作。這裡的所有權限範例都會使用該步驟中的 homeManager 執行個體。

首先,請向 SDK 註冊 ActivityResultCaller。舉例來說,範例應用程式的處理方式如下:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    homeManager.registerActivityResultCallerForPermissions(this)
  }

檢查權限

建議您先確認應用程式使用者是否已同意存取住家結構體,再要求權限。如要這麼做,請呼叫 Home 執行個體的 hasPermissions() 方法,取得 PermissionsState 值的 Flow:

val permissionsReadyState =
homeManager.hasPermissions().collect { state ->
    when (state) {
      PermissionsState.GRANTED -> println("Permissions granted, no need to request permissions")
      PermissionsState.PERMISSIONS_STATE_UNAVAILABLE ->
          println("Permissions state unavailable, request permissions")

      PermissionsState.NOT_GRANTED ->
          println("OAuth permission is enabled but not granted yet, request permissions")

      PermissionsState.PERMISSIONS_STATE_UNINITIALIZED -> println(
          "Permissions state is not initialized yet. Clients should wait for another status update"
      )
      else ->
          throw IllegalStateException("""
            HomeClient.hasPermissions state should be PermissionsState.GRANTED,
            PermissionState.PERMISSIONS_STATE_UNINITIALIZED, or
            PermissionsState.PERMISSIONS_STATE_UNAVAILABLE. Actual state: $state
          """.trimIndent())
    }
}

如果檢查傳回 NOT_GRANTED 或 PERMISSIONS_STATE_UNAVAILABLE 的 PermissionsState,表示使用者或應用程式無法存取結構體。如果檢查傳回 GRANTED 的 PermissionsState,但後續對 structures() 的呼叫未傳回任何結構,則表示使用者已透過 GHA 設定頁面撤銷應用程式的存取權,或使用者不具備必要的存取權。

要求權限

您必須授予應用程式權限,才能存取住家和住家中的裝置。

如果使用者尚未授予權限,請使用 Home 執行個體的 requestPermissions() 方法啟動權限 UI 並處理結果:

fun requestPermissions(scope: CoroutineScope, onShowSnackbar: (String) -> Unit) {
  scope.launch {
    val result =
      try {
        homeManager.requestPermissions()
      } catch (e: HomeException) {
        PermissionsResult(
          PermissionsResultStatus.ERROR,
          "Got HomeException with error: ${e.message}",
        )
      }
    when (result.status) {
      PermissionsResultStatus.SUCCESS -> {
        Log.i(TAG, "Permissions successfully granted.")
      }
      PermissionsResultStatus.CANCELLED -> {
        Log.i(TAG, "User cancelled Permissions flow.")
        onShowSnackbar("User cancelled Permissions flow")
      }
      else -> {
        Log.e(
          TAG,
          "Failed to grant permissions with error: ${result.status}, ${result.errorMessage}",
        )
        onShowSnackbar("Failed to grant permissions with error: ${result.errorMessage}")
      }
    }
  }
}

您必須先為應用程式設定 OAuth,才能正確啟動權限使用者介面。

權限同意聲明使用者介面會以 HomeClient.setLocale() 設定的語言代碼顯示,如果未設定,則預設為應用程式或裝置的語言代碼。如要以特定語言顯示同意畫面,請先在 HomeClient 執行個體上呼叫 setLocale(),再呼叫 requestPermissions()。詳情請參閱「設定應用程式語言代碼」。

授予權限

現在,您應該可以執行應用程式,並讓使用者授予權限。可授予權限的使用者類型,以及可授予權限的裝置類型,會因您是否已在 Google Home Developer Console 註冊應用程式而異。

必須註冊 Developer Console,才能使用 Home API 發布應用程式。不需通過認證,即可測試及使用 Home API。

如果應用程式未在 Developer Console 中註冊,則會處於「未驗證」狀態。建議用於測試 Home API:

  • 只有在 OAuth 控制台中註冊為測試使用者的使用者,才能授予應用程式權限。未經驗證的應用程式最多可有 100 位測試使用者。

  • 未經驗證的應用程式可存取 Home API OAuth 支援的任何裝置類型 (Developer Console 中的裝置類型清單)。住家中的所有裝置都會獲得授權。

如果應用程式已在Developer Console 註冊,且已獲准存取一或多種裝置類型,並已完成 OAuth 品牌驗證,就會處於「已驗證」狀態。必須處於這個狀態,才能將應用程式發布至正式版:

  • 測試使用者人數限制不再適用。任何使用者都能授予應用程式權限。
  • 使用者只能授予 Developer Console 中核准的裝置類型權限。

設定 OAuth 後,應用程式對 requestPermissions() 的呼叫會觸發下列對話方塊:

  1. 系統會提示使用者選取要使用的 Google 帳戶。
  2. 系統會提示使用者選取要授予應用程式存取權的住家結構體。
    1. 如果應用程式未經驗證,則可使用 Home API 支援的所有裝置類型。
    2. 如果是已驗證的應用程式,使用者只能授予 Developer Console 中已核准的裝置類型權限。
    3. 如果應用程式可管理敏感裝置類型,使用者可以限制個別裝置的存取權。舉例來說,如果使用者有三個智慧門鎖,可以只授予其中一個門鎖的存取權。
  • OAuth 同意 - 選取帳戶
  • OAuth 同意 - 連結裝置 01
  • OAuth 同意 - 連結裝置 02
圖 1:OAuth 同意流程範例

取得權限後,應用程式就能使用 Home API 讀取住家裝置的狀態並加以控制。如果使用者未授予應用程式特定裝置類型或機密裝置的權限,應用程式就無法使用 Home API 存取、控制或自動化該裝置。

變更權限

如要授予其他結構體中裝置的存取權,可以啟動帳戶挑選器,讓使用者選擇要切換的 Google 帳戶和結構體。在這個過程中,即使使用者先前已授予同意聲明,系統仍會再次顯示同意畫面。

方法是再次呼叫 requestPermissions(),並將 forcePermissionFlow 設為 ForcePermissionFlow.FORCE_LAUNCH:

homeManager.requestPermissions(forcePermissionFlow = ForcePermissionFlow.FORCE_LAUNCH)

使用住家結構體提示變更權限

應用程式要求逐步變更使用者的 Home API 權限時,可透過住家結構體提示預先選取特定結構體,或限制可用結構體清單。只要將住家結構參數傳入授權要求,權限對話方塊就會自動聚焦於所選住家結構,減少使用者不便,並避免設定錯誤。

住家結構體提示是使用 ConsentScreenOptions 資料類別管理。ConsentScreenOptions 類別接受下列設定參數:

  • structureId:在權限對話方塊中預先選取的特定結構 ID。如要取得這項資訊,請查看要更新的結構體結構體屬性。
  • allowedStructureIds:住家結構體 ID 清單。如果提供,權限對話方塊會篩選可用的結構,只顯示這份清單中的結構。在大多數情況下,您可以不指定這個參數,除非您想確保使用者只能存取已授予權限的住家清單。
  • allowStructureChange:決定是否允許使用者變更預先選取的結構。如果至少指定 allowedStructureIds 和 structureId 其中之一,請在多數情況下將此值設為 true,以支援使用者的自然行為。

在 requestPermissions() 呼叫中,將這個物件做為選用參數傳遞,並將 forcePermissionFlow 設為 ForcePermissionFlow.FORCE_LAUNCH:

import com.google.home.ConsentScreenOptions

// Create the ConsentScreenOptions class, allowing structure changes while
// ensuring the permissions dialog pre-selects the target structure on launch
val consentOptions = ConsentScreenOptions(
    structureId = target-structure-id,
    allowStructureChange = true
)

homeManager.requestPermissions(forcePermissionFlow = ForcePermissionFlow.FORCE_LAUNCH, consentScreenOptions = consentOptions)

使用者會看到同意畫面,其中已篩選出 ConsentScreenOptions 物件中註明的結構。

允許使用者透過住家結構體提示切換住家結構體

如果使用者在應用程式中有多個住家結構,而您想預先選取其中一個,同時允許他們在可用的住家結構之間切換,請使用 allowStructureChange 旗標啟用住家結構變更功能,並在 allowedStructureIds 中提供住家結構清單:

val consentOptions = ConsentScreenOptions(
    structureId = target-structure-id,
    allowedStructureIds = listOf(target-structure-id, another-structure-id),
    allowStructureChange = true
)

撤銷權限

使用者可以撤銷先前授予的存取權:

  1. 依序前往 Google 帳戶設定 >「資料和隱私權」>「第三方應用程式和服務」。這會撤銷在授予初始同意聲明時核發的 OAuth 權杖,並撤銷使用者在所有介面 (手機) 和結構中使用的應用程式執行個體存取權。

    使用者可能會透過深層連結,導向「第三方應用程式和服務」子頁面,網址配置如下:

    https://myaccount.google.com/connections/link?project_number=Cloud project_number
    
  2. 依序前往 GHA >「設定」>「連結的應用程式」頁面。 按一下 GHA 中的 ,即可前往「設定」頁面。接著,按一下「連結的應用程式」圖塊,系統會將您導向與同意畫面類似的頁面。使用者可以從這個頁面移除應用程式的存取權,也可以變更應用程式可存取的裝置類型或特定敏感裝置。

刪除及匯出使用者資料

當使用者刪除 Google 帳戶或離開住家結構體時,平台會自動管理後端資料刪除作業:

  • 如果遭刪除的使用者是住家的唯一擁有者,平台會刪除住家和相關聯的裝置。
  • 如果共用住家結構體還有其他擁有者,則該結構體和共用裝置仍會保留給其他成員。平台會移除離職使用者的成員資格,並取消連結任何僅透過該使用者憑證連結的第三方 Cloud-to-cloud 裝置。
  • 刪除帳戶後,系統會立即永久移除使用者的住家結構資料和應用程式權限。即使使用者日後復原 Google 帳戶,住家結構體和權限也不會還原。

Home API 不提供匯出或刪除使用者 Google 帳戶資料的端點。請引導擁有 Google 帳戶的使用者前往 Google 匯出匯出 Google Home 資料,並前往 Google 帳戶設定管理帳戶刪除事宜。

在 Google Home 生態系統中,使用者可以一次授予某個裝置類型中所有裝置的權限。如果是機密或受限制的裝置類型 (例如智慧門鎖、攝影機或門鈴),使用者必須個別授予權限。

如要判斷使用者是否已授予存取機密或受限裝置類型的權限,請使用住家層級的 consentedDeviceTypes() 函式:

import com.google.home.Structure
import com.google.home.DeviceType
import com.google.home.DeviceTypeFactory
import com.google.home.consentedDeviceTypes // Extension function from the SDK
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch

/**
 * Example of how an app may monitor which device types have been granted access by a user.
 */
fun monitorDeviceConsent(structure: Structure, myScope: CoroutineScope) {
    // Obtain the flow of consented device type factories
    val consentedTypesFlow: Flow<Set<DeviceTypeFactory<out DeviceType>>> =
        structure.consentedDeviceTypes()

    myScope.launch {
        consentedTypesFlow.collect { consentedSet ->
            // Check if the user has consented to share a specific restricted
            // type, such as a Doorbell or Camera.
            val hasCameraAccess = consentedSet.any {
                it.toString() == "matter.google.type.GoogleDoorbellDevice"
            }

            if (hasCameraAccess) {
                // Enable features that require camera access
            } else {
                // Inform the user or disable camera-specific features
            }
        }
    }
}

OkGoogle 權限

okGoogle 指令是裝置層級指令,可用於自動化結構中的任何裝置。不過,Home API 應用程式可能無法存取所有裝置。下表說明這類情況下的權限強制執行方式。

自動化 Trait 權限強制執行
晚上 10 點透過臥室音箱廣播「該睡覺了」。 AssistantBroadcastTrait 在裝置上。 建立自動化動作:
  • 廣播裝置必須是 Google 助理裝置。
  • 應用程式和使用者必須有權存取發生廣播的裝置。
自動化動作執行:
  • 應用程式和使用者必須有權存取發生廣播的裝置。
晚上 10 點透過所有裝置廣播「該睡覺了」 AssistantBroadcastTrait :結構。 建立自動化動作:
  • 結構中至少要有一部應用程式和使用者可存取的 Google 助理裝置。
  • 應用程式和使用者必須具備住家結構體的存取權。
自動化動作執行:
  • 應用程式和使用者必須具備住家結構體的存取權。
晚上 10:00 執行「播放音樂」 AssistantFulfillmentTrait.OkGoogleCommand 建立自動化動作:
  • 應用程式和使用者必須有權存取自動化動作發出指令的裝置。
自動化動作執行:
  • 應用程式和使用者必須有權存取自動化動作發出指令的裝置。
只要有人說「播點音樂」 VoiceStarterTrait.OkGoogleEvent 建立自動化動作:
  • 應用程式和使用者必須具備住家結構體的存取權。自動化動作不需要 Google 助理裝置即可通過驗證或執行,因為任何有權存取住家的使用者都可以使用手機 (使用相同的 Google 帳戶) 與 Google 助理互動,並觸發 VoiceStarter。
自動化動作執行:
  • 應用程式不需要權限,即可存取啟動自動化動作的裝置。
  • 應用程式和使用者必須具備權限,才能存取發生動作的裝置。

使用者撤銷完整權限時的指引

如果使用者撤銷完整權限,所有現有自動化動作都會停止運作。此外,如果使用者撤銷特定裝置的存取權,與這些裝置相關聯的啟動條件、限制條件和動作就會停止運作。

每次應用程式啟動時,請務必檢查權限是否仍有效。如果權限已撤銷,請務必移除所有先前的資料,包括應用程式中快取的任何資料。

當住家結構體存取權遭到撤銷時,系統會將 StructureAccessRevokedEvent 傳送至雲端後端。如要瞭解端對端合作夥伴雲端和行動應用程式撤銷工作流程,請參閱「住家結構體授權」。