เริ่มต้นบ้านใน Android

ก่อนใช้ Home API สำหรับ Android คุณต้องเริ่มต้นบ้านในแอป ในขั้นตอนนี้ คุณจะสร้างอินสแตนซ์ Singleton ของ Home สำหรับบริบทเฉพาะรายการ

ควรมี Home เพียงอินสแตนซ์เดียวที่ทำงานอยู่

นี่คือจุดแรกเข้าของ Home API และยังเกี่ยวข้องกับการประกาศลักษณะและประเภทอุปกรณ์ที่คุณต้องการใช้กับ Device & Structure API และ 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 บริบทนี้ไม่ได้ขึ้นอยู่กับวงจรกิจกรรมใดๆ และจะคงอยู่ตราบเท่าที่แอปของคุณยังทำงานอยู่ คุณสามารถรับรหัสได้โดยเรียกใช้ 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)
    

เพื่อหลีกเลี่ยงข้อผิดพลาดเกี่ยวกับเซสชันที่ไม่ถูกต้อง คุณต้องสร้างอินสแตนซ์ Home แบบ Singleton เท่านั้น โดยรวมไว้ในการประกาศออบเจ็กต์

ตัวอย่างเช่น แอปตัวอย่างจะดำเนินการดังนี้

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, ไดรฟ์, Maps และอื่นๆ ได้

เมื่อใช้การลงชื่อเข้าใช้ Google ที่เริ่มต้นโดยแอป คุณจะได้รับอินสแตนซ์ HomeClient ที่เชื่อมโยงกับผู้ใช้รายใดรายหนึ่งอย่างชัดเจน ซึ่งจะข้ามเครื่องมือเลือกบัญชี Google และหน้าจอขอความยินยอมเมื่อบัญชีได้รับสิทธิ์แล้ว

นอกจากนี้ วิธีนี้ยังช่วยป้องกันไม่ให้ผู้ใช้เห็นหน้าจอการเลือกบัญชี 2 หน้าจอที่แตกต่างกัน ซึ่งหน้าจอหนึ่งมาจากการลงชื่อเข้าใช้ของแอป และอีกหน้าจอหนึ่งมาจาก Google Home

โดยคุณต้องดูตรวจสอบสิทธิ์ผู้ใช้ด้วยฟีเจอร์ลงชื่อเข้าใช้ด้วย Google และทำตามขั้นตอนต่อไปนี้

สร้างรหัสไคลเอ็นต์ OAuth สำหรับเว็บแอปพลิเคชัน

  1. เปิดคอนโซล Google Cloud
    • ไปที่หน้าข้อมูลเข้าสู่ระบบของคอนโซล Google Cloud
    • เลือกโปรเจ็กต์ที่มีอยู่หรือสร้างโปรเจ็กต์ใหม่
  2. กำหนดค่าหน้าจอขอความยินยอม OAuth (หากยังไม่ได้ดำเนินการ)
    • ก่อนสร้างข้อมูลเข้าสู่ระบบ โปรดตรวจสอบว่าได้กำหนดค่าหน้าจอขอความยินยอม OAuth ด้วยรายละเอียดของแอป รวมถึง URL ของนโยบายความเป็นส่วนตัวและข้อกำหนดในการให้บริการแล้ว
  3. สร้างรหัสไคลเอ็นต์ OAuth (ประเภทเว็บแอปพลิเคชัน)
    • ในหน้าข้อมูลเข้าสู่ระบบ ให้คลิก + CREATE CREDENTIALS แล้วเลือกรหัสไคลเอ็นต์ OAuth จากเมนูแบบเลื่อนลง
    • ในส่วนประเภทแอปพลิเคชัน ให้เลือกเว็บแอปพลิเคชัน
    • ป้อนชื่อสำหรับไคลเอ็นต์เว็บ (เช่น "แบ็กเอนด์เว็บของแอปฉัน")
    • คลิกสร้าง
  4. ดึงข้อมูลรหัสไคลเอ็นต์
    • หลังจากสร้างแล้ว คอนโซลจะแสดงรหัสไคลเอ็นต์ใหม่ ค่านี้คือค่าที่คุณจะใช้ในแอปพลิเคชัน Android (เช่น "{project number}-.....apps.googleusercontent.com")
    • ขอแนะนำให้จัดเก็บรหัสไคลเอ็นต์ไว้ภายนอก (เช่น ใน build.gradle) แทนที่จะฮาร์ดโค้ดโดยตรง

สร้างอินสแตนซ์คำขอ Google Sign-In

ใช้รหัสเว็บแอปเพื่อสร้างคำขอลงชื่อเข้าใช้ 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) โดยที่พารามิเตอร์ที่ 2 คือ 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() โดยตั้งค่า 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() แบบเป็นกลุ่ม