| Index: chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java
|
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java
|
| index 97583b682427c1491325995f4a6fa8c3e7c6c11d..4337a76c7e78c51fa3d1556fe705aef92ed4f433 100644
|
| --- a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java
|
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java
|
| @@ -26,6 +26,7 @@ import android.graphics.drawable.Drawable;
|
| import android.net.Uri;
|
| import android.os.AsyncTask;
|
| import android.os.Build;
|
| +import android.os.StrictMode;
|
| import android.support.annotation.Nullable;
|
| import android.support.v7.app.AlertDialog;
|
| import android.text.TextUtils;
|
| @@ -35,25 +36,34 @@ import android.view.View;
|
| import android.widget.AdapterView;
|
| import android.widget.AdapterView.OnItemClickListener;
|
|
|
| +import org.chromium.base.ActivityState;
|
| import org.chromium.base.ApiCompatibilityUtils;
|
| import org.chromium.base.ApplicationState;
|
| import org.chromium.base.ApplicationStatus;
|
| +import org.chromium.base.ApplicationStatus.ActivityStateListener;
|
| import org.chromium.base.Callback;
|
| import org.chromium.base.ContextUtils;
|
| import org.chromium.base.Log;
|
| import org.chromium.base.StreamUtil;
|
| +import org.chromium.base.ThreadUtils;
|
| import org.chromium.base.VisibleForTesting;
|
| import org.chromium.base.annotations.SuppressFBWarnings;
|
| import org.chromium.base.metrics.RecordHistogram;
|
| import org.chromium.chrome.R;
|
| +import org.chromium.chrome.browser.physicalweb.PhysicalWebShareActivity;
|
| +import org.chromium.chrome.browser.printing.PrintShareActivity;
|
| +import org.chromium.chrome.browser.tab.Tab;
|
| import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
|
| import org.chromium.ui.UiUtils;
|
|
|
| import java.io.File;
|
| import java.io.FileOutputStream;
|
| import java.io.IOException;
|
| +import java.util.ArrayList;
|
| import java.util.Collections;
|
| +import java.util.HashSet;
|
| import java.util.List;
|
| +import java.util.Set;
|
| import java.util.concurrent.ExecutionException;
|
| import java.util.concurrent.TimeUnit;
|
| import java.util.concurrent.TimeoutException;
|
| @@ -104,6 +114,12 @@ public class ShareHelper {
|
| /** If non-null, will be used instead of the real activity. */
|
| private static FakeIntentReceiver sFakeIntentReceiverForTesting;
|
|
|
| + /** Variables for adding options dynamically to app chooser. */
|
| + private static Set<Activity> sPendingShareActivities =
|
| + Collections.synchronizedSet(new HashSet<Activity>());
|
| + private static ActivityStateListener sStateListener;
|
| + private static AsyncTask<Void, Void, Void> sStateChangeTask;
|
| +
|
| private ShareHelper() {}
|
|
|
| private static void fireIntent(Activity activity, Intent intent) {
|
| @@ -681,4 +697,154 @@ public class ShareHelper {
|
| if (packageName == null || className == null) return null;
|
| return new ComponentName(packageName, className);
|
| }
|
| +
|
| + /**
|
| + * Dynamically add Chrome activities to Share chooser.
|
| + *
|
| + * @param activity The activity that will be triggering the share action.
|
| + * @param options List of ShareActivity to add to the chooser.
|
| + * @param callback The callback to be triggered after the options have been enabled. This
|
| + * may or may not be synchronous depending on whether this will require
|
| + * interacting with the Android framework.
|
| + */
|
| + public static void enableShareActivitiesAndStartShareIntent(final Activity activity,
|
| + final Tab currentTab, final Runnable callback) {
|
| + ThreadUtils.assertOnUiThread();
|
| +
|
| + if (sStateListener == null) {
|
| + sStateListener = new ActivityStateListener() {
|
| + @Override
|
| + public void onActivityStateChange(Activity activity, int newState) {
|
| + if (newState == ActivityState.PAUSED) return;
|
| + unregisterActivity(activity);
|
| + }
|
| + };
|
| + }
|
| + ApplicationStatus.registerStateListenerForAllActivities(sStateListener);
|
| + boolean wasEmpty = sPendingShareActivities.isEmpty();
|
| + sPendingShareActivities.add(activity);
|
| +
|
| + final List<ShareActivity> options = getEnabledShareActivities(currentTab);
|
| + if (options.isEmpty()) {
|
| + callback.run();
|
| + } else {
|
| + waitForPendingStateChangeTask();
|
| + if (wasEmpty) {
|
| + sStateChangeTask = new AsyncTask<Void, Void, Void>() {
|
| + @Override
|
| + protected Void doInBackground(Void... params) {
|
| + if (sPendingShareActivities.isEmpty()) return null;
|
| +
|
| + for (int i = 0; i < options.size(); i++) {
|
| + activity.getPackageManager().setComponentEnabledSetting(
|
| + new ComponentName(activity, options.get(i).getClass()),
|
| + PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
|
| + PackageManager.DONT_KILL_APP);
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + @Override
|
| + protected void onPostExecute(Void result) {
|
| + if (sStateChangeTask == this) {
|
| + sStateChangeTask = null;
|
| + } else {
|
| + waitForPendingStateChangeTask();
|
| + }
|
| + callback.run();
|
| + }
|
| + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
| + } else {
|
| + callback.run();
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * @param currentTab The current tab of the chrome activity.
|
| + * @return list of all share activities to be enabled.
|
| + */
|
| + public static List<ShareActivity> getEnabledShareActivities(final Tab currentTab) {
|
| + return filterEnabledShareActivities(getPotentialShareActivitiesList(),
|
| + currentTab);
|
| + }
|
| +
|
| + /**
|
| + * @return List of all potential options to add to the share chooser.
|
| + */
|
| + private static List<ShareActivity> getPotentialShareActivitiesList() {
|
| + List<ShareActivity> potentialActivities = new ArrayList<ShareActivity>(2);
|
| + potentialActivities.add(new PrintShareActivity());
|
| + potentialActivities.add(new PhysicalWebShareActivity());
|
| + return potentialActivities;
|
| + }
|
| +
|
| + /**
|
| + * @param potentialOptions List of the potential ShareActivities to enable.
|
| + * @param currentTab The current tab of the chrome activity.
|
| + * @return Filtered list of options to enable for share chooser.
|
| + */
|
| + private static List<ShareActivity> filterEnabledShareActivities(
|
| + List<ShareActivity> potentialOptions, final Tab currentTab) {
|
| + List<ShareActivity> enabledShareActivities = new ArrayList<ShareActivity>(2);
|
| + for (ShareActivity p: potentialOptions) {
|
| + if (p.featureIsEnabled(currentTab)) {
|
| + enabledShareActivities.add(p);
|
| + }
|
| + }
|
| + return enabledShareActivities;
|
| + }
|
| +
|
| + /**
|
| + * Disables Components after Share Chooser is done.
|
| + */
|
| + public static void unregisterActivity(final Activity activity) {
|
| + ThreadUtils.assertOnUiThread();
|
| +
|
| + sPendingShareActivities.remove(activity);
|
| + if (!sPendingShareActivities.isEmpty()) return;
|
| + ApplicationStatus.unregisterActivityStateListener(sStateListener);
|
| + final List<ShareActivity> options = getPotentialShareActivitiesList();
|
| +
|
| + waitForPendingStateChangeTask();
|
| + sStateChangeTask = new AsyncTask<Void, Void, Void>() {
|
| + @Override
|
| + protected Void doInBackground(Void... params) {
|
| + if (!sPendingShareActivities.isEmpty()) return null;
|
| +
|
| + for (int i = 0; i < options.size(); i++) {
|
| + activity.getPackageManager().setComponentEnabledSetting(
|
| + new ComponentName(activity, options.get(i).getClass()),
|
| + PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
|
| + PackageManager.DONT_KILL_APP);
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + @Override
|
| + protected void onPostExecute(Void result) {
|
| + if (sStateChangeTask == this) sStateChangeTask = null;
|
| + }
|
| + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
| + }
|
| +
|
| + /**
|
| + * Waits for any pending state change operations to be completed.
|
| + *
|
| + * This will avoid timing issues described here: crbug.com/649453.
|
| + */
|
| + public static void waitForPendingStateChangeTask() {
|
| + ThreadUtils.assertOnUiThread();
|
| +
|
| + if (sStateChangeTask == null) return;
|
| + StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
|
| + try {
|
| + sStateChangeTask.get();
|
| + sStateChangeTask = null;
|
| + } catch (InterruptedException | ExecutionException e) {
|
| + Log.e(TAG, "Share Helper state change task did not complete as expected");
|
| + } finally {
|
| + StrictMode.setThreadPolicy(oldPolicy);
|
| + }
|
| + }
|
| }
|
|
|