Bug 1329144 - CustomTabsActivity supports ActionButton
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
--- 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