Premiers pas

Cette page vous explique comment configurer votre environnement et créer des segments d'application dans votre application.

Remarque: Android Studio 3.2 ou version ultérieure contient des outils et des fonctionnalités supplémentaires qui peuvent vous aider à développer des segments d'application:

  • Outil de refactorisation AndroidX: obligatoire si vous travaillez sur un projet qui utilise des bibliothèques AndroidX.
  • Vérifications lint des segments d'application: détecte les anti-pratiques courants lors de la création de segments d'application
  • Modèle SliceProvider: gère le code récurrent lors de la création d'un SliceProvider.

Télécharger et installer la visionneuse de segments d'application

Téléchargez la dernière version d'APK pour la visionneuse de segments d'application que vous pouvez utiliser pour tester vos segments d'application sans implémenter l'API SliceView.

Si ADB n'est pas configuré correctement dans votre environnement, consultez le guide ADB pour en savoir plus.

Installez la visionneuse de segments d'application sur votre appareil en exécutant la commande suivante dans le même répertoire que le fichier slice-viewer.apk téléchargé:

adb install -r -t slice-viewer.apk

Exécuter la visionneuse de segments d'application

Vous pouvez lancer le lecteur de segments d'application à partir de votre projet Android Studio ou de la ligne de commande:

Lancez la visionneuse de segments d'application à partir de votre projet Android Studio.

  1. Dans votre projet, sélectionnez Run > Edit Configurations (Exécuter > Modifier les configurations).
  2. Dans le coin supérieur gauche, cliquez sur le signe plus vert.
  3. Sélectionnez Application Android.

  4. Saisissez trance (tranche) dans le champ de nom.

  5. Sélectionnez votre module d'application dans le menu déroulant Module.

  6. Sous Launch Options (Options de lancement), sélectionnez URL dans le menu déroulant Launch (Lancer).

  7. Saisissez slice-<your slice URI> dans le champ d'URL

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

  8. Cliquez sur OK.

Lancer la visionneuse de segments d'application via ADB (ligne de commande)

Exécutez votre application depuis Android Studio:

adb install -t -r <yourapp>.apk

Affichez votre segment d'application en exécutant la commande suivante:

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

Lecteur de segment d'application affichant un seul segment d'application Wi-Fi

Afficher tous vos segments d'application au même endroit

En plus de lancer un seul segment d'application, vous pouvez afficher une liste persistante de vos segments d'application.

  • Utilisez la barre de recherche pour rechercher manuellement vos segments d'application via un URI (par exemple, content://com.example.android.app/hello). Chaque fois que vous effectuez une recherche, le segment d'application est ajouté à la liste.
  • Chaque fois que vous lancez l'outil de visionneuse de segment d'application avec un URI de segment d'application, celui-ci est ajouté à la liste.
  • Vous pouvez balayer un segment d'application pour le supprimer de la liste.
  • Appuyez sur l'URI du segment d'application pour afficher une page ne contenant que ce segment d'application. Cela a le même effet que de lancer la visionneuse de segments d'application avec un URI de segment d'application.

Lecteur de segments d'application affichant une liste de segments d'application

Afficher le segment d'application dans différents modes

Une application qui présente un segment d'application peut modifier le SliceView#mode au moment de l'exécution. Vous devez donc vous assurer qu'il s'affiche comme prévu dans chaque mode. Pour changer de mode, sélectionnez l'icône de menu en haut à droite de la page.

Visionneuse à tranche unique avec le mode défini sur "petit"

Créer votre premier segment d'application

Pour créer un segment d'application, ouvrez votre projet Android Studio, effectuez un clic droit sur le package src, puis sélectionnez New... > Autre > Fournisseur de segments d'application. Cette opération crée une classe qui étend SliceProvider, ajoute l'entrée de fournisseur requise à votre AndroidManifest.xml et modifie votre build.gradle pour ajouter les dépendances de segment d'application requises.

La modification apportée à AndroidManifest.xml est présentée ci-dessous:

<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>

Les dépendances suivantes sont ajoutées à votre build.gradle:

Kotlin

dependencies {
// ...
    implementation "androidx.slice:slice-builders-ktx:(latest version)"
// ...
}

Java

dependencies {
// ...
    implementation "androidx.slice:slice-builders:(latest version)"
// ...
}

Chaque segment d'application est associé à un URI. Lorsqu'une surface souhaite afficher un segment d'application, elle envoie une requête de liaison à votre application avec cet URI. Votre application gère ensuite cette requête et crée le segment d'application de manière dynamique via la méthode onBindSlice. La surface peut alors afficher le segment d'application, le cas échéant.

Vous trouverez ci-dessous un exemple de méthode onBindSlice qui recherche le chemin d'URI /hello et renvoie un segment d'application Hello World:

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

Utilisez la configuration d'exécution de tranche que vous avez créée dans la section "Lecteur de segment d'application" ci-dessus, en transmettant l'URI de segment d'application (par exemple, slice-content://com.android.example.slicesample/hello) du segment d'application Hello World pour l'afficher dans la visionneuse de segments d'application.

Segments d'application interactifs

Comme pour les notifications, vous pouvez gérer les clics au sein d'un segment d'application en associant des objets PendingIntent déclenchés lors d'une interaction de l'utilisateur. L'exemple ci-dessous lance un Activity pouvant recevoir et gérer ces intents:

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

Les segments d'application acceptent également d'autres types d'entrées, tels que les boutons d'activation/de désactivation, qui incluent l'état dans l'intent envoyé à l'application.

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

Le récepteur peut alors vérifier l'état qu'il reçoit:

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

Segments d'application dynamiques

Les segments d'application peuvent également comporter du contenu dynamique. Dans l'exemple suivant, le segment d'application inclut désormais le nombre d'annonces reçues dans son contenu:

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

Dans cet exemple, le nombre affiché n'est pas mis à jour automatiquement. Vous pouvez modifier votre broadcast receiver pour avertir le système qu'une modification a eu lieu à l'aide de 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);
        }
    }
}

Modèles

Les segments d'application sont compatibles avec divers modèles. Pour en savoir plus sur les options et les comportements des modèles, consultez la section Modèles.