Cómo comenzar

En esta página, se muestra cómo configurar tu entorno y compilar secciones 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 secciones:

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

Cómo descargar e instalar Slice Viewer

Descarga la versión del APK de Slice Viewer que puedes usar para probar tus secciones sin implementar la API SliceView. También puedes clonar la fuente de Slice Viewer.

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 sección, ejecuta el siguiente comando:

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

Slice Viewer que muestra una sola sección de Wi-Fi

Visualiza todas tus secciones en un solo lugar

Además de lanzar una sola sección, puedes ver una lista persistente de tus secciones.

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

Slice Viewer con una lista de secciones

Visualiza la sección en diferentes modos

Una app que presenta una sección puede modificar el SliceView#mode en el tiempo de ejecución, por lo que debes asegurarte de que tu sección 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 sección

Para compilar una sección, abre tu proyecto de Android Studio, haz clic con el botón derecho en tu paquete src y selecciona New... > Other > Slice Provider. Esto crea una clase que extiende SliceProvider , agrega la entrada de proveedor requerida al AndroidManifest.xml y modifica tu build.gradle para agregar las dependencias de Sección 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 sección tiene un URI asociado. Cuando una superficie quiere mostrar una sección, 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 sección mediante el método onBindSlice. La superficie puede mostrar luego la sección cuando corresponda.

A continuación, se muestra un ejemplo de un método onBindSlice que comprueba la ruta del URI /hello y muestra una sección 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 lasección que creaste en la sección anterior de Slice Viewer y pasa el URI (por ejemplo, slice-content://com.android.example.slicesample/hello) de la sección Hello World para que se vea en Slice Viewer.

Secciones interactivas

De manera similar a las notificaciones, puedes controlar los clics en tu sección 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"
        );
    }
    

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

Secciones dinámicas

Las secciones también pueden incluir contenido dinámico. En el siguiente ejemplo, la sección 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."
                    }
                }
            }
        }
    }

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

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 secciones admiten una variedad de plantillas. Para obtener más información sobre las opciones y los comportamientos de las plantillas, consulta Plantillas.