앱 작업을 통해 Android 앱을 Google 어시스턴트로 확장

1. 개요

앱 작업을 통해 Google 어시스턴트를 사용하여 음성으로 바로 앱 기능으로 이동하고 작업을 완료할 수 있습니다. Android 개발자는 기능 요소를 구현하여 앱 작업을 추가합니다. 이러한 기능은 사용자 음성 요청을 지원하는 앱 기능과 더불어 개발자가 원하는 요청 처리 방식을 어시스턴트에 알려 줍니다.

이 Codelab에서는 앱 작업을 사용한 개발 관련 초급 수준의 개념을 다룹니다. 이전에 Android 앱 및 Android 인텐트를 개발해 본 경험이 있어야 이 Codelab을 따라올 수 있습니다. Android를 처음 사용하는 경우라면 대신 Android 개발자 기초 Codelab 중 하나를 시작하는 것이 좋습니다.

빌드할 내용

이 Codelab에서는 사용자가 음성으로 운동 타이머를 시작하고 중지할 수 있도록 샘플 피트니스 Android 앱에 앱 작업 내장 인텐트(BII) 2개를 추가할 것입니다.

학습할 내용

건강 및 피트니스 카테고리의 BII를 사용하여 어시스턴트를 Android 앱으로 확장하는 방법을 알아봅니다. Android 스튜디오용 Google 어시스턴트 플러그인을 사용하여 BII를 테스트하는 방법도 알아봅니다.

기본 요건

계속하기 전에 현재 환경에 다음 도구가 갖춰져 있는지 확인하세요.

  • git이 설치된 셸 명령어를 실행하는 터미널
  • 최신 버전의 Android 스튜디오
  • [Google Play Console][]에 대한 액세스 권한이 있는 Google 계정
  • Play 스토어에 인터넷 액세스가 가능한 실제 또는 가상 Android 기기

계속하기 전에 Android 스튜디오에 로그인한 Google 계정과 테스트 기기의 Google 앱에 로그인한 Google 계정이 동일한지 확인합니다.

2. 작동 방식 이해

앱 작업은 Google 어시스턴트의 사용자를 Android 앱에 연결합니다. 어떻게 작동하는 것일까요?

사용자가 어시스턴트에 앱을 사용하여 작업을 처리하도록 요청하면 어시스턴트는 해당 쿼리를 앱의 shortcuts.xml XML 리소스에 정의된 앱 작업 capability에 매칭합니다.

Google 어시스턴트가 앱 작업 음성 쿼리를 처리하는 방법을 보여주는 흐름 다이어그램

그림 1. 어시스턴트가 앱 작업 음성 쿼리를 처리하는 방법을 보여주는 흐름 다이어그램

각 기능 요소는 다음을 정의합니다.

  • 인텐트: 기능을 트리거해야 하는 앱 작업 음성 인텐트입니다.
  • 하나 이상의 처리: 어시스턴트가 앱을 실행하고 사용자 음성 요청을 처리하기 위해 생성하는 Android 인텐트 또는 딥 링크입니다. 처리 정의는 사용자 쿼리에서 예상되는 매개변수와 더불어 이러한 매개변수가 실행 안내로 인코딩되는 방식을 지정합니다.

인텐트

자연어 이해(NLU)에서 인텐트는 비슷한 의미를 갖는 사용자 구문의 그룹입니다. Google에서는 앱 작업에 사용할 수 있는 다양한 요청 유형을 다루는 수십 개의 '내장' 인텐트(BII)를 만들었습니다. 예를 들어 어시스턴트는 "피자 주문" 또는 "디저트 메뉴 표시"라는 문구를 ORDER_MENU_ITEM BII와 연결하도록 학습됩니다. 앱 작업을 사용하면 이러한 BII를 활용하여 일반적인 음성 요청을 앱 기능으로 빠르게 확장할 수 있습니다.

처리

사용자 요청이 shortcuts.xml에서 앱 작업을 트리거할 때 Android 활동은 수신되는 Android 인텐트 또는 딥 링크를 감지 및 처리하여 사용자가 원하는 기능을 사용자에게 제공해야 합니다. 결과적으로 어시스턴트가 사용자의 쿼리에 대한 응답으로 앱을 호출하는 음성 기반 사용자 환경이 만들어집니다.

3. 개발 환경 준비

이 Codelab에서는 Android용 피트니스 샘플 앱을 사용합니다. 이 앱을 통해 사용자는 운동 타이머를 시작 및 중지하고 운동 루틴 통계를 확인할 수 있습니다.

기본 파일 다운로드

이 Codelab의 기본 파일을 가져오려면 다음 명령어를 실행하여 GitHub 저장소를 클론합니다.

git clone --branch codelab-start https://github.com/actions-on-google/appactions-fitness-kotlin.git

저장소를 클론한 후 Android 스튜디오에서 엽니다.

  1. Welcome to Android Studio 대화상자에서 Import project를 클릭합니다.
  2. 저장소를 클론한 폴더를 찾아 선택합니다.

Android 애플리케이션 ID 업데이트

앱의 애플리케이션 ID를 업데이트하면 테스트 기기에서 앱을 고유하게 식별할 수 있으며, 앱이 Play Console에 업로드되어도 '패키지 이름 중복' 오류가 발생하지 않도록 방지할 수 있습니다. 애플리케이션 ID를 업데이트하려면 app/build.gradle을 엽니다.

android {
...
  defaultConfig {
    applicationId "com.MYUNIQUENAME.android.fitactions"
    ...
  }
}

applicationId 필드의 'MYUNIQUENAME'을 고유한 값으로 바꿉니다.

기기에서 앱 사용해 보기

앱 코드에 추가 변경사항을 적용하기 전에 샘플 앱의 기능을 파악하면 도움이 됩니다. 개발 환경에서 앱을 테스트하려면 다음 단계를 따르세요.

  1. 가상 또는 실제 Android 테스트 기기를 엽니다.
  2. 어시스턴트 앱이 작동하는지 확인합니다.
  3. Android 스튜디오를 사용하여 기기에서 샘플 앱을 배포하고 실행합니다.

앱을 테스트하려면 다음 단계를 따르세요.

  1. Android 스튜디오에서 Run > Run app을 선택하거나 툴바에서 Runacabcb8f8634af20.png을 클릭합니다.
  2. 가상 기기를 사용하는 경우 Select Deployment Target 대화상자에서 가상 기기를 선택하고 OK를 클릭합니다. 작업은 최소 Android 5(API 수준 21)에서 실행되지만, Android 8(API 수준 26) 이상의 OS 버전을 사용하는 것이 좋습니다.
  3. 앱을 연 후 홈 버튼을 길게 눌러 어시스턴트를 설정하고 작동하는지 확인합니다. 아직 로그인하지 않았다면 어시스턴트에 로그인합니다.
  4. 앱을 다시 엽니다.

운동 통계를 보여주는 피트니스 작업 앱이 열려 있는 휴대전화

그림 2. 운동 통계가 표시된 피트니스 작업 샘플 앱

앱을 간단히 탐색하여 기능을 확인합니다. 달리기 아이콘을 탭하면 운동 타이머가 시작되고 X 아이콘을 탭하면 타이머가 중지됩니다. 이러한 작업이 바로 앱 작업을 사용하여 음성 제어에 대해 사용 설정할 작업입니다.

Google 어시스턴트 플러그인 설치

Google 어시스턴트 플러그인을 사용하면 테스트 기기에서 앱 작업을 테스트할 수 있습니다. 다음 단계를 따라 Android 스튜디오에 추가하세요.

  1. File > Settings(MacOS의 경우 Android Studio > Preferences)로 이동합니다.
  2. 'Plugins' 섹션에서 Marketplace로 이동하여 'Google Assistant'를 검색합니다.
  3. 도구를 설치하고 Android 스튜디오를 다시 시작합니다.

4. 운동 시작 BII 기능 추가

actions.intent.START_EXERCISE BII를 사용하면 사용자는 음성으로 앱을 열고 운동을 시작할 수 있습니다. 이 단계에서는 사용자가 어시스턴트에 피트니스 앱에서 달리기를 시작하도록 요청할 수 있는 기능을 BII에 구현합니다.

기능 정의

어시스턴트는 shortcuts.xml에 정의된 capability 요소를 사용하여 다음 단계에 따라 음성 명령 음성을 처리합니다.

  1. 어시스턴트는 사용자 음성 쿼리를 앱 기능에 정의된 BII에 매칭합니다.
  2. 어시스턴트는 쿼리에서 값을 BII 매개변수로 추출합니다. 각 매개변수는 생성된 Intent에 연결된 Bundle에 추가됩니다.
  3. 어시스턴트는 Intent를 사용하여 앱을 실행함으로써 번들 매개변수에 대한 액세스 권한을 앱에 부여합니다.

START_EXERCISE BII는 exercise.name BII 매개변수를 지원합니다. 개발자는 이 매개변수를 사용하여 사용자가 앱에서 추적할 운동 유형을 지정하도록 할 수 있습니다.

app/src/main/res/xml 샘플 프로젝트 디렉터리에 있는 shortcuts.xml에 이 capability를 추가하여 START_EXERCISE BII를 앱에 추가합니다.

<!-- shortcuts.xml -->

<capability android:name="actions.intent.START_EXERCISE">
  <intent
    android:action="android.intent.action.VIEW"
    android:targetPackage="PUT_YOUR_APPLICATION_ID_HERE"
    android:targetClass="com.devrel.android.fitactions.FitMainActivity">
    <parameter
      android:name="exercise.name"
      android:key="exerciseType"/>
  </intent>
</capability>

PUT_YOUR_APPLICATION_ID_HERE을 이전 단계에서 정의한 고유한 applicationId로 바꿉니다.

위의 샘플 XML은 다음과 같습니다.

  • START_EXERCISE BII의 기능을 선언합니다.
  • 어시스턴트가 앱을 실행하기 위해 생성하는 Android intent를 지정합니다.
    • targetPackagetargetClass 속성은 수신 활동을 지정합니다.
    • parameter 속성은 exercise.name BII 매개변수를 활동에서 수신한 추가 BundleexerciseType에 매핑합니다.

인라인 인벤토리로 BII 매개변수 처리

BII 매개변수는 어시스턴트 사용자 쿼리에서 추출된 요소를 나타냅니다. 예를 들어 사용자가 "Hey Google, ExampleApp에서 달리기 시작해 줘"라고 말하면 어시스턴트는 exercise.name schema.org BII 매개변수로 'run'을 추출합니다. 일부 BII의 경우 앱에서 예상하는 일련의 식별자에 BII 매개변수를 매칭하도록 어시스턴트에 지시할 수 있습니다.

이렇게 하려면 인라인 인벤토리 요소를 BII 매개변수에 결합하면 됩니다. 인라인 인벤토리는 '달리기', '하이킹', '조깅'과 같이 지원되는 BII 매개변수 값과 이에 상응하는 바로가기 ID(예: EXERCISE_RUN)의 집합입니다. 이 인벤토리 결합을 통해 어시스턴트는 원시 쿼리 값 대신 매칭되는 매개변수의 바로가기 ID를 처리 활동에 전달할 수 있습니다.

exercise.name과 같은 일부 BII 매개변수의 경우 작동하려면 인라인 인벤토리가 필요합니다. 이 매개변수를 처리하려면 다음 인벤토리 shortcut 요소를 shortcuts.xml에 추가합니다.

<!-- shortcuts.xml -->

<shortcuts>
  <shortcut
    android:shortcutId="running"
    android:shortcutShortLabel="@string/activity_running">
    <capability-binding android:key="actions.intent.START_EXERCISE">
      <parameter-binding
        android:key="exercise.name"
        android:value="@array/runningSynonyms"/>
    </capability-binding>
  </shortcut>

  <shortcut
    android:shortcutId="walking"
    android:shortcutShortLabel="@string/activity_walking">
    <capability-binding android:key="actions.intent.START_EXERCISE">
      <parameter-binding
        android:key="exercise.name"
        android:value="@array/walkingSynonyms"/>
    </capability-binding>
  </shortcut>

  <shortcut
    android:shortcutId="cycling"
    android:shortcutShortLabel="@string/activity_cycling">
    <capability-binding android:key="actions.intent.START_EXERCISE">
      <parameter-binding
        android:key="exercise.name"
        android:value="@array/cyclingSynonyms"/>
    </capability-binding>
  </shortcut>

  <capability> ... </capability>
</shortcuts>

위 코드에서는 앱의 지원되는 운동 유형(달리기, 걷기, 자전거 타기)에 대한 인라인 인벤토리를 나타내는 세 가지 바로가기를 정의했습니다. 각 바로가기는 다음과 같은 방식으로 기능에 연결됩니다.

  • 기능에 정의된 동일한 START_EXCERCISE BII를 참조하는 각 capability-binding 요소의 android:key 속성
  • exercise.name BII 매개변수에 대한 각 바로가기 키의 parameter-binding 요소

인라인 인벤토리 동의어 추가

위의 인벤토리 바로가기에서 parameter-binding 요소의 android:value 속성은 각 인벤토리 요소의 동의어로 구성된 배열 리소스를 참조합니다. 동의어를 사용하면 '달리기', '조깅', '단거리 경주'와 같은 요소 유형의 변형을 사용하여 동일한 shortcutId를 참조할 수 있습니다. 프로젝트의 array.xml 리소스에 다음 동의어 항목을 추가합니다.

<!-- array.xml -->
<array name="runningSynonyms">
  <item>Run</item>
  <item>Jog</item>
  <item>Jogging</item>
  <item>Sprint</item>
</array>

<array name="walkingSynonyms">
  <item>Walk</item>
  <item>Hike</item>
  <item>Hiking</item>
</array>

<array name="cyclingSynonyms">
  <item>Biking</item>
  <item>Riding</item>
  <item>Pedaling</item>
</array>

수신되는 Android 인텐트 처리

Android 인텐트는 Android가 다른 앱에서 작업을 요청하는 데 사용하는 메시지 객체입니다. 어시스턴트는 트리거된 기능 내 구성 세부정보로부터 인텐트를 생성하여 사용자의 음성 쿼리를 처리합니다. START_EXERCISE 기능의 인텐트를 처리하려면 수신 인텐트 및 BII 매개변수를 처리하도록 FitMainActivity 대상 클래스를 업데이트합니다.

먼저 Intent.handleIntent 함수를 다음 코드로 바꿉니다.

//FitMainActivity.kt

private fun Intent.handleIntent() {
  when (action) {
    // When the BII is matched, Intent.Action_VIEW will be used
    Intent.ACTION_VIEW -> handleIntent(data)
    // Otherwise start the app as you would normally do.
    else -> showDefaultView()
  }
}

다음으로 다음 코드를 사용하여 클래스에 새 handleIntent 함수를 추가합니다.

//FitMainActivity.kt

/**
 * Use extras provided by the intent to handle the different BIIs
 */

private fun handleIntent(data: Uri?) {
  // path is normally used to indicate which view should be displayed
  // i.e https://fit-actions.firebaseapp.com/start?exerciseType="Running" -> path = "start"
  var actionHandled = true

  val startExercise = intent?.extras?.getString(START_EXERCISE)
  // Add stopExercise variable here

  if (startExercise != null){
    val type = FitActivity.Type.find(startExercise)
    val arguments = Bundle().apply {
      putSerializable(FitTrackingFragment.PARAM_TYPE, type)
    }
    updateView(FitTrackingFragment::class.java, arguments)
  } // Add conditional for stopExercise
  else{
   // path is not supported or invalid, start normal flow.
   showDefaultView()

   // Unknown or invalid action
   actionHandled = false
  }
  notifyActionSuccess(actionHandled)
}

위의 Intent.handleIntent 함수에서 ACTION_VIEW가 트리거되면 앱 작업 인텐트 데이터가 handleIntent 함수에 전달됩니다. START_EXERCISE 인텐트에 번들된 BII 매개변수는 intent?.extras?.getString(START_EXERCISE)을 통해 액세스됩니다. 나머지 함수는 선택된 startExercise 피트니스 유형을 표시하도록 FitTrackingFragment를 업데이트합니다.

앱 작업 테스트

Google 어시스턴트 플러그인을 사용하면 앱 작업 개발 도중에 테스트 기기에서 작업을 미리 볼 수 있습니다. 또한 플러그인을 통해 작업의 인텐트 매개변수 값을 조정하여 사용자가 앱에 관한 어시스턴트 요청을 표현할 만한 다양한 방식을 앱이 어떻게 처리하는지 테스트할 수 있습니다.

플러그인으로 앱 작업을 테스트하려면 다음 단계를 따르세요.

  1. Android 스튜디오에서 Run > Run App을 선택하거나 상단 툴바에서 Run 아이콘을 클릭하여 앱을 실행합니다.
  2. Tools > App Actions > Google Assistant > App Actions Test Tool로 이동합니다.
  3. Create Preview를 클릭합니다. 메시지가 표시되면 앱 작업 정책 및 서비스 약관을 검토하고 이에 동의합니다.
  4. actions.intent.START_EXERCISE 내장 인텐트를 선택합니다.
  5. exercise 상자에서 기본 running 값을 그대로 둡니다.
  6. Run App Action을 클릭합니다. 어시스턴트가 앱의 운동 타이머에 딥 링크로 연결되고 타이머가 달리기 유형의 운동을 시작했는지 확인합니다.

START_EXERCISE BII를 사용하여 첫 번째 앱 작업을 구현했습니다. 축하합니다. 다음으로, 사용자가 앱에서 달리기 운동을 중지할 수 있도록 지원해 보겠습니다.

5. 운동 중지 BII 기능 추가

actions.intent.STOP_EXERCISE BII를 통해 사용자는 "Hey Google, ExampleApp에서 달리기 중지해 줘"와 같이 말하여 운동 세션을 중지할 수 있습니다. shortcuts.xml에 두 번째 capability를 추가하여 피트니스 앱에서 이 BII를 구현합니다.

<!-- shortcuts.xml -->

<capability android:name="actions.intent.STOP_EXERCISE">
  <intent
    android:action="android.intent.action.VIEW"
    android:targetPackage="PUT_YOUR_APPLICATION_ID_HERE"
    android:targetClass="com.devrel.android.fitactions.FitMainActivity">
    <!-- Eg. name = "Running" -->
    <parameter
        android:name="exercise.name"
        android:key="stopExercise"/>
  </intent>
</capability>

PUT_YOUR_APPLICATION_ID_HERE을 고유한 applicationId로 바꿉니다.

인라인 인벤토리로 BII 매개변수 처리

이 BII는 START_EXERCISE BII와 동일한 exercise.name 매개변수를 지원하므로, 이를 통해 사용자는 종료하려는 활성 운동을 지정할 수 있습니다. 이를 사용 설정하려면 두 번째 인벤토리 바로가기 요소 집합을 shortcuts.xml에 추가합니다.

<!-- shortcuts.xml -->

<shortcut
  android:shortcutId="running"
  android:shortcutShortLabel="@string/activity_running">
  <capability-binding android:key="actions.intent.STOP_EXERCISE">
      <parameter-binding
          android:key="exercise.name"
          android:value="@array/runningSynonyms"/>
  </capability-binding>
</shortcut>

<shortcut
  android:shortcutId="walking"
  android:shortcutShortLabel="@string/activity_walking">
  <capability-binding android:key="actions.intent.STOP_EXERCISE">
      <parameter-binding
          android:key="exercise.name"
          android:value="@array/walkingSynonyms"/>
  </capability-binding>
</shortcut>

<shortcut
  android:shortcutId="cycling"
  android:shortcutShortLabel="@string/activity_cycling">
  <capability-binding android:key="actions.intent.STOP_EXERCISE">
      <parameter-binding
          android:key="exercise.name"
          android:value="@array/cyclingSynonyms"/>
  </capability-binding>
</shortcut>

수신되는 Android 인텐트 처리

앱이 FitMainActivity 클래스를 업데이트하여 수신되는 STOP_EXERCISE Android 인텐트를 처리할 수 있도록 합니다. 먼저 다음과 같이 handleIntent 함수에 변수를 추가하여 STOP_EXERCISE 인텐트 데이터를 보관합니다.

// FitMainActivity.kt

private fun handleIntent(data: Uri?) {
  val stopExercise = intent?.extras?.getString(STOP_EXERCISE)
  //...
}

그런 다음 handleIntent 함수의 조건부 로직을 업데이트하여 STOP_EXERCISE 인텐트를 처리합니다.

// FitMainActivity.kt

private fun handleIntent(data: Uri?) {
  //...
  if (startExercise != null){
    val type = FitActivity.Type.find(startExercise)
    val arguments = Bundle().apply {
      putSerializable(FitTrackingFragment.PARAM_TYPE, type)
    }
    updateView(FitTrackingFragment::class.java, arguments)
  } // Add conditional for stopExercise
  <strong>
  } else if(stopExercise != null){
    // Stop the tracking service if any and return to home screen.
    stopService(Intent(this, FitTrackingService::class.java))
    updateView(FitStatsFragment::class.java)
  }
  </strong>
  //...
}

위의 코드에서는 수신되는 Android 인텐트에서 STOP_EXERCISE BII를 확인하도록 handleIntent 함수를 업데이트했습니다. 해당 BII가 확인되면 함수를 통해 활성 타이머가 중지되고 사용자가 홈 화면으로 돌아갑니다.

앱 작업 테스트

다음 단계를 따라 Google 어시스턴트 플러그인을 사용하여 앱 작업을 테스트하세요.

  1. Android 스튜디오에서 Run > Run App을 선택하거나 상단 툴바에서 Run 아이콘을 클릭하여 앱을 실행합니다.
  2. 앱에서 새로운 '달리기' 운동을 시작합니다.
  3. Android 스튜디오에서 go to Tools > App Actions > Google Assistant > App Actions Test Tool로 이동하여 플러그인을 엽니다.
  4. Create Preview를 클릭합니다.
  5. actions.intent.STOP_EXERCISE 내장 인텐트를 선택합니다.
  6. exercise 상자에서 기본 running 값을 그대로 둡니다.
  7. Run App Action을 클릭합니다. 어시스턴트가 운동을 중지하고 홈 화면으로 돌아가지는지 확인합니다.

6. 다음 단계

축하합니다.

이제 어시스턴트 내장 인텐트를 사용하여 Android 앱을 음성으로 사용하도록 설정하는 방법을 배웠습니다. 이 Codelab에서 학습한 내용은 다음과 같습니다.

  • 사용자가 어시스턴트를 사용하여 특정 앱 기능을 자세히 살펴볼 수 있도록 하는 방법
  • 인라인 인벤토리 사용 방법
  • Google 어시스턴트 플러그인을 사용하여 BII를 테스트하는 방법

다음 단계

이제 피트니스 앱을 추가로 조정해 볼 수 있습니다. 완료된 프로젝트를 참조하려면 GitHub의 기본 브랜치를 참고하세요.

다음은 앱 작업으로 이 앱을 확장하는 방법을 자세히 알아보기 위한 몇 가지 제안사항입니다.

Actions on Google 과정을 계속하려면 다음 리소스를 살펴보세요.

트위터에서 @ActionsOnGoogle을 팔로우하여 최신 소식을 확인하고 #appactions로 트윗을 보내 직접 빌드한 결과물을 공유하세요.

의견 설문조사

마지막으로 이 설문조사를 작성하여 이 Codelab 사용 경험에 관한 의견을 보내주세요.