在 Android 上初始化主畫面

使用任何 Android Home API 之前,必須先在應用程式中初始化住家。在這個步驟中,您將為當地特色建立 Home 的單例模式執行個體。

一次只能有一個 Home 執行個體處於啟用狀態。

這是 Home API 的進入點,也涉及宣告要與 Device & Structure 和 Automation API 搭配使用的特徵和裝置類型。如果您剛開始使用 Google Home 生態系統,不確定要註冊哪些特徵或裝置類型,本指南提供了一些最常見的建議。

建立 Home 執行個體

首先,請將這些套件匯入應用程式:

import android.content.Context
import com.google.home.FactoryRegistry
import com.google.home.HomeConfig
import com.google.home.Home

如要初始化 Home API,請按照下列步驟操作:

  1. 取得 Application 結構定義的參照。這個結構定義不依附於任何活動生命週期,只要應用程式處於運作狀態,就會持續存在。您可以在 Activity 或 Service 中呼叫 getApplicationContext() 來取得:

    val context = getApplicationContext()
    
  2. 建立 FactoryRegistry 執行個體,並加入您要在應用程式中使用的所有特徵和裝置類型。

    本指南建議了一些常見的裝置類型 (燈具、插座、感應器、開關和溫度控制器) 和自動化動作特性 (在家狀態和 Google 助理),供不確定需求的使用者參考。詳情請參閱「註冊特徵和裝置類型」。

    val registry = FactoryRegistry(
      traits = listOf(
                AirQuality,
                AreaAttendanceState,
                AreaPresenceState,
                AssistantBroadcast,
                AssistantFulfillment,
                BooleanState,
                ColorControl,
                ExtendedColorControl,
                FlowMeasurement,
                IlluminanceMeasurement,
                LevelControl,
                Notification,
                OccupancySensing,
                OnOff,
                RelativeHumidityMeasurement,
                Switch,
                TemperatureMeasurement,
                Thermostat),
      types = listOf(
                AirQualitySensorDevice,
                ColorDimmerSwitchDevice,
                ColorTemperatureLightDevice,
                ContactSensorDevice,
                DimmableLightDevice,
                DimmablePlugInUnitDevice,
                DimmerSwitchDevice,
                ExtendedColorLightDevice,
                FlowSensorDevice,
                GenericSwitchDevice,
                HumiditySensorDevice,
                LightSensorDevice,
                OccupancySensorDevice,
                OnOffLightDevice,
                OnOffLightSwitchDevice,
                OnOffPluginUnitDevice,
                OnOffSensorDevice,
                SpeakerDevice,
                TemperatureSensorDevice,
                ThermostatDevice))
    

    您必須為此處註冊的每個特徵和裝置類型匯入陳述式 (Android Studio 應會提示您新增這些陳述式)。

  3. 使用 Dispatchers.IO 協同程式內容和登錄檔例項,將 HomeConfig 例項化。

    val homeConfig = HomeConfig(
            coroutineContext = Dispatchers.IO,
            factoryRegistry = registry)
    
  4. 最後,使用結構定義和 HomeConfig 建立 Home 的單例執行個體,這是 API 的進入點。

    val homeManager: HomeClient = Home.getClient(context, homeConfig)
    

為避免無效工作階段發生錯誤,請務必將 Home 包裝在物件宣告中,只建立一個單例模式執行個體。

舉例來說,範例應用程式會這樣做:

internal object HomeClientModule {
  @Provides
  @Singleton
  fun provideHomeClient(@ApplicationContext context: Context): HomeClient {
    return Home.getClient(
      context,
      HomeConfig(
        coroutineContext = IODispatcherModule.provideIoDispatcher(),
        factoryRegistry = registry,
      ),
    )
  }
}

由應用程式啟動 Google 登入

您可能需要在應用程式中管理使用者的 Google 驗證。這樣一來,您就能在 Google Home、雲端硬碟、地圖等各種 Google 服務中使用同一個使用者帳戶。

透過應用程式啟動的 Google 登入功能,您可以取得明確與特定使用者連結的 HomeClient 執行個體,在帳戶已獲得授權時,略過 Google 帳戶挑選器和同意畫面。

此外,這種做法可避免使用者看到兩個不同的帳戶選取畫面,一個來自應用程式登入,另一個來自 Google Home。

如要這麼做,請參閱「使用『使用 Google 帳戶登入』功能驗證使用者」,並完成下列步驟:

建立 OAuth 網頁應用程式用戶端 ID

  1. 開啟 Google Cloud 控制台
    • 前往 Google Cloud 控制台的「憑證」頁面。
    • 選取現有專案或建立新專案。
  2. 設定 OAuth 同意畫面 (如果尚未設定)
    • 建立憑證前,請確認 OAuth 同意畫面已設定應用程式詳細資料,包括隱私權政策和服務條款網址。
  3. 建立 OAuth 用戶端 ID (網頁應用程式類型)
    • 在「Credentials」(憑證) 頁面上,按一下 + CREATE CREDENTIALS,然後從下拉式選單中選取「OAuth client ID」(OAuth 用戶端 ID)。
    • 在「應用程式類型」部分,選取「網頁應用程式」。
    • 輸入網頁用戶端的名稱,例如「My App Web Backend」。
    • 按一下「建立」。
  4. 擷取用戶端 ID
    • 建立完成後,控制台會顯示新的用戶端 ID。您將在 Android 應用程式中使用這個值 (例如「{project number}-.....apps.googleusercontent.com」)
    • 建議您將用戶端 ID 儲存在外部 (例如 build.gradle),而非直接硬式編碼

將 Google 登入要求例項化

使用網頁應用程式 ID 建立 Google 登入要求:

// Your Google Cloud console Web Client ID for Google Sign-In
val serverClientId = BuildConfig.DEFAULT_WEB_CLIENT_ID

// Build the request for Google ID token
val googleIdOption = GetGoogleIdOption.Builder()
    .setFilterByAuthorizedAccounts(false) // Show all Google Accounts on the device
    .setServerClientId(serverClientId) // embed WebClientID in token
    .build()

// Build the GetCredentialRequest
val request = GetCredentialRequest.Builder().addCredentialOption(googleIdOption).build()

建立「使用 Google 帳戶登入」流程

如要實作登入流程,請使用 CredentialManager 執行 Sign in with Google 要求。使用者選取帳戶後,請從產生的 Google ID 權杖中擷取電子郵件地址,建立 android.accounts.Account。接著,這個帳戶會用來初始化與該登入使用者特別連結的 HomeClient 執行個體。

  try {
    // CredentialManager is responsible for interacting with various credential providers on the device
    val credentialManager = CredentialManager.create(context)
    // Credential returns when user has selected an account and the getCredential call completes
    val result = credentialManager.getCredential(context = context, request = request)
    val credential = result.credential

    if (
      credential is CustomCredential &&
      credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL
    ) {
      try {
        val googleCredential = GoogleIdTokenCredential.createFrom(credential.data)
        googleCredential.id.let { userEmail ->
          Log.i(TAG, "Email found in Google ID Token: $email")
          /*
           Why "com.google"?
           The string "com.google" is a standard identifier used in Android's android.accounts.
           Account system to represent accounts managed by Google. This is often used when
           interacting with Android's Account Manager or when using Google-specific APIs. So,
           even if the email ends in "@gmail.com", the underlying account type or provider is
           still considered "com.google" within the Android system.
          */
          val account = Account(userEmail, "com.google")
          Log.d(TAG,"Switched account to : $userEmail")
          // Get the new Home Client Instance with the userEmail
        }
        Log.i(TAG, "Account switch complete. Emitting navigation event.")
      } catch (e: Exception) {
        Log.e(TAG,"Could not convert CustomCredential to Google ID Token", e)
      }
    }
  } catch (e: Exception) {
    Log.e(TAG, "Google Sign-In failed with unexpected error", e)
  }

取得新的 HomeClient 執行個體

請按照「建立 Home 執行個體」一節所述的相同步驟操作,但在步驟 4 中,請改為呼叫 Home.getClient(context, userAccount, homeConfig),而非 Home.getClient(context, homeConfig),其中第二個參數是 Lazy<UserAccount>。這會傳回 HomeClientWithProvidedAccount 的例項,這是 HomeClient 的子類別,明確繫結至指定的 Google 帳戶:

val client =
     Home.getClient(
       context = context.applicationContext,
       account =
         lazy {
         // 1. Create the Account object.
           val androidAccount = Account(userEmail,
                                        GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE)
         // 2. Wrap it in UserAccount.GoogleAccount.
           UserAccount.GoogleAccount(androidAccount)
         },
       homeConfig = HomeConfig()
     )

如果指定的使用者未獲得授權,請在 HomeClientWithProvidedAccount 執行個體上呼叫下列方法,提示使用者授予權限:

  1. registerActivityResultCallerForPermissions(),其中包含您要使用的 ActivityResultCaller 參照。
  2. requestPermissions()。這會開啟 GHP 同意畫面,使用者可在該畫面授予權限。

您可以使用 UserAccount 建立 HomeClient,然後呼叫 requestPermissions(),並將 forcePermissionFlow 設為 ForcePermissionFlow.FORCE_LAUNCH,再次啟動同意畫面,讓使用者更新授予的權限:

val client =
     Home.getClient(
       context = context.applicationContext,
       account =
         lazy {
              UserAccount.GoogleAccount(androidAccount)
         },
       homeConfig = HomeConfig()
     )

client.registerActivityResultCallerForPermissions(this)
client.requestPermissions(forcePermissionFlow = ForcePermissionFlow.FORCE_LAUNCH)

如要進一步瞭解如何管理 Home API 權限,請參閱 Permissions API。

使用新的 HomeClient 重新整理整個活動

取得新的 HomeClient 執行個體後,您必須重新整理整個活動,重新訂閱並擷取與這個使用者帳戶相關聯的完整結構、裝置和其他相關資料。

註冊特徵和裝置類型

FactoryRegistry 類別可讓開發人員明確指出應用程式使用的特徵和裝置類型,協助他們最佳化應用程式二進位檔大小。

請注意,權限和工廠註冊資料庫是分開的。因此,如果應用程式可透過權限使用未註冊的特徵和類型,但這些特徵和類型未包含在工廠註冊表中,則無法使用 Automation API 存取,也不會在大量 traits() 或 types() 方法呼叫中傳回。