Primeiros passos

Esta página mostra como configurar seu ambiente e criar Slices no seu app.

Observação: o Android Studio 3.2 e versões posteriores têm outras ferramentas e recursos que podem ajudar no desenvolvimento do Slice:

  • Ferramenta de refatoramento do AndroidX: necessária para trabalhar em um projeto que usa bibliotecas do AndroidX.
  • Verificações lint para Slices: detecta práticas não recomendadas comuns na criação de Slices.
  • Modelo SliceProvider: processa códigos-clichê na criação de um .

Download e instalação do Slice Viewer

Faça o download da versão mais recente da amostra APK do Slice Viewer (em inglês), que pode ser usada para testar seus Slices sem implementar a API SliceView. Você também pode clonar a origem do Slice Viewer (em inglês).

Se o adb não estiver configurado corretamente no seu ambiente, consulte o Guia do ADB para saber mais.

Instale o Slice Viewer no dispositivo executando o seguinte comando no mesmo diretório em que o slice-viewer.apk foi salvo:

adb install -r -t slice-viewer.apk
    

Executar o Slice Viewer

Você pode abrir o Slice Viewer no projeto do Android Studio ou na linha de comando:

Abrir o Slice Viewer no projeto do Android Studio

  1. No projeto, selecione Run > Edit Configurations....
  2. No canto superior esquerdo, clique no sinal de adição verde.
  3. Selecione Android App.

  4. Digite o nome slice no campo "Name".

  5. Selecione o módulo do app no menu suspenso Module.

  6. Em Launch Options, selecione URL no menu suspenso Launch.

  7. Digite slice-<your slice URI> no campo "URL".

    Por exemplo: slice-content://com.example.your.sliceuri

  8. Clique em OK.

Abrir a ferramenta Slice Viewer pelo ADB (linha de comando)

Execute o app no Android Studio:

adb install -t -r <yourapp>.apk
    

Para visualizar seu Slice, execute este comando:

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

Slice Viewer mostrando um único Slice de Wi-Fi

Ver todos os Slices em um só lugar

Além de iniciar um único Slice, é possível ver uma lista persistente dos seus Slices.

  • Use a barra de pesquisa para pesquisar manualmente seus Slices por meio do URI (por exemplo, content://com.example.android.app/hello). Sempre que você fizer isso, o Slice será adicionado à lista.
  • Todas as vezes que você iniciar a ferramenta Slice Viewer com um URI de Slice, o Slice será adicionado à lista.
  • Você pode deslizar um Slice para removê-lo da lista.
  • Toque no URI do Slice para ver uma página que contém apenas esse Slice. Essa ação tem o mesmo efeito que abrir o Slice Viewer com um URI de Slice.

Slice Viewer mostrando uma lista de Slices

Visualizar o Slice em modos diferentes

Um app que apresenta um Slice pode modificar o SliceView#mode durante a execução. Portanto, verifique se o Slice é exibido conforme o esperado em cada modo. Selecione o ícone de menu no canto superior direito da página para mudar o modo.

Slice Viewer individual com modo definido como "pequeno"

Criar seu primeiro Slice

Para criar um Slice, abra o projeto do Android Studio, clique com o botão direito no pacote src e selecione New… > Other > Slice Provider. Essa opção cria uma classe que estende o SliceProvider, adiciona a entrada de provedor necessária ao AndroidManifest.xml e modifica o build.gradle para adicionar as dependências de Slice necessárias.

A modificação para AndroidManifest.xml é mostrada abaixo:

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

As seguintes dependências são adicionadas ao build.gradle:

Kotlin

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

Java

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

Cada Slice tem um URI associado. Quando uma superfície quer exibir um Slice, ela envia uma solicitação de vinculação ao app com esse URI. Em seguida, o app processa a solicitação e cria dinamicamente o Slice por meio do método onBindSlice. Então, a superfície poderá exibir o Slice sempre que apropriado.

Veja abaixo o exemplo de um método onBindSlice que verifica o caminho do URI /hello e retorna um Slice Olá, mundo:

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

Use a configuração de execução slice criada na seção Slice Viewer acima. Para isso, transmita o URI de Slice (por exemplo, slice-content://com.android.example.slicesample/hello) do Slice Olá, mundo para vê-lo no Slice Viewer.

Slices interativos

Assim como acontece com as notificações, você pode processar cliques no seu Slice anexando objetos PendingIntent que são acionados por meio da interação do usuário. O exemplo abaixo inicia um Activity que pode receber e processar esses 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"
        );
    }
    

Os Slices também são compatíveis com outros tipos de entrada, como botões de alternância, que incluem o estado no intent enviado ao 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);
    }
    

Então, o receptor pode verificar o estado recebido:

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

Slices dinâmicos

Os Slices também podem apresentar conteúdo dinâmico. No exemplo a seguir, o Slice agora inclui o número de transmissões recebidas no conteúdo:

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

Neste exemplo, embora a contagem seja exibida, ela não é atualizada automaticamente. Para modificar o broadcast receiver para notificar o sistema de que uma mudança aconteceu, use 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);
            }
        }
    }
    

Modelos

Os Slices são compatíveis com vários modelos. Para mais detalhes sobre as opções e os comportamentos de modelo, consulte Modelos.