Android에서 홈 초기화

Android용 Home API를 사용하기 전에 앱에서 홈을 초기화해야 합니다. 이 단계에서는 로컬 컨텍스트에 싱글톤 인스턴스인 Home를 만듭니다.

한 번에 하나의 Home 인스턴스만 활성화해야 합니다.

이는 Home API의 진입점이며, 기기 및 구조와 자동화 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 인스턴스를 만듭니다.

    이 가이드에서는 필요한 사항을 잘 모르는 경우를 대비해 일반적인 항목 (조명, 플러그, 센서, 스위치, 온도 조절기 기기 유형, 자동화용 재실 및 어시스턴트 특성)을 제안합니다. 자세한 내용은 특성 및 기기 유형 등록을 참고하세요.

    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를 사용하여 API의 진입점인 Home싱글톤 인스턴스를 만듭니다.

    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, Drive, 지도 등 다양한 Google 서비스에서 동일한 사용자 계정을 사용할 수 있습니다.

앱 시작 Google 로그인을 사용하면 특정 사용자에게 명시적으로 연결된 HomeClient 인스턴스를 획득하여 계정이 이미 승인된 경우 Google 계정 선택기와 동의 화면을 우회할 수 있습니다.

또한 이 방법을 사용하면 사용자가 두 개의 서로 다른 계정 선택 화면(앱의 로그인 화면과 Google Home의 로그인 화면)을 보지 않아도 됩니다.

이렇게 하려면 Google 로그인을 사용하여 사용자 인증을 참고하여 다음 단계를 완료해야 합니다.

OAuth 웹 애플리케이션 클라이언트 ID 만들기

  1. Google Cloud 콘솔을 엽니다.
    • Google Cloud 콘솔 사용자 인증 정보 페이지로 이동합니다.
    • 기존 프로젝트를 선택하거나 새 프로젝트를 만듭니다.
  2. OAuth 동의 화면 구성 (아직 구성하지 않은 경우)
    • 사용자 인증 정보를 만들기 전에 개인정보처리방침 및 서비스 약관 URL을 비롯한 앱 세부정보로 OAuth 동의 화면이 구성되어 있는지 확인하세요.
  3. OAuth 클라이언트 ID (웹 애플리케이션 유형) 만들기
    • 사용자 인증 정보 페이지에서 + CREATE CREDENTIALS를 클릭하고 드롭다운 메뉴에서 OAuth 클라이언트 ID를 선택합니다.
    • 애플리케이션 유형으로 웹 애플리케이션을 선택합니다.
    • 웹 클라이언트의 이름을 입력합니다 (예: '내 앱 웹 백엔드')을 사용합니다.
    • '만들기'를 클릭합니다.
  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 인스턴스 가져오기

홈 인스턴스 만들기에 설명된 것과 동일한 단계를 따르되 4단계에서 Home.getClient(context, homeConfig)를 호출하는 대신 Home.getClient(context, userAccount, homeConfig)를 호출합니다. 여기서 두 번째 매개변수는 Lazy<UserAccount>입니다. 그러면 지정된 Google 계정에 명시적으로 연결된 HomeClient의 하위 클래스인 HomeClientWithProvidedAccount의 인스턴스가 반환됩니다.

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. 사용하려는 ActivityResultCaller를 참조하는 registerActivityResultCallerForPermissions()
  2. requestPermissions(). 그러면 GHP 동의 화면이 표시되며, 여기서 사용자는 권한을 부여할 수 있습니다.

UserAccountHomeClient를 만든 다음 forceLaunch==truerequestPermissions()를 호출하여 동의 화면을 다시 실행하여 사용자가 권한 부여를 업데이트하도록 허용할 수 있습니다.

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

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

Home API 권한 관리에 관한 자세한 내용은 Permissions API를 참고하세요.

새 HomeClient로 전체 활동 새로고침

HomeClient 인스턴스가 있으면 전체 활동을 새로고침하여 이 사용자 계정과 연결된 전체 구조, 기기, 기타 관련 데이터를 다시 구독하고 가져와야 합니다.

특성 및 기기 유형 등록

FactoryRegistry 클래스를 사용하면 개발자가 앱에서 사용하는 특성과 기기 유형을 명시적으로 표시하여 앱 바이너리 크기를 최적화할 수 있습니다.

권한과 팩토리 레지스트리는 분리되어 있습니다. 따라서 권한을 사용하여 앱에서 사용할 수 있지만 팩토리 레지스트리에 포함되지 않은 등록되지 않은 특성과 유형은 자동화 API를 사용하여 액세스할 수 없으며 대량 traits() 또는 types() 메서드 호출에서 반환되지도 않습니다.