diff options
| author | 2021-11-17 17:24:02 -0500 | |
|---|---|---|
| committer | 2021-11-18 12:24:16 -0500 | |
| commit | 83c3ba28092a485bb23f9133596e36a288612a88 (patch) | |
| tree | 6f90410ea203b3d25791c4e659eaea320a8f1005 | |
| parent | 15cd688ea72607f022e983572fe63b0bd6eddecc (diff) | |
"Springboard" from system to new chooser.
These changes are somewhat hacky, but they allow
us to conditionally hand over to the new unbundled
chooser with minimal impact if the experiment
is disabled.
For a safer implementation, we could consider
starting with a separate springboard activity that
launches a normal (reverted) version of the
"bundled" chooser if we intend to show the UI
from the system side, but that would incur some
latency even when the flag is disabled.
Test: manual, as described in ag/16286296.
Bug: 202165481
Change-Id: I4073776b293806052e9ff66c50fea1181afd642a
| -rw-r--r-- | core/java/com/android/internal/app/ChooserActivity.java | 95 | ||||
| -rw-r--r-- | core/java/com/android/internal/app/ResolverActivity.java | 84 |
2 files changed, 177 insertions, 2 deletions
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 9b1bef067cec..fd9ad0d9bcfd 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 9af4f9124bc8..fd8637abfc6b 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 + * <em>not</em> 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 + * <em>not</em> 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 + * <em>not</em> 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 + * <em>not</em> 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 |