diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 9b1bef067ceca..fd9ad0d9bcfd1 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -28,6 +28,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
import android.app.SharedElementCallback;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppPredictionManager;
@@ -70,9 +71,11 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Message;
import android.os.Parcelable;
import android.os.PatternMatcher;
+import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.UserHandle;
import android.os.UserManager;
@@ -151,6 +154,19 @@ public class ChooserActivity extends ResolverActivity implements
ChooserListAdapter.ChooserListCommunicator,
SelectableTargetInfoCommunicator {
private static final String TAG = "ChooserActivity";
+
+ /**
+ * Whether this chooser is operating in "headless springboard" mode (as determined during
+ * onCreate). In this mode, our activity sits in the background and waits for the new
+ * "unbundled" chooser to handle the Sharesheet experience; the system ChooserActivity is
+ * responsible only for providing the startActivityAsCaller permission token and keeping it
+ * valid for the life of the unbundled delegate activity.
+ *
+ * TODO: when the unbundled chooser is fully launched, the system-side "springboard" can use a
+ * simpler implementation that doesn't inherit from ResolverActivity.
+ */
+ private boolean mIsHeadlessSpringboardActivity;
+
private AppPredictor mPersonalAppPredictor;
private AppPredictor mWorkAppPredictor;
private boolean mShouldDisplayLandscape;
@@ -235,6 +251,11 @@ public class ChooserActivity extends ResolverActivity implements
private static final float DIRECT_SHARE_EXPANSION_RATE = 0.78f;
+ private boolean mEnableChooserDelegate =
+ DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.USE_DELEGATE_CHOOSER,
+ false);
+
private static final int DEFAULT_SALT_EXPIRATION_DAYS = 7;
private int mMaxHashSaltDays = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.HASH_SALT_MAX_DAYS,
@@ -500,6 +521,12 @@ public class ChooserActivity extends ResolverActivity implements
@Override
protected void onCreate(Bundle savedInstanceState) {
+ if (handOverToDelegateChooser()) {
+ super_onCreate(savedInstanceState);
+ mIsHeadlessSpringboardActivity = true;
+ return;
+ }
+
final long intentReceivedTime = System.currentTimeMillis();
getChooserActivityLogger().logSharesheetTriggered();
// This is the only place this value is being set. Effectively final.
@@ -714,6 +741,64 @@ public class ChooserActivity extends ResolverActivity implements
postponeEnterTransition();
}
+ private boolean handOverToDelegateChooser() {
+ // Check the explicit classname so that we don't interfere with the flow of any subclasses.
+ if (!this.getClass().getName().equals("com.android.internal.app.ChooserActivity")
+ || !mEnableChooserDelegate) {
+ return false;
+ }
+
+ try {
+ IBinder permissionToken = ActivityTaskManager.getService()
+ .requestStartActivityPermissionToken(getActivityToken());
+ Intent delegationIntent = new Intent();
+ final ComponentName delegateActivity = ComponentName.unflattenFromString(
+ Resources.getSystem().getString(R.string.config_chooserActivity));
+ delegationIntent.setComponent(delegateActivity);
+ delegationIntent.putExtra(Intent.EXTRA_INTENT, getIntent());
+ delegationIntent.putExtra(ActivityTaskManager.EXTRA_PERMISSION_TOKEN, permissionToken);
+ delegationIntent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
+
+ // Don't close until the delegate finishes, or the token will be invalidated.
+ mAwaitingDelegateResponse = true;
+ startActivityForResult(delegationIntent, REQUEST_CODE_RETURN_FROM_DELEGATE_CHOOSER);
+ return true;
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ return false;
+ }
+
+ @Override
+ protected void onRestart() {
+ if (mIsHeadlessSpringboardActivity) {
+ super_onRestart();
+ return;
+ }
+
+ super.onRestart();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ if (mIsHeadlessSpringboardActivity) {
+ super_onSaveInstanceState(outState);
+ return;
+ }
+
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ if (mIsHeadlessSpringboardActivity) {
+ super_onRestoreInstanceState(savedInstanceState);
+ return;
+ }
+
+ super.onRestoreInstanceState(savedInstanceState);
+ }
+
@Override
protected int appliedThemeResId() {
return R.style.Theme_DeviceDefault_Chooser;
@@ -998,6 +1083,11 @@ public class ChooserActivity extends ResolverActivity implements
@Override
public void onConfigurationChanged(Configuration newConfig) {
+ if (mIsHeadlessSpringboardActivity) {
+ super_onConfigurationChanged(newConfig);
+ return;
+ }
+
super.onConfigurationChanged(newConfig);
ViewPager viewPager = findViewById(R.id.profile_pager);
if (viewPager.isLayoutRtl()) {
@@ -1543,6 +1633,11 @@ public class ChooserActivity extends ResolverActivity implements
@Override
protected void onDestroy() {
super.onDestroy();
+
+ if (mIsHeadlessSpringboardActivity) {
+ return;
+ }
+
if (mRefinementResultReceiver != null) {
mRefinementResultReceiver.destroy();
mRefinementResultReceiver = null;
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 9af4f9124bc82..fd8637abfc6bd 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -152,7 +152,7 @@ public class ResolverActivity extends Activity implements
/** See {@link #setRetainInOnStop}. */
private boolean mRetainInOnStop;
- private static final int REQUEST_CODE_RETURN_FROM_DELEGATE_CHOOSER = 20;
+ protected static final int REQUEST_CODE_RETURN_FROM_DELEGATE_CHOOSER = 20;
private static final String EXTRA_SHOW_FRAGMENT_ARGS = ":settings:show_fragment_args";
private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
@@ -323,6 +323,86 @@ public class ResolverActivity extends Activity implements
super.onCreate(savedInstanceState);
}
+ /**
+ * Pass-through API to support {@link ChooserActivity} running in "headless springboard" mode
+ * where we hand over to the unbundled chooser (while violating many of the invariants of a
+ * typical ResolverActivity implementation). Subclasses running in this mode need to be able
+ * to opt-out of the normal ResolverActivity behavior.
+ *
+ * TODO: this should be removed later on in the unbundling migration, when the springboard
+ * activity no longer needs to derive from ResolverActivity. The hold-over design here is
+ * not good practice (e.g. there could be other events that weren't anticipated as
+ * requiring this kind of "pass-through" override, and so might fall back on ResolverActivity
+ * implementations that depend on the invariants that are violated in the headless mode). If
+ * necessary, we could instead consider using a springboard-only activity on the system side
+ * immediately, which would delegate either to the unbundled chooser, or to a
+ * (properly-inheriting) system ChooserActivity. This would have performance implications even
+ * when the unbundling experiment is disabled.
+ */
+ protected void super_onRestart() {
+ super.onRestart();
+ }
+
+ /**
+ * Pass-through API to support {@link ChooserActivity} running in "headless springboard" mode
+ * where we hand over to the unbundled chooser (while violating many of the invariants of a
+ * typical ResolverActivity implementation). Subclasses running in this mode need to be able
+ * to opt-out of the normal ResolverActivity behavior.
+ *
+ * TODO: this should be removed later on in the unbundling migration, when the springboard
+ * activity no longer needs to derive from ResolverActivity. The hold-over design here is
+ * not good practice (e.g. there could be other events that weren't anticipated as
+ * requiring this kind of "pass-through" override, and so might fall back on ResolverActivity
+ * implementations that depend on the invariants that are violated in the headless mode). If
+ * necessary, we could instead consider using a springboard-only activity on the system side
+ * immediately, which would delegate either to the unbundled chooser, or to a
+ * (properly-inheriting) system ChooserActivity. This would have performance implications even
+ * when the unbundling experiment is disabled.
+ */
+ protected void super_onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ }
+
+ /**
+ * Pass-through API to support {@link ChooserActivity} running in "headless springboard" mode
+ * where we hand over to the unbundled chooser (while violating many of the invariants of a
+ * typical ResolverActivity implementation). Subclasses running in this mode need to be able
+ * to opt-out of the normal ResolverActivity behavior.
+ *
+ * TODO: this should be removed later on in the unbundling migration, when the springboard
+ * activity no longer needs to derive from ResolverActivity. The hold-over design here is
+ * not good practice (e.g. there could be other events that weren't anticipated as
+ * requiring this kind of "pass-through" override, and so might fall back on ResolverActivity
+ * implementations that depend on the invariants that are violated in the headless mode). If
+ * necessary, we could instead consider using a springboard-only activity on the system side
+ * immediately, which would delegate either to the unbundled chooser, or to a
+ * (properly-inheriting) system ChooserActivity. This would have performance implications even
+ * when the unbundling experiment is disabled.
+ */
+ protected void super_onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ }
+
+ /**
+ * Pass-through API to support {@link ChooserActivity} running in "headless springboard" mode
+ * where we hand over to the unbundled chooser (while violating many of the invariants of a
+ * typical ResolverActivity implementation). Subclasses running in this mode need to be able
+ * to opt-out of the normal ResolverActivity behavior.
+ *
+ * TODO: this should be removed later on in the unbundling migration, when the springboard
+ * activity no longer needs to derive from ResolverActivity. The hold-over design here is
+ * not good practice (e.g. there could be other events that weren't anticipated as
+ * requiring this kind of "pass-through" override, and so might fall back on ResolverActivity
+ * implementations that depend on the invariants that are violated in the headless mode). If
+ * necessary, we could instead consider using a springboard-only activity on the system side
+ * immediately, which would delegate either to the unbundled chooser, or to a
+ * (properly-inheriting) system ChooserActivity. This would have performance implications even
+ * when the unbundling experiment is disabled.
+ */
+ public void super_onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
// Use a specialized prompt when we're handling the 'Home' app startActivity()
@@ -877,7 +957,7 @@ public class ResolverActivity extends Activity implements
}
final Intent intent = getIntent();
if ((intent.getFlags() & FLAG_ACTIVITY_NEW_TASK) != 0 && !isVoiceInteraction()
- && !mResolvingHome && !mRetainInOnStop) {
+ && !mResolvingHome && !mRetainInOnStop && !mAwaitingDelegateResponse) {
// This resolver is in the unusual situation where it has been
// launched at the top of a new task. We don't let it be added
// to the recent tasks shown to the user, and we need to make sure