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