1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.intentresolver;
18 
19 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
20 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
21 
22 import static androidx.lifecycle.LifecycleKt.getCoroutineScope;
23 
24 import static com.android.intentresolver.ext.CreationExtrasExtKt.addDefaultArgs;
25 import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;
26 
27 import static java.util.Objects.requireNonNull;
28 
29 import android.app.ActivityThread;
30 import android.app.VoiceInteractor.PickOptionRequest;
31 import android.app.VoiceInteractor.PickOptionRequest.Option;
32 import android.app.VoiceInteractor.Prompt;
33 import android.app.admin.DevicePolicyEventLogger;
34 import android.content.ComponentName;
35 import android.content.Context;
36 import android.content.Intent;
37 import android.content.IntentFilter;
38 import android.content.pm.ActivityInfo;
39 import android.content.pm.ApplicationInfo;
40 import android.content.pm.PackageManager;
41 import android.content.pm.PackageManager.NameNotFoundException;
42 import android.content.pm.ResolveInfo;
43 import android.content.pm.UserInfo;
44 import android.content.res.Configuration;
45 import android.graphics.Insets;
46 import android.net.Uri;
47 import android.os.Build;
48 import android.os.Bundle;
49 import android.os.PatternMatcher;
50 import android.os.RemoteException;
51 import android.os.StrictMode;
52 import android.os.Trace;
53 import android.os.UserHandle;
54 import android.os.UserManager;
55 import android.provider.Settings;
56 import android.stats.devicepolicy.DevicePolicyEnums;
57 import android.text.TextUtils;
58 import android.util.Log;
59 import android.util.Slog;
60 import android.view.Gravity;
61 import android.view.LayoutInflater;
62 import android.view.View;
63 import android.view.ViewGroup;
64 import android.view.ViewGroup.LayoutParams;
65 import android.view.Window;
66 import android.view.WindowInsets;
67 import android.view.WindowManager;
68 import android.widget.AbsListView;
69 import android.widget.AdapterView;
70 import android.widget.Button;
71 import android.widget.FrameLayout;
72 import android.widget.ImageView;
73 import android.widget.ListView;
74 import android.widget.Space;
75 import android.widget.TabHost;
76 import android.widget.TextView;
77 import android.widget.Toast;
78 
79 import androidx.annotation.NonNull;
80 import androidx.annotation.Nullable;
81 import androidx.fragment.app.FragmentActivity;
82 import androidx.lifecycle.ViewModelProvider;
83 import androidx.lifecycle.viewmodel.CreationExtras;
84 import androidx.viewpager.widget.ViewPager;
85 
86 import com.android.intentresolver.chooser.DisplayResolveInfo;
87 import com.android.intentresolver.chooser.TargetInfo;
88 import com.android.intentresolver.data.repository.DevicePolicyResources;
89 import com.android.intentresolver.domain.interactor.UserInteractor;
90 import com.android.intentresolver.emptystate.CompositeEmptyStateProvider;
91 import com.android.intentresolver.emptystate.CrossProfileIntentsChecker;
92 import com.android.intentresolver.emptystate.EmptyStateProvider;
93 import com.android.intentresolver.emptystate.NoAppsAvailableEmptyStateProvider;
94 import com.android.intentresolver.emptystate.NoCrossProfileEmptyStateProvider;
95 import com.android.intentresolver.emptystate.WorkProfilePausedEmptyStateProvider;
96 import com.android.intentresolver.icons.DefaultTargetDataLoader;
97 import com.android.intentresolver.icons.TargetDataLoader;
98 import com.android.intentresolver.inject.Background;
99 import com.android.intentresolver.model.ResolverRankerServiceResolverComparator;
100 import com.android.intentresolver.profiles.MultiProfilePagerAdapter;
101 import com.android.intentresolver.profiles.MultiProfilePagerAdapter.ProfileType;
102 import com.android.intentresolver.profiles.OnProfileSelectedListener;
103 import com.android.intentresolver.profiles.OnSwitchOnWorkSelectedListener;
104 import com.android.intentresolver.profiles.ResolverMultiProfilePagerAdapter;
105 import com.android.intentresolver.profiles.TabConfig;
106 import com.android.intentresolver.shared.model.Profile;
107 import com.android.intentresolver.ui.ActionTitle;
108 import com.android.intentresolver.ui.ProfilePagerResources;
109 import com.android.intentresolver.ui.model.ActivityModel;
110 import com.android.intentresolver.ui.model.ResolverRequest;
111 import com.android.intentresolver.ui.viewmodel.ResolverViewModel;
112 import com.android.intentresolver.widget.ResolverDrawerLayout;
113 import com.android.internal.annotations.VisibleForTesting;
114 import com.android.internal.content.PackageMonitor;
115 import com.android.internal.logging.MetricsLogger;
116 import com.android.internal.logging.nano.MetricsProto;
117 
118 import com.google.common.collect.ImmutableList;
119 
120 import dagger.hilt.android.AndroidEntryPoint;
121 
122 import kotlin.Pair;
123 
124 import kotlinx.coroutines.CoroutineDispatcher;
125 
126 import java.util.ArrayList;
127 import java.util.Arrays;
128 import java.util.Iterator;
129 import java.util.List;
130 import java.util.Objects;
131 import java.util.Set;
132 
133 import javax.inject.Inject;
134 
135 /**
136  * This is a copy of ResolverActivity to support IntentResolver's ChooserActivity. This code is
137  * *not* the resolver that is actually triggered by the system right now (you want
138  * frameworks/base/core/java/com/android/internal/app/ResolverActivity.java for that), the full
139  * migration is not complete.
140  */
141 @AndroidEntryPoint(FragmentActivity.class)
142 public class ResolverActivity extends Hilt_ResolverActivity implements
143         ResolverListAdapter.ResolverListCommunicator {
144 
145     @Inject @Background public CoroutineDispatcher mBackgroundDispatcher;
146     @Inject public UserInteractor mUserInteractor;
147     @Inject public ResolverHelper mResolverHelper;
148     @Inject public PackageManager mPackageManager;
149     @Inject public DevicePolicyResources mDevicePolicyResources;
150     @Inject public ProfilePagerResources mProfilePagerResources;
151     @Inject public IntentForwarding mIntentForwarding;
152     @Inject public FeatureFlags mFeatureFlags;
153 
154     private ResolverViewModel mViewModel;
155     private ResolverRequest mRequest;
156     private ProfileHelper mProfiles;
157     private ProfileAvailability mProfileAvailability;
158     protected TargetDataLoader mTargetDataLoader;
159     private boolean mResolvingHome;
160 
161     private Button mAlwaysButton;
162     private Button mOnceButton;
163     protected View mProfileView;
164     private int mLastSelected = AbsListView.INVALID_POSITION;
165     private int mLayoutId;
166     private PickTargetOptionRequest mPickOptionRequest;
167     // Expected to be true if this object is ResolverActivity or is ResolverWrapperActivity.
168     protected ResolverDrawerLayout mResolverDrawerLayout;
169 
170     private static final String TAG = "ResolverActivity";
171     private static final boolean DEBUG = false;
172     private static final String LAST_SHOWN_TAB_KEY = "last_shown_tab_key";
173 
174     private boolean mRegistered;
175 
176     protected Insets mSystemWindowInsets = null;
177     private Space mFooterSpacer = null;
178 
179     protected static final String METRICS_CATEGORY_RESOLVER = "intent_resolver";
180 
181     /** Tracks if we should ignore future broadcasts telling us the work profile is enabled */
182     private final boolean mWorkProfileHasBeenEnabled = false;
183 
184     protected static final String TAB_TAG_PERSONAL = "personal";
185     protected static final String TAB_TAG_WORK = "work";
186 
187     private PackageMonitor mPersonalPackageMonitor;
188     private PackageMonitor mWorkPackageMonitor;
189 
190     protected ResolverMultiProfilePagerAdapter mMultiProfilePagerAdapter;
191 
192     public static final int PROFILE_PERSONAL = MultiProfilePagerAdapter.PROFILE_PERSONAL;
193     public static final int PROFILE_WORK = MultiProfilePagerAdapter.PROFILE_WORK;
194 
195     private UserHandle mHeaderCreatorUser;
196 
197     @Nullable
198     private OnSwitchOnWorkSelectedListener mOnSwitchOnWorkSelectedListener;
199 
createPackageMonitor(ResolverListAdapter listAdapter)200     protected PackageMonitor createPackageMonitor(ResolverListAdapter listAdapter) {
201         return new PackageMonitor() {
202             @Override
203             public void onSomePackagesChanged() {
204                 listAdapter.handlePackagesChanged();
205             }
206 
207             @Override
208             public boolean onPackageChanged(String packageName, int uid, String[] components) {
209                 // We care about all package changes, not just the whole package itself which is
210                 // default behavior.
211                 return true;
212             }
213         };
214     }
215 
216     protected ActivityModel createActivityModel() {
217         return ActivityModel.createFrom(this);
218     }
219 
220     @NonNull
221     @Override
222     public CreationExtras getDefaultViewModelCreationExtras() {
223         return addDefaultArgs(
224                 super.getDefaultViewModelCreationExtras(),
225                 new Pair<>(ActivityModel.ACTIVITY_MODEL_KEY, createActivityModel()));
226     }
227 
228     @Override
229     protected void onCreate(Bundle savedInstanceState) {
230         super.onCreate(savedInstanceState);
231         Log.i(TAG, "onCreate");
232         setTheme(R.style.Theme_DeviceDefault_Resolver);
233         mResolverHelper.setInitializer(this::initialize);
234     }
235 
236     @Override
237     protected final void onStart() {
238         super.onStart();
239         this.getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
240     }
241 
242     @Override
243     protected void onStop() {
244         super.onStop();
245 
246         final Window window = this.getWindow();
247         final WindowManager.LayoutParams attrs = window.getAttributes();
248         attrs.privateFlags &= ~SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
249         window.setAttributes(attrs);
250 
251         if (mRegistered) {
252             mPersonalPackageMonitor.unregister();
253             if (mWorkPackageMonitor != null) {
254                 mWorkPackageMonitor.unregister();
255             }
256             mRegistered = false;
257         }
258         final Intent intent = getIntent();
259         if ((intent.getFlags() & FLAG_ACTIVITY_NEW_TASK) != 0 && !isVoiceInteraction()
260                 && !mResolvingHome) {
261             // This resolver is in the unusual situation where it has been
262             // launched at the top of a new task.  We don't let it be added
263             // to the recent tasks shown to the user, and we need to make sure
264             // that each time we are launched we get the correct launching
265             // uid (not re-using the same resolver from an old launching uid),
266             // so we will now finish ourself since being no longer visible,
267             // the user probably can't get back to us.
268             if (!isChangingConfigurations()) {
269                 finish();
270             }
271         }
272     }
273 
274     @Override
275     protected final void onSaveInstanceState(@NonNull Bundle outState) {
276         super.onSaveInstanceState(outState);
277         ViewPager viewPager = findViewById(com.android.internal.R.id.profile_pager);
278         if (viewPager != null) {
279             outState.putInt(LAST_SHOWN_TAB_KEY, viewPager.getCurrentItem());
280         }
281     }
282 
283     @Override
284     protected final void onRestart() {
285         super.onRestart();
286         if (!mRegistered) {
287             mPersonalPackageMonitor.register(
288                     this,
289                     getMainLooper(),
290                     mProfiles.getPersonalHandle(),
291                     false);
292             if (mProfiles.getWorkProfilePresent()) {
293                 if (mWorkPackageMonitor == null) {
294                     mWorkPackageMonitor = createPackageMonitor(
295                             mMultiProfilePagerAdapter.getWorkListAdapter());
296                 }
297                 mWorkPackageMonitor.register(
298                         this,
299                         getMainLooper(),
300                         mProfiles.getWorkHandle(),
301                         false);
302             }
303             mRegistered = true;
304         }
305         mMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged();
306     }
307 
308     @Override
309     protected void onDestroy() {
310         super.onDestroy();
311         if (!isChangingConfigurations() && mPickOptionRequest != null) {
312             mPickOptionRequest.cancel();
313         }
314         if (mMultiProfilePagerAdapter != null
315                 && mMultiProfilePagerAdapter.getActiveListAdapter() != null) {
316             mMultiProfilePagerAdapter.getActiveListAdapter().onDestroy();
317         }
318     }
319 
320     private void initialize() {
321         mViewModel = new ViewModelProvider(this).get(ResolverViewModel.class);
322         mRequest = mViewModel.getRequest().getValue();
323 
324         mProfiles =  new ProfileHelper(
325                 mUserInteractor,
326                 getCoroutineScope(getLifecycle()),
327                 mBackgroundDispatcher,
328                 mFeatureFlags);
329 
330         mProfileAvailability = new ProfileAvailability(
331                 mUserInteractor,
332                 getCoroutineScope(getLifecycle()),
333                 mBackgroundDispatcher);
334 
335         mProfileAvailability.setOnProfileStatusChange(this::onWorkProfileStatusUpdated);
336 
337         mResolvingHome = mRequest.isResolvingHome();
338         mTargetDataLoader = new DefaultTargetDataLoader(
339                 this,
340                 getLifecycle(),
341                 mRequest.isAudioCaptureDevice());
342 
343         // The last argument of createResolverListAdapter is whether to do special handling
344         // of the last used choice to highlight it in the list.  We need to always
345         // turn this off when running under voice interaction, since it results in
346         // a more complicated UI that the current voice interaction flow is not able
347         // to handle. We also turn it off when multiple tabs are shown to simplify the UX.
348         // We also turn it off when clonedProfile is present on the device, because we might have
349         // different "last chosen" activities in the different profiles, and PackageManager doesn't
350         // provide any more information to help us select between them.
351         boolean filterLastUsed = !isVoiceInteraction()
352                 && !mProfiles.getWorkProfilePresent() && !mProfiles.getCloneUserPresent();
353         mMultiProfilePagerAdapter = createMultiProfilePagerAdapter(
354                 new Intent[0],
355                 /* resolutionList = */ mRequest.getResolutionList(),
356                 filterLastUsed
357         );
358         if (configureContentView(mTargetDataLoader)) {
359             return;
360         }
361 
362         mPersonalPackageMonitor = createPackageMonitor(
363                 mMultiProfilePagerAdapter.getPersonalListAdapter());
364         mPersonalPackageMonitor.register(
365                 this,
366                 getMainLooper(),
367                 mProfiles.getPersonalHandle(),
368                 false
369         );
370         if (mProfiles.getWorkProfilePresent()) {
371             mWorkPackageMonitor = createPackageMonitor(
372                     mMultiProfilePagerAdapter.getWorkListAdapter());
373             mWorkPackageMonitor.register(
374                     this,
375                     getMainLooper(),
376                     mProfiles.getWorkHandle(),
377                     false
378             );
379         }
380 
381         mRegistered = true;
382 
383         final ResolverDrawerLayout rdl = findViewById(com.android.internal.R.id.contentPanel);
384         if (rdl != null) {
385             rdl.setOnDismissedListener(new ResolverDrawerLayout.OnDismissedListener() {
386                 @Override
387                 public void onDismissed() {
388                     finish();
389                 }
390             });
391 
392             boolean hasTouchScreen = mPackageManager
393                     .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);
394 
395             if (isVoiceInteraction() || !hasTouchScreen) {
396                 rdl.setCollapsed(false);
397             }
398 
399             rdl.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
400                     | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
401             rdl.setOnApplyWindowInsetsListener(this::onApplyWindowInsets);
402 
403             mResolverDrawerLayout = rdl;
404         }
405         Intent intent = mViewModel.getRequest().getValue().getIntent();
406         final Set<String> categories = intent.getCategories();
407         MetricsLogger.action(this, mMultiProfilePagerAdapter.getActiveListAdapter().hasFilteredItem()
408                 ? MetricsProto.MetricsEvent.ACTION_SHOW_APP_DISAMBIG_APP_FEATURED
409                 : MetricsProto.MetricsEvent.ACTION_SHOW_APP_DISAMBIG_NONE_FEATURED,
410                 intent.getAction() + ":" + intent.getType() + ":"
411                         + (categories != null ? Arrays.toString(categories.toArray()) : ""));
412     }
413 
414     private void restore(@Nullable Bundle savedInstanceState) {
415         if (savedInstanceState != null) {
416             // onRestoreInstanceState
417             resetButtonBar();
418             ViewPager viewPager = findViewById(com.android.internal.R.id.profile_pager);
419             if (viewPager != null) {
420                 viewPager.setCurrentItem(savedInstanceState.getInt(LAST_SHOWN_TAB_KEY));
421             }
422         }
423 
424         mMultiProfilePagerAdapter.clearInactiveProfileCache();
425     }
426 
427     protected ResolverMultiProfilePagerAdapter createMultiProfilePagerAdapter(
428             Intent[] initialIntents,
429             List<ResolveInfo> resolutionList,
430             boolean filterLastUsed) {
431         ResolverMultiProfilePagerAdapter resolverMultiProfilePagerAdapter = null;
432         if (mProfiles.getWorkProfilePresent()) {
433             resolverMultiProfilePagerAdapter =
434                     createResolverMultiProfilePagerAdapterForTwoProfiles(
435                             initialIntents, resolutionList, filterLastUsed);
436         } else {
437             resolverMultiProfilePagerAdapter = createResolverMultiProfilePagerAdapterForOneProfile(
438                     initialIntents, resolutionList, filterLastUsed);
439         }
440         return resolverMultiProfilePagerAdapter;
441     }
442 
443     protected EmptyStateProvider createBlockerEmptyStateProvider() {
444         boolean shouldShowNoCrossProfileIntentsEmptyState = getUser().equals(getIntentUser());
445 
446         if (!shouldShowNoCrossProfileIntentsEmptyState) {
447             // Implementation that doesn't show any blockers
448             return new EmptyStateProvider() {};
449         }
450         return new NoCrossProfileEmptyStateProvider(
451                 mProfiles,
452                 mDevicePolicyResources,
453                 createCrossProfileIntentsChecker(),
454                 /* isShare= */ false);
455     }
456 
457     /**
458      * Numerous layouts are supported, each with optional ViewGroups.
459      * Make sure the inset gets added to the correct View, using
460      * a footer for Lists so it can properly scroll under the navbar.
461      */
462     protected boolean shouldAddFooterView() {
463         if (useLayoutWithDefault()) return true;
464 
465         View buttonBar = findViewById(com.android.internal.R.id.button_bar);
466         return buttonBar == null || buttonBar.getVisibility() == View.GONE;
467     }
468 
469     protected void applyFooterView(int height) {
470         if (mFooterSpacer == null) {
471             mFooterSpacer = new Space(getApplicationContext());
472         } else {
473             ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter)
474                     .getActiveAdapterView().removeFooterView(mFooterSpacer);
475         }
476         mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
477                 mSystemWindowInsets.bottom));
478         ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter)
479                 .getActiveAdapterView().addFooterView(mFooterSpacer);
480     }
481 
482     protected WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
483         mSystemWindowInsets = insets.getSystemWindowInsets();
484 
485         mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
486                 mSystemWindowInsets.right, 0);
487 
488         resetButtonBar();
489 
490         if (shouldUseMiniResolver()) {
491             View buttonContainer = findViewById(com.android.internal.R.id.button_bar_container);
492             buttonContainer.setPadding(0, 0, 0, mSystemWindowInsets.bottom
493                     + getResources().getDimensionPixelOffset(R.dimen.resolver_button_bar_spacing));
494         }
495 
496         // Need extra padding so the list can fully scroll up
497         if (shouldAddFooterView()) {
498             applyFooterView(mSystemWindowInsets.bottom);
499         }
500 
501         return insets.consumeSystemWindowInsets();
502     }
503 
504     @Override
505     public void onConfigurationChanged(Configuration newConfig) {
506         super.onConfigurationChanged(newConfig);
507         mMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged();
508         if (mProfiles.getWorkProfilePresent() && !useLayoutWithDefault()
509                 && !shouldUseMiniResolver()) {
510             updateIntentPickerPaddings();
511         }
512 
513         if (mSystemWindowInsets != null) {
514             mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
515                     mSystemWindowInsets.right, 0);
516         }
517     }
518 
519     public int getLayoutResource() {
520         return R.layout.resolver_list;
521     }
522 
523     // referenced by layout XML: android:onClick="onButtonClick"
524     public void onButtonClick(View v) {
525         final int id = v.getId();
526         ListView listView = (ListView) mMultiProfilePagerAdapter.getActiveAdapterView();
527         ResolverListAdapter currentListAdapter = mMultiProfilePagerAdapter.getActiveListAdapter();
528         int which = currentListAdapter.hasFilteredItem()
529                 ? currentListAdapter.getFilteredPosition()
530                 : listView.getCheckedItemPosition();
531         boolean hasIndexBeenFiltered = !currentListAdapter.hasFilteredItem();
532         startSelected(which, id == com.android.internal.R.id.button_always, hasIndexBeenFiltered);
533     }
534 
535     public void startSelected(int which, boolean always, boolean hasIndexBeenFiltered) {
536         if (isFinishing()) {
537             return;
538         }
539         ResolveInfo ri = mMultiProfilePagerAdapter.getActiveListAdapter()
540                 .resolveInfoForPosition(which, hasIndexBeenFiltered);
541         if (mResolvingHome && hasManagedProfile() && !supportsManagedProfiles(ri)) {
542             String launcherName = ri.activityInfo.loadLabel(mPackageManager).toString();
543             Toast.makeText(this,
544                     mDevicePolicyResources.getWorkProfileNotSupportedMessage(launcherName),
545                     Toast.LENGTH_LONG).show();
546             return;
547         }
548 
549         TargetInfo target = mMultiProfilePagerAdapter.getActiveListAdapter()
550                 .targetInfoForPosition(which, hasIndexBeenFiltered);
551         if (target == null) {
552             return;
553         }
554         if (onTargetSelected(target, always)) {
555             if (always) {
556                 MetricsLogger.action(
557                         this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_ALWAYS);
558             } else {
559                 MetricsLogger.action(
560                         this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_JUST_ONCE);
561             }
562             MetricsLogger.action(this,
563                     mMultiProfilePagerAdapter.getActiveListAdapter().hasFilteredItem()
564                             ? MetricsProto.MetricsEvent.ACTION_HIDE_APP_DISAMBIG_APP_FEATURED
565                             : MetricsProto.MetricsEvent.ACTION_HIDE_APP_DISAMBIG_NONE_FEATURED);
566             finish();
567         }
568     }
569 
570     @Override // ResolverListCommunicator
571     public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) {
572         return defIntent;
573     }
574 
575     protected void onListRebuilt(ResolverListAdapter listAdapter, boolean rebuildCompleted) {
576         final ItemClickListener listener = new ItemClickListener();
577         setupAdapterListView((ListView) mMultiProfilePagerAdapter.getActiveAdapterView(), listener);
578         if (mProfiles.getWorkProfilePresent()) {
579             final ResolverDrawerLayout rdl = findViewById(com.android.internal.R.id.contentPanel);
580             if (rdl != null) {
581                 rdl.setMaxCollapsedHeight(getResources()
582                         .getDimensionPixelSize(useLayoutWithDefault()
583                                 ? R.dimen.resolver_max_collapsed_height_with_default_with_tabs
584                                 : R.dimen.resolver_max_collapsed_height_with_tabs));
585             }
586         }
587     }
588 
589     protected boolean onTargetSelected(TargetInfo target, boolean always) {
590         final ResolveInfo ri = target.getResolveInfo();
591         final Intent intent = target != null ? target.getResolvedIntent() : null;
592 
593         if (intent != null /*&& mMultiProfilePagerAdapter.getActiveListAdapter().hasFilteredItem()*/
594                 && mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredResolveList()
595                 != null) {
596             // Build a reasonable intent filter, based on what matched.
597             IntentFilter filter = new IntentFilter();
598             Intent filterIntent;
599 
600             if (intent.getSelector() != null) {
601                 filterIntent = intent.getSelector();
602             } else {
603                 filterIntent = intent;
604             }
605 
606             String action = filterIntent.getAction();
607             if (action != null) {
608                 filter.addAction(action);
609             }
610             Set<String> categories = filterIntent.getCategories();
611             if (categories != null) {
612                 for (String cat : categories) {
613                     filter.addCategory(cat);
614                 }
615             }
616             filter.addCategory(Intent.CATEGORY_DEFAULT);
617 
618             int cat = ri.match & IntentFilter.MATCH_CATEGORY_MASK;
619             Uri data = filterIntent.getData();
620             if (cat == IntentFilter.MATCH_CATEGORY_TYPE) {
621                 String mimeType = filterIntent.resolveType(this);
622                 if (mimeType != null) {
623                     try {
624                         filter.addDataType(mimeType);
625                     } catch (IntentFilter.MalformedMimeTypeException e) {
626                         Log.w("ResolverActivity", e);
627                         filter = null;
628                     }
629                 }
630             }
631             if (data != null && data.getScheme() != null) {
632                 // We need the data specification if there was no type,
633                 // OR if the scheme is not one of our magical "file:"
634                 // or "content:" schemes (see IntentFilter for the reason).
635                 if (cat != IntentFilter.MATCH_CATEGORY_TYPE
636                         || (!"file".equals(data.getScheme())
637                         && !"content".equals(data.getScheme()))) {
638                     filter.addDataScheme(data.getScheme());
639 
640                     // Look through the resolved filter to determine which part
641                     // of it matched the original Intent.
642                     Iterator<PatternMatcher> pIt = ri.filter.schemeSpecificPartsIterator();
643                     if (pIt != null) {
644                         String ssp = data.getSchemeSpecificPart();
645                         while (ssp != null && pIt.hasNext()) {
646                             PatternMatcher p = pIt.next();
647                             if (p.match(ssp)) {
648                                 filter.addDataSchemeSpecificPart(p.getPath(), p.getType());
649                                 break;
650                             }
651                         }
652                     }
653                     Iterator<IntentFilter.AuthorityEntry> aIt = ri.filter.authoritiesIterator();
654                     if (aIt != null) {
655                         while (aIt.hasNext()) {
656                             IntentFilter.AuthorityEntry a = aIt.next();
657                             if (a.match(data) >= 0) {
658                                 int port = a.getPort();
659                                 filter.addDataAuthority(a.getHost(),
660                                         port >= 0 ? Integer.toString(port) : null);
661                                 break;
662                             }
663                         }
664                     }
665                     pIt = ri.filter.pathsIterator();
666                     if (pIt != null) {
667                         String path = data.getPath();
668                         while (path != null && pIt.hasNext()) {
669                             PatternMatcher p = pIt.next();
670                             if (p.match(path)) {
671                                 filter.addDataPath(p.getPath(), p.getType());
672                                 break;
673                             }
674                         }
675                     }
676                 }
677             }
678 
679             if (filter != null) {
680                 final int N = mMultiProfilePagerAdapter.getActiveListAdapter()
681                         .getUnfilteredResolveList().size();
682                 ComponentName[] set;
683                 // If we don't add back in the component for forwarding the intent to a managed
684                 // profile, the preferred activity may not be updated correctly (as the set of
685                 // components we tell it we knew about will have changed).
686                 final boolean needToAddBackProfileForwardingComponent =
687                         mMultiProfilePagerAdapter.getActiveListAdapter().getOtherProfile() != null;
688                 if (!needToAddBackProfileForwardingComponent) {
689                     set = new ComponentName[N];
690                 } else {
691                     set = new ComponentName[N + 1];
692                 }
693 
694                 int bestMatch = 0;
695                 for (int i = 0; i < N; i++) {
696                     ResolveInfo r = mMultiProfilePagerAdapter.getActiveListAdapter()
697                             .getUnfilteredResolveList().get(i).getResolveInfoAt(0);
698                     set[i] = new ComponentName(r.activityInfo.packageName,
699                             r.activityInfo.name);
700                     if (r.match > bestMatch) bestMatch = r.match;
701                 }
702 
703                 if (needToAddBackProfileForwardingComponent) {
704                     set[N] = mMultiProfilePagerAdapter.getActiveListAdapter()
705                             .getOtherProfile().getResolvedComponentName();
706                     final int otherProfileMatch = mMultiProfilePagerAdapter.getActiveListAdapter()
707                             .getOtherProfile().getResolveInfo().match;
708                     if (otherProfileMatch > bestMatch) bestMatch = otherProfileMatch;
709                 }
710 
711                 if (always) {
712                     final int userId = getUserId();
713                     final PackageManager pm = mPackageManager;
714 
715                     // Set the preferred Activity
716                     pm.addUniquePreferredActivity(filter, bestMatch, set, intent.getComponent());
717 
718                     if (ri.handleAllWebDataURI) {
719                         // Set default Browser if needed
720                         final String packageName = pm.getDefaultBrowserPackageNameAsUser(userId);
721                         if (TextUtils.isEmpty(packageName)) {
722                             pm.setDefaultBrowserPackageNameAsUser(ri.activityInfo.packageName,
723                                     userId);
724                         }
725                     }
726                 } else {
727                     try {
728                         mMultiProfilePagerAdapter.getActiveListAdapter()
729                                 .mResolverListController.setLastChosen(intent, filter, bestMatch);
730                     } catch (RemoteException re) {
731                         Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
732                     }
733                 }
734             }
735         }
736 
737         safelyStartActivity(target);
738 
739         // Rely on the ActivityManager to pop up a dialog regarding app suspension
740         // and return false
741         return !target.isSuspended();
742     }
743 
744     @Override // ResolverListCommunicator
745     public boolean shouldGetActivityMetadata() {
746         return false;
747     }
748 
749     public boolean shouldAutoLaunchSingleChoice(TargetInfo target) {
750         return !target.isSuspended();
751     }
752 
753     @VisibleForTesting
754     protected ResolverListController createListController(UserHandle userHandle) {
755         ResolverRankerServiceResolverComparator resolverComparator =
756                 new ResolverRankerServiceResolverComparator(
757                         this,
758                         mRequest.getIntent(),
759                         mViewModel.getActivityModel().getReferrerPackage(),
760                         null,
761                         null,
762                         getResolverRankerServiceUserHandleList(userHandle),
763                         null);
764         return new ResolverListController(
765                 this,
766                 mPackageManager,
767                 mRequest.getIntent(),
768                 mViewModel.getActivityModel().getReferrerPackage(),
769                 mViewModel.getActivityModel().getLaunchedFromUid(),
770                 resolverComparator,
771                 mProfiles.getQueryIntentsHandle(userHandle));
772     }
773 
774     /**
775      * Finishing procedures to be performed after the list has been rebuilt.
776      * </p>Subclasses must call postRebuildListInternal at the end of postRebuildList.
777      *
778      * @return <code>true</code> if the activity is finishing and creation should halt.
779      */
780     protected boolean postRebuildList(boolean rebuildCompleted) {
781         return postRebuildListInternal(rebuildCompleted);
782     }
783 
784     /**
785      * Callback called when user changes the profile tab.
786      */
787     /* TODO: consider merging with the customized considerations of our implemented
788      * {@link MultiProfilePagerAdapter.OnProfileSelectedListener}. The only apparent distinctions
789      * between the respective listener callbacks would occur in the triggering patterns during init
790      * (when the `OnProfileSelectedListener` is registered after a possible tab-change), or possibly
791      * if there's some way to trigger an update in one model but not the other.  If there's an
792      * initialization dependency, we can probably reason about it with confidence. If there's a
793      * discrepancy between the `TabHost` and pager-adapter data models, that inconsistency is
794      * likely to be a bug that would benefit from consolidation.
795      */
796     protected void onProfileTabSelected(int currentPage) {
797         setupViewVisibilities();
798         maybeLogProfileChange();
799         if (mProfiles.getWorkProfilePresent()) {
800             // The device policy logger is only concerned with sessions that include a work profile.
801             DevicePolicyEventLogger
802                     .createEvent(DevicePolicyEnums.RESOLVER_SWITCH_TABS)
803                     .setInt(currentPage)
804                     .setStrings(getMetricsCategory())
805                     .write();
806         }
807     }
808 
809     /**
810      * Add a label to signify that the user can pick a different app.
811      *
812      * @param adapter The adapter used to provide data to item views.
813      */
814     public void addUseDifferentAppLabelIfNecessary(ResolverListAdapter adapter) {
815         final boolean useHeader = adapter.hasFilteredItem();
816         if (useHeader) {
817             FrameLayout stub = findViewById(com.android.internal.R.id.stub);
818             stub.setVisibility(View.VISIBLE);
819             TextView textView = (TextView) LayoutInflater.from(this).inflate(
820                     R.layout.resolver_different_item_header, null, false);
821             if (mProfiles.getWorkProfilePresent()) {
822                 textView.setGravity(Gravity.CENTER);
823             }
824             stub.addView(textView);
825         }
826     }
827 
828     protected void resetButtonBar() {
829         final ViewGroup buttonLayout = findViewById(com.android.internal.R.id.button_bar);
830         if (buttonLayout == null) {
831             Log.e(TAG, "Layout unexpectedly does not have a button bar");
832             return;
833         }
834         ResolverListAdapter activeListAdapter =
835                 mMultiProfilePagerAdapter.getActiveListAdapter();
836         View buttonBarDivider = findViewById(com.android.internal.R.id.resolver_button_bar_divider);
837         if (!useLayoutWithDefault()) {
838             int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0;
839             buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(),
840                     buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize(
841                             R.dimen.resolver_button_bar_spacing) + inset);
842         }
843         if (activeListAdapter.isTabLoaded()
844                 && mMultiProfilePagerAdapter.shouldShowEmptyStateScreen(activeListAdapter)
845                 && !useLayoutWithDefault()) {
846             buttonLayout.setVisibility(View.INVISIBLE);
847             if (buttonBarDivider != null) {
848                 buttonBarDivider.setVisibility(View.INVISIBLE);
849             }
850             setButtonBarIgnoreOffset(/* ignoreOffset */ false);
851             return;
852         }
853         if (buttonBarDivider != null) {
854             buttonBarDivider.setVisibility(View.VISIBLE);
855         }
856         buttonLayout.setVisibility(View.VISIBLE);
857         setButtonBarIgnoreOffset(/* ignoreOffset */ true);
858 
859         mOnceButton = (Button) buttonLayout.findViewById(com.android.internal.R.id.button_once);
860         mAlwaysButton = (Button) buttonLayout.findViewById(com.android.internal.R.id.button_always);
861 
862         resetAlwaysOrOnceButtonBar();
863     }
864 
865     protected String getMetricsCategory() {
866         return METRICS_CATEGORY_RESOLVER;
867     }
868 
869     @Override // ResolverListCommunicator
870     public final void onHandlePackagesChanged(ResolverListAdapter listAdapter) {
871         if (!mMultiProfilePagerAdapter.onHandlePackagesChanged(
872                 listAdapter,
873                 mProfileAvailability.getWaitingToEnableProfile())) {
874             // We no longer have any items... just finish the activity.
875             finish();
876         }
877     }
878 
879     protected void maybeLogProfileChange() {}
880 
881     @VisibleForTesting
882     protected CrossProfileIntentsChecker createCrossProfileIntentsChecker() {
883         return new CrossProfileIntentsChecker(getContentResolver());
884     }
885 
886     private void onWorkProfileStatusUpdated() {
887         if (mMultiProfilePagerAdapter.getActiveProfile() == PROFILE_WORK) {
888             mMultiProfilePagerAdapter.rebuildActiveTab(true);
889         } else {
890             mMultiProfilePagerAdapter.clearInactiveProfileCache();
891         }
892     }
893 
894     // @NonFinalForTesting
895     @VisibleForTesting
896     protected ResolverListAdapter createResolverListAdapter(
897             Context context,
898             List<Intent> payloadIntents,
899             Intent[] initialIntents,
900             List<ResolveInfo> resolutionList,
901             boolean filterLastUsed,
902             UserHandle userHandle) {
903         UserHandle initialIntentsUserSpace = mProfiles.getQueryIntentsHandle(userHandle);
904         return new ResolverListAdapter(
905                 context,
906                 payloadIntents,
907                 initialIntents,
908                 resolutionList,
909                 filterLastUsed,
910                 createListController(userHandle),
911                 userHandle,
912                 mRequest.getIntent(),
913                 this,
914                 initialIntentsUserSpace,
915                 mTargetDataLoader);
916     }
917 
918     protected final EmptyStateProvider createEmptyStateProvider(
919             @Nullable UserHandle workProfileUserHandle) {
920         final EmptyStateProvider blockerEmptyStateProvider = createBlockerEmptyStateProvider();
921 
922         final EmptyStateProvider workProfileOffEmptyStateProvider =
923                 new WorkProfilePausedEmptyStateProvider(
924                         this,
925                         mProfiles,
926                         mProfileAvailability,
927                         /* onSwitchOnWorkSelectedListener= */
928                         () -> {
929                             if (mOnSwitchOnWorkSelectedListener != null) {
930                                 mOnSwitchOnWorkSelectedListener.onSwitchOnWorkSelected();
931                             }
932                         },
933                         getMetricsCategory());
934 
935         EmptyStateProvider noAppsEmptyStateProvider = new NoAppsAvailableEmptyStateProvider(
936                 mProfiles,
937                 mProfileAvailability,
938                 getMetricsCategory(),
939                 mProfilePagerResources
940         );
941 
942         // Return composite provider, the order matters (the higher, the more priority)
943         return new CompositeEmptyStateProvider(
944                 blockerEmptyStateProvider,
945                 workProfileOffEmptyStateProvider,
946                 noAppsEmptyStateProvider
947         );
948     }
949 
950     private ResolverMultiProfilePagerAdapter createResolverMultiProfilePagerAdapterForOneProfile(
951             Intent[] initialIntents,
952             List<ResolveInfo> resolutionList,
953             boolean filterLastUsed) {
954         ResolverListAdapter personalAdapter = createResolverListAdapter(
955                 /* context */ this,
956                 mRequest.getPayloadIntents(),
957                 initialIntents,
958                 resolutionList,
959                 filterLastUsed,
960                 /* userHandle */ mProfiles.getPersonalHandle()
961         );
962         return new ResolverMultiProfilePagerAdapter(
963                 /* context */ this,
964                 ImmutableList.of(
965                         new TabConfig<>(
966                                 PROFILE_PERSONAL,
967                                 mDevicePolicyResources.getPersonalTabLabel(),
968                                 mDevicePolicyResources.getPersonalTabAccessibilityLabel(),
969                                 TAB_TAG_PERSONAL,
970                                 personalAdapter)),
971                 createEmptyStateProvider(/* workProfileUserHandle= */ null),
972                 /* workProfileQuietModeChecker= */ () -> false,
973                 /* defaultProfile= */ PROFILE_PERSONAL,
974                 /* workProfileUserHandle= */ null,
975                 mProfiles.getCloneHandle());
976     }
977 
978     private UserHandle getIntentUser() {
979         return Objects.requireNonNullElse(mRequest.getCallingUser(),
980                 mProfiles.getTabOwnerUserHandleForLaunch());
981     }
982 
983     private ResolverMultiProfilePagerAdapter createResolverMultiProfilePagerAdapterForTwoProfiles(
984             Intent[] initialIntents,
985             List<ResolveInfo> resolutionList,
986             boolean filterLastUsed) {
987         // In the edge case when we have 0 apps in the current profile and >1 apps in the other,
988         // the intent resolver is started in the other profile. Since this is the only case when
989         // this happens, we check for it here and set the current profile's tab.
990         int selectedProfile = getCurrentProfile();
991         UserHandle intentUser = getIntentUser();
992         if (!mProfiles.getTabOwnerUserHandleForLaunch().equals(intentUser)) {
993             if (mProfiles.getPersonalHandle().equals(intentUser)) {
994                 selectedProfile = PROFILE_PERSONAL;
995             } else if (mProfiles.getWorkHandle().equals(intentUser)) {
996                 selectedProfile = PROFILE_WORK;
997             }
998         } else {
999             int selectedProfileExtra = getSelectedProfileExtra();
1000             if (selectedProfileExtra != -1) {
1001                 selectedProfile = selectedProfileExtra;
1002             }
1003         }
1004         // We only show the default app for the profile of the current user. The filterLastUsed
1005         // flag determines whether to show a default app and that app is not shown in the
1006         // resolver list. So filterLastUsed should be false for the other profile.
1007         ResolverListAdapter personalAdapter = createResolverListAdapter(
1008                 /* context */ this,
1009                 mRequest.getPayloadIntents(),
1010                 selectedProfile == PROFILE_PERSONAL ? initialIntents : null,
1011                 resolutionList,
1012                 (filterLastUsed && UserHandle.myUserId()
1013                         == mProfiles.getPersonalHandle().getIdentifier()),
1014                 /* userHandle */ mProfiles.getPersonalHandle()
1015         );
1016         UserHandle workProfileUserHandle = mProfiles.getWorkHandle();
1017         ResolverListAdapter workAdapter = createResolverListAdapter(
1018                 /* context */ this,
1019                 mRequest.getPayloadIntents(),
1020                 selectedProfile == PROFILE_WORK ? initialIntents : null,
1021                 resolutionList,
1022                 (filterLastUsed && UserHandle.myUserId()
1023                         == workProfileUserHandle.getIdentifier()),
1024                 /* userHandle */ workProfileUserHandle
1025         );
1026         return new ResolverMultiProfilePagerAdapter(
1027                 /* context */ this,
1028                 ImmutableList.of(
1029                         new TabConfig<>(
1030                                 PROFILE_PERSONAL,
1031                                 mDevicePolicyResources.getPersonalTabLabel(),
1032                                 mDevicePolicyResources.getPersonalTabAccessibilityLabel(),
1033                                 TAB_TAG_PERSONAL,
1034                                 personalAdapter),
1035                         new TabConfig<>(
1036                                 PROFILE_WORK,
1037                                 mDevicePolicyResources.getWorkTabLabel(),
1038                                 mDevicePolicyResources.getWorkTabAccessibilityLabel(),
1039                                 TAB_TAG_WORK,
1040                                 workAdapter)),
1041                 createEmptyStateProvider(workProfileUserHandle),
1042                 /* Supplier<Boolean> (QuietMode enabled) == !(available) */
1043                 () -> !(mProfiles.getWorkProfilePresent()
1044                         && mProfileAvailability.isAvailable(
1045                         requireNonNull(mProfiles.getWorkProfile()))),
1046                 selectedProfile,
1047                 workProfileUserHandle,
1048                 mProfiles.getCloneHandle());
1049     }
1050 
1051     /**
1052      * Returns {@link #PROFILE_PERSONAL} or {@link #PROFILE_WORK} if the {@link
1053      * #EXTRA_SELECTED_PROFILE} extra was supplied, or {@code -1} if no extra was supplied.
1054      */
1055     final int getSelectedProfileExtra() {
1056         Profile.Type selected = mRequest.getSelectedProfile();
1057         if (selected == null) {
1058             return -1;
1059         }
1060         switch (selected) {
1061             case PERSONAL: return PROFILE_PERSONAL;
1062             case WORK: return PROFILE_WORK;
1063             default: return -1;
1064         }
1065     }
1066 
1067     protected final @ProfileType int getCurrentProfile() {
1068         UserHandle launchUser = mProfiles.getTabOwnerUserHandleForLaunch();
1069         UserHandle personalUser = mProfiles.getPersonalHandle();
1070         return launchUser.equals(personalUser) ? PROFILE_PERSONAL : PROFILE_WORK;
1071     }
1072 
1073     private void updateIntentPickerPaddings() {
1074         View titleCont = findViewById(com.android.internal.R.id.title_container);
1075         titleCont.setPadding(
1076                 titleCont.getPaddingLeft(),
1077                 titleCont.getPaddingTop(),
1078                 titleCont.getPaddingRight(),
1079                 getResources().getDimensionPixelSize(R.dimen.resolver_title_padding_bottom));
1080         View buttonBar = findViewById(com.android.internal.R.id.button_bar);
1081         buttonBar.setPadding(
1082                 buttonBar.getPaddingLeft(),
1083                 getResources().getDimensionPixelSize(R.dimen.resolver_button_bar_spacing),
1084                 buttonBar.getPaddingRight(),
1085                 getResources().getDimensionPixelSize(R.dimen.resolver_button_bar_spacing));
1086     }
1087 
1088     private void maybeLogCrossProfileTargetLaunch(TargetInfo cti, UserHandle currentUserHandle) {
1089         // TODO: Test isolation bug, referencing getUser() will break tests with faked profiles
1090         if (!mProfiles.getWorkProfilePresent() || currentUserHandle.equals(getUser())) {
1091             return;
1092         }
1093         DevicePolicyEventLogger
1094                 .createEvent(DevicePolicyEnums.RESOLVER_CROSS_PROFILE_TARGET_OPENED)
1095                 .setBoolean(
1096                         currentUserHandle.equals(
1097                                 mProfiles.getPersonalHandle()))
1098                 .setStrings(getMetricsCategory(),
1099                         cti.isInDirectShareMetricsCategory() ? "direct_share" : "other_target")
1100                 .write();
1101     }
1102 
1103     @Override // ResolverListCommunicator
1104     public final void sendVoiceChoicesIfNeeded() {
1105         if (!isVoiceInteraction()) {
1106             // Clearly not needed.
1107             return;
1108         }
1109 
1110         int count = mMultiProfilePagerAdapter.getActiveListAdapter().getCount();
1111         final Option[] options = new Option[count];
1112         for (int i = 0; i < options.length; i++) {
1113             TargetInfo target = mMultiProfilePagerAdapter.getActiveListAdapter().getItem(i);
1114             if (target == null) {
1115                 // If this occurs, a new set of targets is being loaded. Let that complete,
1116                 // and have the next call to send voice choices proceed instead.
1117                 return;
1118             }
1119             options[i] = optionForChooserTarget(target, i);
1120         }
1121 
1122         mPickOptionRequest = new PickTargetOptionRequest(
1123                 new Prompt(getTitle()), options, null);
1124         getVoiceInteractor().submitRequest(mPickOptionRequest);
1125     }
1126 
1127     final Option optionForChooserTarget(TargetInfo target, int index) {
1128         return new Option(getOrLoadDisplayLabel(target), index);
1129     }
1130 
1131     protected final CharSequence getTitleForAction(Intent intent, int defaultTitleRes) {
1132         final ActionTitle title = mResolvingHome
1133                 ? ActionTitle.HOME
1134                 : ActionTitle.forAction(intent.getAction());
1135 
1136         // While there may already be a filtered item, we can only use it in the title if the list
1137         // is already sorted and all information relevant to it is already in the list.
1138         final boolean named =
1139                 mMultiProfilePagerAdapter.getActiveListAdapter().getFilteredPosition() >= 0;
1140         if (title == ActionTitle.DEFAULT && defaultTitleRes != 0) {
1141             return getString(defaultTitleRes);
1142         } else {
1143             return named
1144                     ? getString(
1145                             title.namedTitleRes,
1146                             getOrLoadDisplayLabel(
1147                                     mMultiProfilePagerAdapter
1148                                         .getActiveListAdapter().getFilteredItem()))
1149                     : getString(title.titleRes);
1150         }
1151     }
1152 
1153     private boolean hasManagedProfile() {
1154         UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
1155         if (userManager == null) {
1156             return false;
1157         }
1158 
1159         try {
1160             List<UserInfo> profiles = userManager.getProfiles(getUserId());
1161             for (UserInfo userInfo : profiles) {
1162                 if (userInfo != null && userInfo.isManagedProfile()) {
1163                     return true;
1164                 }
1165             }
1166         } catch (SecurityException e) {
1167             return false;
1168         }
1169         return false;
1170     }
1171 
1172     private boolean supportsManagedProfiles(ResolveInfo resolveInfo) {
1173         try {
1174             ApplicationInfo appInfo = mPackageManager.getApplicationInfo(
1175                     resolveInfo.activityInfo.packageName, 0 /* default flags */);
1176             return appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP;
1177         } catch (NameNotFoundException e) {
1178             return false;
1179         }
1180     }
1181 
1182     private void setAlwaysButtonEnabled(boolean hasValidSelection, int checkedPos,
1183             boolean filtered) {
1184         if (!mMultiProfilePagerAdapter.getCurrentUserHandle().equals(getUser())) {
1185             // Never allow the inactive profile to always open an app.
1186             mAlwaysButton.setEnabled(false);
1187             return;
1188         }
1189         // In case of clonedProfile being active, we do not allow the 'Always' option in the
1190         // disambiguation dialog of Personal Profile as the package manager cannot distinguish
1191         // between cross-profile preferred activities.
1192         if (mProfiles.getCloneUserPresent()
1193                 && (mMultiProfilePagerAdapter.getActiveProfile() == PROFILE_PERSONAL)) {
1194             mAlwaysButton.setEnabled(false);
1195             return;
1196         }
1197         boolean enabled = false;
1198         ResolveInfo ri = null;
1199         if (hasValidSelection) {
1200             ri = mMultiProfilePagerAdapter.getActiveListAdapter()
1201                     .resolveInfoForPosition(checkedPos, filtered);
1202             if (ri == null) {
1203                 Log.e(TAG, "Invalid position supplied to setAlwaysButtonEnabled");
1204                 return;
1205             } else if (ri.targetUserId != UserHandle.USER_CURRENT) {
1206                 Log.e(TAG, "Attempted to set selection to resolve info for another user");
1207                 return;
1208             } else {
1209                 enabled = true;
1210             }
1211 
1212             mAlwaysButton.setText(getResources()
1213                     .getString(R.string.activity_resolver_use_always));
1214         }
1215 
1216         if (ri != null) {
1217             ActivityInfo activityInfo = ri.activityInfo;
1218 
1219             boolean hasRecordPermission = mPackageManager
1220                     .checkPermission(android.Manifest.permission.RECORD_AUDIO,
1221                             activityInfo.packageName)
1222                             == PackageManager.PERMISSION_GRANTED;
1223 
1224             if (!hasRecordPermission) {
1225                 // OK, we know the record permission, is this a capture device
1226                 boolean hasAudioCapture = mViewModel.getRequest().getValue().isAudioCaptureDevice();
1227                 enabled = !hasAudioCapture;
1228             }
1229         }
1230         mAlwaysButton.setEnabled(enabled);
1231     }
1232 
1233     @Override // ResolverListCommunicator
1234     public final void onPostListReady(ResolverListAdapter listAdapter, boolean doPostProcessing,
1235             boolean rebuildCompleted) {
1236         if (isAutolaunching()) {
1237             return;
1238         }
1239         mMultiProfilePagerAdapter.setUseLayoutWithDefault(useLayoutWithDefault());
1240 
1241         if (mMultiProfilePagerAdapter.shouldShowEmptyStateScreen(listAdapter)) {
1242             mMultiProfilePagerAdapter.showEmptyResolverListEmptyState(listAdapter);
1243         } else {
1244             mMultiProfilePagerAdapter.showListView(listAdapter);
1245         }
1246         // showEmptyResolverListEmptyState can mark the tab as loaded,
1247         // which is a precondition for auto launching
1248         if (rebuildCompleted && maybeAutolaunchActivity()) {
1249             return;
1250         }
1251         if (doPostProcessing) {
1252             maybeCreateHeader(listAdapter);
1253             resetButtonBar();
1254             onListRebuilt(listAdapter, rebuildCompleted);
1255         }
1256     }
1257 
1258     /** Start the activity specified by the {@link TargetInfo}.*/
1259     public final void safelyStartActivity(TargetInfo cti) {
1260         // In case cloned apps are present, we would want to start those apps in cloned user
1261         // space, which will not be same as the adapter's userHandle. resolveInfo.userHandle
1262         // identifies the correct user space in such cases.
1263         UserHandle activityUserHandle = cti.getResolveInfo().userHandle;
1264         safelyStartActivityAsUser(cti, activityUserHandle, null);
1265     }
1266 
1267     /**
1268      * Start activity as a fixed user handle.
1269      * @param cti TargetInfo to be launched.
1270      * @param user User to launch this activity as.
1271      */
1272     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
1273     public final void safelyStartActivityAsUser(TargetInfo cti, UserHandle user) {
1274         safelyStartActivityAsUser(cti, user, null);
1275     }
1276 
1277     protected final void safelyStartActivityAsUser(
1278             TargetInfo cti, UserHandle user, @Nullable Bundle options) {
1279         // We're dispatching intents that might be coming from legacy apps, so
1280         // don't kill ourselves.
1281         StrictMode.disableDeathOnFileUriExposure();
1282         try {
1283             safelyStartActivityInternal(cti, user, options);
1284         } finally {
1285             StrictMode.enableDeathOnFileUriExposure();
1286         }
1287     }
1288 
1289     final void showTargetDetails(ResolveInfo ri) {
1290         Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
1291                 .setData(Uri.fromParts("package", ri.activityInfo.packageName, null))
1292                 .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
1293         startActivityAsUser(in, mMultiProfilePagerAdapter.getCurrentUserHandle());
1294     }
1295 
1296     /**
1297      * Sets up the content view.
1298      * @return <code>true</code> if the activity is finishing and creation should halt.
1299      */
1300     private boolean configureContentView(TargetDataLoader targetDataLoader) {
1301         if (mMultiProfilePagerAdapter.getActiveListAdapter() == null) {
1302             throw new IllegalStateException("mMultiProfilePagerAdapter.getCurrentListAdapter() "
1303                     + "cannot be null.");
1304         }
1305         Trace.beginSection("configureContentView");
1306         // We partially rebuild the inactive adapter to determine if we should auto launch
1307         // isTabLoaded will be true here if the empty state screen is shown instead of the list.
1308         // To date, we really only care about "partially rebuilding" tabs for work and/or personal.
1309         boolean rebuildCompleted =
1310                 mMultiProfilePagerAdapter.rebuildTabs(mProfiles.getWorkProfilePresent());
1311 
1312         if (shouldUseMiniResolver()) {
1313             configureMiniResolverContent(targetDataLoader);
1314             Trace.endSection();
1315             return false;
1316         }
1317 
1318         if (useLayoutWithDefault()) {
1319             mLayoutId = R.layout.resolver_list_with_default;
1320         } else {
1321             mLayoutId = getLayoutResource();
1322         }
1323         setContentView(mLayoutId);
1324         mMultiProfilePagerAdapter.setupViewPager(
1325                 findViewById(com.android.internal.R.id.profile_pager));
1326         boolean result = postRebuildList(rebuildCompleted);
1327         Trace.endSection();
1328         return result;
1329     }
1330 
1331     /**
1332      * Mini resolver is shown when the user is choosing between browser[s] in this profile and a
1333      * single app in the other profile (see shouldUseMiniResolver()). It shows the single app icon
1334      * and asks the user if they'd like to open that cross-profile app or use the in-profile
1335      * browser.
1336      */
1337     private void configureMiniResolverContent(TargetDataLoader targetDataLoader) {
1338         mLayoutId = R.layout.miniresolver;
1339         setContentView(mLayoutId);
1340 
1341         boolean inWorkProfile = getCurrentProfile() == PROFILE_WORK;
1342 
1343         ResolverListAdapter sameProfileAdapter =
1344                 (mMultiProfilePagerAdapter.getActiveProfile() == PROFILE_PERSONAL)
1345                 ? mMultiProfilePagerAdapter.getPersonalListAdapter()
1346                 : mMultiProfilePagerAdapter.getWorkListAdapter();
1347 
1348         ResolverListAdapter inactiveAdapter =
1349                 (mMultiProfilePagerAdapter.getActiveProfile() == PROFILE_PERSONAL)
1350                 ? mMultiProfilePagerAdapter.getWorkListAdapter()
1351                 : mMultiProfilePagerAdapter.getPersonalListAdapter();
1352 
1353         DisplayResolveInfo sameProfileResolveInfo = sameProfileAdapter.getFirstDisplayResolveInfo();
1354 
1355         final DisplayResolveInfo otherProfileResolveInfo =
1356                 inactiveAdapter.getFirstDisplayResolveInfo();
1357 
1358         // Load the icon asynchronously
1359         ImageView icon = findViewById(com.android.internal.R.id.icon);
1360         targetDataLoader.getOrLoadAppTargetIcon(
1361                 otherProfileResolveInfo,
1362                 inactiveAdapter.getUserHandle(),
1363                 (drawable) -> {
1364                     if (!isDestroyed()) {
1365                         otherProfileResolveInfo.getDisplayIconHolder().setDisplayIcon(drawable);
1366                         new ResolverListAdapter.ViewHolder(icon).bindIcon(otherProfileResolveInfo);
1367                     }
1368                 });
1369 
1370         ((TextView) findViewById(com.android.internal.R.id.open_cross_profile)).setText(
1371                 getResources().getString(
1372                         inWorkProfile
1373                                 ? R.string.miniresolver_open_in_personal
1374                                 : R.string.miniresolver_open_in_work,
1375                         getOrLoadDisplayLabel(otherProfileResolveInfo)));
1376         ((Button) findViewById(com.android.internal.R.id.use_same_profile_browser)).setText(
1377                 inWorkProfile ? R.string.miniresolver_use_work_browser
1378                         : R.string.miniresolver_use_personal_browser);
1379 
1380         findViewById(com.android.internal.R.id.use_same_profile_browser).setOnClickListener(
1381                 v -> {
1382                     safelyStartActivity(sameProfileResolveInfo);
1383                     finish();
1384                 });
1385 
1386         findViewById(com.android.internal.R.id.button_open).setOnClickListener(v -> {
1387             Intent intent = otherProfileResolveInfo.getResolvedIntent();
1388             safelyStartActivityAsUser(otherProfileResolveInfo, inactiveAdapter.getUserHandle());
1389             finish();
1390         });
1391     }
1392 
1393     private boolean isTwoPagePersonalAndWorkConfiguration() {
1394         return (mMultiProfilePagerAdapter.getCount() == 2)
1395                 && mMultiProfilePagerAdapter.hasPageForProfile(PROFILE_PERSONAL)
1396                 && mMultiProfilePagerAdapter.hasPageForProfile(PROFILE_WORK);
1397     }
1398 
1399     @VisibleForTesting
1400     protected void safelyStartActivityInternal(
1401             TargetInfo cti, UserHandle user, @Nullable Bundle options) {
1402         // If the target is suspended, the activity will not be successfully launched.
1403         // Do not unregister from package manager updates in this case
1404         if (!cti.isSuspended() && mRegistered) {
1405             if (mPersonalPackageMonitor != null) {
1406                 mPersonalPackageMonitor.unregister();
1407             }
1408             if (mWorkPackageMonitor != null) {
1409                 mWorkPackageMonitor.unregister();
1410             }
1411             mRegistered = false;
1412         }
1413         // If needed, show that intent is forwarded
1414         // from managed profile to owner or other way around.
1415         String profileSwitchMessage =
1416                 mIntentForwarding.forwardMessageFor(mRequest.getIntent());
1417         if (profileSwitchMessage != null) {
1418             Toast.makeText(this, profileSwitchMessage, Toast.LENGTH_LONG).show();
1419         }
1420         try {
1421             if (cti.startAsCaller(this, options, user.getIdentifier())) {
1422                 maybeLogCrossProfileTargetLaunch(cti, user);
1423             }
1424         } catch (RuntimeException e) {
1425             Slog.wtf(TAG,
1426                     "Unable to launch as uid "
1427                             + mViewModel.getActivityModel().getLaunchedFromUid()
1428                             + " package " + mViewModel.getActivityModel().getLaunchedFromPackage()
1429                             + ", while running in " + ActivityThread.currentProcessName(), e);
1430         }
1431     }
1432 
1433     /**
1434      * Finishing procedures to be performed after the list has been rebuilt.
1435      * @param rebuildCompleted
1436      * @return <code>true</code> if the activity is finishing and creation should halt.
1437      */
1438     final boolean postRebuildListInternal(boolean rebuildCompleted) {
1439         int count = mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredCount();
1440 
1441         // We only rebuild asynchronously when we have multiple elements to sort. In the case where
1442         // we're already done, we can check if we should auto-launch immediately.
1443         if (rebuildCompleted && maybeAutolaunchActivity()) {
1444             return true;
1445         }
1446 
1447         setupViewVisibilities();
1448 
1449         if (mProfiles.getWorkProfilePresent()) {
1450             setupProfileTabs();
1451         }
1452 
1453         return false;
1454     }
1455 
1456     /**
1457      * Mini resolver should be used when all of the following are true:
1458      * 1. This is the intent picker (ResolverActivity).
1459      * 2. This profile only has web browser matches.
1460      * 3. The other profile has a single non-browser match.
1461      */
1462     private boolean shouldUseMiniResolver() {
1463         if (!isTwoPagePersonalAndWorkConfiguration()) {
1464             return false;
1465         }
1466 
1467         ResolverListAdapter sameProfileAdapter =
1468                 (mMultiProfilePagerAdapter.getActiveProfile() == PROFILE_PERSONAL)
1469                 ? mMultiProfilePagerAdapter.getPersonalListAdapter()
1470                 : mMultiProfilePagerAdapter.getWorkListAdapter();
1471 
1472         ResolverListAdapter otherProfileAdapter =
1473                 (mMultiProfilePagerAdapter.getActiveProfile() == PROFILE_PERSONAL)
1474                 ? mMultiProfilePagerAdapter.getWorkListAdapter()
1475                 : mMultiProfilePagerAdapter.getPersonalListAdapter();
1476 
1477         if (sameProfileAdapter.getDisplayResolveInfoCount() == 0) {
1478             Log.d(TAG, "No targets in the current profile");
1479             return false;
1480         }
1481 
1482         if (otherProfileAdapter.getDisplayResolveInfoCount() != 1) {
1483             Log.d(TAG, "Other-profile count: " + otherProfileAdapter.getDisplayResolveInfoCount());
1484             return false;
1485         }
1486 
1487         if (otherProfileAdapter.allResolveInfosHandleAllWebDataUri()) {
1488             Log.d(TAG, "Other profile is a web browser");
1489             return false;
1490         }
1491 
1492         if (!sameProfileAdapter.allResolveInfosHandleAllWebDataUri()) {
1493             Log.d(TAG, "Non-browser found in this profile");
1494             return false;
1495         }
1496 
1497         return true;
1498     }
1499 
1500     private boolean maybeAutolaunchIfSingleTarget() {
1501         int count = mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredCount();
1502         if (count != 1) {
1503             return false;
1504         }
1505 
1506         if (mMultiProfilePagerAdapter.getActiveListAdapter().getOtherProfile() != null) {
1507             return false;
1508         }
1509 
1510         // Only one target, so we're a candidate to auto-launch!
1511         final TargetInfo target = mMultiProfilePagerAdapter.getActiveListAdapter()
1512                 .targetInfoForPosition(0, false);
1513         if (shouldAutoLaunchSingleChoice(target)) {
1514             safelyStartActivity(target);
1515             finish();
1516             return true;
1517         }
1518         return false;
1519     }
1520 
1521     /**
1522      * When we have just a personal and a work profile, we auto launch in the following scenario:
1523      * - There is 1 resolved target on each profile
1524      * - That target is the same app on both profiles
1525      * - The target app has permission to communicate cross profiles
1526      * - The target app has declared it supports cross-profile communication via manifest metadata
1527      */
1528     private boolean maybeAutolaunchIfCrossProfileSupported() {
1529         if (!isTwoPagePersonalAndWorkConfiguration()) {
1530             return false;
1531         }
1532 
1533         ResolverListAdapter activeListAdapter =
1534                 (mMultiProfilePagerAdapter.getActiveProfile() == PROFILE_PERSONAL)
1535                 ? mMultiProfilePagerAdapter.getPersonalListAdapter()
1536                 : mMultiProfilePagerAdapter.getWorkListAdapter();
1537 
1538         ResolverListAdapter inactiveListAdapter =
1539                 (mMultiProfilePagerAdapter.getActiveProfile() == PROFILE_PERSONAL)
1540                 ? mMultiProfilePagerAdapter.getWorkListAdapter()
1541                 : mMultiProfilePagerAdapter.getPersonalListAdapter();
1542 
1543         if (!activeListAdapter.isTabLoaded() || !inactiveListAdapter.isTabLoaded()) {
1544             return false;
1545         }
1546 
1547         if ((activeListAdapter.getUnfilteredCount() != 1)
1548                 || (inactiveListAdapter.getUnfilteredCount() != 1)) {
1549             return false;
1550         }
1551 
1552         TargetInfo activeProfileTarget = activeListAdapter.targetInfoForPosition(0, false);
1553         TargetInfo inactiveProfileTarget = inactiveListAdapter.targetInfoForPosition(0, false);
1554         if (!Objects.equals(
1555                 activeProfileTarget.getResolvedComponentName(),
1556                 inactiveProfileTarget.getResolvedComponentName())) {
1557             return false;
1558         }
1559 
1560         if (!shouldAutoLaunchSingleChoice(activeProfileTarget)) {
1561             return false;
1562         }
1563 
1564         String packageName = activeProfileTarget.getResolvedComponentName().getPackageName();
1565         if (!mIntentForwarding.canAppInteractAcrossProfiles(this, packageName)) {
1566             return false;
1567         }
1568 
1569         DevicePolicyEventLogger
1570                 .createEvent(DevicePolicyEnums.RESOLVER_AUTOLAUNCH_CROSS_PROFILE_TARGET)
1571                 .setBoolean(activeListAdapter.getUserHandle()
1572                         .equals(mProfiles.getPersonalHandle()))
1573                 .setStrings(getMetricsCategory())
1574                 .write();
1575         safelyStartActivity(activeProfileTarget);
1576         finish();
1577         return true;
1578     }
1579 
1580     private boolean isAutolaunching() {
1581         return !mRegistered && isFinishing();
1582     }
1583 
1584     /**
1585      * @return {@code true} if a resolved target is autolaunched, otherwise {@code false}
1586      */
1587     private boolean maybeAutolaunchActivity() {
1588         if (!isTwoPagePersonalAndWorkConfiguration()) {
1589             return false;
1590         }
1591 
1592         ResolverListAdapter activeListAdapter =
1593                 (mMultiProfilePagerAdapter.getActiveProfile() == PROFILE_PERSONAL)
1594                         ? mMultiProfilePagerAdapter.getPersonalListAdapter()
1595                         : mMultiProfilePagerAdapter.getWorkListAdapter();
1596 
1597         ResolverListAdapter inactiveListAdapter =
1598                 (mMultiProfilePagerAdapter.getActiveProfile() == PROFILE_PERSONAL)
1599                         ? mMultiProfilePagerAdapter.getWorkListAdapter()
1600                         : mMultiProfilePagerAdapter.getPersonalListAdapter();
1601 
1602         if (!activeListAdapter.isTabLoaded() || !inactiveListAdapter.isTabLoaded()) {
1603             return false;
1604         }
1605 
1606         if ((activeListAdapter.getUnfilteredCount() != 1)
1607                 || (inactiveListAdapter.getUnfilteredCount() != 1)) {
1608             return false;
1609         }
1610 
1611         TargetInfo activeProfileTarget = activeListAdapter.targetInfoForPosition(0, false);
1612         TargetInfo inactiveProfileTarget = inactiveListAdapter.targetInfoForPosition(0, false);
1613         if (!Objects.equals(
1614                 activeProfileTarget.getResolvedComponentName(),
1615                 inactiveProfileTarget.getResolvedComponentName())) {
1616             return false;
1617         }
1618 
1619         if (!shouldAutoLaunchSingleChoice(activeProfileTarget)) {
1620             return false;
1621         }
1622 
1623         String packageName = activeProfileTarget.getResolvedComponentName().getPackageName();
1624         if (!mIntentForwarding.canAppInteractAcrossProfiles(this, packageName)) {
1625             return false;
1626         }
1627 
1628         DevicePolicyEventLogger
1629                 .createEvent(DevicePolicyEnums.RESOLVER_AUTOLAUNCH_CROSS_PROFILE_TARGET)
1630                 .setBoolean(activeListAdapter.getUserHandle()
1631                         .equals(mProfiles.getPersonalHandle()))
1632                 .setStrings(getMetricsCategory())
1633                 .write();
1634         safelyStartActivity(activeProfileTarget);
1635         finish();
1636         return true;
1637     }
1638 
1639     private void maybeHideDivider() {
1640         final View divider = findViewById(com.android.internal.R.id.divider);
1641         if (divider == null) {
1642             return;
1643         }
1644         divider.setVisibility(View.GONE);
1645     }
1646 
1647     private void resetCheckedItem() {
1648         mLastSelected = ListView.INVALID_POSITION;
1649         ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter)
1650                 .clearCheckedItemsInInactiveProfiles();
1651     }
1652 
1653     private void setupViewVisibilities() {
1654         ResolverListAdapter activeListAdapter = mMultiProfilePagerAdapter.getActiveListAdapter();
1655         if (!mMultiProfilePagerAdapter.shouldShowEmptyStateScreen(activeListAdapter)) {
1656             addUseDifferentAppLabelIfNecessary(activeListAdapter);
1657         }
1658     }
1659 
1660     /**
1661      * Updates the button bar container {@code ignoreOffset} layout param.
1662      * <p>Setting this to {@code true} means that the button bar will be glued to the bottom of
1663      * the screen.
1664      */
1665     private void setButtonBarIgnoreOffset(boolean ignoreOffset) {
1666         View buttonBarContainer = findViewById(com.android.internal.R.id.button_bar_container);
1667         if (buttonBarContainer != null) {
1668             ResolverDrawerLayout.LayoutParams layoutParams =
1669                     (ResolverDrawerLayout.LayoutParams) buttonBarContainer.getLayoutParams();
1670             layoutParams.ignoreOffset = ignoreOffset;
1671             buttonBarContainer.setLayoutParams(layoutParams);
1672         }
1673     }
1674 
1675     private void setupAdapterListView(ListView listView, ItemClickListener listener) {
1676         listView.setOnItemClickListener(listener);
1677         listView.setOnItemLongClickListener(listener);
1678         listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
1679     }
1680 
1681     /**
1682      * Configure the area above the app selection list (title, content preview, etc).
1683      */
1684     private void maybeCreateHeader(ResolverListAdapter listAdapter) {
1685         if (mHeaderCreatorUser != null
1686                 && !listAdapter.getUserHandle().equals(mHeaderCreatorUser)) {
1687             return;
1688         }
1689         if (!mProfiles.getWorkProfilePresent()
1690                 && listAdapter.getCount() == 0 && listAdapter.getPlaceholderCount() == 0) {
1691             final TextView titleView = findViewById(com.android.internal.R.id.title);
1692             if (titleView != null) {
1693                 titleView.setVisibility(View.GONE);
1694             }
1695         }
1696         ResolverRequest request = mViewModel.getRequest().getValue();
1697         CharSequence title = mViewModel.getRequest().getValue().getTitle() != null
1698                 ? request.getTitle()
1699                 : getTitleForAction(request.getIntent(), 0);
1700 
1701         if (!TextUtils.isEmpty(title)) {
1702             final TextView titleView = findViewById(com.android.internal.R.id.title);
1703             if (titleView != null) {
1704                 titleView.setText(title);
1705             }
1706             setTitle(title);
1707         }
1708 
1709         final ImageView iconView = findViewById(com.android.internal.R.id.icon);
1710         if (iconView != null) {
1711             listAdapter.loadFilteredItemIconTaskAsync(iconView);
1712         }
1713         mHeaderCreatorUser = listAdapter.getUserHandle();
1714     }
1715 
1716     private void resetAlwaysOrOnceButtonBar() {
1717         // Disable both buttons initially
1718         setAlwaysButtonEnabled(false, ListView.INVALID_POSITION, false);
1719         mOnceButton.setEnabled(false);
1720 
1721         int filteredPosition = mMultiProfilePagerAdapter.getActiveListAdapter()
1722                 .getFilteredPosition();
1723         if (useLayoutWithDefault() && filteredPosition != ListView.INVALID_POSITION) {
1724             setAlwaysButtonEnabled(true, filteredPosition, false);
1725             mOnceButton.setEnabled(true);
1726             // Focus the button if we already have the default option
1727             mOnceButton.requestFocus();
1728             return;
1729         }
1730 
1731         // When the items load in, if an item was already selected, enable the buttons
1732         ListView currentAdapterView = (ListView) mMultiProfilePagerAdapter.getActiveAdapterView();
1733         if (currentAdapterView != null
1734                 && currentAdapterView.getCheckedItemPosition() != ListView.INVALID_POSITION) {
1735             setAlwaysButtonEnabled(true, currentAdapterView.getCheckedItemPosition(), true);
1736             mOnceButton.setEnabled(true);
1737         }
1738     }
1739 
1740     @Override // ResolverListCommunicator
1741     public final boolean useLayoutWithDefault() {
1742         // We only use the default app layout when the profile of the active user has a
1743         // filtered item. We always show the same default app even in the inactive user profile.
1744         return mMultiProfilePagerAdapter.getListAdapterForUserHandle(
1745                 mProfiles.getTabOwnerUserHandleForLaunch()
1746         ).hasFilteredItem();
1747     }
1748 
1749     final class ItemClickListener implements AdapterView.OnItemClickListener,
1750             AdapterView.OnItemLongClickListener {
1751         @Override
1752         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
1753             final ListView listView = parent instanceof ListView ? (ListView) parent : null;
1754             if (listView != null) {
1755                 position -= listView.getHeaderViewsCount();
1756             }
1757             if (position < 0) {
1758                 // Header views don't count.
1759                 return;
1760             }
1761             // If we're still loading, we can't yet enable the buttons.
1762             if (mMultiProfilePagerAdapter.getActiveListAdapter()
1763                     .resolveInfoForPosition(position, true) == null) {
1764                 return;
1765             }
1766             ListView currentAdapterView =
1767                     (ListView) mMultiProfilePagerAdapter.getActiveAdapterView();
1768             final int checkedPos = currentAdapterView.getCheckedItemPosition();
1769             final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
1770             if (!useLayoutWithDefault()
1771                     && (!hasValidSelection || mLastSelected != checkedPos)
1772                     && mAlwaysButton != null) {
1773                 setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
1774                 mOnceButton.setEnabled(hasValidSelection);
1775                 if (hasValidSelection) {
1776                     currentAdapterView.smoothScrollToPosition(checkedPos);
1777                     mOnceButton.requestFocus();
1778                 }
1779                 mLastSelected = checkedPos;
1780             } else {
1781                 startSelected(position, false, true);
1782             }
1783         }
1784 
1785         @Override
1786         public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
1787             final ListView listView = parent instanceof ListView ? (ListView) parent : null;
1788             if (listView != null) {
1789                 position -= listView.getHeaderViewsCount();
1790             }
1791             if (position < 0) {
1792                 // Header views don't count.
1793                 return false;
1794             }
1795             ResolveInfo ri = mMultiProfilePagerAdapter.getActiveListAdapter()
1796                     .resolveInfoForPosition(position, true);
1797             showTargetDetails(ri);
1798             return true;
1799         }
1800 
1801     }
1802 
1803     private void setupProfileTabs() {
1804         maybeHideDivider();
1805 
1806         TabHost tabHost = findViewById(com.android.internal.R.id.profile_tabhost);
1807         ViewPager viewPager = findViewById(com.android.internal.R.id.profile_pager);
1808 
1809         mMultiProfilePagerAdapter.setupProfileTabs(
1810                 getLayoutInflater(),
1811                 tabHost,
1812                 viewPager,
1813                 R.layout.resolver_profile_tab_button,
1814                 com.android.internal.R.id.profile_pager,
1815                 () -> onProfileTabSelected(viewPager.getCurrentItem()),
1816                 new OnProfileSelectedListener() {
1817                     @Override
1818                     public void onProfilePageSelected(@ProfileType int profileId, int pageNumber) {
1819                         resetButtonBar();
1820                         resetCheckedItem();
1821                     }
1822 
1823                     @Override
1824                     public void onProfilePageStateChanged(int state) {}
1825                 });
1826         mOnSwitchOnWorkSelectedListener = () -> {
1827             final View workTab =
1828                     tabHost.getTabWidget().getChildAt(
1829                             mMultiProfilePagerAdapter.getPageNumberForProfile(PROFILE_WORK));
1830             workTab.setFocusable(true);
1831             workTab.setFocusableInTouchMode(true);
1832             workTab.requestFocus();
1833         };
1834     }
1835 
1836     static final class PickTargetOptionRequest extends PickOptionRequest {
1837         public PickTargetOptionRequest(@Nullable Prompt prompt, Option[] options,
1838                 @Nullable Bundle extras) {
1839             super(prompt, options, extras);
1840         }
1841 
1842         @Override
1843         public void onCancel() {
1844             super.onCancel();
1845             final ResolverActivity ra = (ResolverActivity) getActivity();
1846             if (ra != null) {
1847                 ra.mPickOptionRequest = null;
1848                 ra.finish();
1849             }
1850         }
1851 
1852         @Override
1853         public void onPickOptionResult(boolean finished, Option[] selections, Bundle result) {
1854             super.onPickOptionResult(finished, selections, result);
1855             if (selections.length != 1) {
1856                 // TODO In a better world we would filter the UI presented here and let the
1857                 // user refine. Maybe later.
1858                 return;
1859             }
1860 
1861             final ResolverActivity ra = (ResolverActivity) getActivity();
1862             if (ra != null) {
1863                 final TargetInfo ti = ra.mMultiProfilePagerAdapter.getActiveListAdapter()
1864                         .getItem(selections[0].getIndex());
1865                 if (ra.onTargetSelected(ti, false)) {
1866                     ra.mPickOptionRequest = null;
1867                     ra.finish();
1868                 }
1869             }
1870         }
1871     }
1872     /**
1873      * Returns the {@link UserHandle} to use when querying resolutions for intents in a
1874      * {@link ResolverListController} configured for the provided {@code userHandle}.
1875      */
1876     protected final UserHandle getQueryIntentsUser(UserHandle userHandle) {
1877         return mProfiles.getQueryIntentsHandle(userHandle);
1878     }
1879 
1880     /**
1881      * Returns the {@link List} of {@link UserHandle} to pass on to the
1882      * {@link ResolverRankerServiceResolverComparator} as per the provided {@code userHandle}.
1883      */
1884     @VisibleForTesting(visibility = PROTECTED)
1885     public final List<UserHandle> getResolverRankerServiceUserHandleList(UserHandle userHandle) {
1886         return getResolverRankerServiceUserHandleListInternal(userHandle);
1887     }
1888 
1889     @VisibleForTesting
1890     protected List<UserHandle> getResolverRankerServiceUserHandleListInternal(
1891             UserHandle userHandle) {
1892         List<UserHandle> userList = new ArrayList<>();
1893         userList.add(userHandle);
1894         // Add clonedProfileUserHandle to the list only if we are:
1895         // a. Building the Personal Tab.
1896         // b. CloneProfile exists on the device.
1897         if (userHandle.equals(mProfiles.getPersonalHandle())
1898                 && mProfiles.getCloneUserPresent()) {
1899             userList.add(mProfiles.getCloneHandle());
1900         }
1901         return userList;
1902     }
1903 
1904     private CharSequence getOrLoadDisplayLabel(TargetInfo info) {
1905         if (info.isDisplayResolveInfo()) {
1906             mTargetDataLoader.getOrLoadLabel((DisplayResolveInfo) info);
1907         }
1908         CharSequence displayLabel = info.getDisplayLabel();
1909         return displayLabel == null ? "" : displayLabel;
1910     }
1911 }
1912