summaryrefslogtreecommitdiff
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/intentresolver/ResolverActivity.java1299
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