このページでは、アプリの Slice を作成するための環境セットアップ方法と、Slice の作成方法について説明します。
注: Android Studio 3.2 以降には、Slice 開発に役立つ次のツールや機能が用意されています。
- AndroidX リファクタリング ツール: AndroidX ライブラリを使用するプロジェクトに必要です。
- Slice lint チェック: Slice を作成する際の一般的なアンチプラクティスの検出に使用します。
SliceProvider
テンプレート:SliceProvider
作成時にボイラープレートを処理します。
Slice Viewer のダウンロードとインストール
最新のサンプルをダウンロードする
Slice Viewer APK リリース
実装せずに Slice をテストするために使用できる
SliceView
API。
ADB が適切にセットアップされていない場合は、ADB ガイドをご覧ください。
ダウンロードした slice-viewer.apk
と同じディレクトリで次のコマンドを実行し、Slice Viewer をデバイスにインストールします。
adb install -r -t slice-viewer.apk
Slice Viewer の実行
Android Studio プロジェクトまたはコマンドラインから Slice Viewer を起動できます。
Android Studio プロジェクトからの Slice Viewer の起動
- プロジェクトで、[Run] > [Edit Configurations...] を選択します
- ウィンドウ左上のプラス記号をクリックします
[Android App] を選択します
[Name] フィールドに「slice」と入力します
[Module] プルダウンからアプリ モジュールを選択します
[Launch Options] で、[Launch] プルダウンから [URL] を選択します
[URL] フィールドに
slice-<your slice URI>
を入力します例:
slice-content://com.example.your.sliceuri
[OK] をクリックします
ADB(コマンドライン)経由での Slice Viewer ツールの起動
Android Studio から次のようにしてアプリを実行します。
adb install -t -r <yourapp>.apk
Slice を表示するには、次のコマンドを実行します。
adb shell am start -a android.intent.action.VIEW -d slice-<your slice URI>
Slice Viewer に表示された単一の Wi-Fi Slice
すべての Slice の一括表示
1 つの Slice を起動するだけでなく、Slice の永続的なリストを表示することもできます。
- 検索バーから、手動で URI(たとえば、
content://com.example.android.app/hello
)を使用して Slice を検索します。検索するたびに、Slice がリストに追加されます。 - Slice URI を指定して Slice Viewer ツールを起動すると、Slice がリストに追加されます。
- Slice をスワイプすることでリストから削除できます。
- Slice の URI をタップすると、その Slice のみが含まれるページが表示されます。このときの動作は、Slice URI を指定して Slice Viewer を起動した場合と同じです。
Slice Viewer による Slice のリストの表示
さまざまなモードでのスライスの表示
Slice を表示するアプリでは、ランタイムに SliceView#mode
を変更できるため、各モードで Slice が期待どおりに表示されることを確認する必要があります。
モードを変更するには、ページの右上にあるメニュー アイコンをクリックします。
モードが「small」に設定された単一の Slice Viewer
最初の Slice の作成
Slice を作成するには、Android Studio プロジェクトを開き、src
パッケージを右クリックして、[New...] > [Other] > [Slice Provider] を選択します。これにより、SliceProvider
を拡張するクラスが作成されて必要なプロバイダ エントリが AndroidManifest.xml
に追加され、build.gradle
が変更されて必要な Slice 依存関係が追加されます。
AndroidManifest.xml
に対する変更は次のとおりです。
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android.app"> ... <application> ... <provider android:name="MySliceProvider" android:authorities="com.example.android.app" android:exported="true" > <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.app.slice.category.SLICE" /> </intent-filter> </provider> ... </application> </manifest>
次の依存関係が build.gradle
に追加されます。
Kotlin
dependencies { // ... implementation "androidx.slice:slice-builders-ktx:(latest version)" // ... }
Java
dependencies { // ... implementation "androidx.slice:slice-builders:(latest version)" // ... }
各 Slice には、関連付けられた URI があります。サーフェスで Slice を表示するには、この URI でアプリにバインディング リクエストを送信します。アプリでこのリクエストを処理し、onBindSlice
メソッドを介して動的に Slice を作成します。その後は、必要に応じてサーフェスに Slice を表示できます。
以下は、/hello
URI パスを確認して Hello World Slice を返す onBindSlice
メソッドの例です。
Kotlin
override fun onBindSlice(sliceUri: Uri): Slice? { val activityAction = createActivityAction() return if (sliceUri.path == "/hello") { list(context, sliceUri, ListBuilder.INFINITY) { row { primaryAction = activityAction title = "Hello World." } } } else { list(context, sliceUri, ListBuilder.INFINITY) { row { primaryAction = activityAction title = "URI not recognized." } } } }
Java
@Override public Slice onBindSlice(Uri sliceUri) { if (getContext() == null) { return null; } SliceAction activityAction = createActivityAction(); ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY); // Create parent ListBuilder. if ("/hello".equals(sliceUri.getPath())) { listBuilder.addRow(new ListBuilder.RowBuilder() .setTitle("Hello World") .setPrimaryAction(activityAction) ); } else { listBuilder.addRow(new ListBuilder.RowBuilder() .setTitle("URI not recognized") .setPrimaryAction(activityAction) ); } return listBuilder.build(); }
上記 Slice Viewer セクションで作成した Slice 実行設定を使用し、Hello World Slice の URI(たとえば、slice-content://com.android.example.slicesample/hello
)を渡して Slice Viewer に表示します。
インタラクティブ スライス
通知と同様に、ユーザーの操作でトリガーされる PendingIntent
オブジェクトを添付することで、Slice 内のクリックを処理できます。下記の例では、このようなインテントを受信して処理できる Activity
を開始します。
Kotlin
fun createSlice(sliceUri: Uri): Slice { val activityAction = createActivityAction() return list(context, sliceUri, INFINITY) { row { title = "Perform action in app" primaryAction = activityAction } } } fun createActivityAction(): SliceAction { val intent = Intent(context, MainActivity::class.java) return SliceAction.create( PendingIntent.getActivity(context, 0, Intent(context, MainActivity::class.java), 0), IconCompat.createWithResource(context, R.drawable.ic_home), ListBuilder.ICON_IMAGE, "Enter app" ) }
Java
public Slice createSlice(Uri sliceUri) { if (getContext() == null) { return null; } SliceAction activityAction = createActivityAction(); return new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY) .addRow(new ListBuilder.RowBuilder() .setTitle("Perform action in app.") .setPrimaryAction(activityAction) ).build(); } public SliceAction createActivityAction() { if (getContext() == null) { return null; } return SliceAction.create( PendingIntent.getActivity( getContext(), 0, new Intent(getContext(), MainActivity.class), 0 ), IconCompat.createWithResource(getContext(), R.drawable.ic_home), ListBuilder.ICON_IMAGE, "Enter app" ); }
Slice は、他の入力タイプ(たとえば、アプリに送信されるインテントの状態を含む切り替えボタンなど)もサポートします。
Kotlin
fun createBrightnessSlice(sliceUri: Uri): Slice { val toggleAction = SliceAction.createToggle( createToggleIntent(), "Toggle adaptive brightness", true ) return list(context, sliceUri, ListBuilder.INFINITY) { row { title = "Adaptive brightness" subtitle = "Optimizes brightness for available light" primaryAction = toggleAction } inputRange { inputAction = (brightnessPendingIntent) max = 100 value = 45 } } } fun createToggleIntent(): PendingIntent { val intent = Intent(context, MyBroadcastReceiver::class.java) return PendingIntent.getBroadcast(context, 0, intent, 0) }
Java
public Slice createBrightnessSlice(Uri sliceUri) { if (getContext() == null) { return null; } SliceAction toggleAction = SliceAction.createToggle( createToggleIntent(), "Toggle adaptive brightness", true ); ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY) .addRow(new ListBuilder.RowBuilder() .setTitle("Adaptive brightness") .setSubtitle("Optimizes brightness for available light.") .setPrimaryAction(toggleAction) ).addInputRange(new ListBuilder.InputRangeBuilder() .setInputAction(brightnessPendingIntent) .setMax(100) .setValue(45) ); return listBuilder.build(); } public PendingIntent createToggleIntent() { Intent intent = new Intent(getContext(), MyBroadcastReceiver.class); return PendingIntent.getBroadcast(getContext(), 0, intent, 0); }
これにより、受信側は受信した状態を確認できます。
Kotlin
class MyBroadcastReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (intent.hasExtra(Slice.EXTRA_TOGGLE_STATE)) { Toast.makeText(context, "Toggled: " + intent.getBooleanExtra( Slice.EXTRA_TOGGLE_STATE, false), Toast.LENGTH_LONG).show() } } companion object { const val EXTRA_MESSAGE = "message" } }
Java
public class MyBroadcastReceiver extends BroadcastReceiver { public static String EXTRA_MESSAGE = "message"; @Override public void onReceive(Context context, Intent intent) { if (intent.hasExtra(EXTRA_TOGGLE_STATE)) { Toast.makeText(context, "Toggled: " + intent.getBooleanExtra( EXTRA_TOGGLE_STATE, false), Toast.LENGTH_LONG).show(); } } }
動的 Slice
動的コンテンツも Slice に含めることができます。次の例では、Slice のコンテンツに受信したブロードキャストの数が含まれています。
Kotlin
fun createDynamicSlice(sliceUri: Uri): Slice { return when (sliceUri.path) { "/count" -> { val toastAndIncrementAction = SliceAction.create( createToastAndIncrementIntent("Item clicked."), actionIcon, ListBuilder.ICON_IMAGE, "Increment." ) list(context, sliceUri, ListBuilder.INFINITY) { row { primaryAction = toastAndIncrementAction title = "Count: ${MyBroadcastReceiver.receivedCount}" subtitle = "Click me" } } } else -> { list(context, sliceUri, ListBuilder.INFINITY) { row { primaryAction = createActivityAction() title = "URI not found." } } } } }
Java
public Slice createDynamicSlice(Uri sliceUri) { if (getContext() == null || sliceUri.getPath() == null) { return null; } ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY); switch (sliceUri.getPath()) { case "/count": SliceAction toastAndIncrementAction = SliceAction.create( createToastAndIncrementIntent("Item clicked."), actionIcon, ListBuilder.ICON_IMAGE, "Increment." ); listBuilder.addRow( new ListBuilder.RowBuilder() .setPrimaryAction(toastAndIncrementAction) .setTitle("Count: " + MyBroadcastReceiver.sReceivedCount) .setSubtitle("Click me") ); break; default: listBuilder.addRow( new ListBuilder.RowBuilder() .setPrimaryAction(createActivityAction()) .setTitle("URI not found.") ); break; } return listBuilder.build(); } public PendingIntent createToastAndIncrementIntent(String s) { Intent intent = new Intent(getContext(), MyBroadcastReceiver.class) .putExtra(MyBroadcastReceiver.EXTRA_MESSAGE, s); return PendingIntent.getBroadcast(getContext(), 0, intent, 0); }
この例では、数は表示されていますが、自動的に更新されません。ContentResolver#notifyChange
を使用して、変更が発生したことをシステムに通知するよう、ブロードキャスト レシーバを変更できます。
Kotlin
class MyBroadcastReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (intent.hasExtra(Slice.EXTRA_TOGGLE_STATE)) { Toast.makeText( context, "Toggled: " + intent.getBooleanExtra( Slice.EXTRA_TOGGLE_STATE, false ), Toast.LENGTH_LONG ).show() receivedCount++; context.contentResolver.notifyChange(sliceUri, null) } } companion object { var receivedCount = 0 val sliceUri = Uri.parse("content://com.android.example.slicesample/count") const val EXTRA_MESSAGE = "message" } }
Java
public class MyBroadcastReceiver extends BroadcastReceiver { public static int sReceivedCount = 0; public static String EXTRA_MESSAGE = "message"; private static Uri sliceUri = Uri.parse("content://com.android.example.slicesample/count"); @Override public void onReceive(Context context, Intent intent) { if (intent.hasExtra(EXTRA_TOGGLE_STATE)) { Toast.makeText(context, "Toggled: " + intent.getBooleanExtra( EXTRA_TOGGLE_STATE, false), Toast.LENGTH_LONG).show(); sReceivedCount++; context.getContentResolver().notifyChange(sliceUri, null); } } }
テンプレート
Slice はさまざまなテンプレートをサポートしています。テンプレートのオプションと動作の詳細については、Slice テンプレートをご覧ください。