From b3240df7c525de985765e0cbbb094a0f7c83e440 Mon Sep 17 00:00:00 2001
From: Joshua Trask
Date: Fri, 10 Feb 2023 16:25:01 +0000
Subject: Clarify ResolverActivity inheritance contract.
This is a simple mechanical refactoring and (given everything still
builds) it can't possibly have any side effects.
The procedure for generating this change was as follows:
1. Add `final` to all `ResolverActivity` methods.
2. Build, and remove `final` from any of those methods that now
break compilation.
3. Sort the `final` (and `static`) methods to the bottom of the
source file / non-final to the top.
4. Build `IntentResolverUnitTests` and remove `final` from any
`ResolverActivity` methods that broke test-only compilation.
Replace with a comment `// @NonFinalForTesting` on these methods
(referencing an annotation that isn't available in Android, but
still worth noting. There are other patterns we can use to
formalize our design requirements, but these lightweight comments
are more appropriate while the API is in flux).
5. Sort these "non-final for testing" methods above the `final`
methods but below any that are overridden by real clients (since
they're still *really* just internal implementation details).
6. Build and test.
(7. Go back through and remove `final` from any private methods, as
requested by the linter. I'm not sure I agre3 with this rule in a
language where these methods will be "open" by default if their
visibility is ever changed, but c'est la vie...)
This groups together the `ResolverActivity` "override surface," which
roughly outlines the interface of a hypothetical delegate interface
that could be injected as part of `ChooserActivity` configuration in
order to avoid overriding any `ResolverActivity` methods. (That's left
out of scope in this CL, because -- even though we *could* effect such
a change via purely-mechanical transformations -- I suspect we'll want
to apply *some* design discretion along the way.) Ultimately this will
be an important step in decoupling our components from the `Activity`
API (with benefits for code clarity, hypothetical "embedded Sharesheet"
and "build-your-own chooser" APIs, etc).
Test: `atest IntentResolverUnitTests`
Bug: 202167050
Change-Id: Iffe340e1d6e3e3224fb6bd78005c349384716162
---
.../android/intentresolver/ResolverActivity.java | 1667 ++++++++++----------
1 file changed, 839 insertions(+), 828 deletions(-)
(limited to 'java/src')
diff --git a/java/src/com/android/intentresolver/ResolverActivity.java b/java/src/com/android/intentresolver/ResolverActivity.java
index d431d57b..ff436ed8 100644
--- a/java/src/com/android/intentresolver/ResolverActivity.java
+++ b/java/src/com/android/intentresolver/ResolverActivity.java
@@ -239,22 +239,6 @@ public class ResolverActivity extends FragmentActivity implements
protected final LatencyTracker mLatencyTracker = getLatencyTracker();
- private LatencyTracker getLatencyTracker() {
- return LatencyTracker.getInstance(this);
- }
-
- /**
- * Get the string resource to be used as a label for the link to the resolver activity for an
- * action.
- *
- * @param action The action to resolve
- *
- * @return The string resource to be used as a label
- */
- public static @StringRes int getLabelRes(String action) {
- return ActionTitle.forAction(action).labelRes;
- }
-
private enum ActionTitle {
VIEW(Intent.ACTION_VIEW,
com.android.internal.R.string.whichViewApplication,
@@ -338,27 +322,6 @@ public class ResolverActivity extends FragmentActivity implements
};
}
- private Intent makeMyIntent() {
- Intent intent = new Intent(getIntent());
- intent.setComponent(null);
- // The resolver activity is set to be hidden from recent tasks.
- // we don't want this attribute to be propagated to the next activity
- // being launched. Note that if the original Intent also had this
- // flag set, we are now losing it. That should be a very rare case
- // and we can live with this.
- intent.setFlags(intent.getFlags()&~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- return intent;
- }
-
- /**
- * Call {@link Activity#onCreate} without initializing anything further. This should
- * only be used when the activity is about to be immediately finished to avoid wasting
- * initializing steps and leaking resources.
- */
- protected void super_onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-
@Override
protected void onCreate(Bundle savedInstanceState) {
// Use a specialized prompt when we're handling the 'Home' app startActivity()
@@ -492,48 +455,6 @@ public class ResolverActivity extends FragmentActivity implements
return resolverMultiProfilePagerAdapter;
}
- @VisibleForTesting
- protected MyUserIdProvider createMyUserIdProvider() {
- return new MyUserIdProvider();
- }
-
- @VisibleForTesting
- protected CrossProfileIntentsChecker createCrossProfileIntentsChecker() {
- return new CrossProfileIntentsChecker(getContentResolver());
- }
-
- @VisibleForTesting
- protected QuietModeManager createQuietModeManager() {
- UserManager userManager = getSystemService(UserManager.class);
- return new QuietModeManager() {
-
- private boolean mIsWaitingToEnableWorkProfile = false;
-
- @Override
- public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
- return userManager.isQuietModeEnabled(workProfileUserHandle);
- }
-
- @Override
- public void requestQuietModeEnabled(boolean enabled, UserHandle workProfileUserHandle) {
- AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
- userManager.requestQuietModeEnabled(enabled, workProfileUserHandle);
- });
- mIsWaitingToEnableWorkProfile = true;
- }
-
- @Override
- public void markWorkProfileEnabledBroadcastReceived() {
- mIsWaitingToEnableWorkProfile = false;
- }
-
- @Override
- public boolean isWaitingToEnableWorkProfile() {
- return mIsWaitingToEnableWorkProfile;
- }
- };
- }
-
protected EmptyStateProvider createBlockerEmptyStateProvider() {
final boolean shouldShowNoCrossProfileIntentsEmptyState = getUser().equals(getIntentUser());
@@ -551,7 +472,8 @@ public class ResolverActivity extends FragmentActivity implements
/* defaultSubtitleResource= */
R.string.resolver_cant_access_personal_apps_explanation,
/* devicePolicyEventId= */ RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL,
- /* devicePolicyEventCategory= */ ResolverActivity.METRICS_CATEGORY_RESOLVER);
+ /* devicePolicyEventCategory= */
+ ResolverActivity.METRICS_CATEGORY_RESOLVER);
final AbstractMultiProfilePagerAdapter.EmptyState noPersonalToWorkEmptyState =
new DevicePolicyBlockerEmptyState(/* context= */ this,
@@ -561,182 +483,18 @@ public class ResolverActivity extends FragmentActivity implements
/* defaultSubtitleResource= */
R.string.resolver_cant_access_work_apps_explanation,
/* devicePolicyEventId= */ RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK,
- /* devicePolicyEventCategory= */ ResolverActivity.METRICS_CATEGORY_RESOLVER);
+ /* devicePolicyEventCategory= */
+ ResolverActivity.METRICS_CATEGORY_RESOLVER);
return new NoCrossProfileEmptyStateProvider(getPersonalProfileUserHandle(),
noWorkToPersonalEmptyState, noPersonalToWorkEmptyState,
createCrossProfileIntentsChecker(), createMyUserIdProvider());
}
- protected EmptyStateProvider createEmptyStateProvider(
- @Nullable UserHandle workProfileUserHandle) {
- final EmptyStateProvider blockerEmptyStateProvider = createBlockerEmptyStateProvider();
-
- final EmptyStateProvider workProfileOffEmptyStateProvider =
- new WorkProfilePausedEmptyStateProvider(this, workProfileUserHandle,
- mQuietModeManager,
- /* onSwitchOnWorkSelectedListener= */
- () -> { if (mOnSwitchOnWorkSelectedListener != null) {
- mOnSwitchOnWorkSelectedListener.onSwitchOnWorkSelected();
- }},
- getMetricsCategory());
-
- final EmptyStateProvider noAppsEmptyStateProvider = new NoAppsAvailableEmptyStateProvider(
- this,
- workProfileUserHandle,
- getPersonalProfileUserHandle(),
- getMetricsCategory(),
- createMyUserIdProvider()
- );
-
- // Return composite provider, the order matters (the higher, the more priority)
- return new CompositeEmptyStateProvider(
- blockerEmptyStateProvider,
- workProfileOffEmptyStateProvider,
- noAppsEmptyStateProvider
- );
- }
-
- private ResolverMultiProfilePagerAdapter createResolverMultiProfilePagerAdapterForOneProfile(
- Intent[] initialIntents,
- List rList, boolean filterLastUsed) {
- ResolverListAdapter adapter = createResolverListAdapter(
- /* context */ this,
- /* payloadIntents */ mIntents,
- initialIntents,
- rList,
- filterLastUsed,
- /* userHandle */ UserHandle.of(UserHandle.myUserId()));
- QuietModeManager quietModeManager = createQuietModeManager();
- return new ResolverMultiProfilePagerAdapter(
- /* context */ this,
- adapter,
- createEmptyStateProvider(/* workProfileUserHandle= */ null),
- quietModeManager,
- /* workProfileUserHandle= */ null);
- }
-
- private UserHandle getIntentUser() {
- return getIntent().hasExtra(EXTRA_CALLING_USER)
- ? getIntent().getParcelableExtra(EXTRA_CALLING_USER)
- : getUser();
- }
-
- private ResolverMultiProfilePagerAdapter createResolverMultiProfilePagerAdapterForTwoProfiles(
- Intent[] initialIntents,
- List rList,
- boolean filterLastUsed) {
- // In the edge case when we have 0 apps in the current profile and >1 apps in the other,
- // the intent resolver is started in the other profile. Since this is the only case when
- // this happens, we check for it here and set the current profile's tab.
- int selectedProfile = getCurrentProfile();
- UserHandle intentUser = getIntentUser();
- if (!getUser().equals(intentUser)) {
- if (getPersonalProfileUserHandle().equals(intentUser)) {
- selectedProfile = PROFILE_PERSONAL;
- } else if (getWorkProfileUserHandle().equals(intentUser)) {
- selectedProfile = PROFILE_WORK;
- }
- } else {
- int selectedProfileExtra = getSelectedProfileExtra();
- if (selectedProfileExtra != -1) {
- selectedProfile = selectedProfileExtra;
- }
- }
- // We only show the default app for the profile of the current user. The filterLastUsed
- // flag determines whether to show a default app and that app is not shown in the
- // resolver list. So filterLastUsed should be false for the other profile.
- ResolverListAdapter personalAdapter = createResolverListAdapter(
- /* context */ this,
- /* payloadIntents */ mIntents,
- selectedProfile == PROFILE_PERSONAL ? initialIntents : null,
- rList,
- (filterLastUsed && UserHandle.myUserId()
- == getPersonalProfileUserHandle().getIdentifier()),
- /* userHandle */ getPersonalProfileUserHandle());
- UserHandle workProfileUserHandle = getWorkProfileUserHandle();
- ResolverListAdapter workAdapter = createResolverListAdapter(
- /* context */ this,
- /* payloadIntents */ mIntents,
- selectedProfile == PROFILE_WORK ? initialIntents : null,
- rList,
- (filterLastUsed && UserHandle.myUserId()
- == workProfileUserHandle.getIdentifier()),
- /* userHandle */ workProfileUserHandle);
- QuietModeManager quietModeManager = createQuietModeManager();
- return new ResolverMultiProfilePagerAdapter(
- /* context */ this,
- personalAdapter,
- workAdapter,
- createEmptyStateProvider(getWorkProfileUserHandle()),
- quietModeManager,
- selectedProfile,
- getWorkProfileUserHandle());
- }
-
protected int appliedThemeResId() {
return R.style.Theme_DeviceDefault_Resolver;
}
- /**
- * Returns {@link #PROFILE_PERSONAL} or {@link #PROFILE_WORK} if the {@link
- * #EXTRA_SELECTED_PROFILE} extra was supplied, or {@code -1} if no extra was supplied.
- * @throws IllegalArgumentException if the value passed to the {@link #EXTRA_SELECTED_PROFILE}
- * extra is not {@link #PROFILE_PERSONAL} or {@link #PROFILE_WORK}
- */
- int getSelectedProfileExtra() {
- int selectedProfile = -1;
- if (getIntent().hasExtra(EXTRA_SELECTED_PROFILE)) {
- selectedProfile = getIntent().getIntExtra(EXTRA_SELECTED_PROFILE, /* defValue = */ -1);
- if (selectedProfile != PROFILE_PERSONAL && selectedProfile != PROFILE_WORK) {
- throw new IllegalArgumentException(EXTRA_SELECTED_PROFILE + " has invalid value "
- + selectedProfile + ". Must be either ResolverActivity.PROFILE_PERSONAL or "
- + "ResolverActivity.PROFILE_WORK.");
- }
- }
- return selectedProfile;
- }
-
- protected @Profile int getCurrentProfile() {
- return (UserHandle.myUserId() == UserHandle.USER_SYSTEM ? PROFILE_PERSONAL : PROFILE_WORK);
- }
-
- protected final AnnotatedUserHandles getAnnotatedUserHandles() {
- return mLazyAnnotatedUserHandles.get();
- }
-
- protected final UserHandle getPersonalProfileUserHandle() {
- return getAnnotatedUserHandles().personalProfileUserHandle;
- }
-
- // TODO: have tests override `getAnnotatedUserHandles()`, and make this method `final`.
- @Nullable
- protected UserHandle getWorkProfileUserHandle() {
- return getAnnotatedUserHandles().workProfileUserHandle;
- }
-
- private boolean hasWorkProfile() {
- return getWorkProfileUserHandle() != null;
- }
-
- protected boolean shouldShowTabs() {
- return hasWorkProfile();
- }
-
- protected void onProfileClick(View v) {
- final DisplayResolveInfo dri =
- mMultiProfilePagerAdapter.getActiveListAdapter().getOtherProfile();
- if (dri == null) {
- return;
- }
-
- // Do not show the profile switch message anymore.
- mProfileSwitchMessage = null;
-
- onTargetSelected(dri, false);
- finish();
- }
-
/**
* Numerous layouts are supported, each with optional ViewGroups.
* Make sure the inset gets added to the correct View, using
@@ -801,219 +559,13 @@ public class ResolverActivity extends FragmentActivity implements
}
}
- private void updateIntentPickerPaddings() {
- View titleCont = findViewById(com.android.internal.R.id.title_container);
- titleCont.setPadding(
- titleCont.getPaddingLeft(),
- titleCont.getPaddingTop(),
- titleCont.getPaddingRight(),
- getResources().getDimensionPixelSize(R.dimen.resolver_title_padding_bottom));
- View buttonBar = findViewById(com.android.internal.R.id.button_bar);
- buttonBar.setPadding(
- buttonBar.getPaddingLeft(),
- getResources().getDimensionPixelSize(R.dimen.resolver_button_bar_spacing),
- buttonBar.getPaddingRight(),
- getResources().getDimensionPixelSize(R.dimen.resolver_button_bar_spacing));
+ public int getLayoutResource() {
+ return R.layout.resolver_list;
}
- @Override // ResolverListCommunicator
- public void sendVoiceChoicesIfNeeded() {
- if (!isVoiceInteraction()) {
- // Clearly not needed.
- return;
- }
-
- int count = mMultiProfilePagerAdapter.getActiveListAdapter().getCount();
- final Option[] options = new Option[count];
- for (int i = 0, N = options.length; i < N; i++) {
- TargetInfo target = mMultiProfilePagerAdapter.getActiveListAdapter().getItem(i);
- if (target == null) {
- // If this occurs, a new set of targets is being loaded. Let that complete,
- // and have the next call to send voice choices proceed instead.
- return;
- }
- options[i] = optionForChooserTarget(target, i);
- }
-
- mPickOptionRequest = new PickTargetOptionRequest(
- new Prompt(getTitle()), options, null);
- getVoiceInteractor().submitRequest(mPickOptionRequest);
- }
-
- Option optionForChooserTarget(TargetInfo target, int index) {
- return new Option(target.getDisplayLabel(), index);
- }
-
- protected final void setAdditionalTargets(Intent[] intents) {
- if (intents != null) {
- for (Intent intent : intents) {
- mIntents.add(intent);
- }
- }
- }
-
- public Intent getTargetIntent() {
- return mIntents.isEmpty() ? null : mIntents.get(0);
- }
-
- protected String getReferrerPackageName() {
- final Uri referrer = getReferrer();
- if (referrer != null && "android-app".equals(referrer.getScheme())) {
- return referrer.getHost();
- }
- return null;
- }
-
- public int getLayoutResource() {
- return R.layout.resolver_list;
- }
-
- @Override // ResolverListCommunicator
- public void updateProfileViewButton() {
- if (mProfileView == null) {
- return;
- }
-
- final DisplayResolveInfo dri =
- mMultiProfilePagerAdapter.getActiveListAdapter().getOtherProfile();
- if (dri != null && !shouldShowTabs()) {
- mProfileView.setVisibility(View.VISIBLE);
- View text = mProfileView.findViewById(com.android.internal.R.id.profile_button);
- if (!(text instanceof TextView)) {
- text = mProfileView.findViewById(com.android.internal.R.id.text1);
- }
- ((TextView) text).setText(dri.getDisplayLabel());
- } else {
- mProfileView.setVisibility(View.GONE);
- }
- }
-
- private void setProfileSwitchMessage(int contentUserHint) {
- if (contentUserHint != UserHandle.USER_CURRENT &&
- contentUserHint != UserHandle.myUserId()) {
- UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
- UserInfo originUserInfo = userManager.getUserInfo(contentUserHint);
- boolean originIsManaged = originUserInfo != null ? originUserInfo.isManagedProfile()
- : false;
- boolean targetIsManaged = userManager.isManagedProfile();
- if (originIsManaged && !targetIsManaged) {
- mProfileSwitchMessage = getForwardToPersonalMsg();
- } else if (!originIsManaged && targetIsManaged) {
- mProfileSwitchMessage = getForwardToWorkMsg();
- }
- }
- }
-
- private String getForwardToPersonalMsg() {
- return getSystemService(DevicePolicyManager.class).getResources().getString(
- FORWARD_INTENT_TO_PERSONAL,
- () -> getString(com.android.internal.R.string.forward_intent_to_owner));
- }
-
- private String getForwardToWorkMsg() {
- return getSystemService(DevicePolicyManager.class).getResources().getString(
- FORWARD_INTENT_TO_WORK,
- () -> getString(com.android.internal.R.string.forward_intent_to_work));
- }
-
- /**
- * Turn on launch mode that is safe to use when forwarding intents received from
- * applications and running in system processes. This mode uses Activity.startActivityAsCaller
- * instead of the normal Activity.startActivity for launching the activity selected
- * by the user.
- *
- * This mode is set to true by default if the activity is initialized through
- * {@link #onCreate(android.os.Bundle)}. If a subclass calls one of the other onCreate
- * methods, it is set to false by default. You must set it before calling one of the
- * more detailed onCreate methods, so that it will be set correctly in the case where
- * there is only one intent to resolve and it is thus started immediately.
- */
- public void setSafeForwardingMode(boolean safeForwarding) {
- mSafeForwardingMode = safeForwarding;
- }
-
- protected CharSequence getTitleForAction(Intent intent, int defaultTitleRes) {
- final ActionTitle title = mResolvingHome
- ? ActionTitle.HOME
- : ActionTitle.forAction(intent.getAction());
-
- // While there may already be a filtered item, we can only use it in the title if the list
- // is already sorted and all information relevant to it is already in the list.
- final boolean named =
- mMultiProfilePagerAdapter.getActiveListAdapter().getFilteredPosition() >= 0;
- if (title == ActionTitle.DEFAULT && defaultTitleRes != 0) {
- return getString(defaultTitleRes);
- } else {
- return named
- ? getString(title.namedTitleRes, mMultiProfilePagerAdapter
- .getActiveListAdapter().getFilteredItem().getDisplayLabel())
- : getString(title.titleRes);
- }
- }
-
- void dismiss() {
- if (!isFinishing()) {
- finish();
- }
- }
-
- @Override
- protected void onRestart() {
- super.onRestart();
- if (!mRegistered) {
- mPersonalPackageMonitor.register(this, getMainLooper(),
- getPersonalProfileUserHandle(), false);
- if (shouldShowTabs()) {
- if (mWorkPackageMonitor == null) {
- mWorkPackageMonitor = createPackageMonitor(
- mMultiProfilePagerAdapter.getWorkListAdapter());
- }
- mWorkPackageMonitor.register(this, getMainLooper(),
- getWorkProfileUserHandle(), false);
- }
- mRegistered = true;
- }
- if (shouldShowTabs() && mQuietModeManager.isWaitingToEnableWorkProfile()) {
- if (mQuietModeManager.isQuietModeEnabled(getWorkProfileUserHandle())) {
- mQuietModeManager.markWorkProfileEnabledBroadcastReceived();
- }
- }
- mMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged();
- updateProfileViewButton();
- }
-
- @Override
- protected void onStart() {
- super.onStart();
-
- this.getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
- if (shouldShowTabs()) {
- mWorkProfileStateReceiver = createWorkProfileStateReceiver();
- registerWorkProfileStateReceiver();
-
- mWorkProfileHasBeenEnabled = isWorkProfileEnabled();
- }
- }
-
- private boolean isWorkProfileEnabled() {
- UserHandle workUserHandle = getWorkProfileUserHandle();
- UserManager userManager = getSystemService(UserManager.class);
-
- return !userManager.isQuietModeEnabled(workUserHandle)
- && userManager.isUserUnlocked(workUserHandle);
- }
-
- private void registerWorkProfileStateReceiver() {
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_UNLOCKED);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
- registerReceiverAsUser(mWorkProfileStateReceiver, UserHandle.ALL, filter, null, null);
- }
-
- @Override
- protected void onStop() {
- super.onStop();
+ @Override
+ protected void onStop() {
+ super.onStop();
final Window window = this.getWindow();
final WindowManager.LayoutParams attrs = window.getAttributes();
@@ -1059,156 +611,53 @@ public class ResolverActivity extends FragmentActivity implements
}
}
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- ViewPager viewPager = findViewById(com.android.internal.R.id.profile_pager);
- if (viewPager != null) {
- outState.putInt(LAST_SHOWN_TAB_KEY, viewPager.getCurrentItem());
- }
+ public void onButtonClick(View v) {
+ final int id = v.getId();
+ ListView listView = (ListView) mMultiProfilePagerAdapter.getActiveAdapterView();
+ ResolverListAdapter currentListAdapter = mMultiProfilePagerAdapter.getActiveListAdapter();
+ int which = currentListAdapter.hasFilteredItem()
+ ? currentListAdapter.getFilteredPosition()
+ : listView.getCheckedItemPosition();
+ boolean hasIndexBeenFiltered = !currentListAdapter.hasFilteredItem();
+ startSelected(which, id == com.android.internal.R.id.button_always, hasIndexBeenFiltered);
}
- @Override
- protected void onRestoreInstanceState(Bundle savedInstanceState) {
- super.onRestoreInstanceState(savedInstanceState);
- resetButtonBar();
- ViewPager viewPager = findViewById(com.android.internal.R.id.profile_pager);
- if (viewPager != null) {
- viewPager.setCurrentItem(savedInstanceState.getInt(LAST_SHOWN_TAB_KEY));
+ public void startSelected(int which, boolean always, boolean hasIndexBeenFiltered) {
+ if (isFinishing()) {
+ return;
}
- mMultiProfilePagerAdapter.clearInactiveProfileCache();
- }
-
- private boolean hasManagedProfile() {
- UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
- if (userManager == null) {
- return false;
+ ResolveInfo ri = mMultiProfilePagerAdapter.getActiveListAdapter()
+ .resolveInfoForPosition(which, hasIndexBeenFiltered);
+ if (mResolvingHome && hasManagedProfile() && !supportsManagedProfiles(ri)) {
+ Toast.makeText(this,
+ getWorkProfileNotSupportedMsg(
+ ri.activityInfo.loadLabel(getPackageManager()).toString()),
+ Toast.LENGTH_LONG).show();
+ return;
}
- try {
- List profiles = userManager.getProfiles(getUserId());
- for (UserInfo userInfo : profiles) {
- if (userInfo != null && userInfo.isManagedProfile()) {
- return true;
- }
+ TargetInfo target = mMultiProfilePagerAdapter.getActiveListAdapter()
+ .targetInfoForPosition(which, hasIndexBeenFiltered);
+ if (target == null) {
+ return;
+ }
+ if (onTargetSelected(target, always)) {
+ if (always && mSupportsAlwaysUseOption) {
+ MetricsLogger.action(
+ this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_ALWAYS);
+ } else if (mSupportsAlwaysUseOption) {
+ MetricsLogger.action(
+ this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_JUST_ONCE);
+ } else {
+ MetricsLogger.action(
+ this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_TAP);
}
- } catch (SecurityException e) {
- return false;
+ MetricsLogger.action(this,
+ mMultiProfilePagerAdapter.getActiveListAdapter().hasFilteredItem()
+ ? MetricsProto.MetricsEvent.ACTION_HIDE_APP_DISAMBIG_APP_FEATURED
+ : MetricsProto.MetricsEvent.ACTION_HIDE_APP_DISAMBIG_NONE_FEATURED);
+ finish();
}
- return false;
- }
-
- private boolean supportsManagedProfiles(ResolveInfo resolveInfo) {
- try {
- ApplicationInfo appInfo = getPackageManager().getApplicationInfo(
- resolveInfo.activityInfo.packageName, 0 /* default flags */);
- return appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP;
- } catch (NameNotFoundException e) {
- return false;
- }
- }
-
- private void setAlwaysButtonEnabled(boolean hasValidSelection, int checkedPos,
- boolean filtered) {
- if (!mMultiProfilePagerAdapter.getCurrentUserHandle().equals(getUser())) {
- // Never allow the inactive profile to always open an app.
- mAlwaysButton.setEnabled(false);
- return;
- }
- boolean enabled = false;
- ResolveInfo ri = null;
- if (hasValidSelection) {
- ri = mMultiProfilePagerAdapter.getActiveListAdapter()
- .resolveInfoForPosition(checkedPos, filtered);
- if (ri == null) {
- Log.e(TAG, "Invalid position supplied to setAlwaysButtonEnabled");
- return;
- } else if (ri.targetUserId != UserHandle.USER_CURRENT) {
- Log.e(TAG, "Attempted to set selection to resolve info for another user");
- return;
- } else {
- enabled = true;
- }
-
- mAlwaysButton.setText(getResources()
- .getString(R.string.activity_resolver_use_always));
- }
-
- if (ri != null) {
- ActivityInfo activityInfo = ri.activityInfo;
-
- boolean hasRecordPermission =
- mPm.checkPermission(android.Manifest.permission.RECORD_AUDIO,
- activityInfo.packageName)
- == android.content.pm.PackageManager.PERMISSION_GRANTED;
-
- if (!hasRecordPermission) {
- // OK, we know the record permission, is this a capture device
- boolean hasAudioCapture =
- getIntent().getBooleanExtra(
- ResolverActivity.EXTRA_IS_AUDIO_CAPTURE_DEVICE, false);
- enabled = !hasAudioCapture;
- }
- }
- mAlwaysButton.setEnabled(enabled);
- }
-
- public void onButtonClick(View v) {
- final int id = v.getId();
- ListView listView = (ListView) mMultiProfilePagerAdapter.getActiveAdapterView();
- ResolverListAdapter currentListAdapter = mMultiProfilePagerAdapter.getActiveListAdapter();
- int which = currentListAdapter.hasFilteredItem()
- ? currentListAdapter.getFilteredPosition()
- : listView.getCheckedItemPosition();
- boolean hasIndexBeenFiltered = !currentListAdapter.hasFilteredItem();
- startSelected(which, id == com.android.internal.R.id.button_always, hasIndexBeenFiltered);
- }
-
- public void startSelected(int which, boolean always, boolean hasIndexBeenFiltered) {
- if (isFinishing()) {
- return;
- }
- ResolveInfo ri = mMultiProfilePagerAdapter.getActiveListAdapter()
- .resolveInfoForPosition(which, hasIndexBeenFiltered);
- if (mResolvingHome && hasManagedProfile() && !supportsManagedProfiles(ri)) {
- Toast.makeText(this,
- getWorkProfileNotSupportedMsg(
- ri.activityInfo.loadLabel(getPackageManager()).toString()),
- Toast.LENGTH_LONG).show();
- return;
- }
-
- TargetInfo target = mMultiProfilePagerAdapter.getActiveListAdapter()
- .targetInfoForPosition(which, hasIndexBeenFiltered);
- if (target == null) {
- return;
- }
- if (onTargetSelected(target, always)) {
- if (always && mSupportsAlwaysUseOption) {
- MetricsLogger.action(
- this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_ALWAYS);
- } else if (mSupportsAlwaysUseOption) {
- MetricsLogger.action(
- this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_JUST_ONCE);
- } else {
- MetricsLogger.action(
- this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_TAP);
- }
- MetricsLogger.action(this,
- mMultiProfilePagerAdapter.getActiveListAdapter().hasFilteredItem()
- ? MetricsProto.MetricsEvent.ACTION_HIDE_APP_DISAMBIG_APP_FEATURED
- : MetricsProto.MetricsEvent.ACTION_HIDE_APP_DISAMBIG_NONE_FEATURED);
- finish();
- }
- }
-
- private String getWorkProfileNotSupportedMsg(String launcherName) {
- return getSystemService(DevicePolicyManager.class).getResources().getString(
- RESOLVER_WORK_PROFILE_NOT_SUPPORTED,
- () -> getString(
- com.android.internal.R.string.activity_resolver_work_profiles_support,
- launcherName),
- launcherName);
}
/**
@@ -1219,33 +668,6 @@ public class ResolverActivity extends FragmentActivity implements
return defIntent;
}
- @Override // ResolverListCommunicator
- public final void onPostListReady(ResolverListAdapter listAdapter, boolean doPostProcessing,
- boolean rebuildCompleted) {
- if (isAutolaunching()) {
- return;
- }
- if (mIsIntentPicker) {
- ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter)
- .setUseLayoutWithDefault(useLayoutWithDefault());
- }
- if (mMultiProfilePagerAdapter.shouldShowEmptyStateScreen(listAdapter)) {
- mMultiProfilePagerAdapter.showEmptyResolverListEmptyState(listAdapter);
- } else {
- mMultiProfilePagerAdapter.showListView(listAdapter);
- }
- // showEmptyResolverListEmptyState can mark the tab as loaded,
- // which is a precondition for auto launching
- if (rebuildCompleted && maybeAutolaunchActivity()) {
- return;
- }
- if (doPostProcessing) {
- maybeCreateHeader(listAdapter);
- resetButtonBar();
- onListRebuilt(listAdapter, rebuildCompleted);
- }
- }
-
protected void onListRebuilt(ResolverListAdapter listAdapter, boolean rebuildCompleted) {
final ItemClickListener listener = new ItemClickListener();
setupAdapterListView((ListView) mMultiProfilePagerAdapter.getActiveAdapterView(), listener);
@@ -1407,29 +829,783 @@ public class ResolverActivity extends FragmentActivity implements
}
}
- if (target != null) {
- safelyStartActivity(target);
+ if (target != null) {
+ safelyStartActivity(target);
+
+ // Rely on the ActivityManager to pop up a dialog regarding app suspension
+ // and return false
+ if (target.isSuspended()) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public void onActivityStarted(TargetInfo cti) {
+ // Do nothing
+ }
+
+ @Override // ResolverListCommunicator
+ public boolean shouldGetActivityMetadata() {
+ return false;
+ }
+
+ public boolean shouldAutoLaunchSingleChoice(TargetInfo target) {
+ return !target.isSuspended();
+ }
+
+ @VisibleForTesting
+ protected ResolverListController createListController(UserHandle userHandle) {
+ return new ResolverListController(
+ this,
+ mPm,
+ getTargetIntent(),
+ getReferrerPackageName(),
+ getAnnotatedUserHandles().userIdOfCallingApp,
+ userHandle);
+ }
+
+ /**
+ * Finishing procedures to be performed after the list has been rebuilt.
+ *
Subclasses must call postRebuildListInternal at the end of postRebuildList.
+ * @param rebuildCompleted
+ * @return true if the activity is finishing and creation should halt.
+ */
+ protected boolean postRebuildList(boolean rebuildCompleted) {
+ return postRebuildListInternal(rebuildCompleted);
+ }
+
+ void onHorizontalSwipeStateChanged(int state) {}
+
+ /**
+ * Callback called when user changes the profile tab.
+ * This method is intended to be overridden by subclasses.
+ */
+ protected void onProfileTabSelected() { }
+
+ /**
+ * Add a label to signify that the user can pick a different app.
+ * @param adapter The adapter used to provide data to item views.
+ */
+ public void addUseDifferentAppLabelIfNecessary(ResolverListAdapter adapter) {
+ final boolean useHeader = adapter.hasFilteredItem();
+ if (useHeader) {
+ FrameLayout stub = findViewById(com.android.internal.R.id.stub);
+ stub.setVisibility(View.VISIBLE);
+ TextView textView = (TextView) LayoutInflater.from(this).inflate(
+ R.layout.resolver_different_item_header, null, false);
+ if (shouldShowTabs()) {
+ textView.setGravity(Gravity.CENTER);
+ }
+ stub.addView(textView);
+ }
+ }
+
+ protected void resetButtonBar() {
+ if (!mSupportsAlwaysUseOption) {
+ return;
+ }
+ final ViewGroup buttonLayout = findViewById(com.android.internal.R.id.button_bar);
+ if (buttonLayout == null) {
+ Log.e(TAG, "Layout unexpectedly does not have a button bar");
+ return;
+ }
+ ResolverListAdapter activeListAdapter =
+ mMultiProfilePagerAdapter.getActiveListAdapter();
+ View buttonBarDivider = findViewById(com.android.internal.R.id.resolver_button_bar_divider);
+ if (!useLayoutWithDefault()) {
+ int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0;
+ buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(),
+ buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize(
+ R.dimen.resolver_button_bar_spacing) + inset);
+ }
+ if (activeListAdapter.isTabLoaded()
+ && mMultiProfilePagerAdapter.shouldShowEmptyStateScreen(activeListAdapter)
+ && !useLayoutWithDefault()) {
+ buttonLayout.setVisibility(View.INVISIBLE);
+ if (buttonBarDivider != null) {
+ buttonBarDivider.setVisibility(View.INVISIBLE);
+ }
+ setButtonBarIgnoreOffset(/* ignoreOffset */ false);
+ return;
+ }
+ if (buttonBarDivider != null) {
+ buttonBarDivider.setVisibility(View.VISIBLE);
+ }
+ buttonLayout.setVisibility(View.VISIBLE);
+ setButtonBarIgnoreOffset(/* ignoreOffset */ true);
+
+ mOnceButton = (Button) buttonLayout.findViewById(com.android.internal.R.id.button_once);
+ mAlwaysButton = (Button) buttonLayout.findViewById(com.android.internal.R.id.button_always);
+
+ resetAlwaysOrOnceButtonBar();
+ }
+
+ protected String getMetricsCategory() {
+ return METRICS_CATEGORY_RESOLVER;
+ }
+
+ @Override // ResolverListCommunicator
+ public void onHandlePackagesChanged(ResolverListAdapter listAdapter) {
+ if (listAdapter == mMultiProfilePagerAdapter.getActiveListAdapter()) {
+ if (listAdapter.getUserHandle().equals(getWorkProfileUserHandle())
+ && mQuietModeManager.isWaitingToEnableWorkProfile()) {
+ // We have just turned on the work profile and entered the pass code to start it,
+ // now we are waiting to receive the ACTION_USER_UNLOCKED broadcast. There is no
+ // point in reloading the list now, since the work profile user is still
+ // turning on.
+ return;
+ }
+ boolean listRebuilt = mMultiProfilePagerAdapter.rebuildActiveTab(true);
+ if (listRebuilt) {
+ ResolverListAdapter activeListAdapter =
+ mMultiProfilePagerAdapter.getActiveListAdapter();
+ activeListAdapter.notifyDataSetChanged();
+ if (activeListAdapter.getCount() == 0 && !inactiveListAdapterHasItems()) {
+ // We no longer have any items... just finish the activity.
+ finish();
+ }
+ }
+ } else {
+ mMultiProfilePagerAdapter.clearInactiveProfileCache();
+ }
+ }
+
+ protected void maybeLogProfileChange() {}
+
+ // @NonFinalForTesting
+ @VisibleForTesting
+ protected MyUserIdProvider createMyUserIdProvider() {
+ return new MyUserIdProvider();
+ }
+
+ // @NonFinalForTesting
+ @VisibleForTesting
+ protected CrossProfileIntentsChecker createCrossProfileIntentsChecker() {
+ return new CrossProfileIntentsChecker(getContentResolver());
+ }
+
+ // @NonFinalForTesting
+ @VisibleForTesting
+ protected QuietModeManager createQuietModeManager() {
+ UserManager userManager = getSystemService(UserManager.class);
+ return new QuietModeManager() {
+
+ private boolean mIsWaitingToEnableWorkProfile = false;
+
+ @Override
+ public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
+ return userManager.isQuietModeEnabled(workProfileUserHandle);
+ }
+
+ @Override
+ public void requestQuietModeEnabled(boolean enabled, UserHandle workProfileUserHandle) {
+ AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
+ userManager.requestQuietModeEnabled(enabled, workProfileUserHandle);
+ });
+ mIsWaitingToEnableWorkProfile = true;
+ }
+
+ @Override
+ public void markWorkProfileEnabledBroadcastReceived() {
+ mIsWaitingToEnableWorkProfile = false;
+ }
+
+ @Override
+ public boolean isWaitingToEnableWorkProfile() {
+ return mIsWaitingToEnableWorkProfile;
+ }
+ };
+ }
+
+ // TODO: have tests override `getAnnotatedUserHandles()`, and make this method `final`.
+ // @NonFinalForTesting
+ @Nullable
+ protected UserHandle getWorkProfileUserHandle() {
+ return getAnnotatedUserHandles().workProfileUserHandle;
+ }
+
+ // @NonFinalForTesting
+ @VisibleForTesting
+ public void safelyStartActivity(TargetInfo cti) {
+ // We're dispatching intents that might be coming from legacy apps, so
+ // don't kill ourselves.
+ StrictMode.disableDeathOnFileUriExposure();
+ try {
+ UserHandle currentUserHandle = mMultiProfilePagerAdapter.getCurrentUserHandle();
+ safelyStartActivityInternal(cti, currentUserHandle, null);
+ } finally {
+ StrictMode.enableDeathOnFileUriExposure();
+ }
+ }
+
+ // @NonFinalForTesting
+ @VisibleForTesting
+ protected ResolverListAdapter createResolverListAdapter(Context context,
+ List payloadIntents, Intent[] initialIntents, List rList,
+ boolean filterLastUsed, UserHandle userHandle) {
+ Intent startIntent = getIntent();
+ boolean isAudioCaptureDevice =
+ startIntent.getBooleanExtra(EXTRA_IS_AUDIO_CAPTURE_DEVICE, false);
+ return new ResolverListAdapter(
+ context,
+ payloadIntents,
+ initialIntents,
+ rList,
+ filterLastUsed,
+ createListController(userHandle),
+ userHandle,
+ getTargetIntent(),
+ this,
+ isAudioCaptureDevice);
+ }
+
+ private LatencyTracker getLatencyTracker() {
+ return LatencyTracker.getInstance(this);
+ }
+
+ /**
+ * Get the string resource to be used as a label for the link to the resolver activity for an
+ * action.
+ *
+ * @param action The action to resolve
+ *
+ * @return The string resource to be used as a label
+ */
+ public static @StringRes int getLabelRes(String action) {
+ return ActionTitle.forAction(action).labelRes;
+ }
+
+ protected final EmptyStateProvider createEmptyStateProvider(
+ @Nullable UserHandle workProfileUserHandle) {
+ final EmptyStateProvider blockerEmptyStateProvider = createBlockerEmptyStateProvider();
+
+ final EmptyStateProvider workProfileOffEmptyStateProvider =
+ new WorkProfilePausedEmptyStateProvider(this, workProfileUserHandle,
+ mQuietModeManager,
+ /* onSwitchOnWorkSelectedListener= */
+ () -> {
+ if (mOnSwitchOnWorkSelectedListener != null) {
+ mOnSwitchOnWorkSelectedListener.onSwitchOnWorkSelected();
+ }
+ },
+ getMetricsCategory());
+
+ final EmptyStateProvider noAppsEmptyStateProvider = new NoAppsAvailableEmptyStateProvider(
+ this,
+ workProfileUserHandle,
+ getPersonalProfileUserHandle(),
+ getMetricsCategory(),
+ createMyUserIdProvider()
+ );
+
+ // Return composite provider, the order matters (the higher, the more priority)
+ return new CompositeEmptyStateProvider(
+ blockerEmptyStateProvider,
+ workProfileOffEmptyStateProvider,
+ noAppsEmptyStateProvider
+ );
+ }
+
+ private Intent makeMyIntent() {
+ Intent intent = new Intent(getIntent());
+ intent.setComponent(null);
+ // The resolver activity is set to be hidden from recent tasks.
+ // we don't want this attribute to be propagated to the next activity
+ // being launched. Note that if the original Intent also had this
+ // flag set, we are now losing it. That should be a very rare case
+ // and we can live with this.
+ intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ return intent;
+ }
+
+ /**
+ * Call {@link Activity#onCreate} without initializing anything further. This should
+ * only be used when the activity is about to be immediately finished to avoid wasting
+ * initializing steps and leaking resources.
+ */
+ protected final void super_onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ private ResolverMultiProfilePagerAdapter
+ createResolverMultiProfilePagerAdapterForOneProfile(
+ Intent[] initialIntents,
+ List rList,
+ boolean filterLastUsed) {
+ ResolverListAdapter adapter = createResolverListAdapter(
+ /* context */ this,
+ /* payloadIntents */ mIntents,
+ initialIntents,
+ rList,
+ filterLastUsed,
+ /* userHandle */ UserHandle.of(UserHandle.myUserId()));
+ QuietModeManager quietModeManager = createQuietModeManager();
+ return new ResolverMultiProfilePagerAdapter(
+ /* context */ this,
+ adapter,
+ createEmptyStateProvider(/* workProfileUserHandle= */ null),
+ quietModeManager,
+ /* workProfileUserHandle= */ null);
+ }
+
+ private UserHandle getIntentUser() {
+ return getIntent().hasExtra(EXTRA_CALLING_USER)
+ ? getIntent().getParcelableExtra(EXTRA_CALLING_USER)
+ : getUser();
+ }
+
+ private ResolverMultiProfilePagerAdapter createResolverMultiProfilePagerAdapterForTwoProfiles(
+ Intent[] initialIntents,
+ List rList,
+ boolean filterLastUsed) {
+ // In the edge case when we have 0 apps in the current profile and >1 apps in the other,
+ // the intent resolver is started in the other profile. Since this is the only case when
+ // this happens, we check for it here and set the current profile's tab.
+ int selectedProfile = getCurrentProfile();
+ UserHandle intentUser = getIntentUser();
+ if (!getUser().equals(intentUser)) {
+ if (getPersonalProfileUserHandle().equals(intentUser)) {
+ selectedProfile = PROFILE_PERSONAL;
+ } else if (getWorkProfileUserHandle().equals(intentUser)) {
+ selectedProfile = PROFILE_WORK;
+ }
+ } else {
+ int selectedProfileExtra = getSelectedProfileExtra();
+ if (selectedProfileExtra != -1) {
+ selectedProfile = selectedProfileExtra;
+ }
+ }
+ // We only show the default app for the profile of the current user. The filterLastUsed
+ // flag determines whether to show a default app and that app is not shown in the
+ // resolver list. So filterLastUsed should be false for the other profile.
+ ResolverListAdapter personalAdapter = createResolverListAdapter(
+ /* context */ this,
+ /* payloadIntents */ mIntents,
+ selectedProfile == PROFILE_PERSONAL ? initialIntents : null,
+ rList,
+ (filterLastUsed && UserHandle.myUserId()
+ == getPersonalProfileUserHandle().getIdentifier()),
+ /* userHandle */ getPersonalProfileUserHandle());
+ UserHandle workProfileUserHandle = getWorkProfileUserHandle();
+ ResolverListAdapter workAdapter = createResolverListAdapter(
+ /* context */ this,
+ /* payloadIntents */ mIntents,
+ selectedProfile == PROFILE_WORK ? initialIntents : null,
+ rList,
+ (filterLastUsed && UserHandle.myUserId()
+ == workProfileUserHandle.getIdentifier()),
+ /* userHandle */ workProfileUserHandle);
+ QuietModeManager quietModeManager = createQuietModeManager();
+ return new ResolverMultiProfilePagerAdapter(
+ /* context */ this,
+ personalAdapter,
+ workAdapter,
+ createEmptyStateProvider(getWorkProfileUserHandle()),
+ quietModeManager,
+ selectedProfile,
+ getWorkProfileUserHandle());
+ }
+
+ /**
+ * Returns {@link #PROFILE_PERSONAL} or {@link #PROFILE_WORK} if the {@link
+ * #EXTRA_SELECTED_PROFILE} extra was supplied, or {@code -1} if no extra was supplied.
+ * @throws IllegalArgumentException if the value passed to the {@link #EXTRA_SELECTED_PROFILE}
+ * extra is not {@link #PROFILE_PERSONAL} or {@link #PROFILE_WORK}
+ */
+ final int getSelectedProfileExtra() {
+ int selectedProfile = -1;
+ if (getIntent().hasExtra(EXTRA_SELECTED_PROFILE)) {
+ selectedProfile = getIntent().getIntExtra(EXTRA_SELECTED_PROFILE, /* defValue = */ -1);
+ if (selectedProfile != PROFILE_PERSONAL && selectedProfile != PROFILE_WORK) {
+ throw new IllegalArgumentException(EXTRA_SELECTED_PROFILE + " has invalid value "
+ + selectedProfile + ". Must be either ResolverActivity.PROFILE_PERSONAL or "
+ + "ResolverActivity.PROFILE_WORK.");
+ }
+ }
+ return selectedProfile;
+ }
+
+ protected final @Profile int getCurrentProfile() {
+ return (UserHandle.myUserId() == UserHandle.USER_SYSTEM ? PROFILE_PERSONAL : PROFILE_WORK);
+ }
+
+ protected final AnnotatedUserHandles getAnnotatedUserHandles() {
+ return mLazyAnnotatedUserHandles.get();
+ }
+
+ protected final UserHandle getPersonalProfileUserHandle() {
+ return getAnnotatedUserHandles().personalProfileUserHandle;
+ }
+
+ private boolean hasWorkProfile() {
+ return getWorkProfileUserHandle() != null;
+ }
+
+ protected final boolean shouldShowTabs() {
+ return hasWorkProfile();
+ }
+
+ protected final void onProfileClick(View v) {
+ final DisplayResolveInfo dri =
+ mMultiProfilePagerAdapter.getActiveListAdapter().getOtherProfile();
+ if (dri == null) {
+ return;
+ }
+
+ // Do not show the profile switch message anymore.
+ mProfileSwitchMessage = null;
+
+ onTargetSelected(dri, false);
+ finish();
+ }
+
+ private void updateIntentPickerPaddings() {
+ View titleCont = findViewById(com.android.internal.R.id.title_container);
+ titleCont.setPadding(
+ titleCont.getPaddingLeft(),
+ titleCont.getPaddingTop(),
+ titleCont.getPaddingRight(),
+ getResources().getDimensionPixelSize(R.dimen.resolver_title_padding_bottom));
+ View buttonBar = findViewById(com.android.internal.R.id.button_bar);
+ buttonBar.setPadding(
+ buttonBar.getPaddingLeft(),
+ getResources().getDimensionPixelSize(R.dimen.resolver_button_bar_spacing),
+ buttonBar.getPaddingRight(),
+ getResources().getDimensionPixelSize(R.dimen.resolver_button_bar_spacing));
+ }
+
+ private void maybeLogCrossProfileTargetLaunch(TargetInfo cti, UserHandle currentUserHandle) {
+ if (!hasWorkProfile() || currentUserHandle.equals(getUser())) {
+ return;
+ }
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.RESOLVER_CROSS_PROFILE_TARGET_OPENED)
+ .setBoolean(currentUserHandle.equals(getPersonalProfileUserHandle()))
+ .setStrings(getMetricsCategory(),
+ cti.isInDirectShareMetricsCategory() ? "direct_share" : "other_target")
+ .write();
+ }
+
+ @Override // ResolverListCommunicator
+ public final void sendVoiceChoicesIfNeeded() {
+ if (!isVoiceInteraction()) {
+ // Clearly not needed.
+ return;
+ }
+
+ int count = mMultiProfilePagerAdapter.getActiveListAdapter().getCount();
+ final Option[] options = new Option[count];
+ for (int i = 0; i < options.length; i++) {
+ TargetInfo target = mMultiProfilePagerAdapter.getActiveListAdapter().getItem(i);
+ if (target == null) {
+ // If this occurs, a new set of targets is being loaded. Let that complete,
+ // and have the next call to send voice choices proceed instead.
+ return;
+ }
+ options[i] = optionForChooserTarget(target, i);
+ }
+
+ mPickOptionRequest = new PickTargetOptionRequest(
+ new Prompt(getTitle()), options, null);
+ getVoiceInteractor().submitRequest(mPickOptionRequest);
+ }
+
+ final Option optionForChooserTarget(TargetInfo target, int index) {
+ return new Option(target.getDisplayLabel(), index);
+ }
+
+ protected final void setAdditionalTargets(Intent[] intents) {
+ if (intents != null) {
+ for (Intent intent : intents) {
+ mIntents.add(intent);
+ }
+ }
+ }
+
+ public final Intent getTargetIntent() {
+ return mIntents.isEmpty() ? null : mIntents.get(0);
+ }
+
+ protected final String getReferrerPackageName() {
+ final Uri referrer = getReferrer();
+ if (referrer != null && "android-app".equals(referrer.getScheme())) {
+ return referrer.getHost();
+ }
+ return null;
+ }
+
+ @Override // ResolverListCommunicator
+ public final void updateProfileViewButton() {
+ if (mProfileView == null) {
+ return;
+ }
+
+ final DisplayResolveInfo dri =
+ mMultiProfilePagerAdapter.getActiveListAdapter().getOtherProfile();
+ if (dri != null && !shouldShowTabs()) {
+ mProfileView.setVisibility(View.VISIBLE);
+ View text = mProfileView.findViewById(com.android.internal.R.id.profile_button);
+ if (!(text instanceof TextView)) {
+ text = mProfileView.findViewById(com.android.internal.R.id.text1);
+ }
+ ((TextView) text).setText(dri.getDisplayLabel());
+ } else {
+ mProfileView.setVisibility(View.GONE);
+ }
+ }
+
+ private void setProfileSwitchMessage(int contentUserHint) {
+ if ((contentUserHint != UserHandle.USER_CURRENT)
+ && (contentUserHint != UserHandle.myUserId())) {
+ UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
+ UserInfo originUserInfo = userManager.getUserInfo(contentUserHint);
+ boolean originIsManaged = originUserInfo != null ? originUserInfo.isManagedProfile()
+ : false;
+ boolean targetIsManaged = userManager.isManagedProfile();
+ if (originIsManaged && !targetIsManaged) {
+ mProfileSwitchMessage = getForwardToPersonalMsg();
+ } else if (!originIsManaged && targetIsManaged) {
+ mProfileSwitchMessage = getForwardToWorkMsg();
+ }
+ }
+ }
+
+ private String getForwardToPersonalMsg() {
+ return getSystemService(DevicePolicyManager.class).getResources().getString(
+ FORWARD_INTENT_TO_PERSONAL,
+ () -> getString(com.android.internal.R.string.forward_intent_to_owner));
+ }
+
+ private String getForwardToWorkMsg() {
+ return getSystemService(DevicePolicyManager.class).getResources().getString(
+ FORWARD_INTENT_TO_WORK,
+ () -> getString(com.android.internal.R.string.forward_intent_to_work));
+ }
+
+ /**
+ * Turn on launch mode that is safe to use when forwarding intents received from
+ * applications and running in system processes. This mode uses Activity.startActivityAsCaller
+ * instead of the normal Activity.startActivity for launching the activity selected
+ * by the user.
+ *
+ * This mode is set to true by default if the activity is initialized through
+ * {@link #onCreate(android.os.Bundle)}. If a subclass calls one of the other onCreate
+ * methods, it is set to false by default. You must set it before calling one of the
+ * more detailed onCreate methods, so that it will be set correctly in the case where
+ * there is only one intent to resolve and it is thus started immediately.
+ */
+ public final void setSafeForwardingMode(boolean safeForwarding) {
+ mSafeForwardingMode = safeForwarding;
+ }
+
+ protected final CharSequence getTitleForAction(Intent intent, int defaultTitleRes) {
+ final ActionTitle title = mResolvingHome
+ ? ActionTitle.HOME
+ : ActionTitle.forAction(intent.getAction());
+
+ // While there may already be a filtered item, we can only use it in the title if the list
+ // is already sorted and all information relevant to it is already in the list.
+ final boolean named =
+ mMultiProfilePagerAdapter.getActiveListAdapter().getFilteredPosition() >= 0;
+ if (title == ActionTitle.DEFAULT && defaultTitleRes != 0) {
+ return getString(defaultTitleRes);
+ } else {
+ return named
+ ? getString(title.namedTitleRes, mMultiProfilePagerAdapter
+ .getActiveListAdapter().getFilteredItem().getDisplayLabel())
+ : getString(title.titleRes);
+ }
+ }
+
+ final void dismiss() {
+ if (!isFinishing()) {
+ finish();
+ }
+ }
+
+ @Override
+ protected final void onRestart() {
+ super.onRestart();
+ if (!mRegistered) {
+ mPersonalPackageMonitor.register(this, getMainLooper(),
+ getPersonalProfileUserHandle(), false);
+ if (shouldShowTabs()) {
+ if (mWorkPackageMonitor == null) {
+ mWorkPackageMonitor = createPackageMonitor(
+ mMultiProfilePagerAdapter.getWorkListAdapter());
+ }
+ mWorkPackageMonitor.register(this, getMainLooper(),
+ getWorkProfileUserHandle(), false);
+ }
+ mRegistered = true;
+ }
+ if (shouldShowTabs() && mQuietModeManager.isWaitingToEnableWorkProfile()) {
+ if (mQuietModeManager.isQuietModeEnabled(getWorkProfileUserHandle())) {
+ mQuietModeManager.markWorkProfileEnabledBroadcastReceived();
+ }
+ }
+ mMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged();
+ updateProfileViewButton();
+ }
+
+ @Override
+ protected final void onStart() {
+ super.onStart();
+
+ this.getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ if (shouldShowTabs()) {
+ mWorkProfileStateReceiver = createWorkProfileStateReceiver();
+ registerWorkProfileStateReceiver();
+
+ mWorkProfileHasBeenEnabled = isWorkProfileEnabled();
+ }
+ }
+
+ private boolean isWorkProfileEnabled() {
+ UserHandle workUserHandle = getWorkProfileUserHandle();
+ UserManager userManager = getSystemService(UserManager.class);
+
+ return !userManager.isQuietModeEnabled(workUserHandle)
+ && userManager.isUserUnlocked(workUserHandle);
+ }
+
+ private void registerWorkProfileStateReceiver() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_USER_UNLOCKED);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
+ registerReceiverAsUser(mWorkProfileStateReceiver, UserHandle.ALL, filter, null, null);
+ }
+
+ @Override
+ protected final void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ ViewPager viewPager = findViewById(com.android.internal.R.id.profile_pager);
+ if (viewPager != null) {
+ outState.putInt(LAST_SHOWN_TAB_KEY, viewPager.getCurrentItem());
+ }
+ }
+
+ @Override
+ protected final void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ resetButtonBar();
+ ViewPager viewPager = findViewById(com.android.internal.R.id.profile_pager);
+ if (viewPager != null) {
+ viewPager.setCurrentItem(savedInstanceState.getInt(LAST_SHOWN_TAB_KEY));
+ }
+ mMultiProfilePagerAdapter.clearInactiveProfileCache();
+ }
+
+ private boolean hasManagedProfile() {
+ UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
+ if (userManager == null) {
+ return false;
+ }
+
+ try {
+ List profiles = userManager.getProfiles(getUserId());
+ for (UserInfo userInfo : profiles) {
+ if (userInfo != null && userInfo.isManagedProfile()) {
+ return true;
+ }
+ }
+ } catch (SecurityException e) {
+ return false;
+ }
+ return false;
+ }
+
+ private boolean supportsManagedProfiles(ResolveInfo resolveInfo) {
+ try {
+ ApplicationInfo appInfo = getPackageManager().getApplicationInfo(
+ resolveInfo.activityInfo.packageName, 0 /* default flags */);
+ return appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP;
+ } catch (NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ private void setAlwaysButtonEnabled(boolean hasValidSelection, int checkedPos,
+ boolean filtered) {
+ if (!mMultiProfilePagerAdapter.getCurrentUserHandle().equals(getUser())) {
+ // Never allow the inactive profile to always open an app.
+ mAlwaysButton.setEnabled(false);
+ return;
+ }
+ boolean enabled = false;
+ ResolveInfo ri = null;
+ if (hasValidSelection) {
+ ri = mMultiProfilePagerAdapter.getActiveListAdapter()
+ .resolveInfoForPosition(checkedPos, filtered);
+ if (ri == null) {
+ Log.e(TAG, "Invalid position supplied to setAlwaysButtonEnabled");
+ return;
+ } else if (ri.targetUserId != UserHandle.USER_CURRENT) {
+ Log.e(TAG, "Attempted to set selection to resolve info for another user");
+ return;
+ } else {
+ enabled = true;
+ }
+
+ mAlwaysButton.setText(getResources()
+ .getString(R.string.activity_resolver_use_always));
+ }
+
+ if (ri != null) {
+ ActivityInfo activityInfo = ri.activityInfo;
- // Rely on the ActivityManager to pop up a dialog regarding app suspension
- // and return false
- if (target.isSuspended()) {
- return false;
+ boolean hasRecordPermission =
+ mPm.checkPermission(android.Manifest.permission.RECORD_AUDIO,
+ activityInfo.packageName)
+ == android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+ if (!hasRecordPermission) {
+ // OK, we know the record permission, is this a capture device
+ boolean hasAudioCapture =
+ getIntent().getBooleanExtra(
+ ResolverActivity.EXTRA_IS_AUDIO_CAPTURE_DEVICE, false);
+ enabled = !hasAudioCapture;
}
}
+ mAlwaysButton.setEnabled(enabled);
+ }
- return true;
+ private String getWorkProfileNotSupportedMsg(String launcherName) {
+ return getSystemService(DevicePolicyManager.class).getResources().getString(
+ RESOLVER_WORK_PROFILE_NOT_SUPPORTED,
+ () -> getString(
+ com.android.internal.R.string.activity_resolver_work_profiles_support,
+ launcherName),
+ launcherName);
}
- @VisibleForTesting
- public void safelyStartActivity(TargetInfo cti) {
- // We're dispatching intents that might be coming from legacy apps, so
- // don't kill ourselves.
- StrictMode.disableDeathOnFileUriExposure();
- try {
- UserHandle currentUserHandle = mMultiProfilePagerAdapter.getCurrentUserHandle();
- safelyStartActivityInternal(cti, currentUserHandle, null);
- } finally {
- StrictMode.enableDeathOnFileUriExposure();
+ @Override // ResolverListCommunicator
+ public final void onPostListReady(ResolverListAdapter listAdapter, boolean doPostProcessing,
+ boolean rebuildCompleted) {
+ if (isAutolaunching()) {
+ return;
+ }
+ if (mIsIntentPicker) {
+ ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter)
+ .setUseLayoutWithDefault(useLayoutWithDefault());
+ }
+ if (mMultiProfilePagerAdapter.shouldShowEmptyStateScreen(listAdapter)) {
+ mMultiProfilePagerAdapter.showEmptyResolverListEmptyState(listAdapter);
+ } else {
+ mMultiProfilePagerAdapter.showListView(listAdapter);
+ }
+ // showEmptyResolverListEmptyState can mark the tab as loaded,
+ // which is a precondition for auto launching
+ if (rebuildCompleted && maybeAutolaunchActivity()) {
+ return;
+ }
+ if (doPostProcessing) {
+ maybeCreateHeader(listAdapter);
+ resetButtonBar();
+ onListRebuilt(listAdapter, rebuildCompleted);
}
}
@@ -1439,11 +1615,11 @@ public class ResolverActivity extends FragmentActivity implements
* @param user User to launch this activity as.
*/
@VisibleForTesting
- public void safelyStartActivityAsUser(TargetInfo cti, UserHandle user) {
+ public final void safelyStartActivityAsUser(TargetInfo cti, UserHandle user) {
safelyStartActivityAsUser(cti, user, null);
}
- protected void safelyStartActivityAsUser(
+ protected final void safelyStartActivityAsUser(
TargetInfo cti, UserHandle user, @Nullable Bundle options) {
// We're dispatching intents that might be coming from legacy apps, so
// don't kill ourselves.
@@ -1493,70 +1669,13 @@ public class ResolverActivity extends FragmentActivity implements
}
}
- private void maybeLogCrossProfileTargetLaunch(TargetInfo cti, UserHandle currentUserHandle) {
- if (!hasWorkProfile() || currentUserHandle.equals(getUser())) {
- return;
- }
- DevicePolicyEventLogger
- .createEvent(DevicePolicyEnums.RESOLVER_CROSS_PROFILE_TARGET_OPENED)
- .setBoolean(currentUserHandle.equals(getPersonalProfileUserHandle()))
- .setStrings(getMetricsCategory(),
- cti.isInDirectShareMetricsCategory() ? "direct_share" : "other_target")
- .write();
- }
-
-
- public void onActivityStarted(TargetInfo cti) {
- // Do nothing
- }
-
- @Override // ResolverListCommunicator
- public boolean shouldGetActivityMetadata() {
- return false;
- }
-
- public boolean shouldAutoLaunchSingleChoice(TargetInfo target) {
- return !target.isSuspended();
- }
-
- void showTargetDetails(ResolveInfo ri) {
+ final void showTargetDetails(ResolveInfo ri) {
Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
.setData(Uri.fromParts("package", ri.activityInfo.packageName, null))
.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
startActivityAsUser(in, mMultiProfilePagerAdapter.getCurrentUserHandle());
}
- @VisibleForTesting
- protected ResolverListAdapter createResolverListAdapter(Context context,
- List payloadIntents, Intent[] initialIntents, List rList,
- boolean filterLastUsed, UserHandle userHandle) {
- Intent startIntent = getIntent();
- boolean isAudioCaptureDevice =
- startIntent.getBooleanExtra(EXTRA_IS_AUDIO_CAPTURE_DEVICE, false);
- return new ResolverListAdapter(
- context,
- payloadIntents,
- initialIntents,
- rList,
- filterLastUsed,
- createListController(userHandle),
- userHandle,
- getTargetIntent(),
- this,
- isAudioCaptureDevice);
- }
-
- @VisibleForTesting
- protected ResolverListController createListController(UserHandle userHandle) {
- return new ResolverListController(
- this,
- mPm,
- getTargetIntent(),
- getReferrerPackageName(),
- getAnnotatedUserHandles().userIdOfCallingApp,
- userHandle);
- }
-
/**
* Sets up the content view.
* @return true if the activity is finishing and creation should halt.
@@ -1691,16 +1810,6 @@ public class ResolverActivity extends FragmentActivity implements
return true;
}
- /**
- * Finishing procedures to be performed after the list has been rebuilt.
- *
Subclasses must call postRebuildListInternal at the end of postRebuildList.
- * @param rebuildCompleted
- * @return true if the activity is finishing and creation should halt.
- */
- protected boolean postRebuildList(boolean rebuildCompleted) {
- return postRebuildListInternal(rebuildCompleted);
- }
-
/**
* Finishing procedures to be performed after the list has been rebuilt.
* @param rebuildCompleted
@@ -1958,8 +2067,6 @@ public class ResolverActivity extends FragmentActivity implements
RESOLVER_WORK_TAB, () -> getString(R.string.resolver_work_tab));
}
- void onHorizontalSwipeStateChanged(int state) {}
-
private void maybeHideDivider() {
if (!mIsIntentPicker) {
return;
@@ -1971,12 +2078,6 @@ public class ResolverActivity extends FragmentActivity implements
divider.setVisibility(View.GONE);
}
- /**
- * Callback called when user changes the profile tab.
- * This method is intended to be overridden by subclasses.
- */
- protected void onProfileTabSelected() { }
-
private void resetCheckedItem() {
if (!mIsIntentPicker) {
return;
@@ -2023,20 +2124,17 @@ public class ResolverActivity extends FragmentActivity implements
}
/**
- * Add a label to signify that the user can pick a different app.
- * @param adapter The adapter used to provide data to item views.
+ * Updates the button bar container {@code ignoreOffset} layout param.
+ *
Setting this to {@code true} means that the button bar will be glued to the bottom of
+ * the screen.
*/
- public void addUseDifferentAppLabelIfNecessary(ResolverListAdapter adapter) {
- final boolean useHeader = adapter.hasFilteredItem();
- if (useHeader) {
- FrameLayout stub = findViewById(com.android.internal.R.id.stub);
- stub.setVisibility(View.VISIBLE);
- TextView textView = (TextView) LayoutInflater.from(this).inflate(
- R.layout.resolver_different_item_header, null, false);
- if (shouldShowTabs()) {
- textView.setGravity(Gravity.CENTER);
- }
- stub.addView(textView);
+ private void setButtonBarIgnoreOffset(boolean ignoreOffset) {
+ View buttonBarContainer = findViewById(com.android.internal.R.id.button_bar_container);
+ if (buttonBarContainer != null) {
+ ResolverDrawerLayout.LayoutParams layoutParams =
+ (ResolverDrawerLayout.LayoutParams) buttonBarContainer.getLayoutParams();
+ layoutParams.ignoreOffset = ignoreOffset;
+ buttonBarContainer.setLayoutParams(layoutParams);
}
}
@@ -2084,61 +2182,6 @@ public class ResolverActivity extends FragmentActivity implements
mHeaderCreatorUser = listAdapter.getUserHandle();
}
- protected void resetButtonBar() {
- if (!mSupportsAlwaysUseOption) {
- return;
- }
- final ViewGroup buttonLayout = findViewById(com.android.internal.R.id.button_bar);
- if (buttonLayout == null) {
- Log.e(TAG, "Layout unexpectedly does not have a button bar");
- return;
- }
- ResolverListAdapter activeListAdapter =
- mMultiProfilePagerAdapter.getActiveListAdapter();
- View buttonBarDivider = findViewById(com.android.internal.R.id.resolver_button_bar_divider);
- if (!useLayoutWithDefault()) {
- int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0;
- buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(),
- buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize(
- R.dimen.resolver_button_bar_spacing) + inset);
- }
- if (activeListAdapter.isTabLoaded()
- && mMultiProfilePagerAdapter.shouldShowEmptyStateScreen(activeListAdapter)
- && !useLayoutWithDefault()) {
- buttonLayout.setVisibility(View.INVISIBLE);
- if (buttonBarDivider != null) {
- buttonBarDivider.setVisibility(View.INVISIBLE);
- }
- setButtonBarIgnoreOffset(/* ignoreOffset */ false);
- return;
- }
- if (buttonBarDivider != null) {
- buttonBarDivider.setVisibility(View.VISIBLE);
- }
- buttonLayout.setVisibility(View.VISIBLE);
- setButtonBarIgnoreOffset(/* ignoreOffset */ true);
-
- mOnceButton = (Button) buttonLayout.findViewById(com.android.internal.R.id.button_once);
- mAlwaysButton = (Button) buttonLayout.findViewById(com.android.internal.R.id.button_always);
-
- resetAlwaysOrOnceButtonBar();
- }
-
- /**
- * Updates the button bar container {@code ignoreOffset} layout param.
- *
Setting this to {@code true} means that the button bar will be glued to the bottom of
- * the screen.
- */
- private void setButtonBarIgnoreOffset(boolean ignoreOffset) {
- View buttonBarContainer = findViewById(com.android.internal.R.id.button_bar_container);
- if (buttonBarContainer != null) {
- ResolverDrawerLayout.LayoutParams layoutParams =
- (ResolverDrawerLayout.LayoutParams) buttonBarContainer.getLayoutParams();
- layoutParams.ignoreOffset = ignoreOffset;
- buttonBarContainer.setLayoutParams(layoutParams);
- }
- }
-
private void resetAlwaysOrOnceButtonBar() {
// Disable both buttons initially
setAlwaysButtonEnabled(false, ListView.INVALID_POSITION, false);
@@ -2164,7 +2207,7 @@ public class ResolverActivity extends FragmentActivity implements
}
@Override // ResolverListCommunicator
- public boolean useLayoutWithDefault() {
+ public final boolean useLayoutWithDefault() {
// We only use the default app layout when the profile of the active user has a
// filtered item. We always show the same default app even in the inactive user profile.
boolean currentUserAdapterHasFilteredItem;
@@ -2183,7 +2226,7 @@ public class ResolverActivity extends FragmentActivity implements
* If {@code retainInOnStop} is set to true, we will not finish ourselves when onStop gets
* called and we are launched in a new task.
*/
- protected void setRetainInOnStop(boolean retainInOnStop) {
+ protected final void setRetainInOnStop(boolean retainInOnStop) {
mRetainInOnStop = retainInOnStop;
}
@@ -2191,43 +2234,13 @@ public class ResolverActivity extends FragmentActivity implements
* Check a simple match for the component of two ResolveInfos.
*/
@Override // ResolverListCommunicator
- public boolean resolveInfoMatch(ResolveInfo lhs, ResolveInfo rhs) {
+ public final boolean resolveInfoMatch(ResolveInfo lhs, ResolveInfo rhs) {
return lhs == null ? rhs == null
: lhs.activityInfo == null ? rhs.activityInfo == null
: Objects.equals(lhs.activityInfo.name, rhs.activityInfo.name)
&& Objects.equals(lhs.activityInfo.packageName, rhs.activityInfo.packageName);
}
- protected String getMetricsCategory() {
- return METRICS_CATEGORY_RESOLVER;
- }
-
- @Override // ResolverListCommunicator
- public void onHandlePackagesChanged(ResolverListAdapter listAdapter) {
- if (listAdapter == mMultiProfilePagerAdapter.getActiveListAdapter()) {
- if (listAdapter.getUserHandle().equals(getWorkProfileUserHandle())
- && mQuietModeManager.isWaitingToEnableWorkProfile()) {
- // We have just turned on the work profile and entered the pass code to start it,
- // now we are waiting to receive the ACTION_USER_UNLOCKED broadcast. There is no
- // point in reloading the list now, since the work profile user is still
- // turning on.
- return;
- }
- boolean listRebuilt = mMultiProfilePagerAdapter.rebuildActiveTab(true);
- if (listRebuilt) {
- ResolverListAdapter activeListAdapter =
- mMultiProfilePagerAdapter.getActiveListAdapter();
- activeListAdapter.notifyDataSetChanged();
- if (activeListAdapter.getCount() == 0 && !inactiveListAdapterHasItems()) {
- // We no longer have any items... just finish the activity.
- finish();
- }
- }
- } else {
- mMultiProfilePagerAdapter.clearInactiveProfileCache();
- }
- }
-
private boolean inactiveListAdapterHasItems() {
if (!shouldShowTabs()) {
return false;
@@ -2329,7 +2342,7 @@ public class ResolverActivity extends FragmentActivity implements
}
}
- class ItemClickListener implements AdapterView.OnItemClickListener,
+ final class ItemClickListener implements AdapterView.OnItemClickListener,
AdapterView.OnItemLongClickListener {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
@@ -2390,7 +2403,7 @@ public class ResolverActivity extends FragmentActivity implements
&& match <= IntentFilter.MATCH_CATEGORY_PATH;
}
- static class PickTargetOptionRequest extends PickOptionRequest {
+ static final class PickTargetOptionRequest extends PickOptionRequest {
public PickTargetOptionRequest(@Nullable Prompt prompt, Option[] options,
@Nullable Bundle extras) {
super(prompt, options, extras);
@@ -2426,6 +2439,4 @@ public class ResolverActivity extends FragmentActivity implements
}
}
}
-
- protected void maybeLogProfileChange() {}
-}
+}
\ No newline at end of file
--
cgit v1.2.3-59-g8ed1b