Using a proxy activity for startActivityForResult
This ensures that the home task is never blocked by a different task
Bug: 74500048
Change-Id: I01fd26f1d6242e39b2d8fabac5e064b748aebe62
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 97fc284..be275e0 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" />
+ <activity
+ android:name="com.android.launcher3.proxy.ProxyActivityStarter"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen"
+ android:launchMode="singleTask"
+ android:clearTaskOnLaunch="true"
+ android:exported="false" />
+
</application>
</manifest>
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 0000000..e302b4f
--- /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 0000000..1e8bd93
--- /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<StartActivityParams> CREATOR =
+ new Parcelable.Creator<StartActivityParams>() {
+ 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 77ac35c..4891746 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.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.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 @@
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 359d8d9..40eb912 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -72,7 +72,6 @@
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 @@
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<Parcelable>
@@ -264,6 +265,8 @@
* {@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 @@
@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 @@
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 @@
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 @@
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 @@
@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 f5040b3..b1a3fc9 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -452,7 +452,7 @@
}
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 77b2cdc..7af12c5 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.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.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 @@
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 6008d14..550327d 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 @@
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 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) { }
+
}