[Custom Tabs] Add updateVisuals() in CustomTabsService

In ICustomTabsService.aidl, updateVisuals() is a generic API that
takes a bundle and an IBinder. The bundle needs to have the same structure as
a custom tabs VIEW INTENT, in terms of UI customizations.

In CustomTabsSession, a public method is created for updating the action
button for the current session with a new Bitmap icon and new content
description.

Change-Id: I6f30da7c524599342997987081838c73a54e4880
diff --git a/customtabs/api/current.txt b/customtabs/api/current.txt
index 7343163..caa5ea4 100644
--- a/customtabs/api/current.txt
+++ b/customtabs/api/current.txt
@@ -62,6 +62,7 @@
     method protected abstract boolean mayLaunchUrl(android.support.customtabs.CustomTabsSessionToken, android.net.Uri, android.os.Bundle, java.util.List<android.os.Bundle>);
     method protected abstract boolean newSession(android.support.customtabs.CustomTabsSessionToken);
     method public android.os.IBinder onBind(android.content.Intent);
+    method protected abstract boolean updateVisuals(android.support.customtabs.CustomTabsSessionToken, android.os.Bundle);
     method protected abstract boolean warmup(long);
     field public static final java.lang.String ACTION_CUSTOM_TABS_CONNECTION = "android.support.customtabs.action.CustomTabsService";
     field public static final java.lang.String KEY_URL = "android.support.customtabs.otherurls.URL";
@@ -75,6 +76,7 @@
 
   public final class CustomTabsSession {
     method public boolean mayLaunchUrl(android.net.Uri, android.os.Bundle, java.util.List<android.os.Bundle>);
+    method public boolean setActionButton(android.graphics.Bitmap, java.lang.String);
   }
 
   public class CustomTabsSessionToken {
diff --git a/customtabs/src/android/support/customtabs/CustomTabsService.java b/customtabs/src/android/support/customtabs/CustomTabsService.java
index 56940e2..25697c5 100644
--- a/customtabs/src/android/support/customtabs/CustomTabsService.java
+++ b/customtabs/src/android/support/customtabs/CustomTabsService.java
@@ -90,6 +90,12 @@
          public Bundle extraCommand(String commandName, Bundle args) {
              return CustomTabsService.this.extraCommand(commandName, args);
          }
+
+         @Override
+         public boolean updateVisuals(ICustomTabsCallback callback, Bundle bundle) {
+             return CustomTabsService.this.updateVisuals(
+                     new CustomTabsSessionToken(callback), bundle);
+         }
      };
 
      @Override
@@ -176,4 +182,15 @@
       * @return The result {@link Bundle}, or null.
       */
      protected abstract Bundle extraCommand(String commandName, Bundle args);
+
+    /**
+     * Updates the visuals of custom tabs for the given session. Will only succeed if the given
+     * session matches the currently active one.
+     * @param sessionToken The currently active session that the custom tab belongs to.
+     * @param bundle       The action button configuration bundle. This bundle should be constructed
+     *                     with the same structure in {@link CustomTabsIntent.Builder}.
+     * @return Whether the operation was successful.
+     */
+     protected abstract boolean updateVisuals(CustomTabsSessionToken sessionToken,
+             Bundle bundle);
  }
diff --git a/customtabs/src/android/support/customtabs/CustomTabsSession.java b/customtabs/src/android/support/customtabs/CustomTabsSession.java
index ee45b7c..16fe06d 100644
--- a/customtabs/src/android/support/customtabs/CustomTabsSession.java
+++ b/customtabs/src/android/support/customtabs/CustomTabsSession.java
@@ -17,10 +17,12 @@
 package android.support.customtabs;
 
 import android.content.ComponentName;
+import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.support.annotation.NonNull;
 
 import java.util.List;
 
@@ -65,6 +67,27 @@
         }
     }
 
+    /**
+     * Update the visuals for the button on a custom tab. Will only succeed if the given
+     * session is the active one in browser.
+     * @param icon          The new icon of tbe action button.
+     * @param description   Content description of the action button.
+     * @return              Whether the update succeeded.
+     */
+    public boolean setActionButton(@NonNull Bitmap icon, @NonNull String description) {
+        Bundle bundle = new Bundle();
+        bundle.putParcelable(CustomTabsIntent.KEY_ICON, icon);
+        bundle.putString(CustomTabsIntent.KEY_DESCRIPTION, description);
+
+        Bundle metaBundle = new Bundle();
+        metaBundle.putBundle(CustomTabsIntent.EXTRA_ACTION_BUTTON_BUNDLE, bundle);
+        try {
+            return mService.updateVisuals(mCallback, metaBundle);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
     /* package */ IBinder getBinder() {
         return mCallback.asBinder();
     }
@@ -72,4 +95,4 @@
     /* package */ ComponentName getComponentName() {
         return mComponentName;
     }
-}
\ No newline at end of file
+}
diff --git a/customtabs/src/android/support/customtabs/ICustomTabsService.aidl b/customtabs/src/android/support/customtabs/ICustomTabsService.aidl
index 2565928..e53ca84 100644
--- a/customtabs/src/android/support/customtabs/ICustomTabsService.aidl
+++ b/customtabs/src/android/support/customtabs/ICustomTabsService.aidl
@@ -32,4 +32,5 @@
     boolean mayLaunchUrl(in ICustomTabsCallback callback, in Uri url,
             in Bundle extras, in List<Bundle> otherLikelyBundles) = 3;
     Bundle extraCommand(String commandName, in Bundle args) = 4;
+    boolean updateVisuals(in ICustomTabsCallback callback, in Bundle bundle) = 5;
 }