Bug 1329144 - IntentUtil can extract ActionButton info from Intent
To support ActionButton in CustomTabsActivity, information should be
extract from intent.
MozReview-Commit-ID: 3C19U0EQfV6
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/IntentUtil.java
+++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/IntentUtil.java
@@ -1,16 +1,18 @@
/* -*- 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.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.customtabs.CustomTabsIntent;
/**
* A utility class for CustomTabsActivity to extract information from intent.
* For example, this class helps to extract exit-animation resource id.
@@ -23,16 +25,85 @@ class IntentUtil {
private static final String PREFIX = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
? "android:activity."
: "android:";
private static final String KEY_PACKAGE_NAME = PREFIX + "packageName";
private static final String KEY_ANIM_ENTER_RES_ID = PREFIX + "animEnterRes";
private static final String KEY_ANIM_EXIT_RES_ID = PREFIX + "animExitRes";
/**
+ * To determine whether the intent has necessary information to build an Action-Button.
+ *
+ * @param intent which to launch a Custom-Tabs-Activity
+ * @return true, if intent has all necessary information.
+ */
+ static boolean hasActionButton(@NonNull Intent intent) {
+ return (getActionButtonBundle(intent) != null)
+ && (getActionButtonIcon(intent) != null)
+ && (getActionButtonDescription(intent) != null)
+ && (getActionButtonPendingIntent(intent) != null);
+ }
+
+ /**
+ * To extract bitmap icon from intent for Action-Button.
+ *
+ * @param intent which to launch a Custom-Tabs-Activity
+ * @return bitmap icon, if any. Otherwise, null.
+ */
+ static Bitmap getActionButtonIcon(@NonNull Intent intent) {
+ final Bundle bundle = getActionButtonBundle(intent);
+ return (bundle == null) ? null : (Bitmap) bundle.getParcelable(CustomTabsIntent.KEY_ICON);
+ }
+
+ /**
+ * To extract description from intent for Action-Button. This description is used for
+ * accessibility.
+ *
+ * @param intent which to launch a Custom-Tabs-Activity
+ * @return description, if any. Otherwise, null.
+ */
+ static String getActionButtonDescription(@NonNull Intent intent) {
+ final Bundle bundle = getActionButtonBundle(intent);
+ return (bundle == null) ? null : bundle.getString(CustomTabsIntent.KEY_DESCRIPTION);
+ }
+
+ /**
+ * To extract pending-intent from intent for Action-Button.
+ *
+ * @param intent which to launch a Custom-Tabs-Activity
+ * @return PendingIntent, if any. Otherwise, null.
+ */
+ static PendingIntent getActionButtonPendingIntent(@NonNull Intent intent) {
+ final Bundle bundle = getActionButtonBundle(intent);
+ return (bundle == null)
+ ? null
+ : (PendingIntent) bundle.getParcelable(CustomTabsIntent.KEY_PENDING_INTENT);
+ }
+
+ /**
+ * To know whether the Action-Button should be tinted.
+ *
+ * @param intent which to launch a Custom-Tabs-Activity
+ * @return true, if Action-Button should be tinted. Default value is false.
+ */
+ static boolean isActionButtonTinted(@NonNull Intent intent) {
+ return intent.getBooleanExtra(CustomTabsIntent.EXTRA_TINT_ACTION_BUTTON, false);
+ }
+
+ /**
+ * To extract extra Action-button bundle from an intent.
+ *
+ * @param intent which to launch a Custom-Tabs-Activity
+ * @return bundle for Action-Button, if any. Otherwise, null.
+ */
+ private static Bundle getActionButtonBundle(@NonNull Intent intent) {
+ return intent.getBundleExtra(CustomTabsIntent.EXTRA_ACTION_BUTTON_BUNDLE);
+ }
+
+ /**
* To get package name of 3rd-party-app from an intent.
* If the app defined extra exit-animation to use, it should also provide its package name
* to get correct animation resource.
*
* @param intent which to launch a Custom-Tabs-Activity
* @return package name, if the intent defined extra exit-animation bundle. Otherwise, null.
*/
static String getAnimationPackageName(@NonNull Intent intent) {
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/customtabs/TestIntentUtil.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/customtabs/TestIntentUtil.java
@@ -1,41 +1,87 @@
/* 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.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.support.annotation.AnimRes;
import android.support.customtabs.CustomTabsIntent;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mozilla.gecko.R;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.robolectric.RuntimeEnvironment;
+import java.util.Objects;
+
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
@RunWith(TestRunner.class)
public class TestIntentUtil {
private static final String THIRD_PARTY_PACKAGE_NAME = "mozilla.unit.test";
private Context spyContext; // 3rd party app context
@Before
public void setUp() {
spyContext = spy(RuntimeEnvironment.application);
doReturn(THIRD_PARTY_PACKAGE_NAME).when(spyContext).getPackageName();
}
@Test
+ public void testIntentWithActionButton() {
+ // create properties for CustomTabsIntent
+ final String description = "Description";
+ final boolean tinted = true;
+ 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
+
+ final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
+ builder.setActionButton(bitmap, description, pendingIntent, tinted);
+
+ Intent intent = builder.build().intent;
+ Assert.assertTrue(IntentUtil.hasActionButton(intent));
+ Assert.assertEquals(tinted, IntentUtil.isActionButtonTinted(intent));
+ Assert.assertEquals(bitmap, IntentUtil.getActionButtonIcon(intent));
+ Assert.assertEquals(description, IntentUtil.getActionButtonDescription(intent));
+ Assert.assertTrue(
+ Objects.equals(pendingIntent, IntentUtil.getActionButtonPendingIntent(intent)));
+ }
+
+ @Test
+ public void testIntentWithoutActionButton() {
+ final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
+
+ Intent intent = builder.build().intent;
+ Assert.assertFalse(IntentUtil.hasActionButton(intent));
+ Assert.assertFalse(IntentUtil.isActionButtonTinted(intent));
+ Assert.assertNull(IntentUtil.getActionButtonIcon(intent));
+ Assert.assertNull(IntentUtil.getActionButtonDescription(intent));
+ Assert.assertNull(IntentUtil.getActionButtonPendingIntent(intent));
+ }
+
+ @Test
public void testIntentWithCustomAnimation() {
@AnimRes final int enterRes = 0x123; // arbitrary number as animation resource id
@AnimRes final int exitRes = 0x456; // arbitrary number as animation resource id
final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
builder.setExitAnimations(spyContext, enterRes, exitRes);
final Intent i = builder.build().intent;