Bug 1329144 - CustomTabsActivity supports ActionButton r=sebastian
authorJulian_Chu <walkingice0204@gmail.com>
Fri, 03 Feb 2017 16:03:21 +0800
changeset 341330 eac05c94053ab43bf8d4c13a48a43c01ff0840f9
parent 341329 e474c788d36a245945e172e0a029cc1b69a9ce2a
child 341331 94a82bf4f8b2450bfa731c4c1168b818c2a44289
push id86684
push usercbook@mozilla.com
push dateWed, 08 Feb 2017 10:31:03 +0000
treeherdermozilla-inbound@c5b88e4e70f4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssebastian
bugs1329144
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1329144 - CustomTabsActivity supports ActionButton r=sebastian 3rd-party-app could launch CustomTabsActivity, and could also configure a custom action button. Now let CustomTabsActivity supports it by creating a MenuItem. MozReview-Commit-ID: 2KuMgBJy2gz
mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
mobile/android/base/resources/values/ids.xml
mobile/android/tests/background/junit4/src/org/mozilla/gecko/customtabs/TestCustomTabsActivity.java
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
@@ -1,23 +1,30 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.customtabs;
 
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.graphics.Bitmap;
 import android.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.view.MenuItemCompat;
 import android.support.v7.app.ActionBar;
 import android.support.v7.widget.Toolbar;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.Window;
 import android.view.WindowManager;
 import android.widget.TextView;
 
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.GeckoApp;
@@ -193,25 +200,59 @@ public class CustomTabsActivity extends 
             final Tab tab = tabs.getTab(lastSelectedTabId);
             if (tab == null) {
                 finish();
             }
         }
         super.onResume();
     }
 
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        insertActionButton(menu, getIntent());
+        return super.onPrepareOptionsMenu(menu);
+    }
+
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
             case android.R.id.home:
                 finish();
                 return true;
+            case R.id.action_button:
+                onActionButtonClicked();
+                return true;
         }
         return super.onOptionsItemSelected(item);
     }
 
+    /**
+     * To insert a MenuItem (as an ActionButton) into Menu.
+     *
+     * @param menu   The options menu in which to place items.
+     * @param intent which to launch this activity
+     * @return the MenuItem which be created and inserted into menu. Otherwise, null.
+     */
+    @VisibleForTesting
+    MenuItem insertActionButton(Menu menu, Intent intent) {
+        if (!IntentUtil.hasActionButton(intent)) {
+            return null;
+        }
+
+        // TODO: Bug 1336373 - Action button icon should support tint
+        MenuItem item = menu.add(Menu.NONE,
+                R.id.action_button,
+                Menu.NONE,
+                IntentUtil.getActionButtonDescription(intent));
+        Bitmap bitmap = IntentUtil.getActionButtonIcon(intent);
+        item.setIcon(new BitmapDrawable(getResources(), bitmap));
+        MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_ALWAYS);
+
+        return item;
+    }
+
     private void updateActionBarWithToolbar(final Toolbar toolbar) {
         setSupportActionBar(toolbar);
         final ActionBar ab = getSupportActionBar();
         if (ab != null) {
             ab.setDisplayHomeAsUpEnabled(true);
         }
     }
 
@@ -223,9 +264,18 @@ public class CustomTabsActivity extends 
         toolbar.setBackgroundColor(toolbarColor);
 
         final Window window = getWindow();
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
             window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
             window.setStatusBarColor(ColorUtil.darken(toolbarColor, 0.25));
         }
     }
+
+    private void onActionButtonClicked() {
+        PendingIntent pendingIntent = IntentUtil.getActionButtonPendingIntent(getIntent());
+        try {
+            pendingIntent.send();
+        } catch (PendingIntent.CanceledException e) {
+            Log.w(LOGTAG, "Action Button clicked, but pending intent was canceled", e);
+        }
+    }
 }
--- a/mobile/android/base/resources/values/ids.xml
+++ b/mobile/android/base/resources/values/ids.xml
@@ -14,10 +14,11 @@
     <item type="id" name="recycler_view_click_support" />
     <item type="id" name="range_list"/>
     <item type="id" name="pref_header_general"/>
     <item type="id" name="pref_header_privacy"/>
     <item type="id" name="pref_header_search"/>
     <item type="id" name="updateServicePermissionNotification" />
     <item type="id" name="websiteContentNotification" />
     <item type="id" name="foregroundNotification" />
+    <item type="id" name="action_button"/>
 
 </resources>
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/customtabs/TestCustomTabsActivity.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/customtabs/TestCustomTabsActivity.java
@@ -1,26 +1,34 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 package org.mozilla.gecko.customtabs;
 
+import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.support.annotation.AnimRes;
 import android.support.customtabs.CustomTabsIntent;
+import android.view.Menu;
+import android.view.MenuItem;
 
 import junit.framework.Assert;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.internal.util.reflection.Whitebox;
+import org.mozilla.gecko.R;
 import org.mozilla.gecko.background.testhelpers.TestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.fakes.RoboMenu;
 
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -37,16 +45,17 @@ public class TestCustomTabsActivity {
     private final int exitRes = 0x456; // arbitrary number as animation resource id
 
     @Before
     public void setUp() {
         spyContext = spy(RuntimeEnvironment.application);
         doReturn(THIRD_PARTY_PACKAGE_NAME).when(spyContext).getPackageName();
 
         spyActivity = spy(new CustomTabsActivity());
+        doReturn(RuntimeEnvironment.application.getResources()).when(spyActivity).getResources();
 
         final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
         builder.setExitAnimations(spyContext, enterRes, exitRes);
         final Intent i = builder.build().intent;
     }
 
     /**
      * Activity should not call overridePendingTransition if custom animation does not exist.
@@ -86,9 +95,36 @@ public class TestCustomTabsActivity {
         builder.setExitAnimations(spyContext, enterRes, exitRes);
         final Intent i = builder.build().intent;
 
         doReturn(i).when(spyActivity).getIntent();
         Whitebox.setInternalState(spyActivity, "usingCustomAnimation", true);
 
         Assert.assertEquals(THIRD_PARTY_PACKAGE_NAME, spyActivity.getPackageName());
     }
+
+    @Test
+    public void testInsertActionButton() {
+        // create properties for CustomTabsIntent
+        final String description = "Description";
+        final Intent actionIntent = new Intent(Intent.ACTION_VIEW);
+        final int reqCode = 0x123;
+        final PendingIntent pendingIntent = PendingIntent.getActivities(spyContext,
+                reqCode,
+                new Intent[]{actionIntent},
+                PendingIntent.FLAG_CANCEL_CURRENT);
+        final Bitmap bitmap = BitmapFactory.decodeResource(
+                spyContext.getResources(),
+                R.drawable.ic_action_settings); // arbitrary icon resource
+
+        // To create a CustomTabsIntent which is asking for ActionButton.
+        final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
+        builder.setActionButton(bitmap, description, pendingIntent, true);
+
+        // CustomTabsActivity should return a MenuItem with corresponding attributes.
+        Menu menu = new RoboMenu(spyContext);
+        MenuItem item = spyActivity.insertActionButton(menu, builder.build().intent);
+        Assert.assertNotNull(item);
+        Assert.assertEquals(item.getTitle(), description);
+        Assert.assertEquals(0, item.getOrder()); // should be the first one
+        Assert.assertTrue(item.isVisible());
+    }
 }
\ No newline at end of file