Cómo crear una app que se ejecuta en un perfil de trabajo

1. Antes de comenzar

¿Qué es un perfil de trabajo?

Un perfil de trabajo es un perfil secundario que se puede habilitar en el dispositivo personal de un usuario cuando una empresa permite que sus empleados utilicen sus dispositivos personales para trabajar.

Un administrador de TI puede controlar los perfiles de trabajo, y la funcionalidad disponible para ellos se configura de manera independiente a las funciones del perfil principal del usuario. Este enfoque permite que las organizaciones controlen el entorno en el que las apps y datos específicos de la empresa se ejecutan en el dispositivo de un usuario, a la vez que permiten que sus usuarios utilicen sus aplicaciones y perfiles personales.

¿Cómo afecta a tu app? Cualquier app se puede instalar en un perfil de trabajo, lo que significa que puede enfrentar restricciones de tiempo de ejecución y cambios de comportamiento. También, te recomendamos que te asegures de que tu app sea confiable si se usa con fines laborales. Incluso si tu app se ejecuta en un perfil personal, un perfil de trabajo puede afectar la manera en que debe comportarse tu app.

Requisitos previos

Este codelab se diseñó para desarrolladores de Android con habilidades básicas e intermedias.

Se supone que ya creaste una app, usaste Android Studio y la probaste en un dispositivo o emulador.

Actividades

En este codelab, modificarás una app para que ofrezca la mejor experiencia del usuario cuando se instale en un dispositivo con un perfil de trabajo. Aprenderás a configurar tu app para que haga lo siguiente:

  • Procese los contactos personales y de trabajo al mismo tiempo.
  • Cambia entre perfiles personales y de trabajo desde la misma app.

caf809dbd1e16c75.png

Requisitos

  • Un dispositivo Android no administrado (que no pertenezca a una organización ni que esté administrado por una).

2. Prepárate

Cómo configurar un dispositivo de prueba

Para este codelab, te recomendamos que uses un dispositivo físico. Sin embargo, puedes realizar la misma configuración a continuación en un emulador con una imagen que incluya Google Play Store.

TestDPC

Google crea la app TestDPC para ayudarte a simular y probar un entorno administrado en tu propio dispositivo. Esta herramienta configurará un perfil de trabajo y te proporcionará controles para habilitar o inhabilitar ciertas funciones en el dispositivo, al igual que un administrador de TI.

Cómo instalar la app TestDPC

En tu dispositivo, abre Google Play Store y descarga la app TestDPC.

Cómo configurar un perfil de trabajo

Una vez que se instale la app TestDPC, observarás 2 íconos en el dispositivo, un ícono de configuración y el ícono de la app TestDPC. Presiona el ícono de configuración y sigue los pasos.

Ahora, tienes dos perfiles independientes: uno para las apps personales y otro para las apps de trabajo. Puedes cambiar entre estos por medio de las pestañas en la parte superior de la lista de apps.

Ten en cuenta que cada perfil tiene su propia app de Play Store. Puedes identificar las apps de trabajo desde la foto pequeña de maletín en la parte superior del ícono de selector.

46175af7ad32979d.gif

Puedes instalar apps mediante Play Store como lo sueles hacer, y según la versión de Play Store en la que inicias la app (perfil personal o de trabajo), esta app solo se instalará en ese perfil. Las apps también pueden existir en ambos perfiles cuando se instalan desde ambos Play Store. En ese caso, cada versión de la app tendrá espacios de almacenamiento y configuración completamente aislados.

Cómo instalar la app en un perfil específico

En los siguientes párrafos, verificaremos cómo podemos alternar entre el perfil predeterminado y el de trabajo con la clase CrossProfileApps. Para confirmar ese comportamiento, debes instalar la app en ambos perfiles.

Puedes confirmar el número de ID del perfil con el comando adb que aparece a continuación.

$ adb shell pm list users

Puedes instalar una app en un perfil designado con el comando ADB que aparece a continuación.

$ adb install --user [id number of profile] [path of apk file]

Puedes obtener los mismos resultados y establecer la configuración de ejecución y depuración en tu proyecto si seleccionas la opción "Install for all users".

7634e3dcb0a744ca.png

Cuando ejecutes la app desde Android Studio para actualizarla, esta se instalará en ambos perfiles.

3. Carga contactos

Configura algunos contactos de prueba para usar en la app de demostración:

  1. Inicia la app de Contactos del dispositivo desde el perfil personal.
  2. Agrega algunos contactos de prueba que puedas identificar como contactos personales.
  3. Inicia la app de Contactos desde el perfil de trabajo. (No verás ninguno de los contactos personales que recién agregaste).
  4. Agrega algunos contactos de prueba que puedas identificar como contactos de trabajo.

Una vez que estés conforme con los contactos que configuraste, prueba el código de partida de la app de demo.

4. Obtén el código de partida

  1. Para obtener la app de ejemplo, haz lo siguiente:
  • clona el repositorio desde GitHub
$ git clone https://github.com/android/enterprise-samples.git

$ cd enterprise-samples/Work-profile-codelab
  • También puedes descargar el proyecto desde el siguiente vínculo.

Descargar ZIP

  1. Abre y ejecuta la app en Android Studio.

Cuando inicies la app por primera vez, se verá de la siguiente manera:

f9779ab476511718.png

Ponlo a prueba

Al final de este codelab, tu app mostrará contactos personales y de trabajo cuando se ejecute en el perfil personal. También podrás cambiar entre perfiles. Para ello, inicia otra instancia de la app en el otro perfil desde la misma app.

5. Muestra contactos personales y de trabajo

Cuando cargues contactos con ContactsContract.Contacts.CONTENT_URI, la app decidirá qué contactos mostrar, según el perfil en el que se esté ejecutando. Sin embargo, en muchos casos, es posible que desees que la app cargue ambas listas de contactos al mismo tiempo. Por ejemplo, es posible que el usuario desee compartir un elemento personal (foto, documento) con un colega del trabajo. Para ello, deberás recuperar ambas listas de contactos.

Abre MainActivity.kt

El método onCreateLoader() se encarga de crear el elemento CursorLoader para recuperar y cargar los contactos. Por el momento, solo muestra un elemento CursorLoader con el objeto ContentURI predeterminado. Llamarás a este método dos veces, una para los contactos personales y la otra para los contactos de trabajo. Con el objeto de diferenciarlos, pasaremos un ID diferente a onCreateLoader() para cada caso. Deberás verificar el ID que se pasó al método a fin de decidir qué ContentURI usar.

Primero, cambia el valor de la variable ContentURI según el valor de ID que se pasó al método. En el caso de PERSONAL_CONTACTS_LOADER_ID, asígnalo al objeto ContactsContract.Contacts.CONTENT_URI predeterminado. De lo contrario, crearás un elemento ENTERPRISE_CONTENT_FILTER_URI como se describe aquí.

ContactsContract.Contacts.ENTERPRISE_CONTENT_FILTER_URI
                    .buildUpon()
                    .appendPath(nameFilter)
                    .appendQueryParameter(
                        ContactsContract.DIRECTORY_PARAM_KEY,
                        ContactsContract.Directory.ENTERPRISE_DEFAULT.toString()
                    )
                    .build()

Observarás que, como se trata de un URI de filtro de contenido, el creador necesita un filtro de búsqueda (frase de búsqueda) para usarlo cuando se realizan búsquedas o se cargan los contactos.

Por ahora, codifica la frase de búsqueda para que sea cualquier nombre que comience con la letra "a".

val nameFilter = Uri.encode("a") // names that start with a

También, debes especificar el directorio de contactos en el que se realizará la búsqueda. Usarás el directorio ENTERPRISE_DEFAULT que busca contactos almacenados, de manera local, en el dispositivo.

El método onCreateLoader() debería verse de la siguiente manera:

override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
        val nameFilter = Uri.encode("a") // names that start with a
        val contentURI = when (id) {
            PERSONAL_CONTACTS_LOADER_ID -> ContactsContract.Contacts.CONTENT_URI
            else -> {
                ContactsContract.Contacts.ENTERPRISE_CONTENT_FILTER_URI
                    .buildUpon()
                    .appendPath(nameFilter)
                    .appendQueryParameter(
                        ContactsContract.DIRECTORY_PARAM_KEY,
                        ContactsContract.Directory.ENTERPRISE_DEFAULT.toString()
                    )
                    .build()
            }
        }
        return CursorLoader(
            this, contentURI, arrayOf(
                ContactsContract.Contacts.DISPLAY_NAME_PRIMARY
            ), null, null, null
        )
    }

Ahora, debes inicializar otro elemento Loader con un valor nuevo de ID para activar el método anterior.

Primero, crea un nuevo valor de ID constante para los contactos de trabajo en la parte superior de MainActivity:

const val WORK_CONTACTS_LOADER_ID = 1

Luego, en initLoaders(), usa LoaderManager para inicializar un elemento Loader nuevo con el ID nuevo que se creó anteriormente:

private fun initLoaders() {
        LoaderManager.getInstance(this).
            initLoader(PERSONAL_CONTACTS_LOADER_ID, null, this)
        LoaderManager.getInstance(this).
            initLoader(WORK_CONTACTS_LOADER_ID, null, this)
    }

Todos los demás métodos deben funcionar de la misma manera, ya que el cursor de datos de ambos cargadores tiene la misma estructura.

Ponlo a prueba

Ejecuta la app en el perfil personal. Ahora, puedes ver los contactos personales y de trabajo.

3b8f9c73feee88fb.png

¿Qué sucede con el perfil de trabajo?

Si ejecutas la app en el perfil de trabajo, solo verás los contactos de trabajo y ninguno de los personales. Esto se debe a que uno de los objetivos principales de los perfiles de trabajo es proteger la privacidad del usuario. Por este motivo, en general, las apps de trabajo no pueden acceder a ningún tipo de información personal desde el perfil personal.

9312158a2dc03891.png

6. Cambia de perfiles en una app

Android incluye API para iniciar otra instancia de tu app en un perfil diferente, lo que permite que los usuarios cambien entre las cuentas. Por ejemplo, una app de correo electrónico puede proporcionar una IU que le permita al usuario pasar del perfil personal al de trabajo para acceder a dos cuentas de correo electrónico.

Todas las apps pueden llamar a estas API para lanzar la actividad principal de la misma app si ya está instalada en el otro perfil.

Para agregar a tu app el cambio de cuenta en diferentes perfiles, primero, debes agregar un botón a nuestro diseño de actividad principal, de modo que los usuarios puedan cambiar de perfil.

Abre activity_main.xml y agrega un widget de botón debajo del widget de la vista de reciclador:

<androidx.appcompat.widget.AppCompatButton
        android:id="@+id/button"
        app:layout_constraintTop_toBottomOf="@+id/contacts_rv"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />

En MainActivity.kt, configura el evento de clic del botón para cambiar de perfil en el método onCreate.

Para ello, primero, debes obtener el servicio del sistema CrossProfileApps:

val crossProfileApps = getSystemService(CrossProfileApps::class.java)

Esta clase brinda todas las API que necesitas para implementar una función de cambio de perfil. Para recuperar la lista de perfiles del usuario, llama a targetUserProfiles, que mostrará todos los demás perfiles en los que se instaló esta app.

val userHandles = crossProfileApps.targetUserProfiles

Ahora, puedes usar el primer elemento userHandle que se muestra e iniciar la app en el otro perfil.

crossProfileApps.startMainActivity(
                    componentName,
                    userHandles.first()
                )

Incluso, puedes obtener un texto localizado que le solicite al usuario cambiar de perfil y usarlo para configurar el valor del texto del botón.

val label = crossProfileApps.getProfileSwitchingLabel(userHandles.first())

Ahora, uniendo todas estas partes, esto es lo que agregarás al final del método onCreate en MainActivity.kt:

override fun onCreate(savedInstanceState: Bundle?) {

     ...

     val crossProfileApps = getSystemService(CrossProfileApps::class.java)
       val userHandles = crossProfileApps.targetUserProfiles
       val label = crossProfileApps.getProfileSwitchingLabel(userHandles.first())
        binding.button.apply {
            text = label
            setOnClickListener {
                crossProfileApps.startMainActivity(
                    componentName,
                    userHandles.first()
                )
            }
        }
}

Ponlo a prueba

Si ejecutas la app ahora, observarás el botón en la parte inferior que indica que está lista para cambiar al perfil de trabajo o al perfil personal, según desde dónde hayas iniciado la app.

Si haces clic en ese botón, se iniciará la app en el otro perfil.

db741d4872052fbc.gif

7. ¡Felicitaciones!

Modificaste con éxito una app que funciona tanto en el perfil personal como en el perfil de trabajo, reconoce cuando hay un perfil de trabajo instalado y recupera los contactos de trabajo incluso cuando se ejecuta en el modo personal.

Además, implementaste una manera para que los usuarios cambien entre el perfil de trabajo y el perfil personal de la misma app mientras ejecutan la app sin tener que cerrarla ni reiniciarla desde el perfil correcto. Te recomendamos que ayudes a los usuarios que usan tu app de manera diferente en los distintos perfiles.

Más información