From dedda05568a3f144c548d60e2bb1d7f5e7c58c6c Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 14 May 2019 15:23:48 -0700 Subject: [PATCH] Using a proxy activity for startActivityForResult This ensures that the home task is never blocked by a different task Bug: 74500048 Change-Id: I01fd26f1d6242e39b2d8fabac5e064b748aebe62 --- quickstep/AndroidManifest.xml | 7 ++ .../launcher3/proxy/ProxyActivityStarter.java | 82 ++++++++++++++ .../launcher3/proxy/StartActivityParams.java | 103 ++++++++++++++++++ .../launcher3/uioverrides/UiFactory.java | 41 +++++++ src/com/android/launcher3/Launcher.java | 47 ++++++-- .../launcher3/LauncherStateManager.java | 2 +- .../android/launcher3/dragndrop/DragView.java | 25 ++++- .../launcher3/uioverrides/UiFactory.java | 17 +++ 8 files changed, 314 insertions(+), 10 deletions(-) create mode 100644 quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java create mode 100644 quickstep/src/com/android/launcher3/proxy/StartActivityParams.java diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml index 97fc2845c3..be275e0e8a 100644 --- a/quickstep/AndroidManifest.xml +++ b/quickstep/AndroidManifest.xml @@ -85,6 +85,13 @@ android:name="com.android.launcher3.uioverrides.dynamicui.WallpaperManagerCompatVL$ColorExtractionService" tools:node="remove" /> + + diff --git a/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java b/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java new file mode 100644 index 0000000000..e302b4f152 --- /dev/null +++ b/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2019 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 com.android.launcher3.proxy; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.IntentSender.SendIntentException; +import android.os.Bundle; +import android.util.Log; + +public class ProxyActivityStarter extends Activity { + + private static final String TAG = "ProxyActivityStarter"; + + public static final String EXTRA_PARAMS = "start-activity-params"; + + private StartActivityParams mParams; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setVisible(false); + + mParams = getIntent().getParcelableExtra(EXTRA_PARAMS); + if (mParams == null) { + Log.d(TAG, "Proxy activity started without params"); + finishAndRemoveTask(); + return; + } + + if (savedInstanceState != null) { + // Already started the activity. Just wait for the result. + return; + } + + if (mParams.intent != null) { + startActivityForResult(mParams.intent, mParams.requestCode, mParams.options); + return; + } else if (mParams.intentSender != null) { + try { + startIntentSenderForResult(mParams.intentSender, mParams.requestCode, + mParams.fillInIntent, mParams.flagsMask, mParams.flagsValues, + mParams.extraFlags, + mParams.options); + return; + } catch (SendIntentException e) { + mParams.deliverResult(this, RESULT_CANCELED, null); + } + } + finishAndRemoveTask(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == mParams.requestCode) { + mParams.deliverResult(this, resultCode, data); + } + finishAndRemoveTask(); + } + + public static Intent getLaunchIntent(Context context, StartActivityParams params) { + return new Intent(context, ProxyActivityStarter.class) + .putExtra(EXTRA_PARAMS, params) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED + | Intent.FLAG_ACTIVITY_CLEAR_TASK); + } +} diff --git a/quickstep/src/com/android/launcher3/proxy/StartActivityParams.java b/quickstep/src/com/android/launcher3/proxy/StartActivityParams.java new file mode 100644 index 0000000000..1e8bd933f9 --- /dev/null +++ b/quickstep/src/com/android/launcher3/proxy/StartActivityParams.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2019 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 com.android.launcher3.proxy; + +import android.app.Activity; +import android.app.PendingIntent; +import android.app.PendingIntent.CanceledException; +import android.content.Context; +import android.content.Intent; +import android.content.IntentSender; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +public class StartActivityParams implements Parcelable { + + private static final String TAG = "StartActivityParams"; + + private final PendingIntent mCallback; + public final int requestCode; + + public Intent intent; + + public IntentSender intentSender; + public Intent fillInIntent; + public int flagsMask; + public int flagsValues; + public int extraFlags; + public Bundle options; + + public StartActivityParams(Activity activity, int requestCode) { + mCallback = activity.createPendingResult(requestCode, new Intent(), + PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT); + this.requestCode = requestCode; + } + + private StartActivityParams(Parcel parcel) { + mCallback = parcel.readTypedObject(PendingIntent.CREATOR); + requestCode = parcel.readInt(); + intent = parcel.readTypedObject(Intent.CREATOR); + + intentSender = parcel.readTypedObject(IntentSender.CREATOR); + fillInIntent = parcel.readTypedObject(Intent.CREATOR); + flagsMask = parcel.readInt(); + flagsValues = parcel.readInt(); + extraFlags = parcel.readInt(); + options = parcel.readBundle(); + } + + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeTypedObject(mCallback, flags); + parcel.writeInt(requestCode); + parcel.writeTypedObject(intent, flags); + + parcel.writeTypedObject(intentSender, flags); + parcel.writeTypedObject(fillInIntent, flags); + parcel.writeInt(flagsMask); + parcel.writeInt(flagsValues); + parcel.writeInt(extraFlags); + parcel.writeBundle(options); + } + + public void deliverResult(Context context, int resultCode, Intent data) { + try { + mCallback.send(context, resultCode, data); + } catch (CanceledException e) { + Log.e(TAG, "Unable to send back result", e); + } + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public StartActivityParams createFromParcel(Parcel source) { + return new StartActivityParams(source); + } + + public StartActivityParams[] newArray(int size) { + return new StartActivityParams[size]; + } + }; +} diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java index 77ac35c50a..4891746140 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java @@ -16,6 +16,8 @@ package com.android.launcher3.uioverrides; +import static android.app.Activity.RESULT_CANCELED; + import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON; import static com.android.launcher3.LauncherState.ALL_APPS; @@ -31,6 +33,9 @@ import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.app.Activity; import android.content.Context; +import android.content.Intent; +import android.content.IntentSender; +import android.os.Bundle; import android.os.CancellationSignal; import android.util.Base64; @@ -43,6 +48,8 @@ import com.android.launcher3.LauncherStateManager.StateHandler; import com.android.launcher3.QuickstepAppTransitionManagerImpl; import com.android.launcher3.Utilities; import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.proxy.ProxyActivityStarter; +import com.android.launcher3.proxy.StartActivityParams; import com.android.quickstep.OverviewInteractionState; import com.android.quickstep.RecentsModel; import com.android.quickstep.SysUINavigationMode; @@ -192,6 +199,40 @@ public class UiFactory extends RecentsUiFactory { return true; } + public static boolean startIntentSenderForResult(Activity activity, IntentSender intent, + int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, + Bundle options) { + StartActivityParams params = new StartActivityParams(activity, requestCode); + params.intentSender = intent; + params.fillInIntent = fillInIntent; + params.flagsMask = flagsMask; + params.flagsValues = flagsValues; + params.extraFlags = extraFlags; + params.options = options; + ((Context) activity).startActivity(ProxyActivityStarter.getLaunchIntent(activity, params)); + return true; + } + + public static boolean startActivityForResult(Activity activity, Intent intent, int requestCode, + Bundle options) { + StartActivityParams params = new StartActivityParams(activity, requestCode); + params.intent = intent; + params.options = options; + activity.startActivity(ProxyActivityStarter.getLaunchIntent(activity, params)); + return true; + } + + /** + * Removes any active ProxyActivityStarter task and sends RESULT_CANCELED to Launcher. + * + * ProxyActivityStarter is started with clear task to reset the task after which it removes the + * task itself. + */ + public static void resetPendingActivityResults(Launcher launcher, int requestCode) { + launcher.onActivityResult(requestCode, RESULT_CANCELED, null); + launcher.startActivity(ProxyActivityStarter.getLaunchIntent(launcher, null)); + } + public static ScaleAndTranslation getOverviewScaleAndTranslationForNormalState(Launcher l) { if (SysUINavigationMode.getMode(l) == Mode.NO_BUTTON) { float offscreenTranslationX = l.getDeviceProfile().widthPx diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 359d8d9320..40eb912df5 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -72,7 +72,6 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.View; import android.view.ViewGroup; -import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.animation.OvershootInterpolator; import android.widget.Toast; @@ -191,6 +190,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, private static final String RUNTIME_STATE = "launcher.state"; // Type: PendingRequestArgs private static final String RUNTIME_STATE_PENDING_REQUEST_ARGS = "launcher.request_args"; + // Type: int + private static final String RUNTIME_STATE_PENDING_REQUEST_CODE = "launcher.request_code"; // Type: ActivityResultInfo private static final String RUNTIME_STATE_PENDING_ACTIVITY_RESULT = "launcher.activity_result"; // Type: SparseArray @@ -264,6 +265,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, * {@link #startActivityForResult(Intent, int)} or {@link #requestPermissions(String[], int)} */ private PendingRequestArgs mPendingRequestArgs; + // Request id for any pending activity result + private int mPendingActivityRequestCode = -1; public ViewGroupFocusHelper mFocusHandler; @@ -762,6 +765,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, @Override public void onActivityResult( final int requestCode, final int resultCode, final Intent data) { + mPendingActivityRequestCode = -1; handleActivityResult(requestCode, resultCode, data); if (mLauncherCallbacks != null) { mLauncherCallbacks.onActivityResult(requestCode, resultCode, data); @@ -890,9 +894,21 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, UiFactory.onLauncherStateOrResumeChanged(this); AppLaunchTracker.INSTANCE.get(this).onReturnedToHome(); + resetPendingActivityResultIfNeeded(); } } + private void resetPendingActivityResultIfNeeded() { + if (hasBeenResumed() && mPendingActivityRequestCode != -1 && isInState(NORMAL)) { + UiFactory.resetPendingActivityResults(this, mPendingActivityRequestCode); + } + } + + protected void onStateSet(LauncherState state) { + getAppWidgetHost().setResumed(state == LauncherState.NORMAL); + resetPendingActivityResultIfNeeded(); + } + @Override protected void onResume() { RaceConditionTracker.onEvent(ON_RESUME_EVT, ENTER); @@ -1009,6 +1025,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, if (requestArgs != null) { setWaitingForResult(requestArgs); } + mPendingActivityRequestCode = savedState.getInt(RUNTIME_STATE_PENDING_REQUEST_CODE); mPendingActivityResult = savedState.getParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT); @@ -1392,6 +1409,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, if (mPendingRequestArgs != null) { outState.putParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS, mPendingRequestArgs); } + outState.putInt(RUNTIME_STATE_PENDING_REQUEST_CODE, mPendingActivityRequestCode); + if (mPendingActivityResult != null) { outState.putParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT, mPendingActivityResult); } @@ -1448,17 +1467,29 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, @Override public void startActivityForResult(Intent intent, int requestCode, Bundle options) { - super.startActivityForResult(intent, requestCode, options); + if (requestCode != -1) { + mPendingActivityRequestCode = requestCode; + } + if (requestCode == -1 + || !UiFactory.startActivityForResult(this, intent, requestCode, options)) { + super.startActivityForResult(intent, requestCode, options); + } } @Override - public void startIntentSenderForResult (IntentSender intent, int requestCode, + public void startIntentSenderForResult(IntentSender intent, int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) { - try { - super.startIntentSenderForResult(intent, requestCode, - fillInIntent, flagsMask, flagsValues, extraFlags, options); - } catch (IntentSender.SendIntentException e) { - throw new ActivityNotFoundException(); + if (requestCode != -1) { + mPendingActivityRequestCode = requestCode; + } + if (requestCode == -1 || !UiFactory.startIntentSenderForResult(this, intent, requestCode, + fillInIntent, flagsMask, flagsValues, extraFlags, options)) { + try { + super.startIntentSenderForResult(intent, requestCode, + fillInIntent, flagsMask, flagsValues, extraFlags, options); + } catch (IntentSender.SendIntentException e) { + throw new ActivityNotFoundException(); + } } } diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java index f5040b3ba8..b1a3fc992e 100644 --- a/src/com/android/launcher3/LauncherStateManager.java +++ b/src/com/android/launcher3/LauncherStateManager.java @@ -452,7 +452,7 @@ public class LauncherStateManager { } mState = state; mState.onStateEnabled(mLauncher); - mLauncher.getAppWidgetHost().setResumed(state == LauncherState.NORMAL); + mLauncher.onStateSet(mState); if (state.disablePageClipping) { // Only disable clipping if needed, otherwise leave it as previous value. diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java index 77b2cdc34d..7af12c57d4 100644 --- a/src/com/android/launcher3/dragndrop/DragView.java +++ b/src/com/android/launcher3/dragndrop/DragView.java @@ -46,6 +46,8 @@ import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherSettings; +import com.android.launcher3.LauncherState; +import com.android.launcher3.LauncherStateManager; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.FirstFrameAnimatorHelper; @@ -60,7 +62,7 @@ import androidx.dynamicanimation.animation.FloatPropertyCompat; import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; -public class DragView extends View { +public class DragView extends View implements LauncherStateManager.StateListener { private static final ColorMatrix sTempMatrix1 = new ColorMatrix(); private static final ColorMatrix sTempMatrix2 = new ColorMatrix(); @@ -172,6 +174,27 @@ public class DragView extends View { setElevation(getResources().getDimension(R.dimen.drag_elevation)); } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mLauncher.getStateManager().addStateListener(this); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mLauncher.getStateManager().removeStateListener(this); + } + + @Override + public void onStateTransitionStart(LauncherState toState) { } + + @Override + public void onStateTransitionComplete(LauncherState finalState) { + setVisibility((finalState == LauncherState.NORMAL + || finalState == LauncherState.SPRING_LOADED) ? VISIBLE : INVISIBLE); + } + /** * Initialize {@code #mIconDrawable} if the item can be represented using * an {@link AdaptiveIconDrawable} or {@link FolderAdaptiveIcon}. diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java index 6008d14508..550327d961 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java @@ -18,6 +18,9 @@ package com.android.launcher3.uioverrides; import android.app.Activity; import android.content.Context; +import android.content.Intent; +import android.content.IntentSender; +import android.os.Bundle; import android.os.CancellationSignal; import com.android.launcher3.DeviceProfile; @@ -79,4 +82,18 @@ public class UiFactory { public static RotationMode getRotationMode(DeviceProfile dp) { return RotationMode.NORMAL; } + + public static boolean startIntentSenderForResult(Activity activity, IntentSender intent, + int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, + Bundle options) { + return false; + } + + public static boolean startActivityForResult(Activity activity, Intent intent, int requestCode, + Bundle options) { + return false; + } + + public static void resetPendingActivityResults(Launcher launcher, int requestCode) { } + }