Chromium Code Reviews| 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..649e5c8a335a6e7a7d98e9e8430a6e07d65a5229 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; |
| @@ -98,12 +108,27 @@ public class ShareHelper { |
| */ |
| private static final String SHARE_IMAGES_DIRECTORY_NAME = "screenshot"; |
| + /** |
| + * Potential activities to add to the share picker intent. |
| + */ |
| + private static final List<ShareActivity> SHARE_ACTIVITIES = Collections.unmodifiableList( |
| + new ArrayList<ShareActivity>() { { |
| + add(new PrintShareActivity()); |
| + add(new PhysicalWebShareActivity()); |
| + }}); |
| + |
| /** Force the use of a Chrome-specific intent chooser, not the system chooser. */ |
| private static boolean sForceCustomChooserForTesting; |
| /** If non-null, will be used instead of the real activity. */ |
| private static FakeIntentReceiver sFakeIntentReceiverForTesting; |
| + /** Variables for adding activities dynamically to share picker intent. */ |
| + private static Set<Activity> sPendingShareActivities = |
|
cco3
2017/02/07 19:51:39
Should these be ShareActivities?
|
| + 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 +706,134 @@ 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 currentTab The current tab of the chrome Activity that is launching the share intent. |
| + * @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; |
| + unregisterShareActivities(activity); |
| + } |
| + }; |
| + } |
| + ApplicationStatus.registerStateListenerForAllActivities(sStateListener); |
| + boolean wasEmpty = sPendingShareActivities.isEmpty(); |
| + sPendingShareActivities.add(activity); |
| + |
| + final List<ShareActivity> enabledActivities = filterEnabledShareActivities(currentTab); |
| + waitForPendingStateChangeTask(); |
| + if (enabledActivities.isEmpty()) { |
| + callback.run(); |
| + return; |
| + } |
| + sStateChangeTask = new StateChangeTask(activity, enabledActivities, |
| + PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { |
| + @Override |
| + protected void onPostExecute(Void result) { |
| + if (sStateChangeTask == this) { |
| + sStateChangeTask = null; |
| + } else { |
| + waitForPendingStateChangeTask(); |
| + } |
| + callback.run(); |
| + } |
| + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
| + } |
| + |
| + /** |
| + * Disables Components after Share Chooser is done. |
| + */ |
| + public static void unregisterShareActivities(final Activity activity) { |
|
cco3
2017/02/07 19:51:39
this parameter needs a more helpful name...is it t
|
| + ThreadUtils.assertOnUiThread(); |
| + |
| + sPendingShareActivities.remove(activity); |
| + if (!sPendingShareActivities.isEmpty()) return; |
| + ApplicationStatus.unregisterActivityStateListener(sStateListener); |
| + |
| + waitForPendingStateChangeTask(); |
| + sStateChangeTask = new StateChangeTask(activity, SHARE_ACTIVITIES, |
| + PackageManager.COMPONENT_ENABLED_STATE_DISABLED) { |
| + @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. |
| + */ |
| + private 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); |
| + } |
| + } |
| + |
| + /** |
| + * 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(Tab currentTab) { |
| + List<ShareActivity> enabledShareActivities = new ArrayList<ShareActivity>(2); |
| + for (ShareActivity activity: SHARE_ACTIVITIES) { |
| + if (activity.featureIsEnabled(currentTab)) { |
| + enabledShareActivities.add(activity); |
| + } |
| + } |
| + return enabledShareActivities; |
| + } |
| + |
| + private static class StateChangeTask extends AsyncTask<Void, Void, Void> { |
| + |
| + private Activity mCallingActivity; |
| + private List<ShareActivity> mActivities; |
| + private int mComponentEnabledState; |
| + |
| + public StateChangeTask(Activity activity, List<ShareActivity> activityList, int state) { |
| + mCallingActivity = activity; |
| + mActivities = activityList; |
| + mComponentEnabledState = state; |
| + } |
| + |
| + @Override |
| + protected Void doInBackground(Void... params) { |
| + if (sPendingShareActivities.isEmpty()) return null; |
| + |
| + for (int i = 0; i < mActivities.size(); i++) { |
| + mCallingActivity.getPackageManager().setComponentEnabledSetting( |
| + new ComponentName(mCallingActivity, |
| + mActivities.get(i).getClass()), |
| + mComponentEnabledState, |
| + PackageManager.DONT_KILL_APP); |
| + } |
| + return null; |
| + } |
| + } |
| } |