スタートガイド

このページでは、アプリのスライスを作成するための環境セットアップ方法と、スライスの作成方法について説明します。

: Android Studio 3.2 以降には、スライス開発に役立つ次のツールや機能が用意されています。

  • AndroidX リファクタリング ツール: AndroidX ライブラリを使用するプロジェクトに必要です。
  • スライス lint チェック: スライスを作成する際の一般的なアンチプラクティスの検出に使用します。
  • SliceProvider テンプレート: 作成時にボイラープレートを処理します。

Slice Viewer のダウンロードとインストール

SliceView API を実装せずにスライスをテストできる最新のサンプル Slice Viewer APK リリースをダウンロードします。Slice Viewer のソースのクローンを作成することもできます。

ADB が適切にセットアップされていない場合は、ADB ガイドをご覧ください。

ダウンロードした slice-viewer.apk と同じディレクトリで次のコマンドを実行し、Slice Viewer をデバイスにインストールします。

adb install -r -t slice-viewer.apk
    

Slice Viewer の実行

Android Studio プロジェクトまたはコマンドラインから Slice Viewer を起動できます。

Android Studio プロジェクトからの Slice Viewer の起動

  1. プロジェクトで、[Run] > [Edit Configurations...] を選択します
  2. ウィンドウ左上のプラス記号をクリックします
  3. [Android App] を選択します

  4. [Name] フィールドに「slice」と入力します

  5. [Module] プルダウンからアプリ モジュールを選択します

  6. [Launch Options] で、[Launch] プルダウンから [URL] を選択します

  7. [URL] フィールドに slice-<your slice URI> を入力します

    例: slice-content://com.example.your.sliceuri

  8. [OK] をクリックします

ADB(コマンドライン)経由での Slice Viewer ツールの起動

Android Studio から次のようにしてアプリを実行します。

adb install -t -r <yourapp>.apk
    

スライスを表示するには、次のコマンドを実行します。

adb shell am start -a android.intent.action.VIEW -d slice-<your slice URI>
    

Slice Viewer に表示された単一の Wi-Fi スライス

すべてのスライスの一括表示

1 つのスライスを起動するだけでなく、スライスの永続的なリストを表示することもできます。

  • 検索バーから、手動で URI(たとえば、content://com.example.android.app/hello)を使用してスライスを検索します。検索するたびに、スライスがリストに追加されます。
  • スライス URI を指定して Slice Viewer ツールを起動すると、スライスがリストに追加されます。
  • スライスをスワイプすることでリストから削除できます。
  • スライスの URI をタップすると、そのスライスのみが含まれるページが表示されます。このときの動作は、スライス URI を指定して Slice Viewer を起動した場合と同じです。

Slice Viewer によるスライスのリストの表示

さまざまなモードでのスライスの表示

スライスを表示するアプリでは、ランタイムに SliceView#mode を変更できるため、各モードでスライスが期待どおりに表示されることを確認する必要があります。 モードを変更するには、ページの右上にあるメニュー アイコンをクリックします。

モードが「small」に設定された単一の Slice Viewer

最初のスライスの作成

スライスを作成するには、Android Studio プロジェクトを開き、src パッケージを右クリックして、[New...] > [Other] > [Slice Provider] を選択します。これにより、SliceProvider を拡張するクラスが作成されて必要なプロバイダ エントリが AndroidManifest.xml に追加され、build.gradle が変更されて必要なスライス依存関係が追加されます。

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)"
    // ...
    }
    

各スライスには、関連付けられた URI があります。サーフェスでスライスを表示するには、この URI でアプリにバインディング リクエストを送信します。アプリでこのリクエストを処理し、onBindSlice メソッドを介して動的にスライスを作成します。その後は、必要に応じてサーフェスにスライスを表示できます。

以下は、/hello URI パスを確認して Hello World スライスを返す 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 セクションで作成したスライス実行設定を使用し、Hello World スライスのスライス URI(たとえば、slice-content://com.android.example.slicesample/hello)を渡して Slice Viewer に表示します。

インタラクティブ スライス

通知と同様に、ユーザーの操作でトリガーされる PendingIntent オブジェクトを添付することで、スライス内のクリックを処理できます。下記の例では、このようなインテントを受信して処理できる 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"
        );
    }
    

スライスは、他の入力タイプ(たとえば、アプリに送信されるインテントの状態を含む切り替えボタンなど)もサポートします。

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();
            }
        }
    }
    

動的スライス

動的コンテンツもスライスに含めることができます。次の例では、スライスのコンテンツに受信したブロードキャストの数が含まれています。

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."
                    }
                }
            }
        }
    }

    fun createToastAndIncrementIntent(s: String): PendingIntent {
        return PendingIntent.getBroadcast(
            context, 0,
            Intent(context, MyBroadcastReceiver::class.java)
                .putExtra(MyBroadcastReceiver.EXTRA_MESSAGE, s), 0
        )
    }
    

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);
            }
        }
    }
    

テンプレート

スライスはさまざまなテンプレートをサポートしています。テンプレートのオプションと動作の詳細については、スライス テンプレートをご覧ください。