איך מפעילים את הבית ב-Android

לפני שמשתמשים באחד מממשקי Home API ל-Android, צריך לאתחל את הבית באפליקציה. בשלב הזה, יוצרים מופע singleton של Home עבור ההקשר המקומי.

רק מופע אחד של Home צריך להיות פעיל בכל פעם.

זוהי נקודת הכניסה לממשקי Home API, וכוללת גם הצהרה על המאפיינים וסוגי המכשירים שבהם אתם מתכוונים להשתמש עם ממשקי Device & Structure ו-Automation API. אם אתם רק מתחילים להשתמש במערכת האקולוגית של Google Home ולא בטוחים אילו תכונות או סוגי מכשירים כדאי לרשום, ריכזנו כאן כמה מהתכונות הנפוצות ביותר.

יצירת מופע ביתי

כדי להתחיל, מייבאים את החבילות האלה לאפליקציה:

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

כדי לאתחל את ממשקי ה-API של Home:

  1. קבלת הפניה להקשר של Application. ההקשר הזה לא תלוי במחזור חיים של פעילות כלשהי, והוא יתקיים כל עוד האפליקציה פעילה. אפשר לקבל אותו בשיחה עם getApplicationContext() תוך Activity או Service:

    val context = getApplicationContext()
    
  2. יוצרים מופע של FactoryRegistry עם כל המאפיינים וסוגי המכשירים שרוצים להשתמש בהם באפליקציה.

    במדריך הזה הצענו כמה סוגים נפוצים (סוגי מכשירים: תאורה, שקע, חיישן, מתג, תרמוסטט; מאפיינים של נוכחות ו-Assistant לאוטומציות), למקרה שאתם לא בטוחים מה אתם צריכים. מידע נוסף זמין במאמר בנושא רישום מאפיינים וסוגי מכשירים.

    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. יוצרים מופע של HomeConfig באמצעות קורוטינה Dispatchers.IO ומופע הרישום.

    val homeConfig = HomeConfig(
            coroutineContext = Dispatchers.IO,
            factoryRegistry = registry)
    
  4. לבסוף, יוצרים את מופע singleton של Home, שהוא נקודת הכניסה לממשקי ה-API, באמצעות ההקשר ו-HomeConfig.

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

כדי להימנע משגיאות שקשורות להפעלות לא תקינות, חשוב ליצור רק מופע singleton של Home, על ידי הוספתו להצהרת אובייקט.

לדוגמה, באפליקציה Sample App זה נעשה כך:

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

כניסה באמצעות חשבון Google שמתבצעת מתוך האפליקציה

יכול להיות שתרצו לנהל את אימותי Google של המשתמשים באפליקציה שלכם. הפעולה הזו מאפשרת להשתמש באותו חשבון משתמש בשירותי Google שונים, כמו Google Home, ‏ Drive, ‏ מפות Google וכו'.

בעזרת כניסה לחשבון Google שמתחילה באפליקציה, אפשר לקבל מופע HomeClient שקשור באופן מפורש למשתמש מסוים, וכך לעקוף את הכלי לבחירת חשבון Google ואת מסך ההסכמה אם החשבון כבר קיבל הרשאה.

בנוסף, הגישה הזו מונעת מהמשתמשים לראות שני מסכים שונים לבחירת חשבון – אחד מתוך הכניסה לאפליקציה ואחד מתוך Google Home.

כדי לעשות זאת, צריך לעיין במאמר אימות משתמשים באמצעות 'כניסה באמצעות חשבון Google' ולבצע את השלבים הבאים:

יצירת מזהה לקוח של אפליקציית אינטרנט OAuth

  1. פותחים את מסוף Google Cloud.
    • עוברים לדף Credentials במסוף Google Cloud.
    • בוחרים פרויקט קיים או יוצרים פרויקט חדש.
  2. הגדרת מסך ההסכמה ל-OAuth (אם עדיין לא עשיתם זאת)
    • לפני שיוצרים פרטי כניסה, מוודאים שמסך ההסכמה ל-OAuth מוגדר עם פרטי האפליקציה, כולל כתובות URL של מדיניות הפרטיות והתנאים וההגבלות.
  3. יצירת מזהה לקוח OAuth (סוג אפליקציית אינטרנט)
    • בדף Credentials (פרטי כניסה), לוחצים על + CREATE CREDENTIALS ובוחרים באפשרות OAuth client ID (מזהה לקוח OAuth) מהתפריט הנפתח.
    • ב-Application type בוחרים באפשרות Web application.
    • מזינים שם ללקוח האינטרנט (למשל, My App Web Backend').
    • לוחצים על 'יצירה'.
  4. אחזור מזהה הלקוח
    • אחרי היצירה, מזהה הלקוח החדש יוצג במסוף. זה הערך שתשתמשו בו באפליקציית Android (למשל, {project number}-.....apps.googleusercontent.com)
    • מומלץ לא לקודד את מזהה הלקוח ישירות, אלא לאחסן אותו באופן חיצוני (למשל, ב-build.gradle).

יצירת מופע של בקשה לכניסה באמצעות חשבון Google

משתמשים במזהה של אפליקציית האינטרנט כדי ליצור בקשה לכניסה באמצעות חשבון 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 שמתקבל כדי ליצור 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, אבל במקום להפעיל את Home.getClient(context, homeConfig) בשלב 4, מפעילים את Home.getClient(context, userAccount, 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, שבו המשתמש יכול להעניק את ההרשאה.

אפשר ליצור HomeClient עם UserAccount ואז להתקשר אל requestPermissions() עם forceLaunch==true כדי להפעיל שוב את מסך ההסכמה ולאפשר למשתמש לעדכן את ההרשאות שהוא העניק:

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

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

מידע נוסף על ניהול הרשאות ל-API של Home זמין במאמר בנושא Permissions API.

רענון כל הפעילות באמצעות HomeClient החדש

אחרי שיוצרים מופע חדש של HomeClient, צריך לרענן את כל הפעילות כדי להירשם מחדש ולשלוף את המבנים המלאים, המכשירים ונתונים רלוונטיים אחרים שמשויכים לחשבון המשתמש הזה.

רישום של מאפיינים וסוגי מכשירים

המחלקות FactoryRegistry עוזרות למפתחים לבצע אופטימיזציה של גודל הקובץ הבינארי של האפליקציה. הן מאפשרות להם לציין במפורש אילו מאפיינים וסוגי מכשירים נמצאים בשימוש באפליקציה.

שימו לב שההרשאות והרישום במפעל מופרדים. לכן, לא ניתן לגשת לתכונות ולסוגים לא רשומים שזמינים לאפליקציה באמצעות הרשאות, אבל לא נכללים במאגר המידע של היצרן, באמצעות Automation API, והם גם לא מוחזרים בקריאות לשיטות traits() או types() בכמות גדולה.