Como começar

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

Observação: o Android Studio 3.2 e versões mais recentes têm outras ferramentas e recursos que podem ajudar no desenvolvimento de 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 de texto clichê na criação de um SliceProvider.

Download e instalação do Slice Viewer

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

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

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

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 Hello World 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 na intent enviada 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."
                }
            }
        }
    }
}

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.