Inicjowanie domu na urządzeniu z Androidem

Zanim zaczniesz korzystać z interfejsów API Home na Androida, musisz zainicjować dom w swojej aplikacji. W tym kroku utworzysz singletonową instancję Home dla kontekstu lokalnego.

W danym momencie powinna być aktywna tylko 1 instancja Home.

Jest to punkt wejścia do interfejsów API Home. Obejmuje też deklarowanie, których cech i typów urządzeń zamierzasz używać z interfejsami API urządzeń i struktur oraz automatyzacji. Jeśli dopiero zaczynasz korzystać z ekosystemu Google Home i nie wiesz, jakie cechy lub typy urządzeń zarejestrować, w tym przewodniku znajdziesz kilka najpopularniejszych propozycji.

Tworzenie instancji Home

Aby rozpocząć, zaimportuj te pakiety do aplikacji:

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

Aby zainicjować interfejsy API Home:

  1. Uzyskaj odniesienie do Application kontekstu. Ten kontekst nie zależy od cyklu życia działania i będzie aktywny tak długo, jak długo działa aplikacja. Możesz go uzyskać, wywołując getApplicationContext() w Activity lub Service:

    val context = getApplicationContext()
    
  2. Utwórz FactoryRegistry instancję ze wszystkimi cechami i typami urządzeń, których zamierzasz używać w aplikacji.

    W tym przewodniku proponujemy kilka typowych (typy urządzeń Light, Plug, Sensor, Switch i Thermostat oraz cechy presence i Assistant do automatyzacji), jeśli nie wiesz, czego potrzebujesz. Więcej informacji znajdziesz w sekcji Rejestracja cech i typów urządzeń.

    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))
    

    Wymagane są instrukcje importowania każdej zarejestrowanej tu cechy i typu urządzenia (Android Studio powinno wyświetlić prośbę o ich dodanie).

  3. Utwórz instancję HomeConfig używając kontekstu współprogramu Dispatchers.IO i instancji rejestru.

    val homeConfig = HomeConfig(
            coroutineContext = Dispatchers.IO,
            factoryRegistry = registry)
    
  4. Na koniec utwórz singletonową instancję Home, która jest punktem wejścia do interfejsów API, używając kontekstu i HomeConfig.

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

Aby uniknąć błędów związanych z nieprawidłowymi sesjami, ważne jest, aby utworzyć tylko singletonową instancję Home, umieszczając ją w deklaracji obiektu.

Na przykład aplikacja przykładowa robi to w ten sposób:

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

Logowanie przez Google inicjowane przez aplikację

Możesz zarządzać uwierzytelnianiem Google użytkownika w aplikacji. Dzięki temu możesz używać tego samego konta użytkownika w różnych usługach Google, takich jak Google Home, Dysk, Mapy itp.

Dzięki logowaniu przez Google inicjowanemu przez aplikację możesz uzyskać instancję HomeClient powiązaną z konkretnym użytkownikiem, co pozwala pominąć ekran wyboru konta Google i ekran zgody, gdy konto jest już autoryzowane.

Dodatkowo to podejście zapobiega wyświetlaniu użytkownikom 2 różnych ekranów wyboru konta – jednego z logowania w aplikacji, a drugiego z Google Home.

Aby to zrobić, musisz zapoznać się z artykułem Uwierzytelnianie użytkowników za pomocą funkcji Zaloguj się przez Google i wykonać te czynności:

Tworzenie identyfikatora klienta aplikacji internetowej OAuth

  1. Otwórz konsolę Google Cloud.
    • Otwórz stronę Dane logowania w konsoli Google Cloud.
    • Wybierz istniejący projekt lub utwórz nowy.
  2. Skonfiguruj ekran zgody OAuth (jeśli jeszcze tego nie zrobisz).
    • Zanim utworzysz dane logowania, upewnij się, że ekran zgody OAuth jest skonfigurowany z informacjami o aplikacji, w tym z adresami URL polityki prywatności i warunków korzystania z usługi.
  3. Utwórz identyfikator klienta OAuth (typ aplikacji internetowej).
    • Na stronie Dane logowania kliknij + CREATE CREDENTIALS i w menu kliknij Identyfikator klienta OAuth.
    • Jako Typ aplikacji wybierz Aplikacja internetowa.
    • Wpisz nazwę klienta internetowego (np. „Backend internetowy mojej aplikacji”).
    • Kliknij Utwórz.
  4. Pobierz identyfikator klienta.
    • Po utworzeniu konsola wyświetli nowy identyfikator klienta. Jest to wartość, której będziesz używać w aplikacji na Androida (np. "{project number}-.....apps.googleusercontent.com")
    • Zalecamy przechowywanie identyfikatora klienta na zewnątrz (np.w pliku build.gradle), a nie kodowanie go na stałe.

Tworzenie instancji żądania logowania przez Google

Użyj identyfikatora aplikacji internetowej, aby utworzyć żądanie logowania przez 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()

Tworzenie procedury logowania przez Google

Aby zaimplementować procedurę logowania, użyj CredentialManager, aby wykonać żądanie Sign in with Google. Gdy użytkownik wybierze konto, wyodrębnij jego adres e-mail z wynikowego tokena identyfikatora Google, aby utworzyć android.accounts.Account. To konto jest następnie używane do zainicjowania instancji HomeClient powiązanej z tym zalogowanym użytkownikiem.

  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)
  }

Uzyskiwanie nowej instancji HomeClient

Wykonaj te same czynności, które zostały opisane w sekcji Tworzenie instancji Home, ale zamiast wywoływać Home.getClient(context, homeConfig) w kroku 4, wywołaj Home.getClient(context, userAccount, homeConfig), gdzie drugi parametr to Lazy<UserAccount>. Spowoduje to zwrócenie instancji HomeClientWithProvidedAccount, podklasy HomeClient, która jest powiązana z określonym kontem 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()
     )

Jeśli określony użytkownik nie jest autoryzowany, poproś go o zgodę, wywołując te metody w HomeClientWithProvidedAccount instancji:

  1. registerActivityResultCallerForPermissions() z odniesieniem do ActivityResultCaller, którego chcesz użyć.
  2. requestPermissions(). Spowoduje to wyświetlenie ekranu zgody GHP, na którym użytkownik może przyznać uprawnienia.

Możesz utworzyć HomeClient za pomocą UserAccount, a następnie wywołać requestPermissions() z forceLaunch==true, aby ponownie uruchomić ekran zgody i umożliwić użytkownikowi zaktualizowanie uprawnień:

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

client.registerActivityResultCallerForPermissions(this)
client.requestPermissions(forceLaunch= true)

Więcej informacji o zarządzaniu uprawnieniami interfejsów API Home znajdziesz w dokumentacji interfejsu API uprawnień.

Odświeżanie całej aktywności za pomocą nowego HomeClient

Gdy masz nową instancję HomeClient, musisz odświeżyć całą aktywność, aby ponownie zasubskrybować i pobrać pełne struktury, urządzenia i inne istotne dane powiązane z tym kontem użytkownika.

Rejestracja cech i typów urządzeń

Klasa FactoryRegistry pomaga deweloperom zoptymalizować rozmiar pliku binarnego aplikacji, umożliwiając im wyraźne wskazanie, których cech i typów urządzeń używa ich aplikacja.

Pamiętaj, że uprawnienia i rejestr fabryczny są od siebie niezależne. Dlatego niezarejestrowane cechy i typy, które są dostępne dla aplikacji za pomocą uprawnień ale nie są uwzględnione w rejestrze fabrycznym, są niedostępne za pomocą interfejsu API automatyzacji i nie są zwracane w wywołaniach zbiorczych traits() ani types().