Cómo empezar

En esta página, se muestra cómo configurar tu entorno y compilar Slices en tu app.

Nota: Android Studio 3.2 y las versiones posteriores contienen herramientas y funcionalidades adicionales que pueden ayudarte con el desarrollo de las Slices:

  • Herramienta de refactorización de AndroidX: Es necesaria si estás trabajando en un proyecto que usa bibliotecas de AndroidX.
  • Comprobaciones de lint de Slices: Esta herramienta detecta prácticas comunes cuando se compilan Slices.
  • Plantilla SliceProvider: Controla el texto repetitivo cuando se compila un SliceProvider.

Cómo descargar e instalar Slice Viewer

Descarga la versión del APK de Slice Viewer que puedes usar para probar tus Slices sin implementar la API de SliceView.

Si ADB no está configurado correctamente en tu entorno, consulta la Guía de ADB para obtener más información.

Instala Slice Viewer en tu dispositivo ejecutando el siguiente comando en el mismo directorio que el slice-viewer.apk descargado:

adb install -r -t slice-viewer.apk

Cómo ejecutar Slice Viewer

Puedes iniciar Slice Viewer desde tu proyecto de Android Studio o desde la línea de comandos:

Inicia Slice Viewer desde tu proyecto de Android Studio

  1. En tu proyecto, selecciona Run > Edit Configurations….
  2. En la esquina superior izquierda, haz clic en el signo más de color verde.
  3. Selecciona Android App.

  4. Ingresa slice en el campo de nombre.

  5. Selecciona el módulo de tu app en el menú desplegable Module.

  6. En Launch Options, selecciona URL del menú desplegable Launch.

  7. Ingresa slice-<your slice URI> en el campo URL.

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

  8. Haz clic en OK.

Inicia la herramienta Slice Viewer a través de ADB (línea de comandos)

Ejecuta tu app desde Android Studio:

adb install -t -r <yourapp>.apk

Para ver tu Slice, ejecuta el siguiente comando:

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

Slice Viewer muestra una sola Slice de Wi-Fi

Visualiza todas tus Slices en un solo lugar

Además de iniciar una sola Slice, puedes ver la lista permanente de tus Slices.

  • Utiliza la barra de búsqueda para buscar manualmente tus Slices a través del URI (por ejemplo, content://com.example.android.app/hello). Cada vez que realices una búsqueda, la Slice se agregará a la lista.
  • Cada vez que inicies la herramienta Slice Viewer con un URI de Slice, esta se agregará a la lista.
  • Puedes deslizar una Slice para quitarla de la lista.
  • Presiona el URI de la Slice para ver una página que contenga solo esa Slice. Esto tiene el mismo efecto que iniciar Slice Viewer con un URI de Slice.

Slice Viewer con una lista de Slices

Visualiza la Slice en diferentes modos

Una app que presenta una Slice puede modificar el SliceView#mode en el tiempo de ejecución, por lo que debes asegurarte de que tu Slice se muestre según se espera en cada modo. Selecciona el ícono de menú en el área superior derecha de la página para cambiar el modo.

Instancia única de Slice Viewer con el modo definido en "small"

Compila tu primera Slice

Para compilar una Slice, abre tu proyecto de Android Studio, haz clic con el botón derecho en tu paquete src y selecciona New… > Other > Slice Provider. Se creará una clase que extiende SliceProvider, se agregará la entrada de proveedor requerida al AndroidManifest.xml y se modificará tu build.gradle para agregar las dependencias de Slice requeridas.

La modificación de AndroidManifest.xml se muestra a continuación:

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

Las siguientes dependencias se agregan a tu build.gradle:

Kotlin

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

Java

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

Cada Slice tiene un URI asociado. Cuando una superficie quiere mostrar una Slice, envía una solicitud de vinculación a tu app con este URI. Luego, tu app controla esta solicitud y, de forma dinámica, compila la Slice con el método onBindSlice. La superficie puede mostrar luego la Slice cuando corresponda.

A continuación, se muestra un ejemplo de un método onBindSlice que comprueba la ruta del URI /hello y muestra una 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();
}

Usa la configuración de ejecución de la slice que creaste en la Slice anterior de Slice Viewer y pasa el URI (por ejemplo, slice-content://com.android.example.slicesample/hello) de la Slice Hello World para que se vea en Slice Viewer.

Secciones interactivas

De manera similar a las notificaciones, puedes controlar los clics en tu Slice si adjuntas objetos PendingIntent que se activen con la interacción del usuario. En el siguiente ejemplo, se inicia una Activity que puede recibir y controlar esos 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"
    );
}

Las Slices también admiten otros tipos de entrada, como botones de activación, que incluyen el estado en el intent que se envía a la 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);
}

El receptor puede comprobar el estado que recibe:

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

Las Slices también pueden incluir contenido dinámico. En el siguiente ejemplo, la Slice ahora incluye el número de emisiones recibidas en el contenido:

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

En este ejemplo, mientras se muestra el recuento, no se actualiza por sí mismo. Puedes modificar el receptor de emisión para notificar al sistema que se produjo un cambio mediante 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);
        }
    }
}

Plantillas

Las Slices admiten una variedad de plantillas. Para obtener más información sobre las opciones y los comportamientos de las plantillas, consulta Plantillas.