Android에서 홈 초기화

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

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

이는 Home API의 진입점이며, 기기 및 구조와 자동화 API에서 사용할 특성 및 기기 유형을 선언하는 작업도 포함됩니다. Google 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)를 호출하는 대신 두 번째 매개변수가 Lazy<UserAccount>Home.getClient(context, userAccount, homeConfig)를 호출합니다. 그러면 지정된 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() 메서드 호출에서 반환되지도 않습니다.