Android: Integración con Chrome (Custom Tabs)

android

A finales del verano de 2015, Google publicó dentro de las librerías de compatibilidad un nuevo módulo para permitir realizar cierta integración entre nuestras apps y el navegador Chrome 45+. El objetivo consiste básicamente en poder solicitar a Chrome mostrar un contenido web y que el navegador «parezca» que forma parte de nuestra app y no de la sensación al usuario que hemos salido de la misma.

Algunas de las funcionalidades que ofrece esta integración:

  • Botón cerrar en la toolbar para retornar a la Activity de nuestra app
  • Color de la Toolbar
  • Entradas personalizadas en el menú.
  • «Precarga» en segundo plano de Chrome al iniciarse la Activity de tal modo que al abrirse Chrome desde la misma la carga sea más rápida con respecto a abrir un navegador o incluso un WebView.
  • Capturar eventos de navegación o pulsación de enlaces como si fuera un WebView.

La limitación que tenemos es que el dispositivo ha de tener instalado Chrome y, si no implementamos la funcionalidad de precarga que veremos al final de este tutorial, además debe estar configurado como navegador por defecto. Si no se verifican estas restricciones la url se mostrará en el navegador por defecto (mismo comportamiento que si no utilizamos custom tabs).

La especificación de la integración con Chrome es abierta por lo que puede ser implementada por otros navegadores en un futuro.

Para poder abrir una instancia de Chrome desde nuestra app y beneficiarnos de esta integración seguiremos los siguientes pasos.

  1. Incluir la correspondiente librería de compatibilidad.
    compile 'com.android.support:customtabs:23.2.1'
  2. Solicitar abrir una url con la clase CustomTabsIntent. Utilizaremos un builder.
      public void openTab(View view) {
        String url = "https://www.danielme.com";
        CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();   
        CustomTabsIntent customTabsIntent = builder.build();
        customTabsIntent.launchUrl(this, Uri.parse(url));
      }
    

La ejecución del método anterior muestra la url en Chrome siempre y cuando sea el navegador por defecto.

android custom tab - default

Las personalizaciones más fáciles que podemos hacer son las siguientes:

  • Color de la ActionBar
    builder.setToolbarColor(ContextCompat.getColor(this, R.color.primary));
    
  • Cambiar el icono del botón de cerrar por la flecha de volver. Este icono lo debemos tener en nuestros drawables.
    builder.setCloseButtonIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_arrow_back));
    
  • Mostrar el title de la página además de la url.
    builder.setShowTitle(true);
    
  • android custom tab - customized

    Añadir elementos al menú

    Se pueden añadir hasta cinco entradas personalizadas en el menú de Chrome. Los pasos para añadir un elemento son los siguientes:

    1. Crear un broadcast receiver para atender la pulsación del elemento del menú. En este ejemplo recibimos un texto y lo mostramos en un Toast.
      package com.danielme.android.customtabs;
      
      import android.content.BroadcastReceiver;
      import android.content.Context;
      import android.content.Intent;
      import android.widget.Toast;
      
      public class CustomTabsBroadcastReceiver extends BroadcastReceiver {
      
        @Override
        public void onReceive(Context context, Intent intent) {
      
          Toast.makeText(context, intent.getStringExtra("text"), Toast.LENGTH_SHORT).show();
      
        }
      }
      
      
    2. Registrar el brodcast en el AndroidManifest.xml.
       <receiver
                  android:name=".CustomTabsBroadcastReceiver"
                  android:enabled="true">
       </receiver>
      
    3. Añadir la entrada del menú en el builder. En el ejemplo se han añadido dos, no se ha abstraído el código para que se vea más claro. Se envía el texto que queremos recibir en el broadcast receiver.
         
          Intent intent1 = new Intent(this, CustomTabsBroadcastReceiver.class);
          intent1.putExtra("text", getString(R.string.menu1));
          PendingIntent pendingIntent1 = PendingIntent.getBroadcast(this, 1, intent1, 0);
          builder.addMenuItem(getString(R.string.menu1), pendingIntent1);
      
          Intent intent2 = new Intent(this, CustomTabsBroadcastReceiver.class);
          intent2.putExtra("text", getString(R.string.menu2));
          PendingIntent pendingIntent2 = PendingIntent.getBroadcast(this, 2, intent2, 0);
          builder.addMenuItem(getString(R.string.menu2), pendingIntent2);
      

    android custom tab - menu

    Añadir botón

    También podemos añadir un botón a la ActionBar de Chrome siguiendo los mismos pasos que acabamos de utilizar para el menú. La única diferencia es que hay que llamar al método setActionButton en lugar de addMenuItem pasándole el icono a utilizar.

    Bitmap icon = BitmapFactory.decodeResource(getResources(),
            android.R.drawable.ic_menu_add);
    builder.setActionButton(icon, getString(R.string.action), pendingIntent, true);
    

    android custom tab - actionbutton

    Precargar Chrome

    Chrome proporciona un servicio que permite realizar una especie de «precarga» en segundo plano para que al solicitarse la visualización de una url la carga del navegador sea más rápida.

    Para registrar el servicio llamaremos al método CustomTabsClient#bindCustomTabsService proporcionándole una implementación de CustomTabsServiceConnection. En esta implementación cuando recibamos la conexión con el servicio realizaremos la «precarga» o warmup de Chrome y si conocemos de antemano la url que probablemente va a solicitar el usuario también podemos indicarla. Asimismo hay que crear una sesión (CustomTabsSession) que tendremos que utilizar al mostrar la url en Chrome. Reutilizaremos siempre la misma sesión para mayor eficiencia.

    Nota 1: es conveniente «desregistrar» el servicio en el onDestroy para evitar memory leaks.

    Nota 2: el método onNavigationEvent se invocará cada vez que se solicite una url desde Chrome.

      private static final String URL = "https://www.danielme.com";
      private static final String CHROME_PACKAGE = "com.android.chrome";
    
      private CustomTabsServiceConnection ctConnection;
      private CustomTabsSession customTabsSession;
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
    
        ctConnection = new CustomTabsServiceConnection() {
          @Override
          public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabsClient
              customTabsClient) {
            customTabsClient.warmup(0);
            customTabsSession = getSession(customTabsClient);
            customTabsSession.mayLaunchUrl(Uri.parse(URL), null, null);
          }
    
          @Override
          public void onServiceDisconnected(ComponentName componentName) {
            //nothing here
          }
        };
    
        CustomTabsClient.bindCustomTabsService(this, CHROME_PACKAGE, ctConnection);
    
      }
    
      @Override
      protected void onDestroy() {
        super.onDestroy();
        if (ctConnection != null) {
          unbindService(ctConnection);
        }    
      }
    
      private CustomTabsSession getSession(CustomTabsClient customTabsClient) {
        if (customTabsClient != null) {
          return customTabsClient.newSession(new CustomTabsCallback() {
            /*@Override
            public void onNavigationEvent(int navigationEvent, Bundle extras) {
              super.onNavigationEvent(navigationEvent, extras);
            }*/
          });
        }
        return null;
      }
    
      public void openTab(View view) {
        CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(customTabsSession);
    

    Proyecto de ejemplo

    El proyecto completo se encuentra disponible en GitHub. Para más información sobre cómo utilizar GitHub, consultar este artículo.

Deja una respuesta

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.