diff options
| author | 2023-02-14 05:25:13 +0000 | |
|---|---|---|
| committer | 2023-02-14 05:25:13 +0000 | |
| commit | a1355cd0ff84d0e734e001a261e7751f2c4d5c95 (patch) | |
| tree | 6f624f7456c8a9b0d195ceaa5433926b4e5507b3 /java/src | |
| parent | 4df530f5e4b42341a0dfc945244b73356a4031ff (diff) | |
| parent | b3240df7c525de985765e0cbbb094a0f7c83e440 (diff) | |
Merge "Clarify ResolverActivity inheritance contract." into tm-qpr-dev
Diffstat (limited to 'java/src')
| -rw-r--r-- | java/src/com/android/intentresolver/ResolverActivity.java | 1299 |
1 files changed, 655 insertions, 644 deletions
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,16 +455,538 @@ public class ResolverActivity extends FragmentActivity implements return resolverMultiProfilePagerAdapter; } + protected EmptyStateProvider createBlockerEmptyStateProvider() { + final boolean shouldShowNoCrossProfileIntentsEmptyState = getUser().equals(getIntentUser()); + + if (!shouldShowNoCrossProfileIntentsEmptyState) { + // Implementation that doesn't show any blockers + return new EmptyStateProvider() {}; + } + + final AbstractMultiProfilePagerAdapter.EmptyState + noWorkToPersonalEmptyState = + new DevicePolicyBlockerEmptyState(/* context= */ this, + /* devicePolicyStringTitleId= */ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE, + /* defaultTitleResource= */ R.string.resolver_cross_profile_blocked, + /* devicePolicyStringSubtitleId= */ RESOLVER_CANT_ACCESS_PERSONAL, + /* defaultSubtitleResource= */ + R.string.resolver_cant_access_personal_apps_explanation, + /* devicePolicyEventId= */ RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL, + /* devicePolicyEventCategory= */ + ResolverActivity.METRICS_CATEGORY_RESOLVER); + + final AbstractMultiProfilePagerAdapter.EmptyState noPersonalToWorkEmptyState = + new DevicePolicyBlockerEmptyState(/* context= */ this, + /* devicePolicyStringTitleId= */ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE, + /* defaultTitleResource= */ R.string.resolver_cross_profile_blocked, + /* devicePolicyStringSubtitleId= */ RESOLVER_CANT_ACCESS_WORK, + /* defaultSubtitleResource= */ + R.string.resolver_cant_access_work_apps_explanation, + /* devicePolicyEventId= */ RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK, + /* devicePolicyEventCategory= */ + ResolverActivity.METRICS_CATEGORY_RESOLVER); + + return new NoCrossProfileEmptyStateProvider(getPersonalProfileUserHandle(), + noWorkToPersonalEmptyState, noPersonalToWorkEmptyState, + createCrossProfileIntentsChecker(), createMyUserIdProvider()); + } + + protected int appliedThemeResId() { + return R.style.Theme_DeviceDefault_Resolver; + } + + /** + * Numerous layouts are supported, each with optional ViewGroups. + * Make sure the inset gets added to the correct View, using + * a footer for Lists so it can properly scroll under the navbar. + */ + protected boolean shouldAddFooterView() { + if (useLayoutWithDefault()) return true; + + View buttonBar = findViewById(com.android.internal.R.id.button_bar); + if (buttonBar == null || buttonBar.getVisibility() == View.GONE) return true; + + return false; + } + + protected void applyFooterView(int height) { + if (mFooterSpacer == null) { + mFooterSpacer = new Space(getApplicationContext()); + } else { + ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter) + .getActiveAdapterView().removeFooterView(mFooterSpacer); + } + mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT, + mSystemWindowInsets.bottom)); + ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter) + .getActiveAdapterView().addFooterView(mFooterSpacer); + } + + protected WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { + mSystemWindowInsets = insets.getSystemWindowInsets(); + + mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top, + mSystemWindowInsets.right, 0); + + resetButtonBar(); + + if (shouldUseMiniResolver()) { + View buttonContainer = findViewById(com.android.internal.R.id.button_bar_container); + buttonContainer.setPadding(0, 0, 0, mSystemWindowInsets.bottom + + getResources().getDimensionPixelOffset(R.dimen.resolver_button_bar_spacing)); + } + + // Need extra padding so the list can fully scroll up + if (shouldAddFooterView()) { + applyFooterView(mSystemWindowInsets.bottom); + } + + return insets.consumeSystemWindowInsets(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + mMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged(); + if (mIsIntentPicker && shouldShowTabs() && !useLayoutWithDefault() + && !shouldUseMiniResolver()) { + updateIntentPickerPaddings(); + } + + if (mSystemWindowInsets != null) { + mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top, + mSystemWindowInsets.right, 0); + } + } + + public int getLayoutResource() { + return R.layout.resolver_list; + } + + @Override + protected void onStop() { + super.onStop(); + + final Window window = this.getWindow(); + final WindowManager.LayoutParams attrs = window.getAttributes(); + attrs.privateFlags &= ~SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; + window.setAttributes(attrs); + + if (mRegistered) { + mPersonalPackageMonitor.unregister(); + if (mWorkPackageMonitor != null) { + mWorkPackageMonitor.unregister(); + } + mRegistered = false; + } + final Intent intent = getIntent(); + if ((intent.getFlags() & FLAG_ACTIVITY_NEW_TASK) != 0 && !isVoiceInteraction() + && !mResolvingHome && !mRetainInOnStop) { + // 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 + // that each time we are launched we get the correct launching + // uid (not re-using the same resolver from an old launching uid), + // so we will now finish ourself since being no longer visible, + // the user probably can't get back to us. + if (!isChangingConfigurations()) { + finish(); + } + } + if (mWorkPackageMonitor != null) { + unregisterReceiver(mWorkProfileStateReceiver); + mWorkPackageMonitor = null; + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (!isChangingConfigurations() && mPickOptionRequest != null) { + mPickOptionRequest.cancel(); + } + if (mMultiProfilePagerAdapter != null + && mMultiProfilePagerAdapter.getActiveListAdapter() != null) { + mMultiProfilePagerAdapter.getActiveListAdapter().onDestroy(); + } + } + + 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(); + } + } + + /** + * Replace me in subclasses! + */ + @Override // ResolverListCommunicator + public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) { + return defIntent; + } + + protected void onListRebuilt(ResolverListAdapter listAdapter, boolean rebuildCompleted) { + final ItemClickListener listener = new ItemClickListener(); + setupAdapterListView((ListView) mMultiProfilePagerAdapter.getActiveAdapterView(), listener); + if (shouldShowTabs() && mIsIntentPicker) { + final ResolverDrawerLayout rdl = findViewById(com.android.internal.R.id.contentPanel); + if (rdl != null) { + rdl.setMaxCollapsedHeight(getResources() + .getDimensionPixelSize(useLayoutWithDefault() + ? R.dimen.resolver_max_collapsed_height_with_default_with_tabs + : R.dimen.resolver_max_collapsed_height_with_tabs)); + } + } + } + + protected boolean onTargetSelected(TargetInfo target, boolean always) { + final ResolveInfo ri = target.getResolveInfo(); + final Intent intent = target != null ? target.getResolvedIntent() : null; + + if (intent != null && (mSupportsAlwaysUseOption + || mMultiProfilePagerAdapter.getActiveListAdapter().hasFilteredItem()) + && mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredResolveList() != null) { + // Build a reasonable intent filter, based on what matched. + IntentFilter filter = new IntentFilter(); + Intent filterIntent; + + if (intent.getSelector() != null) { + filterIntent = intent.getSelector(); + } else { + filterIntent = intent; + } + + String action = filterIntent.getAction(); + if (action != null) { + filter.addAction(action); + } + Set<String> categories = filterIntent.getCategories(); + if (categories != null) { + for (String cat : categories) { + filter.addCategory(cat); + } + } + filter.addCategory(Intent.CATEGORY_DEFAULT); + + int cat = ri.match & IntentFilter.MATCH_CATEGORY_MASK; + Uri data = filterIntent.getData(); + if (cat == IntentFilter.MATCH_CATEGORY_TYPE) { + String mimeType = filterIntent.resolveType(this); + if (mimeType != null) { + try { + filter.addDataType(mimeType); + } catch (IntentFilter.MalformedMimeTypeException e) { + Log.w("ResolverActivity", e); + filter = null; + } + } + } + if (data != null && data.getScheme() != null) { + // We need the data specification if there was no type, + // OR if the scheme is not one of our magical "file:" + // or "content:" schemes (see IntentFilter for the reason). + if (cat != IntentFilter.MATCH_CATEGORY_TYPE + || (!"file".equals(data.getScheme()) + && !"content".equals(data.getScheme()))) { + filter.addDataScheme(data.getScheme()); + + // Look through the resolved filter to determine which part + // of it matched the original Intent. + Iterator<PatternMatcher> pIt = ri.filter.schemeSpecificPartsIterator(); + if (pIt != null) { + String ssp = data.getSchemeSpecificPart(); + while (ssp != null && pIt.hasNext()) { + PatternMatcher p = pIt.next(); + if (p.match(ssp)) { + filter.addDataSchemeSpecificPart(p.getPath(), p.getType()); + break; + } + } + } + Iterator<IntentFilter.AuthorityEntry> aIt = ri.filter.authoritiesIterator(); + if (aIt != null) { + while (aIt.hasNext()) { + IntentFilter.AuthorityEntry a = aIt.next(); + if (a.match(data) >= 0) { + int port = a.getPort(); + filter.addDataAuthority(a.getHost(), + port >= 0 ? Integer.toString(port) : null); + break; + } + } + } + pIt = ri.filter.pathsIterator(); + if (pIt != null) { + String path = data.getPath(); + while (path != null && pIt.hasNext()) { + PatternMatcher p = pIt.next(); + if (p.match(path)) { + filter.addDataPath(p.getPath(), p.getType()); + break; + } + } + } + } + } + + if (filter != null) { + final int N = mMultiProfilePagerAdapter.getActiveListAdapter() + .getUnfilteredResolveList().size(); + ComponentName[] set; + // If we don't add back in the component for forwarding the intent to a managed + // profile, the preferred activity may not be updated correctly (as the set of + // components we tell it we knew about will have changed). + final boolean needToAddBackProfileForwardingComponent = + mMultiProfilePagerAdapter.getActiveListAdapter().getOtherProfile() != null; + if (!needToAddBackProfileForwardingComponent) { + set = new ComponentName[N]; + } else { + set = new ComponentName[N + 1]; + } + + int bestMatch = 0; + for (int i=0; i<N; i++) { + ResolveInfo r = mMultiProfilePagerAdapter.getActiveListAdapter() + .getUnfilteredResolveList().get(i).getResolveInfoAt(0); + set[i] = new ComponentName(r.activityInfo.packageName, + r.activityInfo.name); + if (r.match > bestMatch) bestMatch = r.match; + } + + if (needToAddBackProfileForwardingComponent) { + set[N] = mMultiProfilePagerAdapter.getActiveListAdapter() + .getOtherProfile().getResolvedComponentName(); + final int otherProfileMatch = mMultiProfilePagerAdapter.getActiveListAdapter() + .getOtherProfile().getResolveInfo().match; + if (otherProfileMatch > bestMatch) bestMatch = otherProfileMatch; + } + + if (always) { + final int userId = getUserId(); + final PackageManager pm = getPackageManager(); + + // Set the preferred Activity + pm.addUniquePreferredActivity(filter, bestMatch, set, intent.getComponent()); + + if (ri.handleAllWebDataURI) { + // Set default Browser if needed + final String packageName = pm.getDefaultBrowserPackageNameAsUser(userId); + if (TextUtils.isEmpty(packageName)) { + pm.setDefaultBrowserPackageNameAsUser(ri.activityInfo.packageName, userId); + } + } + } else { + try { + mMultiProfilePagerAdapter.getActiveListAdapter() + .mResolverListController.setLastChosen(intent, filter, bestMatch); + } catch (RemoteException re) { + Log.d(TAG, "Error calling setLastChosenActivity\n" + re); + } + } + } + } + + 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. + * </p>Subclasses must call postRebuildListInternal at the end of postRebuildList. + * @param rebuildCompleted + * @return <code>true</code> 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. + * <p>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); @@ -534,41 +1019,65 @@ public class ResolverActivity extends FragmentActivity implements }; } - protected EmptyStateProvider createBlockerEmptyStateProvider() { - final boolean shouldShowNoCrossProfileIntentsEmptyState = getUser().equals(getIntentUser()); + // TODO: have tests override `getAnnotatedUserHandles()`, and make this method `final`. + // @NonFinalForTesting + @Nullable + protected UserHandle getWorkProfileUserHandle() { + return getAnnotatedUserHandles().workProfileUserHandle; + } - if (!shouldShowNoCrossProfileIntentsEmptyState) { - // Implementation that doesn't show any blockers - return new EmptyStateProvider() {}; + // @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(); } + } - final AbstractMultiProfilePagerAdapter.EmptyState - noWorkToPersonalEmptyState = - new DevicePolicyBlockerEmptyState(/* context= */ this, - /* devicePolicyStringTitleId= */ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE, - /* defaultTitleResource= */ R.string.resolver_cross_profile_blocked, - /* devicePolicyStringSubtitleId= */ RESOLVER_CANT_ACCESS_PERSONAL, - /* defaultSubtitleResource= */ - R.string.resolver_cant_access_personal_apps_explanation, - /* devicePolicyEventId= */ RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL, - /* devicePolicyEventCategory= */ ResolverActivity.METRICS_CATEGORY_RESOLVER); + // @NonFinalForTesting + @VisibleForTesting + protected ResolverListAdapter createResolverListAdapter(Context context, + List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> 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); + } - final AbstractMultiProfilePagerAdapter.EmptyState noPersonalToWorkEmptyState = - new DevicePolicyBlockerEmptyState(/* context= */ this, - /* devicePolicyStringTitleId= */ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE, - /* defaultTitleResource= */ R.string.resolver_cross_profile_blocked, - /* devicePolicyStringSubtitleId= */ RESOLVER_CANT_ACCESS_WORK, - /* defaultSubtitleResource= */ - R.string.resolver_cant_access_work_apps_explanation, - /* devicePolicyEventId= */ RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK, - /* devicePolicyEventCategory= */ ResolverActivity.METRICS_CATEGORY_RESOLVER); + private LatencyTracker getLatencyTracker() { + return LatencyTracker.getInstance(this); + } - return new NoCrossProfileEmptyStateProvider(getPersonalProfileUserHandle(), - noWorkToPersonalEmptyState, noPersonalToWorkEmptyState, - createCrossProfileIntentsChecker(), createMyUserIdProvider()); + /** + * 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 EmptyStateProvider createEmptyStateProvider( + protected final EmptyStateProvider createEmptyStateProvider( @Nullable UserHandle workProfileUserHandle) { final EmptyStateProvider blockerEmptyStateProvider = createBlockerEmptyStateProvider(); @@ -576,9 +1085,11 @@ public class ResolverActivity extends FragmentActivity implements new WorkProfilePausedEmptyStateProvider(this, workProfileUserHandle, mQuietModeManager, /* onSwitchOnWorkSelectedListener= */ - () -> { if (mOnSwitchOnWorkSelectedListener != null) { - mOnSwitchOnWorkSelectedListener.onSwitchOnWorkSelected(); - }}, + () -> { + if (mOnSwitchOnWorkSelectedListener != null) { + mOnSwitchOnWorkSelectedListener.onSwitchOnWorkSelected(); + } + }, getMetricsCategory()); final EmptyStateProvider noAppsEmptyStateProvider = new NoAppsAvailableEmptyStateProvider( @@ -597,9 +1108,32 @@ public class ResolverActivity extends FragmentActivity implements ); } - private ResolverMultiProfilePagerAdapter createResolverMultiProfilePagerAdapterForOneProfile( - Intent[] initialIntents, - List<ResolveInfo> rList, boolean filterLastUsed) { + 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<ResolveInfo> rList, + boolean filterLastUsed) { ResolverListAdapter adapter = createResolverListAdapter( /* context */ this, /* payloadIntents */ mIntents, @@ -674,17 +1208,13 @@ public class ResolverActivity extends FragmentActivity implements 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() { + final int getSelectedProfileExtra() { int selectedProfile = -1; if (getIntent().hasExtra(EXTRA_SELECTED_PROFILE)) { selectedProfile = getIntent().getIntExtra(EXTRA_SELECTED_PROFILE, /* defValue = */ -1); @@ -697,7 +1227,7 @@ public class ResolverActivity extends FragmentActivity implements return selectedProfile; } - protected @Profile int getCurrentProfile() { + protected final @Profile int getCurrentProfile() { return (UserHandle.myUserId() == UserHandle.USER_SYSTEM ? PROFILE_PERSONAL : PROFILE_WORK); } @@ -709,21 +1239,15 @@ public class ResolverActivity extends FragmentActivity implements 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() { + protected final boolean shouldShowTabs() { return hasWorkProfile(); } - protected void onProfileClick(View v) { + protected final void onProfileClick(View v) { final DisplayResolveInfo dri = mMultiProfilePagerAdapter.getActiveListAdapter().getOtherProfile(); if (dri == null) { @@ -737,70 +1261,6 @@ public class ResolverActivity extends FragmentActivity implements finish(); } - /** - * Numerous layouts are supported, each with optional ViewGroups. - * Make sure the inset gets added to the correct View, using - * a footer for Lists so it can properly scroll under the navbar. - */ - protected boolean shouldAddFooterView() { - if (useLayoutWithDefault()) return true; - - View buttonBar = findViewById(com.android.internal.R.id.button_bar); - if (buttonBar == null || buttonBar.getVisibility() == View.GONE) return true; - - return false; - } - - protected void applyFooterView(int height) { - if (mFooterSpacer == null) { - mFooterSpacer = new Space(getApplicationContext()); - } else { - ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter) - .getActiveAdapterView().removeFooterView(mFooterSpacer); - } - mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT, - mSystemWindowInsets.bottom)); - ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter) - .getActiveAdapterView().addFooterView(mFooterSpacer); - } - - protected WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { - mSystemWindowInsets = insets.getSystemWindowInsets(); - - mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top, - mSystemWindowInsets.right, 0); - - resetButtonBar(); - - if (shouldUseMiniResolver()) { - View buttonContainer = findViewById(com.android.internal.R.id.button_bar_container); - buttonContainer.setPadding(0, 0, 0, mSystemWindowInsets.bottom - + getResources().getDimensionPixelOffset(R.dimen.resolver_button_bar_spacing)); - } - - // Need extra padding so the list can fully scroll up - if (shouldAddFooterView()) { - applyFooterView(mSystemWindowInsets.bottom); - } - - return insets.consumeSystemWindowInsets(); - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - mMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged(); - if (mIsIntentPicker && shouldShowTabs() && !useLayoutWithDefault() - && !shouldUseMiniResolver()) { - updateIntentPickerPaddings(); - } - - if (mSystemWindowInsets != null) { - mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top, - mSystemWindowInsets.right, 0); - } - } - private void updateIntentPickerPaddings() { View titleCont = findViewById(com.android.internal.R.id.title_container); titleCont.setPadding( @@ -816,8 +1276,20 @@ public class ResolverActivity extends FragmentActivity implements 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 void sendVoiceChoicesIfNeeded() { + public final void sendVoiceChoicesIfNeeded() { if (!isVoiceInteraction()) { // Clearly not needed. return; @@ -825,7 +1297,7 @@ public class ResolverActivity extends FragmentActivity implements int count = mMultiProfilePagerAdapter.getActiveListAdapter().getCount(); final Option[] options = new Option[count]; - for (int i = 0, N = options.length; i < N; i++) { + 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, @@ -840,7 +1312,7 @@ public class ResolverActivity extends FragmentActivity implements getVoiceInteractor().submitRequest(mPickOptionRequest); } - Option optionForChooserTarget(TargetInfo target, int index) { + final Option optionForChooserTarget(TargetInfo target, int index) { return new Option(target.getDisplayLabel(), index); } @@ -852,11 +1324,11 @@ public class ResolverActivity extends FragmentActivity implements } } - public Intent getTargetIntent() { + public final Intent getTargetIntent() { return mIntents.isEmpty() ? null : mIntents.get(0); } - protected String getReferrerPackageName() { + protected final String getReferrerPackageName() { final Uri referrer = getReferrer(); if (referrer != null && "android-app".equals(referrer.getScheme())) { return referrer.getHost(); @@ -864,12 +1336,8 @@ public class ResolverActivity extends FragmentActivity implements return null; } - public int getLayoutResource() { - return R.layout.resolver_list; - } - @Override // ResolverListCommunicator - public void updateProfileViewButton() { + public final void updateProfileViewButton() { if (mProfileView == null) { return; } @@ -889,8 +1357,8 @@ public class ResolverActivity extends FragmentActivity implements } private void setProfileSwitchMessage(int contentUserHint) { - if (contentUserHint != UserHandle.USER_CURRENT && - contentUserHint != UserHandle.myUserId()) { + 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() @@ -928,11 +1396,11 @@ public class ResolverActivity extends FragmentActivity implements * 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.</p> */ - public void setSafeForwardingMode(boolean safeForwarding) { + public final void setSafeForwardingMode(boolean safeForwarding) { mSafeForwardingMode = safeForwarding; } - protected CharSequence getTitleForAction(Intent intent, int defaultTitleRes) { + protected final CharSequence getTitleForAction(Intent intent, int defaultTitleRes) { final ActionTitle title = mResolvingHome ? ActionTitle.HOME : ActionTitle.forAction(intent.getAction()); @@ -951,14 +1419,14 @@ public class ResolverActivity extends FragmentActivity implements } } - void dismiss() { + final void dismiss() { if (!isFinishing()) { finish(); } } @Override - protected void onRestart() { + protected final void onRestart() { super.onRestart(); if (!mRegistered) { mPersonalPackageMonitor.register(this, getMainLooper(), @@ -983,7 +1451,7 @@ public class ResolverActivity extends FragmentActivity implements } @Override - protected void onStart() { + protected final void onStart() { super.onStart(); this.getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); @@ -1012,55 +1480,7 @@ public class ResolverActivity extends FragmentActivity implements } @Override - protected void onStop() { - super.onStop(); - - final Window window = this.getWindow(); - final WindowManager.LayoutParams attrs = window.getAttributes(); - attrs.privateFlags &= ~SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; - window.setAttributes(attrs); - - if (mRegistered) { - mPersonalPackageMonitor.unregister(); - if (mWorkPackageMonitor != null) { - mWorkPackageMonitor.unregister(); - } - mRegistered = false; - } - final Intent intent = getIntent(); - if ((intent.getFlags() & FLAG_ACTIVITY_NEW_TASK) != 0 && !isVoiceInteraction() - && !mResolvingHome && !mRetainInOnStop) { - // 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 - // that each time we are launched we get the correct launching - // uid (not re-using the same resolver from an old launching uid), - // so we will now finish ourself since being no longer visible, - // the user probably can't get back to us. - if (!isChangingConfigurations()) { - finish(); - } - } - if (mWorkPackageMonitor != null) { - unregisterReceiver(mWorkProfileStateReceiver); - mWorkPackageMonitor = null; - } - } - - @Override - protected void onDestroy() { - super.onDestroy(); - if (!isChangingConfigurations() && mPickOptionRequest != null) { - mPickOptionRequest.cancel(); - } - if (mMultiProfilePagerAdapter != null - && mMultiProfilePagerAdapter.getActiveListAdapter() != null) { - mMultiProfilePagerAdapter.getActiveListAdapter().onDestroy(); - } - } - - @Override - protected void onSaveInstanceState(Bundle outState) { + protected final void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); ViewPager viewPager = findViewById(com.android.internal.R.id.profile_pager); if (viewPager != null) { @@ -1069,7 +1489,7 @@ public class ResolverActivity extends FragmentActivity implements } @Override - protected void onRestoreInstanceState(Bundle savedInstanceState) { + protected final void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); resetButtonBar(); ViewPager viewPager = findViewById(com.android.internal.R.id.profile_pager); @@ -1153,55 +1573,6 @@ public class ResolverActivity extends FragmentActivity implements 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, @@ -1211,14 +1582,6 @@ public class ResolverActivity extends FragmentActivity implements launcherName); } - /** - * Replace me in subclasses! - */ - @Override // ResolverListCommunicator - public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) { - return defIntent; - } - @Override // ResolverListCommunicator public final void onPostListReady(ResolverListAdapter listAdapter, boolean doPostProcessing, boolean rebuildCompleted) { @@ -1246,204 +1609,17 @@ public class ResolverActivity extends FragmentActivity implements } } - protected void onListRebuilt(ResolverListAdapter listAdapter, boolean rebuildCompleted) { - final ItemClickListener listener = new ItemClickListener(); - setupAdapterListView((ListView) mMultiProfilePagerAdapter.getActiveAdapterView(), listener); - if (shouldShowTabs() && mIsIntentPicker) { - final ResolverDrawerLayout rdl = findViewById(com.android.internal.R.id.contentPanel); - if (rdl != null) { - rdl.setMaxCollapsedHeight(getResources() - .getDimensionPixelSize(useLayoutWithDefault() - ? R.dimen.resolver_max_collapsed_height_with_default_with_tabs - : R.dimen.resolver_max_collapsed_height_with_tabs)); - } - } - } - - protected boolean onTargetSelected(TargetInfo target, boolean always) { - final ResolveInfo ri = target.getResolveInfo(); - final Intent intent = target != null ? target.getResolvedIntent() : null; - - if (intent != null && (mSupportsAlwaysUseOption - || mMultiProfilePagerAdapter.getActiveListAdapter().hasFilteredItem()) - && mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredResolveList() != null) { - // Build a reasonable intent filter, based on what matched. - IntentFilter filter = new IntentFilter(); - Intent filterIntent; - - if (intent.getSelector() != null) { - filterIntent = intent.getSelector(); - } else { - filterIntent = intent; - } - - String action = filterIntent.getAction(); - if (action != null) { - filter.addAction(action); - } - Set<String> categories = filterIntent.getCategories(); - if (categories != null) { - for (String cat : categories) { - filter.addCategory(cat); - } - } - filter.addCategory(Intent.CATEGORY_DEFAULT); - - int cat = ri.match & IntentFilter.MATCH_CATEGORY_MASK; - Uri data = filterIntent.getData(); - if (cat == IntentFilter.MATCH_CATEGORY_TYPE) { - String mimeType = filterIntent.resolveType(this); - if (mimeType != null) { - try { - filter.addDataType(mimeType); - } catch (IntentFilter.MalformedMimeTypeException e) { - Log.w("ResolverActivity", e); - filter = null; - } - } - } - if (data != null && data.getScheme() != null) { - // We need the data specification if there was no type, - // OR if the scheme is not one of our magical "file:" - // or "content:" schemes (see IntentFilter for the reason). - if (cat != IntentFilter.MATCH_CATEGORY_TYPE - || (!"file".equals(data.getScheme()) - && !"content".equals(data.getScheme()))) { - filter.addDataScheme(data.getScheme()); - - // Look through the resolved filter to determine which part - // of it matched the original Intent. - Iterator<PatternMatcher> pIt = ri.filter.schemeSpecificPartsIterator(); - if (pIt != null) { - String ssp = data.getSchemeSpecificPart(); - while (ssp != null && pIt.hasNext()) { - PatternMatcher p = pIt.next(); - if (p.match(ssp)) { - filter.addDataSchemeSpecificPart(p.getPath(), p.getType()); - break; - } - } - } - Iterator<IntentFilter.AuthorityEntry> aIt = ri.filter.authoritiesIterator(); - if (aIt != null) { - while (aIt.hasNext()) { - IntentFilter.AuthorityEntry a = aIt.next(); - if (a.match(data) >= 0) { - int port = a.getPort(); - filter.addDataAuthority(a.getHost(), - port >= 0 ? Integer.toString(port) : null); - break; - } - } - } - pIt = ri.filter.pathsIterator(); - if (pIt != null) { - String path = data.getPath(); - while (path != null && pIt.hasNext()) { - PatternMatcher p = pIt.next(); - if (p.match(path)) { - filter.addDataPath(p.getPath(), p.getType()); - break; - } - } - } - } - } - - if (filter != null) { - final int N = mMultiProfilePagerAdapter.getActiveListAdapter() - .getUnfilteredResolveList().size(); - ComponentName[] set; - // If we don't add back in the component for forwarding the intent to a managed - // profile, the preferred activity may not be updated correctly (as the set of - // components we tell it we knew about will have changed). - final boolean needToAddBackProfileForwardingComponent = - mMultiProfilePagerAdapter.getActiveListAdapter().getOtherProfile() != null; - if (!needToAddBackProfileForwardingComponent) { - set = new ComponentName[N]; - } else { - set = new ComponentName[N + 1]; - } - - int bestMatch = 0; - for (int i=0; i<N; i++) { - ResolveInfo r = mMultiProfilePagerAdapter.getActiveListAdapter() - .getUnfilteredResolveList().get(i).getResolveInfoAt(0); - set[i] = new ComponentName(r.activityInfo.packageName, - r.activityInfo.name); - if (r.match > bestMatch) bestMatch = r.match; - } - - if (needToAddBackProfileForwardingComponent) { - set[N] = mMultiProfilePagerAdapter.getActiveListAdapter() - .getOtherProfile().getResolvedComponentName(); - final int otherProfileMatch = mMultiProfilePagerAdapter.getActiveListAdapter() - .getOtherProfile().getResolveInfo().match; - if (otherProfileMatch > bestMatch) bestMatch = otherProfileMatch; - } - - if (always) { - final int userId = getUserId(); - final PackageManager pm = getPackageManager(); - - // Set the preferred Activity - pm.addUniquePreferredActivity(filter, bestMatch, set, intent.getComponent()); - - if (ri.handleAllWebDataURI) { - // Set default Browser if needed - final String packageName = pm.getDefaultBrowserPackageNameAsUser(userId); - if (TextUtils.isEmpty(packageName)) { - pm.setDefaultBrowserPackageNameAsUser(ri.activityInfo.packageName, userId); - } - } - } else { - try { - mMultiProfilePagerAdapter.getActiveListAdapter() - .mResolverListController.setLastChosen(intent, filter, bestMatch); - } catch (RemoteException re) { - Log.d(TAG, "Error calling setLastChosenActivity\n" + re); - } - } - } - } - - 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; - } - - @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(); - } - } - /** * Start activity as a fixed user handle. * @param cti TargetInfo to be launched. * @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<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> 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 <code>true</code> if the activity is finishing and creation should halt. @@ -1693,16 +1812,6 @@ public class ResolverActivity extends FragmentActivity implements /** * Finishing procedures to be performed after the list has been rebuilt. - * </p>Subclasses must call postRebuildListInternal at the end of postRebuildList. - * @param rebuildCompleted - * @return <code>true</code> 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 * @return <code>true</code> if the activity is finishing and creation should halt. */ @@ -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. - * <p>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. + * <p>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. - * <p>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 |