summaryrefslogtreecommitdiff
path: root/androidx/browser/browseractions/BrowserActionsIntent.java
diff options
context:
space:
mode:
Diffstat (limited to 'androidx/browser/browseractions/BrowserActionsIntent.java')
-rw-r--r--androidx/browser/browseractions/BrowserActionsIntent.java391
1 files changed, 391 insertions, 0 deletions
diff --git a/androidx/browser/browseractions/BrowserActionsIntent.java b/androidx/browser/browseractions/BrowserActionsIntent.java
new file mode 100644
index 00000000..beb3d6ca
--- /dev/null
+++ b/androidx/browser/browseractions/BrowserActionsIntent.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.browser.browseractions;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+import android.support.v4.content.ContextCompat;
+import android.text.TextUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Class holding the {@link Intent} and start bundle for a Browser Actions Activity.
+ *
+ * <p>
+ * <strong>Note:</strong> The constants below are public for the browser implementation's benefit.
+ * You are strongly encouraged to use {@link BrowserActionsIntent.Builder}.</p>
+ */
+public class BrowserActionsIntent {
+ private static final String TAG = "BrowserActions";
+ // Used to verify that an URL intent handler exists.
+ private static final String TEST_URL = "https://www.example.com";
+
+ /**
+ * Extra that specifies {@link PendingIntent} indicating which Application sends the {@link
+ * BrowserActionsIntent}.
+ */
+ public static final String EXTRA_APP_ID = "androidx.browser.browseractions.APP_ID";
+
+ /**
+ * Indicates that the user explicitly opted out of Browser Actions in the calling application.
+ */
+ public static final String ACTION_BROWSER_ACTIONS_OPEN =
+ "androidx.browser.browseractions.browser_action_open";
+
+ /**
+ * Extra resource id that specifies the icon of a custom item shown in the Browser Actions menu.
+ */
+ public static final String KEY_ICON_ID = "androidx.browser.browseractions.ICON_ID";
+
+ /**
+ * Extra string that specifies the title of a custom item shown in the Browser Actions menu.
+ */
+ public static final String KEY_TITLE = "androidx.browser.browseractions.TITLE";
+
+ /**
+ * Extra PendingIntent to be launched when a custom item is selected in the Browser Actions
+ * menu.
+ */
+ public static final String KEY_ACTION = "androidx.browser.browseractions.ACTION";
+
+ /**
+ * Extra that specifies the type of url for the Browser Actions menu.
+ */
+ public static final String EXTRA_TYPE = "androidx.browser.browseractions.extra.TYPE";
+
+ /**
+ * Extra that specifies List<Bundle> used for adding custom items to the Browser Actions menu.
+ */
+ public static final String EXTRA_MENU_ITEMS =
+ "androidx.browser.browseractions.extra.MENU_ITEMS";
+
+ /**
+ * Extra that specifies the PendingIntent to be launched when a browser specified menu item is
+ * selected. The id of the chosen item will be notified through the data of its Intent.
+ */
+ public static final String EXTRA_SELECTED_ACTION_PENDING_INTENT =
+ "androidx.browser.browseractions.extra.SELECTED_ACTION_PENDING_INTENT";
+
+ /**
+ * The maximum allowed number of custom items.
+ */
+ public static final int MAX_CUSTOM_ITEMS = 5;
+
+ /**
+ * Defines the types of url for Browser Actions menu.
+ */
+ /** @hide */
+ @RestrictTo(LIBRARY_GROUP)
+ @IntDef({URL_TYPE_NONE, URL_TYPE_IMAGE, URL_TYPE_VIDEO, URL_TYPE_AUDIO, URL_TYPE_FILE,
+ URL_TYPE_PLUGIN})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BrowserActionsUrlType {}
+ public static final int URL_TYPE_NONE = 0;
+ public static final int URL_TYPE_IMAGE = 1;
+ public static final int URL_TYPE_VIDEO = 2;
+ public static final int URL_TYPE_AUDIO = 3;
+ public static final int URL_TYPE_FILE = 4;
+ public static final int URL_TYPE_PLUGIN = 5;
+
+ /**
+ * Defines the the ids of the browser specified menu items in Browser Actions.
+ * TODO(ltian): A long term solution need, since other providers might have customized menus.
+ */
+ /** @hide */
+ @RestrictTo(LIBRARY_GROUP)
+ @IntDef({ITEM_INVALID_ITEM, ITEM_OPEN_IN_NEW_TAB, ITEM_OPEN_IN_INCOGNITO, ITEM_DOWNLOAD,
+ ITEM_COPY, ITEM_SHARE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BrowserActionsItemId {}
+ public static final int ITEM_INVALID_ITEM = -1;
+ public static final int ITEM_OPEN_IN_NEW_TAB = 0;
+ public static final int ITEM_OPEN_IN_INCOGNITO = 1;
+ public static final int ITEM_DOWNLOAD = 2;
+ public static final int ITEM_COPY = 3;
+ public static final int ITEM_SHARE = 4;
+
+ /**
+ * An {@link Intent} used to start the Browser Actions Activity.
+ */
+ @NonNull private final Intent mIntent;
+
+ /**
+ * Gets the Intent of {@link BrowserActionsIntent}.
+ * @return the Intent of {@link BrowserActionsIntent}.
+ */
+ @NonNull public Intent getIntent() {
+ return mIntent;
+ }
+
+ private BrowserActionsIntent(@NonNull Intent intent) {
+ this.mIntent = intent;
+ }
+
+ /**
+ * Builder class for opening a Browser Actions context menu.
+ */
+ public static final class Builder {
+ private final Intent mIntent = new Intent(BrowserActionsIntent.ACTION_BROWSER_ACTIONS_OPEN);
+ private Context mContext;
+ private Uri mUri;
+ @BrowserActionsUrlType
+ private int mType;
+ private ArrayList<Bundle> mMenuItems = null;
+ private PendingIntent mOnItemSelectedPendingIntent = null;
+
+ /**
+ * Constructs a {@link BrowserActionsIntent.Builder} object associated with default setting
+ * for a selected url.
+ * @param context The context requesting the Browser Actions context menu.
+ * @param uri The selected url for Browser Actions menu.
+ */
+ public Builder(Context context, Uri uri) {
+ mContext = context;
+ mUri = uri;
+ mType = URL_TYPE_NONE;
+ mMenuItems = new ArrayList<>();
+ }
+
+ /**
+ * Sets the type of Browser Actions context menu.
+ * @param type The type of url.
+ */
+ public Builder setUrlType(@BrowserActionsUrlType int type) {
+ mType = type;
+ return this;
+ }
+
+ /**
+ * Sets the custom items list.
+ * Only maximum MAX_CUSTOM_ITEMS custom items are allowed,
+ * otherwise throws an {@link IllegalStateException}.
+ * @param items The list of {@link BrowserActionItem} for custom items.
+ */
+ public Builder setCustomItems(ArrayList<BrowserActionItem> items) {
+ if (items.size() > MAX_CUSTOM_ITEMS) {
+ throw new IllegalStateException(
+ "Exceeded maximum toolbar item count of " + MAX_CUSTOM_ITEMS);
+ }
+ for (int i = 0; i < items.size(); i++) {
+ if (TextUtils.isEmpty(items.get(i).getTitle())
+ || items.get(i).getAction() == null) {
+ throw new IllegalArgumentException(
+ "Custom item should contain a non-empty title and non-null intent.");
+ } else {
+ mMenuItems.add(getBundleFromItem(items.get(i)));
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Sets the custom items list.
+ * Only maximum MAX_CUSTOM_ITEMS custom items are allowed,
+ * otherwise throws an {@link IllegalStateException}.
+ * @param items The varargs of {@link BrowserActionItem} for custom items.
+ */
+ public Builder setCustomItems(BrowserActionItem... items) {
+ return setCustomItems(new ArrayList<BrowserActionItem>(Arrays.asList(items)));
+ }
+
+ /**
+ * Set the PendingIntent to be launched when a a browser specified menu item is selected.
+ * @param onItemSelectedPendingIntent The PendingIntent to be launched.
+ */
+ public Builder setOnItemSelectedAction(PendingIntent onItemSelectedPendingIntent) {
+ mOnItemSelectedPendingIntent = onItemSelectedPendingIntent;
+ return this;
+ }
+
+ /**
+ * Populates a {@link Bundle} to hold a custom item for Browser Actions menu.
+ * @param item A custom item for Browser Actions menu.
+ * @return The Bundle of custom item.
+ */
+ private Bundle getBundleFromItem(BrowserActionItem item) {
+ Bundle bundle = new Bundle();
+ bundle.putString(KEY_TITLE, item.getTitle());
+ bundle.putParcelable(KEY_ACTION, item.getAction());
+ if (item.getIconId() != 0) bundle.putInt(KEY_ICON_ID, item.getIconId());
+ return bundle;
+ }
+
+ /**
+ * Combines all the options that have been set and returns a new {@link
+ * BrowserActionsIntent} object.
+ */
+ public BrowserActionsIntent build() {
+ mIntent.setData(mUri);
+ mIntent.putExtra(EXTRA_TYPE, mType);
+ mIntent.putParcelableArrayListExtra(EXTRA_MENU_ITEMS, mMenuItems);
+ PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ mIntent.putExtra(EXTRA_APP_ID, pendingIntent);
+ if (mOnItemSelectedPendingIntent != null) {
+ mIntent.putExtra(
+ EXTRA_SELECTED_ACTION_PENDING_INTENT, mOnItemSelectedPendingIntent);
+ }
+ return new BrowserActionsIntent(mIntent);
+ }
+ }
+
+ /**
+ * Construct a BrowserActionsIntent with default settings and launch it to open a Browser
+ * Actions menu.
+ * @param context The context requesting for a Browser Actions menu.
+ * @param uri The url for Browser Actions menu.
+ */
+ public static void openBrowserAction(Context context, Uri uri) {
+ BrowserActionsIntent intent = new BrowserActionsIntent.Builder(context, uri).build();
+ launchIntent(context, intent.getIntent());
+ }
+
+ /**
+ * Construct a BrowserActionsIntent with custom settings and launch it to open a Browser Actions
+ * menu.
+ * @param context The context requesting for a Browser Actions menu.
+ * @param uri The url for Browser Actions menu.
+ * @param type The type of the url for context menu to be opened.
+ * @param items List of custom items to be added to Browser Actions menu.
+ * @param pendingIntent The PendingIntent to be launched when a browser specified menu item is
+ * selected.
+ */
+ public static void openBrowserAction(Context context, Uri uri, int type,
+ ArrayList<BrowserActionItem> items, PendingIntent pendingIntent) {
+ BrowserActionsIntent intent = new BrowserActionsIntent.Builder(context, uri)
+ .setUrlType(type)
+ .setCustomItems(items)
+ .setOnItemSelectedAction(pendingIntent)
+ .build();
+ launchIntent(context, intent.getIntent());
+ }
+
+ /**
+ * Launch an Intent to open a Browser Actions menu.
+ * It first checks if any Browser Actions provider is available to create the menu.
+ * If the default Browser supports Browser Actions, menu will be opened by the default Browser,
+ * otherwise show a intent picker.
+ * If not provider, a Browser Actions menu is opened locally from support library.
+ * @param context The context requesting for a Browser Actions menu.
+ * @param intent The {@link Intent} holds the setting for Browser Actions menu.
+ */
+ public static void launchIntent(Context context, Intent intent) {
+ List<ResolveInfo> handlers = getBrowserActionsIntentHandlers(context);
+ if (handlers == null || handlers.size() == 0) {
+ openFallbackBrowserActionsMenu(context, intent);
+ return;
+ } else if (handlers.size() == 1) {
+ intent.setPackage(handlers.get(0).activityInfo.packageName);
+ } else {
+ Intent viewIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(TEST_URL));
+ PackageManager pm = context.getPackageManager();
+ ResolveInfo defaultHandler =
+ pm.resolveActivity(viewIntent, PackageManager.MATCH_DEFAULT_ONLY);
+ if (defaultHandler != null) {
+ String defaultPackageName = defaultHandler.activityInfo.packageName;
+ for (int i = 0; i < handlers.size(); i++) {
+ if (defaultPackageName.equals(handlers.get(i).activityInfo.packageName)) {
+ intent.setPackage(defaultPackageName);
+ break;
+ }
+ }
+ }
+ }
+ ContextCompat.startActivity(context, intent, null);
+ }
+
+ /**
+ * Returns a list of Browser Actions providers available to handle the {@link
+ * BrowserActionsIntent}.
+ * @param context The context requesting for a Browser Actions menu.
+ * @return List of Browser Actions providers available to handle the intent.
+ */
+ private static List<ResolveInfo> getBrowserActionsIntentHandlers(Context context) {
+ Intent intent =
+ new Intent(BrowserActionsIntent.ACTION_BROWSER_ACTIONS_OPEN, Uri.parse(TEST_URL));
+ PackageManager pm = context.getPackageManager();
+ return pm.queryIntentActivities(intent, PackageManager.MATCH_ALL);
+ }
+
+ private static void openFallbackBrowserActionsMenu(Context context, Intent intent) {
+ Uri uri = intent.getData();
+ int type = intent.getIntExtra(EXTRA_TYPE, URL_TYPE_NONE);
+ ArrayList<Bundle> bundles = intent.getParcelableArrayListExtra(EXTRA_MENU_ITEMS);
+ List<BrowserActionItem> items = bundles != null ? parseBrowserActionItems(bundles) : null;
+ // TODO(ltian): display a fallback dialog showing all custom items from support library.
+ // http://crbug.com/789806.
+ return;
+ }
+
+ /**
+ * Gets custom item list for browser action menu.
+ * @param bundles Data for custom items from {@link BrowserActionsIntent}.
+ * @return List of {@link BrowserActionItem}
+ */
+ public static List<BrowserActionItem> parseBrowserActionItems(ArrayList<Bundle> bundles) {
+ List<BrowserActionItem> mActions = new ArrayList<>();
+ for (int i = 0; i < bundles.size(); i++) {
+ Bundle bundle = bundles.get(i);
+ String title = bundle.getString(BrowserActionsIntent.KEY_TITLE);
+ PendingIntent action = bundle.getParcelable(BrowserActionsIntent.KEY_ACTION);
+ @DrawableRes
+ int iconId = bundle.getInt(BrowserActionsIntent.KEY_ICON_ID);
+ if (TextUtils.isEmpty(title) || action == null) {
+ throw new IllegalArgumentException(
+ "Custom item should contain a non-empty title and non-null intent.");
+ } else {
+ BrowserActionItem item = new BrowserActionItem(title, action, iconId);
+ mActions.add(item);
+ }
+ }
+ return mActions;
+ }
+
+ /**
+ * Get the package name of the creator application.
+ * @param intent The {@link BrowserActionsIntent}.
+ * @return The creator package name.
+ */
+ @SuppressWarnings("deprecation")
+ public static String getCreatorPackageName(Intent intent) {
+ PendingIntent pendingIntent = intent.getParcelableExtra(BrowserActionsIntent.EXTRA_APP_ID);
+ if (pendingIntent != null) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ return pendingIntent.getCreatorPackage();
+ } else {
+ return pendingIntent.getTargetPackage();
+ }
+ }
+ return null;
+ }
+}