Google은 흑인 공동체를 위한 인종 간 평등을 진전시키기 위해 노력하고 있습니다. Google에서 어떤 노력을 하고 있는지 확인하세요.

앱 위젯 호스트 빌드

대부분의 Android 기기에서 사용할 수 있는 Android 홈 화면을 통해 사용자는 앱 위젯을 삽입하고 콘텐츠에 빠르게 액세스할 수 있습니다. 홈 화면 대체 앱 또는 유사한 앱을 빌드하는 경우에는 사용자가 AppWidgetHost를 구현하여 앱 위젯을 삽입하도록 허용할 수도 있습니다. 대부분의 앱에서는 이렇게 해야 할 필요가 없지만 자체 호스트를 만드는 경우 호스트가 암시적으로 동의하는 계약 의무사항을 이해해야 합니다.

이 문서에서는 맞춤 AppWidgetHost의 구현과 관련된 책임을 중점적으로 설명합니다. AppWidgetHost를 구현하는 방법의 예는 Android 홈 화면 런처의 소스 코드를 참조하세요.

다음은 맞춤 AppWidgetHost의 구현과 관련된 키 클래스와 개념의 개요입니다.

  • 앱 위젯 호스트 - AppWidgetHost는 홈 화면과 같이 UI에 앱 위젯을 삽입하고자 하는 AppWidget 서비스와의 상호작용을 제공합니다. AppWidgetHost에는 호스트의 자체 패키지 내에서 고유한 ID가 있어야 합니다. 이 ID는 호스트에서 지속적으로 사용됩니다. ID는 일반적으로 애플리케이션에서 할당하는 하드 코딩 값입니다.
  • 앱 위젯 ID - 각 앱 위젯 인스턴스에는 바인딩시 고유 ID가 할당됩니다(앱 위젯 바인딩에 자세히 설명되어 있는 bindAppWidgetIdIfAllowed() 참조). 고유 ID는 호스트에서 allocateAppWidgetId()를 사용하여 가져옵니다. 이 ID는 위젯의 전체 기간 동안, 즉 위젯이 호스트에서 삭제될 때까지 유지됩니다. 호스트별 상태(예: 위젯의 크기와 위치)는 호스팅 패키지에 의해 유지되고 앱 위젯 ID와 연결되어야 합니다.
  • 앱 위젯 호스트 뷰 - AppWidgetHostView는 위젯이 표시되어야 할 때마다 래핑되는 프레임이라고 생각할 수 있습니다. 위젯이 호스트에 의해 확장될 때마다 앱 위젯이 AppWidgetHostView에 할당됩니다.
  • 옵션 번들 - AppWidgetHost는 옵션 번들을 사용하여 위젯이 표시되는 방식(예: 크기 범위, 위젯이 잠금 화면에 있는지, 홈 화면에 있는지 여부)에 관한 정보를 AppWidgetProvider에 전달합니다. AppWidgetProvider에서는 이 정보를 사용하여 위젯이 표시되는 방식과 위치를 기반으로 위젯의 콘텐츠와 모양을 조정할 수 있습니다. 앱 위젯의 번들을 수정하려면 updateAppWidgetOptions()updateAppWidgetSize()를 사용합니다. 두 메서드 모두 AppWidgetProvider에 콜백을 트리거합니다.

앱 위젯 바인딩

사용자가 호스트에 앱 위젯을 추가하면 바인딩 프로세스가 발생합니다. 바인딩은 특정 앱 위젯 ID를 특정 호스트와 특정 AppWidgetProvider에 연결하는 것을 가리킵니다. 이 작업을 실행하는 데는 앱이 실행되는 Android의 버전에 따라 다양한 방법이 있습니다.

Android 4.0 이하에서 앱 위젯 바인딩하기

Android 버전 4.0 이하를 실행하는 기기에서는 사용자가 위젯을 선택할 수 있는 시스템 활동을 통해 앱 위젯을 추가합니다. 이 경우 암시적으로 권한 확인을 실행합니다. 즉 사용자가 앱 위젯을 추가하여 앱에 호스트에 앱 위젯을 추가할 수 있는 권한을 암시적으로 부여합니다. 다음은 이 방법을 보여주는 예로 원래 런처에서 가져온 것입니다. 이 스니펫에서 이벤트 핸들러는 사용자 작업에 응답하여 REQUEST_PICK_APPWIDGET 요청 코드를 사용하여 startActivityForResult()를 호출합니다.

Kotlin

val REQUEST_CREATE_APPWIDGET = 5
val REQUEST_PICK_APPWIDGET = 9
...
override fun onClick(dialog: DialogInterface?, which: Int) {
    when (which) {
        ...
        AddAdapter.ITEM_APPWIDGET -> {
            ...
            val appWidgetId: Int = appWidgetHost.allocateAppWidgetId()
            val pickIntent = Intent(AppWidgetManager.ACTION_APPWIDGET_PICK).apply {
                putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
            }
            ...
            startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET)
        }
        ...
    }
}

자바

private static final int REQUEST_CREATE_APPWIDGET = 5;
private static final int REQUEST_PICK_APPWIDGET = 9;
...
public void onClick(DialogInterface dialog, int which) {
    switch (which) {
    ...
        case AddAdapter.ITEM_APPWIDGET: {
            ...
            int appWidgetId =
                    Launcher.this.appWidgetHost.allocateAppWidgetId();
            Intent pickIntent =
                    new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
            pickIntent.putExtra
                    (AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
            ...
            startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET);
            break;
    }
    ...
}

시스템 활동이 완료되면 사용자가 선택한 앱 위젯이 포함된 결과를 활동에 반환합니다. 다음 예에서는 활동이 앱 위젯을 추가하기 위해 addAppWidget()을 호출하여 응답합니다.

Kotlin

class Launcher : Activity(), View.OnClickListener, View.OnLongClickListener {
    ...
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
        waitingFroResult = false

        if (resultCode == RESULT_OK && addItemCellInfo != null) {
            when (requestCode) {
                ...
                REQUEST_PICK_APPWIDGET -> addAppWidget(data)
                REQUEST_CREATE_APPWIDGET ->
                    completeAddAppWidget(data, addItemCellInfo, !desktopLocked)
                ...
            }
        }
        ...
    }
}

자바

public final class Launcher extends Activity
        implements View.OnClickListener, OnLongClickListener {
    ...
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        waitingForResult = false;

        if (resultCode == RESULT_OK && addItemCellInfo != null) {
            switch (requestCode) {
                ...
                case REQUEST_PICK_APPWIDGET:
                    addAppWidget(data);
                    break;
                case REQUEST_CREATE_APPWIDGET:
                    completeAddAppWidget(data, addItemCellInfo, !desktopLocked);
                    break;
                }
        }
        ...
    }
}

addAppWidget() 메서드는 앱 위젯을 추가하기 전에 구성해야 하는지 확인합니다.

Kotlin

fun addAppWidget(data: Intent?) {
    if (data != null) {
        val appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1)

        val customWidget = data.getStringExtra(EXTRA_CUSTOM_WIDGET)
        val appWidget: AppWidgetProviderInfo? = appWidgetManager.getAppWidgetInfo(appWidgetId)

        appWidget?.configure?.apply {
            // Launch over to configure widget, if needed.
            val intent = Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE)
            intent.component = this
            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
            startActivityForResult(intent, REQUEST_CREATE_APPWIDGET)
        } ?: run {
            // Otherwise, finish adding the widget.
        }
    }
}

자바

void addAppWidget(Intent data) {
    int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);

    String customWidget = data.getStringExtra(EXTRA_CUSTOM_WIDGET);
    AppWidgetProviderInfo appWidget =
            appWidgetManager.getAppWidgetInfo(appWidgetId);

    if (appWidget.configure != null) {
        // Launch over to configure widget, if needed.
        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
        intent.setComponent(appWidget.configure);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        startActivityForResult(intent, REQUEST_CREATE_APPWIDGET);
    } else {
        // Otherwise, finish adding the widget.
    }
}

구성에 관한 자세한 내용은 앱 위젯 구성 활동 만들기를 참조하세요.

앱 위젯이 준비된 후 다음 단계는 앱 위젯을 실제로 작업공간에 추가하는 것입니다. 원래 런처completeAddAppWidget() 메서드를 사용하여 이 작업을 실행합니다.

Android 4.1 이상에서 앱 위젯 바인딩하기

Android 4.1은 더 간소화된 바인딩 프로세스를 위한 API를 추가합니다. 이러한 API를 사용하면 호스트에서 바인딩을 위한 맞춤 UI를 제공할 수도 있습니다. 이 개선된 프로세스를 사용하려면 앱이 매니페스트에서 BIND_APPWIDGET 권한을 선언해야 합니다.

<uses-permission android:name="android.permission.BIND_APPWIDGET" />

하지만 이 단계는 첫 번째 단계일 뿐입니다. 런타임에 사용자는 호스트에 앱 위젯을 추가할 수 있는 권한을 앱에 명시적으로 부여해야 합니다. 앱에 위젯을 추가할 수 있는 권한이 있는지 테스트하려면 bindAppWidgetIdIfAllowed() 메서드를 사용하세요. bindAppWidgetIdIfAllowed()에서 false를 반환하는 경우 앱에서 사용자에게 권한(향후 모든 앱 위젯을 추가할 수 있는 '허용' 또는 '항상 허용')을 부여하도록 요청하는 대화상자를 표시해야 합니다. 다음 스니펫은 대화상자를 표시하는 방법의 예를 보여줍니다.

Kotlin

val intent = Intent(AppWidgetManager.ACTION_APPWIDGET_BIND).apply {
    putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
    putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.componentName)
    // This is the options bundle discussed above
    putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options)
}
startActivityForResult(intent, REQUEST_BIND_APPWIDGET)

자바

Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.componentName);
// This is the options bundle discussed above
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
startActivityForResult(intent, REQUEST_BIND_APPWIDGET);

호스트는 사용자가 구성이 필요한 앱 위젯을 추가했는지도 확인해야 합니다. 이 주제에 관한 자세한 내용은 앱 위젯 구성 활동 만들기를 참조하세요.

호스트 책임

위젯 개발자는 AppWidgetProviderInfo 메타데이터를 사용하여 위젯의 다양한 구성 설정을 지정할 수 있습니다. 이러한 구성 옵션은 아래에 자세히 설명되어 있으며 호스트가 위젯 공급자와 연결된 AppWidgetProviderInfo 객체에서 가져올 수 있습니다.

타겟팅하는 Android의 버전에 관계없이 모든 호스트에는 다음과 같은 책임이 있습니다.

  • 위젯을 추가할 때 위에 설명된 대로 위젯 ID를 할당해야 합니다. 또한 위젯이 호스트에서 삭제될 때 deleteAppWidgetId()를 호출하여 위젯 ID를 할당 취소해야 합니다.
  • 위젯을 추가할 때 구성 활동에서 앱 위젯 업데이트에 설명된 대로 구성 활동을 실행해야 합니다(구성 활동이 존재하는 경우). 대부분의 앱 위젯은 이 단계를 완료해야 제대로 표시됩니다.
  • 모든 앱 위젯은 AppWidgetProviderInfo 메타데이터에 정의된 대로 android:minWidthandroid:minHeight를 사용하여 최소 너비와 높이를 dps 단위로 지정합니다. 위젯이 이 dps 단위 이상으로 배치되었는지 확인하세요. 예를 들어 대부분의 호스트는 아이콘과 위젯을 그리드에 정렬합니다. 이 시나리오에서 호스트는 기본적으로 minWidthminHeight 제약 조건을 만족하는 최소 개수의 셀을 사용하여 앱 위젯을 추가해야 합니다.

위에 나열된 요구사항 외에 특정 플랫폼 버전에는 호스트에 새로운 책임을 부여하는 기능이 도입됩니다.

어떤 버전을 타겟팅하나요?

호스트를 구현할 때 사용하는 방법은 타겟팅하는 Android 버전에 따라 달라집니다. 이 섹션에 설명된 대부분의 기능은 3.0 이상에서 도입되었습니다. 예:

  • Android 3.0(API 수준 11)에는 위젯을 위한 자동 진행 동작이 도입되었습니다.
  • Android 3.1(API 수준 12)에는 위젯의 크기를 조절할 수 있는 기능이 도입되었습니다.
  • Android 4.0(API 수준 15)에서는 호스트에서 패딩을 관리하도록 패딩 정책이 변경되었습니다.
  • Android 4.1(API 수준 16)에는 위젯 공급자가 위젯 인스턴스가 호스팅되는 환경에 관한 자세한 정보를 가져올 수 있는 API가 추가되었습니다.
  • Android 4.2(API 수준 17)에는 옵션 번들과 bindAppWidgetIdIfAllowed() 메서드가 도입되었습니다. 잠금 화면 위젯도 도입되었습니다.

이전 기기를 타겟팅하는 경우 원래 런처를 예로 참조하세요.

다음 섹션에서는 호스트에 새로운 책임을 부여하는 기능을 자세히 설명합니다.

Android 3.0

Android 3.0(API 수준 11)에는 위젯이 autoAdvanceViewId()를 지정할 수 있는 기능이 도입되었습니다. 이 뷰 ID는 StackView 또는 AdapterViewFlipperAdvanceable의 인스턴스를 가리켜야 합니다. 즉 호스트가 위젯을 진행하는 것이 합리적인지 고려하여(예를 들어 위젯이 다른 페이지에 있거나 화면이 꺼져 있는 경우 호스트가 위젯을 진행하고 싶지 않을 수도 있습니다) 호스트에 의해 적절하다고 간주되는 간격으로 이 뷰에서 advance()를 호출해야 합니다.

Android 3.1

Android 3.1(API 수준 12)에는 위젯의 크기를 조절할 수 있는 기능이 도입되었습니다. 위젯에서 AppWidgetProviderInfo 메타데이터의 android:resizeMode 속성을 사용하여 위젯의 크기 조절이 가능함을 지정하고 가로 또는 세로 크기 조절이 가능한지 표시할 수 있습니다. Android 4.0(API 수준 14)에서는 위젯이 android:minResizeWidthandroid:minResizeHeight도 지정할 수 있습니다.

위젯에 의해 지정된 대로 위젯의 크기를 가로 또는 세로로 조정할 수 있도록 설정하는 것은 호스트의 책임입니다. 크기 조절이 가능함을 지정하는 위젯은 크기를 임의로 조정할 수 있지만 android:minResizeWidthandroid:minResizeHeight에 의해 지정된 값보다 작게 조정하면 안 됩니다. 샘플 구현의 경우 Launcher2AppWidgetResizeFrame을 참조하세요.

Android 4.0

Android 4.0(API 수준 15)에서는 호스트에서 패딩을 관리하도록 패딩 정책이 변경되었습니다. 4.0 현재 앱 위젯에 더 이상 자체 패딩이 포함되지 않습니다. 대신 시스템에서 현재 화면의 특성을 기반으로 각 위젯을 위한 패딩을 추가합니다. 이에 따라 위젯이 그리드에 더 균일하고 일관되게 표시됩니다. 플랫폼은 앱 위젯을 호스팅하는 애플리케이션을 지원하기 위해 getDefaultPaddingForWidget() 메서드를 제공합니다. 애플리케이션에서 이 메서드를 호출하여 시스템 정의 패딩을 가져오고 위젯에 할당할 셀 수를 계산할 때 이를 고려할 수 있습니다.

Android 4.1

Android 4.1(API 수준 16)에는 위젯 공급자가 위젯 인스턴스가 호스팅되는 환경에 관한 자세한 정보를 가져올 수 있는 API가 추가되었습니다. 구체적으로 호스트는 위젯이 표시되는 크기를 위젯 공급자에게 알려줍니다. 이 크기 정보를 제공하는 것은 호스트의 책임입니다.

호스트는 updateAppWidgetSize()를 통해 이 정보를 제공합니다. 크기는 최소 및 최대 너비/높이(dps 단위)로 지정됩니다. 고정 크기가 범위가 지정되는 이유는 위젯의 너비와 높이가 방향에 따라 변경될 수 있기 때문입니다. 회전 시 호스트가 모든 위젯을 업데이트해야 하면 시스템이 심각하게 느려질 수 있습니다. 이 값은 위젯이 배치될 때, 위젯의 크기가 조절될 때, 런처가 지정된 부팅에서 처음으로 위젯을 확장할 때 업데이트됩니다(값은 부팅 간에 지속되지 않습니다).

Android 4.2

Android 4.2(API 수준 17)에는 바인딩 시 옵션 번들을 지정할 수 있는 기능이 추가되었습니다. 이 방법은 첫 번째 업데이트 시 AppWidgetProvider에서 옵션 데이터에 즉시 액세스할 수 있으므로 크기 등 앱 위젯 옵션을 지정하는 가장 이상적인 방법입니다. 이 작업은 bindAppWidgetIdIfAllowed() 메서드를 사용하여 실행할 수 있습니다. 이 주제에 관한 자세한 내용은 앱 위젯 바인딩을 참조하세요.

Android 4.2에는 잠금 화면 위젯도 도입되었습니다. 잠금 화면에서 위젯을 호스팅할 때 호스트는 앱 위젯 옵션 번들 내에서 이 정보를 지정해야 합니다.AppWidgetProvider에서 이 정보를 사용하여 위젯의 스타일을 적절하게 지정할 수 있습니다. 위젯을 잠금 화면 위젯으로 지정하려면 updateAppWidgetOptions()를 사용하여 OPTION_APPWIDGET_HOST_CATEGORY 필드를 WIDGET_CATEGORY_KEYGUARD 값과 함께 포함하세요. 이 옵션은 WIDGET_CATEGORY_HOME_SCREEN으로 기본 설정되므로 홈 화면 호스트에 이 설정을 명시적으로 지정하지 않아도 됩니다.

호스트가 앱에 적절한 앱 위젯만 추가하는지 확인하세요. 예를 들어 호스트가 홈 화면인 경우 AppWidgetProviderInfo 메타데이터의 android:widgetCategory 속성에 WIDGET_CATEGORY_HOME_SCREEN 플래그가 포함되는지 확인하세요. 마찬가지로 잠금 화면의 경우 필드에 WIDGET_CATEGORY_KEYGUARD 플래그가 포함되는지 확인하세요. 이 주제에 관한 자세한 내용은 잠금 화면에서 앱 위젯 사용 설정을 참조하세요.