앱 작업과 Android 위젯 통합

그림 1. GET_EXERCISE_OBSERVATION을 위한 위젯 실행.

대부분 인텐트의 가장 좋은 응답은 사용자에게 간단한 답변이나 간단한 확인 또는 빠른 양방향 환경을 제공하는 것입니다. Google 어시스턴트에 Android 앱 위젯을 표시하여 이러한 종류의 인텐트를 처리할 수 있습니다.

이 가이드에서는 위젯을 사용하여 어시스턴트 사용자 쿼리를 처리하는 방법과 앱 작업 위젯 확장 프로그램 라이브러리를 사용하여 어시스턴트의 위젯 환경을 강화하는 방법을 설명합니다.

이점

위젯은 런처 또는 잠금 화면 같은 Android 표시 경로에 삽입할 수 있는 소형 애플리케이션 뷰입니다. 앱 작업을 사용하면 위젯을 어시스턴트에 표시할 수 있기 때문에 위젯의 영향력을 높일 수 있습니다.

  1. 검색: 사용자의 자연어 쿼리에 대한 응답으로 선제적으로 위젯을 표시합니다.
  2. 참여: 어시스턴트가 잠금 화면과 Android Auto개인 검색결과를 제공하는 등의 핸즈프리 컨텍스트에서 위젯을 표시합니다.
  3. 보관: 사용자가 어시스턴트에 표시된 위젯을 런처에 고정할 수 있습니다. 고정 기능을 사용하려면 위젯 확장 프로그램 라이브러리가 필요합니다.

어시스턴트가 위젯을 표시하는 방법

사용자가 어시스턴트에서 위젯을 호출하는 방법에는 두 가지가 있습니다.

  • 이름으로 위젯을 명시적으로 요청하기 또는
  • 위젯 처리용으로 구성된 내장 인텐트(BII) 또는 맞춤 인텐트를 트리거하는 쿼리를 어시스턴트에게 말하기

명시적 호출

사용자는 설치된 앱의 위젯을 명시적으로 호출하기 위해 어시스턴트에게 다음과 같이 요청할 수 있습니다.

  • "Hey Google, ExampleApp 위젯을 보여 줘."
  • "ExampleApp 위젯."

어시스턴트는 "ExampleApp 위젯입니다." 같은 일반적 소개와 함께 위젯을 표시합니다. 앱 개발자가 아무런 작업을 하지 않아도 기본적으로 어시스턴트는 요청된 위젯을 이러한 방식으로 반환하지만, 이 호출 방법에서는 사용자가 요청할 위젯을 명시적으로 알고 있어야 합니다. 위젯 검색을 단순화하려면 다음 섹션에서 자세히 설명하는 인텐트 처리 메서드를 사용합니다.

인텐트 처리

사용자가 어시스턴트에서 실행하는 자연어 쿼리 처리에 위젯을 사용하면 위젯이 더 쉽게 찾아집니다. 예를 들어 사용자가 "Hey Google, 내가 ExampleApp에서 이번주에 몇 km나 뛰었지?"라고 질문하여 피트니스 앱에서 GET_EXERCISE_OBSERVATION BII를 트리거할 때마다 위젯을 반환할 수 있습니다. 검색 간소화 외에도 위젯을 앱 작업과 통합하면 다음과 같은 이점이 있습니다.

  • 매개변수 액세스: 어시스턴트가 사용자 쿼리에서 추출된 인텐트 매개변수를 위젯에 제공하므로, 맞춤 응답이 가능합니다.
  • 맞춤 TTS 소개: 위젯을 표시할 때 어시스턴트가 안내할 텍스트 음성 변환(TTS) 문자열을 제공할 수 있습니다.
  • 위젯 고정: 어시스턴트가 위젯 근처에 이 위젯 추가 버튼을 표시하므로, 사용자는 위젯을 런처에 쉽게 고정할 수 있습니다.

위젯 구현

인텐트에 위젯 처리를 구현하려면 다음 단계를 따르세요.

  1. 간단한 위젯 만들기에 설명된 단계에 따라 Android 위젯을 구현합니다.
  2. 앱의 shortcuts.xml 리소스 파일에서 처리 세부정보와 BII <parameter> 태그가 포함된 기능에 <app-widget> 요소를 추가합니다. 매개변수를 처리하도록 위젯을 업데이트합니다.
  3. (필수) 위젯 확장 프로그램 라이브러리를 추가하면 어시스턴트가 BII 이름과 매개변수를 위젯에 전달할 수 있습니다. 또한 맞춤 TTS 소개 및 위젯 고정 기능을 사용 설정합니다.

다음 섹션에서는 shortcuts.xml<app-widget> 스키마를 설명합니다.

위젯 스키마

<app-widget> 요소는 shortcuts.xml<capability> 요소 내에서 처리로 정의됩니다. 이러한 요소에서 다음 속성은 선택사항으로 명시되지 않는 한 필수 속성입니다.

Shortcuts.xml 태그포함된 요소속성
<app-widget> <capability>
  • android:identifier
  • android:targetClass
<parameter> <app-widget>
<extra> <app-widget>
  • android:name(TTS에만 해당)
  • android:value(선택사항)

위젯 스키마 설명

<app-widget>

최상위 위젯 처리 요소

속성:

  • android:identifier: 처리에 관한 고유 식별자. 이 값은 <capability> 내에 정의된 <app-widget> 처리 요소와 <intent> 처리 요소 간에 고유해야 합니다.
  • android:targetClass: 인텐트를 처리할 AppWidgetProvider의 전체 클래스 이름

<parameter>

BII 매개변수를 인텐트 <parameter> 값에 매핑합니다. 각 <app-widget> 요소와 관련해 0개 이상의 매개변수를 정의할 수 있습니다. 처리 중에 어시스턴트는 위젯 인스턴스의 추가 항목을 다음 형식을 사용하는 키-값 쌍으로 업데이트하여 매개변수를 전달합니다.

  • 키: 매개변수에 관해 정의된 android:key
  • 값: BII가 사용자의 음성 입력에서 추출한 값

개발자는 연결된 AppWidgetManager 객체에서 getAppWidgetOptions()를 호출하여 이러한 추가 항목에 액세스할 수 있습니다. 그러면 트리거 BII의 이름과 매개변수가 포함된 Bundle이 반환됩니다. 자세한 내용은 매개변수 값 추출을 참고하세요.

BII 매개변수 일치에 관한 자세한 내용은 매개변수 데이터 및 일치를 참고하세요.

<extra>

이 위젯에 맞춤 TTS 소개를 사용해야 한다고 선언하는 선택적 태그입니다. 이 태그에는 다음 속성 값이 필요합니다.

  • android:name: "hasTts"
  • android:value: "true"

샘플 코드

다음 XML에서는 GET_EXERCISE_OBSERVATION BII 기능의 위젯 처리 구성을 보여줍니다.

shortcuts.xml

<capability android:name="actions.intent.GET_EXERCISE_OBSERVATION">
  <app-widget
    android:identifier="GET_EXERCISE_OBSERVATION_1"
    android:targetClass="com.exampleapp.providers.exampleAppWidgetProvider"
    android:targetPackage="com.exampleapp">
    <parameter
      android:name="exerciseObservation.aboutExercise.name"
      android:key="exercisename">
    </parameter>
    <extra android:name="hasTts" android:value="true"/>
  </app-widget>
</capability>

기능별로 여러 <app-widget> 요소를 지정하거나 <app-widget> 요소와 <intent> 요소를 함께 사용할 수 있습니다. 이 접근 방식에서는 사용자가 제공한 다양한 매개변수 조합을 기반으로 맞춤설정된 환경을 제공할 수 있습니다. 예를 들어 사용자가 쿼리에 하차 위치를 지정하지 않으면 승차 위치와 하차 위치를 설정하는 옵션을 보여주는 앱 내 활동으로 사용자를 안내할 수 있습니다. 대체 인텐트 정의에 관한 자세한 내용은 대체 인텐트 섹션을 참고하세요.

매개변수 값 추출

다음 샘플 AppWidgetProvider 클래스에서는 위젯 옵션 Bundle에서 BII 이름과 매개변수를 추출하는 데 비공개 함수 updateAppWidget()이 사용됩니다.

Kotlin

package com.example.exampleapp

//... Other module imports
import com.google.assistant.appactions.widgets.AppActionsWidgetExtension

/**
 * Implementation of App Widget functionality.
 */
class MyAppWidget : AppWidgetProvider() {
    override fun onUpdate(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetIds: IntArray
    ) {
        // There may be multiple widgets active, so update all of them
        for (appWidgetId in appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId)
        }
    }

    private fun updateAppWidget(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetId: Int
    ) {
        val widgetText: CharSequence = context.getString(R.string.appwidget_text)

        // Construct the RemoteViews object
        val views = RemoteViews(context.packageName, R.layout.my_app_widget)
        views.setTextViewText(R.id.appwidget_text, widgetText)

        // Extract the name and parameters of the BII from the widget options.
        val optionsBundle = appWidgetManager.getAppWidgetOptions(appWidgetId)
        val bii = optionsBundle.getString(AppActionsWidgetExtension.EXTRA_APP_ACTIONS_BII) // "actions.intent.CREATE_TAXI_RESERVATION"
        val params = optionsBundle.getBundle(AppActionsWidgetExtension.EXTRA_APP_ACTIONS_PARAMS)
        if (params != null && params.containsKey("dropoff")) {
            val dropoffLocation = params.getString("dropoff")
            // Build your RemoteViews with the extracted BII parameter
            // ...
        }
        appWidgetManager.updateAppWidget(appWidgetId, views)
    }
}

자바

package com.example.exampleapp;

//... Other module imports
import com.google.assistant.appactions.widgets.AppActionsWidgetExtension;

/**
 * Implementation of App Widget functionality.
 */
public class MyAppWidget extends AppWidgetProvider {

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // There may be multiple widgets active, so update all of them
        for (int appWidgetId : appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId);
        }
    }

    private static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {

        CharSequence widgetText = context.getString(R.string.appwidget_text);

        // Construct the RemoteViews object
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.my_app_widget);
        views.setTextViewText(R.id.appwidget_text, widgetText);

        // Extract the name and parameters of the BII from the widget options.
        Bundle optionsBundle = appWidgetManager.getAppWidgetOptions(appWidgetId);
        String bii =
                optionsBundle.getString(AppActionsWidgetExtension.EXTRA_APP_ACTIONS_BII); // "actions.intent.CREATE_TAXI_RESERVATION"
        Bundle params =
                optionsBundle.getBundle(AppActionsWidgetExtension.EXTRA_APP_ACTIONS_PARAMS);

        if (params != null && params.containsKey(("dropoff"))){
            String dropoffLocation = params.getString("dropoff");
            // Build your RemoteViews with the extracted BII parameter
            // ...
        }

        appWidgetManager.updateAppWidget(appWidgetId, views);
    }
}

위젯 확장 프로그램 라이브러리

앱 작업 위젯 확장 프로그램 라이브러리를 활용하면 음성 전달 어시스턴트 경험을 위한 위젯 기능이 개선됩니다. 이 라이브러리를 사용하면 BII 이름 및 사용자 쿼리에서 추출된 인텐트 매개변수를 비롯해 위젯이 트리거 BII로부터 중요한 처리 정보를 수신할 수 있습니다. 이 Maven 라이브러리를 사용하면 각 위젯에 맞춤 TTS(텍스트 음성 변환) 소개를 제공할 수 있고, 이를 통해 어시스턴트는 시각적으로 렌더링된 콘텐츠를 사용자에게 요약해 줄 수 있습니다. 또한 런처 고정도 사용되므로, 사용자는 어시스턴트에 표시된 위젯을 런처 화면에 손쉽게 저장할 수 있습니다.

먼저 앱 모듈의 build.gradle 파일에 있는 종속 항목 섹션에 라이브러리를 추가합니다.

app/build.gradle

dependencies {
    //...
    implementation "com.google.assistant.appactions:widgets:0.0.1"
}

맞춤 소개

위젯 확장 프로그램 라이브러리를 가져왔으면 위젯에 맞춤 TTS 소개를 제공할 수 있습니다. 위젯의 AppWidgetProvider에 정의를 추가하려면 IDE에서 클래스를 열고 위젯 확장 프로그램 라이브러리를 가져옵니다.

Kotlin

import com.google.assistant.appactions.widgets.AppActionsWidgetExtension

자바

import com.google.assistant.appactions.widgets.AppActionsWidgetExtension;
다음으로, 라이브러리를 사용하여 소개 문자열을 정의하고 위젯을 업데이트합니다.
ExampleAppWidget

Kotlin

package com.example.exampleapp

//... Other module imports
import com.google.assistant.appactions.widgets.AppActionsWidgetExtension

/**
 * Implementation of App Widget functionality.
 */
object MyAppWidget : AppWidgetProvider() {
    fun updateAppWidget(
        context: Context?,
        appWidgetManager: AppWidgetManager,
        appWidgetId: Int
    ) {
        val appActionsWidgetExtension = AppActionsWidgetExtension.newBuilder(appWidgetManager)
            .setResponseSpeech("Hello world") // TTS to be played back to the user.
            .setResponseText("Hello world!") // Response text to be displayed in Assistant.
            .build()

        // Update widget with TTS.
        appActionsWidgetExtension.updateWidget(appWidgetId)

        // Update widget UI.
        appWidgetManager.updateAppWidget(appWidgetId, views)
    }
}

자바

package com.example.exampleapp;

//... Other module imports
import com.google.assistant.appactions.widgets.AppActionsWidgetExtension;

/**
 * Implementation of App Widget functionality.
 */
public class MyAppWidget extends AppWidgetProvider {

  static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
    int appWidgetId) {

    AppActionsWidgetExtension appActionsWidgetExtension = AppActionsWidgetExtension.newBuilder(appWidgetManager)
      .setResponseSpeech("Hello world")  // TTS to be played back to the user.
      .setResponseText("Hello world!")  // Response text to be displayed in Assistant.
      .build();

      // Update widget with TTS.
      appActionsWidgetExtension.updateWidget(appWidgetId);

      // Update widget UI.
      appWidgetManager.updateAppWidget(appWidgetId, views);
    }

}

런처 고정

이 라이브러리를 사용하면 어시스턴트에서 위젯과 함께 이 위젯 추가 버튼이 표시됩니다. 고정 기능을 사용하려면 AndroidManifest.xml에 다음 리시버 정의를 추가해야 합니다.

AndroidManifest.xml

<application>
  <receiver android:name="com.google.assistant.appactions.widgets.pinappwidget.PinAppWidgetBroadcastReceiver"
    android:exported="false">
    <intent-filter>
      <action android:name="com.google.assistant.appactions.widgets.COMPLETE_PIN_APP_WIDGET" />
    </intent-filter>
  </receiver>
  <service
    android:name=
    "com.google.assistant.appactions.widgets.pinappwidget.PinAppWidgetService"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
      <action
        android:name="com.google.assistant.appactions.widgets.PIN_APP_WIDGET" />
    </intent-filter>
  </service>
</application>

인벤토리 가용성

인라인 인벤토리 또는 웹 인벤토리를 지원하는 BII는 이러한 인벤토리를 위젯 처리로 확장할 수 있습니다.

인라인 인벤토리

다음 코드 샘플에서는 인라인 인벤토리와 위젯 처리에 구성된 START_EXERCISE BII 기능을 보여줍니다.

shortcuts.xml

<capability
  android:name="actions.intent.START_EXERCISE">
  <app-widget
    android:identifier="START_EXERCISE_1"
    android:targetClass="com.example.exampleapp.StartExerciseAppWidgetProvider">
    <parameter
      android:name="exercise.name"
      android:key="exerciseName"
      app:shortcutMatchRequired="true">
    </parameter>
  </app-widget>
</capability>

<shortcut android:shortcutId="RunningShortcut">
  <intent
    android:action="android.intent.action.VIEW"
    android:targetClass="com.example.exampleapp.StartExcerciseActivity" />
  <capability-binding
    android:capability="actions.intent.START_EXERCISE"
    android:parameter="exercise.name"
    android:value="running;runs" />
</shortcut>

이전 샘플에서는 사용자가 어시스턴트에 “ExampleApp으로 달리기 시작”을 요청하여 이 기능을 트리거하면 <app-widget> 처리의 선택적 번들에 다음 키-값 쌍이 포함됩니다.

  • 키 = “exerciseName”
  • 값 = “RunningShortcut”

웹 인벤토리

웹 인벤토리 및 위젯 처리에 사용 설정된 기능에 관해서는 다음 샘플 코드를 참고하세요.

shortcuts.xml

<shortcuts>
  <capability
    android:name="actions.intent.START_EXERCISE">
    <app-widget
      android:identifier="START_EXERCISE_1"
      android:targetClass="com.example.exampleapp.CreateTaxiAppWidgetProvider">
      <parameter
        android:name="exercise.name"
        android:key="exerciseName"
        android:mimeType="text/*">
        <data android:pathPattern="https://exampleapp.com/exercise/.*" />
      </parameter>
    </app-widget>
  </capability>
</shortcuts>

테스트

Android 스튜디오용 Google 어시스턴트 플러그인 기능인 앱 작업 테스트 도구를 사용하여 실제 기기 또는 가상 기기에서 위젯을 테스트합니다. 테스트 도구를 사용하려면 다음 단계를 따르세요.

  1. 테스트 기기를 실행 중인 앱과 연결합니다.
  2. Android 스튜디오에서 Tools > App Actions > App Actions Test Tool로 이동합니다.
  3. Create preview를 클릭합니다.
  4. Android 스튜디오에서 테스트 기기에 앱을 실행합니다.
  5. 테스트 기기에서 어시스턴트 앱을 사용하여 앱 작업을 테스트합니다. 예를 들어 "Hey Google, 내가 ExampleApp에서 이번주에 몇 km나 뛰었지?"라고 물어볼 수 있습니다.
  6. 원하는 작업 결과인지 앱 동작을 관찰하거나 Android 스튜디오 디버거를 사용하여 확인합니다.

품질 가이드라인

이 섹션에서는 앱 작업을 위젯과 통합할 때의 핵심 요구사항과 권장사항을 중점적으로 설명합니다.

위젯 콘텐츠

  • (필수) 위젯에는 광고를 표시해서는 안 됩니다.
  • 위젯 콘텐츠는 전적으로 인텐트 처리에 중점을 두어야 합니다. 위젯 하나로 여러 인텐트를 처리하거나 관련 없는 콘텐츠를 추가하지 않도록 합니다.

인증 처리

  • (필수) 사용자 흐름을 완료하는 데 사용자 인증이 필요한 경우 사용자가 앱에서 계속 진행해야 함을 설명하는 위젯을 반환해야 합니다. Google 어시스턴트의 인라인 사용자 인증은 앱 작업에 지원되지 않습니다.
  • 앱이 위젯을 통해 데이터를 표시하도록 사용자가 허용하는 경우 개발자는 승인되지 않은 사용자에게 런타임에 오류 위젯을 반환할 수 있습니다.

대체 인텐트

  • (필수) 위젯의 지정된 기능 처리 외에도 항상 대체 <intent>shortcuts.xml에 제공해야 합니다. 대체 인텐트는 필수 <parameter> 값이 없는 <intent> 요소입니다. 그렇게 하면 기능에 정의된 다른 처리 요소에 필요한 매개변수가 사용자 쿼리가 포함되지 않은 경우에도 어시스턴트가 작업을 처리할 수 있습니다. 단, 그 기능에 필요한 매개변수가 없는 경우에는 예외입니다. 이 경우에는 위젯 처리만 필요합니다.
  • 대체 인텐트는 홈 화면이 아닌 앱의 관련 화면으로 앱을 열어야 합니다.

다음 샘플 코드에서는 기본 <app-widget> 처리를 지원하는 대체 <intent>가 포함된 <capability>를 보여줍니다.

shortcuts.xml

<shortcuts>
  <capability
    android:name="actions.intent.CREATE_TAXI_RESERVATION">
    <!-- Widget with required parameter, specified using the "android:required" attribute. -->
    <app-widget
      android:identifier="CREATE_TAXI_RESERVATION_1"
      android:targetClass="com.example.myapplication.CreateTaxiAppWidgetProvider">
      <parameter
        android:name="taxiReservation.dropoffLocation.name"
        android:key="dropoff"
        android:required="true">
      </parameter>
    </app-widget>
    <!-- Fallback intent with no parameters required to successfully execute. -->
    <intent
      android:identifier="CREATE_TAXI_RESERVATION_3"
      android:action="myapplication.intent.CREATE_TAXI_RESERVATION_1"
      android:targetClass="com.example.myapplication.TaxiReservationActivity">
    </intent>
  </capability>
</shortcuts>

Google Play 데이터 공개

이 섹션에는 최신 버전의 위젯 확장 프로그램 라이브러리에서 수집된 최종 사용자 데이터가 나열되어 있습니다.

이 SDK가 개발자 제공 텍스트 음성 변환(TTS) 응답을 보내면 그 응답은 Google 어시스턴트가 어시스턴트의 음성 기술을 사용하여 사용자에게 알려줍니다. 이 정보는 Google에 저장되지 않습니다.

앱 작업에서는 다음 목적으로 클라이언트 앱 메타데이터를 수집할 수도 있습니다.

  • 다양한 SDK 버전의 채택률 모니터링
  • 앱 전반의 SDK 기능 사용량 수량화