Merge changes I0677bca4,I7cad6529

* changes:
  Fix autolaunching.
  Create a PackageMonitor instance for each profile.
This commit is contained in:
TreeHugger Robot
2020-01-30 18:15:07 +00:00
committed by Android (Google) Code Review
9 changed files with 427 additions and 101 deletions

View File

@@ -18,7 +18,9 @@ package com.android.internal.app;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ResolveInfo;
import android.os.UserHandle;
import android.os.UserManager;
import android.view.View;
import android.view.ViewGroup;
@@ -27,6 +29,7 @@ import com.android.internal.widget.PagerAdapter;
import com.android.internal.widget.ViewPager;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -46,11 +49,17 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
private int mCurrentPage;
private OnProfileSelectedListener mOnProfileSelectedListener;
private Set<Integer> mLoadedPages;
private final UserHandle mPersonalProfileUserHandle;
private final UserHandle mWorkProfileUserHandle;
AbstractMultiProfilePagerAdapter(Context context, int currentPage) {
AbstractMultiProfilePagerAdapter(Context context, int currentPage,
UserHandle personalProfileUserHandle,
UserHandle workProfileUserHandle) {
mContext = Objects.requireNonNull(context);
mCurrentPage = currentPage;
mLoadedPages = new HashSet<>();
mPersonalProfileUserHandle = personalProfileUserHandle;
mWorkProfileUserHandle = workProfileUserHandle;
}
void setOnProfileSelectedListener(OnProfileSelectedListener listener) {
@@ -72,7 +81,7 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
public void onPageSelected(int position) {
mCurrentPage = position;
if (!mLoadedPages.contains(position)) {
getActiveListAdapter().rebuildList();
rebuildActiveTab(true);
mLoadedPages.add(position);
}
if (mOnProfileSelectedListener != null) {
@@ -85,6 +94,13 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
mLoadedPages.add(mCurrentPage);
}
void clearInactiveProfileCache() {
if (mLoadedPages.size() == 1) {
return;
}
mLoadedPages.remove(1 - mCurrentPage);
}
@Override
public ViewGroup instantiateItem(ViewGroup container, int position) {
final ProfileDescriptor profileDescriptor = getItem(position);
@@ -187,12 +203,53 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
@VisibleForTesting
public abstract @Nullable ResolverListAdapter getInactiveListAdapter();
public abstract ResolverListAdapter getPersonalListAdapter();
public abstract @Nullable ResolverListAdapter getWorkListAdapter();
abstract Object getCurrentRootAdapter();
abstract ViewGroup getActiveAdapterView();
abstract @Nullable ViewGroup getInactiveAdapterView();
boolean rebuildActiveTab(boolean post) {
return rebuildTab(getActiveListAdapter(), post);
}
boolean rebuildInactiveTab(boolean post) {
if (getItemCount() == 1) {
return false;
}
return rebuildTab(getInactiveListAdapter(), post);
}
private boolean rebuildTab(ResolverListAdapter activeListAdapter, boolean doPostProcessing) {
UserHandle listUserHandle = activeListAdapter.getUserHandle();
if (UserHandle.myUserId() != listUserHandle.getIdentifier() &&
!hasAppsInOtherProfile(activeListAdapter)) {
// TODO(arangelov): Show empty state UX here
return false;
} else {
return activeListAdapter.rebuildList(doPostProcessing);
}
}
private boolean hasAppsInOtherProfile(ResolverListAdapter adapter) {
if (mWorkProfileUserHandle == null) {
return false;
}
List<ResolverActivity.ResolvedComponentInfo> resolversForIntent =
adapter.getResolversForUser(UserHandle.of(UserHandle.myUserId()));
for (ResolverActivity.ResolvedComponentInfo info : resolversForIntent) {
ResolveInfo resolveInfo = info.getResolveInfoAt(0);
if (resolveInfo.targetUserId != UserHandle.USER_CURRENT) {
return true;
}
}
return false;
}
protected class ProfileDescriptor {
final ViewGroup rootView;
ProfileDescriptor(ViewGroup rootView) {

View File

@@ -793,7 +793,9 @@ public class ChooserActivity extends ResolverActivity implements
/* userHandle */ UserHandle.of(UserHandle.myUserId()));
return new ChooserMultiProfilePagerAdapter(
/* context */ this,
adapter);
adapter,
getPersonalProfileUserHandle(),
/* workProfileUserHandle= */ null);
}
private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForTwoProfiles(
@@ -820,7 +822,9 @@ public class ChooserActivity extends ResolverActivity implements
/* context */ this,
personalAdapter,
workAdapter,
/* defaultProfile */ getCurrentProfile());
/* defaultProfile */ getCurrentProfile(),
getPersonalProfileUserHandle(),
getWorkProfileUserHandle());
}
@Override
@@ -872,11 +876,11 @@ public class ChooserActivity extends ResolverActivity implements
}
@Override
protected PackageMonitor createPackageMonitor() {
protected PackageMonitor createPackageMonitor(ResolverListAdapter listAdapter) {
return new PackageMonitor() {
@Override
public void onSomePackagesChanged() {
handlePackagesChanged();
handlePackagesChanged(listAdapter);
}
};
}
@@ -885,9 +889,19 @@ public class ChooserActivity extends ResolverActivity implements
* Update UI to reflect changes in data.
*/
public void handlePackagesChanged() {
// TODO(arangelov): Dispatch this to all adapters when we have the helper methods
// in a follow-up CL
mChooserMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged();
handlePackagesChanged(/* listAdapter */ null);
}
/**
* Update UI to reflect changes in data.
* <p>If {@code listAdapter} is {@code null}, both profile list adapters are updated.
*/
private void handlePackagesChanged(@Nullable ResolverListAdapter listAdapter) {
if (listAdapter == null) {
mChooserMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged();
} else {
listAdapter.handlePackagesChanged();
}
updateProfileViewButton();
}
@@ -2458,10 +2472,10 @@ public class ChooserActivity extends ResolverActivity implements
}
@Override // ResolverListCommunicator
public void onHandlePackagesChanged() {
public void onHandlePackagesChanged(ResolverListAdapter listAdapter) {
mServicesRequested.clear();
mChooserMultiProfilePagerAdapter.getActiveListAdapter().notifyDataSetChanged();
super.onHandlePackagesChanged();
super.onHandlePackagesChanged(listAdapter);
}
@Override // SelectableTargetInfoCommunicator

View File

@@ -19,7 +19,6 @@ package com.android.internal.app;
import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE;
import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.prediction.AppPredictor;
import android.content.ComponentName;
@@ -176,7 +175,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
Log.d(TAG, "clearing queryTargets on package change");
}
createPlaceHolders();
mChooserListCommunicator.onHandlePackagesChanged();
mChooserListCommunicator.onHandlePackagesChanged(this);
}
@@ -541,7 +540,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
@Override
AsyncTask<List<ResolvedComponentInfo>,
Void,
List<ResolvedComponentInfo>> createSortingTask() {
List<ResolvedComponentInfo>> createSortingTask(boolean doPostProcessing) {
return new AsyncTask<List<ResolvedComponentInfo>,
Void,
List<ResolvedComponentInfo>>() {
@@ -554,9 +553,11 @@ public class ChooserListAdapter extends ResolverListAdapter {
}
@Override
protected void onPostExecute(List<ResolvedComponentInfo> sortedComponents) {
processSortedList(sortedComponents);
mChooserListCommunicator.updateProfileViewButton();
notifyDataSetChanged();
processSortedList(sortedComponents, doPostProcessing);
if (doPostProcessing) {
mChooserListCommunicator.updateProfileViewButton();
notifyDataSetChanged();
}
}
};
}

View File

@@ -38,8 +38,10 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
private final ChooserProfileDescriptor[] mItems;
ChooserMultiProfilePagerAdapter(Context context,
ChooserActivity.ChooserGridAdapter adapter) {
super(context, /* currentPage */ 0);
ChooserActivity.ChooserGridAdapter adapter,
UserHandle personalProfileUserHandle,
UserHandle workProfileUserHandle) {
super(context, /* currentPage */ 0, personalProfileUserHandle, workProfileUserHandle);
mItems = new ChooserProfileDescriptor[] {
createProfileDescriptor(adapter)
};
@@ -48,8 +50,11 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
ChooserMultiProfilePagerAdapter(Context context,
ChooserActivity.ChooserGridAdapter personalAdapter,
ChooserActivity.ChooserGridAdapter workAdapter,
@Profile int defaultProfile) {
super(context, /* currentPage */ defaultProfile);
@Profile int defaultProfile,
UserHandle personalProfileUserHandle,
UserHandle workProfileUserHandle) {
super(context, /* currentPage */ defaultProfile, personalProfileUserHandle,
workProfileUserHandle);
mItems = new ChooserProfileDescriptor[] {
createProfileDescriptor(personalAdapter),
createProfileDescriptor(workAdapter)
@@ -130,6 +135,17 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
return getAdapterForIndex(1 - getCurrentPage()).getListAdapter();
}
@Override
public ResolverListAdapter getPersonalListAdapter() {
return getAdapterForIndex(PROFILE_PERSONAL).getListAdapter();
}
@Override
@Nullable
public ResolverListAdapter getWorkListAdapter() {
return getAdapterForIndex(PROFILE_WORK).getListAdapter();
}
@Override
ChooserActivity.ChooserGridAdapter getCurrentRootAdapter() {
return getAdapterForIndex(getCurrentPage());

View File

@@ -16,7 +16,9 @@
package com.android.internal.app;
import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.PermissionChecker.PID_UNKNOWN;
import static com.android.internal.app.AbstractMultiProfilePagerAdapter.PROFILE_PERSONAL;
import static com.android.internal.app.AbstractMultiProfilePagerAdapter.PROFILE_WORK;
@@ -36,6 +38,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.PermissionChecker;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -157,7 +160,8 @@ public class ResolverActivity extends Activity implements
private static final String TAB_TAG_PERSONAL = "personal";
private static final String TAB_TAG_WORK = "work";
private final PackageMonitor mPackageMonitor = createPackageMonitor();
private PackageMonitor mPersonalPackageMonitor;
private PackageMonitor mWorkPackageMonitor;
@VisibleForTesting
protected AbstractMultiProfilePagerAdapter mMultiProfilePagerAdapter;
@@ -243,11 +247,11 @@ public class ResolverActivity extends Activity implements
}
}
protected PackageMonitor createPackageMonitor() {
protected PackageMonitor createPackageMonitor(ResolverListAdapter listAdapter) {
return new PackageMonitor() {
@Override
public void onSomePackagesChanged() {
mMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged();
listAdapter.handlePackagesChanged();
updateProfileViewButton();
}
@@ -327,8 +331,6 @@ public class ResolverActivity extends Activity implements
mPm = getPackageManager();
mPackageMonitor.register(this, getMainLooper(), false);
mRegistered = true;
mReferrerPackage = getReferrerPackageName();
// Add our initial intent as the first item, regardless of what else has already been added.
@@ -353,6 +355,18 @@ public class ResolverActivity extends Activity implements
return;
}
mPersonalPackageMonitor = createPackageMonitor(
mMultiProfilePagerAdapter.getPersonalListAdapter());
mPersonalPackageMonitor.register(
this, getMainLooper(), getPersonalProfileUserHandle(), false);
if (hasWorkProfile()) {
mWorkPackageMonitor = createPackageMonitor(
mMultiProfilePagerAdapter.getWorkListAdapter());
mWorkPackageMonitor.register(this, getMainLooper(), getWorkProfileUserHandle(), false);
}
mRegistered = true;
final ResolverDrawerLayout rdl = findViewById(R.id.contentPanel);
if (rdl != null) {
rdl.setOnDismissedListener(new ResolverDrawerLayout.OnDismissedListener() {
@@ -419,7 +433,9 @@ public class ResolverActivity extends Activity implements
/* userHandle */ UserHandle.of(UserHandle.myUserId()));
return new ResolverMultiProfilePagerAdapter(
/* context */ this,
adapter);
adapter,
getPersonalProfileUserHandle(),
/* workProfileUserHandle= */ null);
}
private ResolverMultiProfilePagerAdapter createResolverMultiProfilePagerAdapterForTwoProfiles(
@@ -438,20 +454,23 @@ public class ResolverActivity extends Activity implements
== getPersonalProfileUserHandle().getIdentifier()),
mUseLayoutForBrowsables,
/* userHandle */ getPersonalProfileUserHandle());
UserHandle workProfileUserHandle = getWorkProfileUserHandle();
ResolverListAdapter workAdapter = createResolverListAdapter(
/* context */ this,
/* payloadIntents */ mIntents,
initialIntents,
rList,
(filterLastUsed && UserHandle.myUserId()
== getWorkProfileUserHandle().getIdentifier()),
== workProfileUserHandle.getIdentifier()),
mUseLayoutForBrowsables,
/* userHandle */ getWorkProfileUserHandle());
/* userHandle */ workProfileUserHandle);
return new ResolverMultiProfilePagerAdapter(
/* context */ this,
personalAdapter,
workAdapter,
/* defaultProfile */ getCurrentProfile());
/* defaultProfile */ getCurrentProfile(),
getPersonalProfileUserHandle(),
getWorkProfileUserHandle());
}
protected @Profile int getCurrentProfile() {
@@ -543,9 +562,6 @@ public class ResolverActivity extends Activity implements
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged();
if (mMultiProfilePagerAdapter.getInactiveListAdapter() != null) {
mMultiProfilePagerAdapter.getInactiveListAdapter().handlePackagesChanged();
}
if (mSystemWindowInsets != null) {
mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
@@ -707,7 +723,16 @@ public class ResolverActivity extends Activity implements
protected void onRestart() {
super.onRestart();
if (!mRegistered) {
mPackageMonitor.register(this, getMainLooper(), false);
mPersonalPackageMonitor.register(this, getMainLooper(),
getPersonalProfileUserHandle(), false);
if (hasWorkProfile()) {
if (mWorkPackageMonitor == null) {
mWorkPackageMonitor = createPackageMonitor(
mMultiProfilePagerAdapter.getWorkListAdapter());
}
mWorkPackageMonitor.register(this, getMainLooper(),
getWorkProfileUserHandle(), false);
}
mRegistered = true;
}
mMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged();
@@ -718,7 +743,10 @@ public class ResolverActivity extends Activity implements
protected void onStop() {
super.onStop();
if (mRegistered) {
mPackageMonitor.unregister();
mPersonalPackageMonitor.unregister();
if (mWorkPackageMonitor != null) {
mWorkPackageMonitor.unregister();
}
mRegistered = false;
}
final Intent intent = getIntent();
@@ -913,26 +941,21 @@ public class ResolverActivity extends Activity implements
}
@Override // ResolverListCommunicator
public void onPostListReady(ResolverListAdapter listAdapter) {
if (mMultiProfilePagerAdapter.getCurrentUserHandle().getIdentifier()
== UserHandle.myUserId()) {
setHeader();
public void onPostListReady(ResolverListAdapter listAdapter, boolean doPostProcessing) {
if (isAutolaunching() || maybeAutolaunchActivity()) {
return;
}
if (doPostProcessing) {
if (mMultiProfilePagerAdapter.getCurrentUserHandle().getIdentifier()
== UserHandle.myUserId()) {
setHeader();
}
resetButtonBar();
onListRebuilt(listAdapter);
}
resetButtonBar();
onListRebuilt(listAdapter);
}
protected void onListRebuilt(ResolverListAdapter listAdapter) {
int count = listAdapter.getUnfilteredCount();
if (count == 1 && listAdapter.getOtherProfile() == null) {
// Only one target, so we're a candidate to auto-launch!
final TargetInfo target = listAdapter.targetInfoForPosition(0, false);
if (shouldAutoLaunchSingleChoice(target)) {
safelyStartActivity(target);
finish();
}
}
final ItemClickListener listener = new ItemClickListener();
setupAdapterListView((ListView) mMultiProfilePagerAdapter.getActiveAdapterView(), listener);
}
@@ -1132,6 +1155,11 @@ public class ResolverActivity extends Activity implements
}
private void safelyStartActivityInternal(TargetInfo cti) {
mPersonalPackageMonitor.unregister();
if (mWorkPackageMonitor != null) {
mWorkPackageMonitor.unregister();
}
mRegistered = false;
// If needed, show that intent is forwarded
// from managed profile to owner or other way around.
if (mProfileSwitchMessageId != -1) {
@@ -1247,7 +1275,11 @@ public class ResolverActivity extends Activity implements
throw new IllegalStateException("mMultiProfilePagerAdapter.getCurrentListAdapter() "
+ "cannot be null.");
}
boolean rebuildCompleted = mMultiProfilePagerAdapter.getActiveListAdapter().rebuildList();
boolean rebuildCompleted = mMultiProfilePagerAdapter.rebuildActiveTab(true);
// We partially rebuild the inactive adapter to determine if we should auto launch
mMultiProfilePagerAdapter.rebuildInactiveTab(false);
if (useLayoutWithDefault()) {
mLayoutId = R.layout.resolver_list_with_default;
} else {
@@ -1274,25 +1306,12 @@ public class ResolverActivity extends Activity implements
* @return <code>true</code> if the activity is finishing and creation should halt.
*/
final boolean postRebuildListInternal(boolean rebuildCompleted) {
int count = mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredCount();
// We only rebuild asynchronously when we have multiple elements to sort. In the case where
// we're already done, we can check if we should auto-launch immediately.
if (rebuildCompleted) {
if (count == 1
&& mMultiProfilePagerAdapter.getActiveListAdapter().getOtherProfile() == null) {
// Only one target, so we're a candidate to auto-launch!
final TargetInfo target = mMultiProfilePagerAdapter.getActiveListAdapter()
.targetInfoForPosition(0, false);
if (shouldAutoLaunchSingleChoice(target)) {
safelyStartActivity(target);
mPackageMonitor.unregister();
mRegistered = false;
finish();
return true;
}
}
if (rebuildCompleted && maybeAutolaunchActivity()) {
return true;
}
setupViewVisibilities();
@@ -1304,6 +1323,129 @@ public class ResolverActivity extends Activity implements
return false;
}
private int isPermissionGranted(String permission, int uid) {
return ActivityManager.checkComponentPermission(permission, uid,
/* owningUid= */-1, /* exported= */ true);
}
/**
* @return {@code true} if a resolved target is autolaunched, otherwise {@code false}
*/
private boolean maybeAutolaunchActivity() {
int numberOfProfiles = mMultiProfilePagerAdapter.getItemCount();
if (numberOfProfiles == 1 && maybeAutolaunchIfSingleTarget()) {
return true;
} else if (numberOfProfiles == 2 && maybeAutolaunchIfCrossProfileSupported()) {
// note that autolaunching when we have 2 profiles, 1 resolved target on the active
// tab and 0 resolved targets on the inactive tab, is already handled before launching
// ResolverActivity
return true;
}
return false;
}
private boolean maybeAutolaunchIfSingleTarget() {
int count = mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredCount();
if (count != 1) {
return false;
}
// Only one target, so we're a candidate to auto-launch!
final TargetInfo target = mMultiProfilePagerAdapter.getActiveListAdapter()
.targetInfoForPosition(0, false);
if (shouldAutoLaunchSingleChoice(target)) {
safelyStartActivity(target);
finish();
return true;
}
return false;
}
/**
* When we have a personal and a work profile, we auto launch in the following scenario:
* - There is 1 resolved target on each profile
* - That target is the same app on both profiles
* - The target app has permission to communicate cross profiles
* - The target app has declared it supports cross-profile communication via manifest metadata
*/
private boolean maybeAutolaunchIfCrossProfileSupported() {
int count = mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredCount();
if (count != 1) {
return false;
}
ResolverListAdapter inactiveListAdapter =
mMultiProfilePagerAdapter.getInactiveListAdapter();
if (inactiveListAdapter.getUnfilteredCount() != 1) {
return false;
}
TargetInfo activeProfileTarget = mMultiProfilePagerAdapter.getActiveListAdapter()
.targetInfoForPosition(0, false);
TargetInfo inactiveProfileTarget = inactiveListAdapter.targetInfoForPosition(0, false);
if (!Objects.equals(activeProfileTarget.getResolvedComponentName(),
inactiveProfileTarget.getResolvedComponentName())) {
return false;
}
if (!shouldAutoLaunchSingleChoice(activeProfileTarget)) {
return false;
}
String packageName = activeProfileTarget.getResolvedComponentName().getPackageName();
if (!canAppInteractCrossProfiles(packageName)) {
return false;
}
safelyStartActivity(activeProfileTarget);
finish();
return true;
}
/**
* Returns whether the package has the necessary permissions to interact across profiles on
* behalf of a given user.
*
* <p>This means meeting the following condition:
* <ul>
* <li>The app's {@link ApplicationInfo#crossProfile} flag must be true, and at least
* one of the following conditions must be fulfilled</li>
* <li>{@code Manifest.permission.INTERACT_ACROSS_USERS_FULL} granted.</li>
* <li>{@code Manifest.permission.INTERACT_ACROSS_USERS} granted.</li>
* <li>{@code Manifest.permission.INTERACT_ACROSS_PROFILES} granted, or the corresponding
* AppOps {@code android:interact_across_profiles} is set to "allow".</li>
* </ul>
*
*/
private boolean canAppInteractCrossProfiles(String packageName) {
ApplicationInfo applicationInfo;
try {
applicationInfo = getPackageManager().getApplicationInfo(packageName, 0);
} catch (NameNotFoundException e) {
Log.e(TAG, "Package " + packageName + " does not exist on current user.");
return false;
}
if (!applicationInfo.crossProfile) {
return false;
}
int packageUid = applicationInfo.uid;
if (isPermissionGranted(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
packageUid) == PackageManager.PERMISSION_GRANTED) {
return true;
}
if (isPermissionGranted(android.Manifest.permission.INTERACT_ACROSS_USERS, packageUid)
== PackageManager.PERMISSION_GRANTED) {
return true;
}
if (PermissionChecker.checkPermissionForPreflight(this, INTERACT_ACROSS_PROFILES,
PID_UNKNOWN, packageUid, packageName) == PackageManager.PERMISSION_GRANTED) {
return true;
}
return false;
}
private boolean isAutolaunching() {
return !mRegistered && isFinishing();
}
private void setupProfileTabs() {
TabHost tabHost = findViewById(R.id.profile_tabhost);
tabHost.setup();
@@ -1499,12 +1641,20 @@ public class ResolverActivity extends Activity implements
}
@Override // ResolverListCommunicator
public void onHandlePackagesChanged() {
ResolverListAdapter activeListAdapter = mMultiProfilePagerAdapter.getActiveListAdapter();
activeListAdapter.rebuildList();
if (activeListAdapter.getCount() == 0) {
// We no longer have any items... just finish the activity.
finish();
public void onHandlePackagesChanged(ResolverListAdapter listAdapter) {
if (listAdapter == mMultiProfilePagerAdapter.getActiveListAdapter()) {
boolean listRebuilt = mMultiProfilePagerAdapter.rebuildActiveTab(true);
if (listRebuilt) {
ResolverListAdapter activeListAdapter =
mMultiProfilePagerAdapter.getActiveListAdapter();
activeListAdapter.notifyDataSetChanged();
if (activeListAdapter.getCount() == 0) {
// We no longer have any items... just finish the activity.
finish();
}
}
} else {
mMultiProfilePagerAdapter.clearInactiveProfileCache();
}
}

View File

@@ -113,7 +113,7 @@ public class ResolverListAdapter extends BaseAdapter {
}
public void handlePackagesChanged() {
mResolverListCommunicator.onHandlePackagesChanged();
mResolverListCommunicator.onHandlePackagesChanged(this);
}
public void setPlaceholderCount(int count) {
@@ -176,9 +176,14 @@ public class ResolverListAdapter extends BaseAdapter {
* Rebuild the list of resolvers. In some cases some parts will need some asynchronous work
* to complete.
*
* The {@code doPostProcessing } parameter is used to specify whether to update the UI and
* load additional targets (e.g. direct share) after the list has been rebuilt. This is used
* in the case where we want to load the inactive profile's resolved apps to know the
* number of targets.
*
* @return Whether or not the list building is completed.
*/
protected boolean rebuildList() {
protected boolean rebuildList(boolean doPostProcessing) {
List<ResolvedComponentInfo> currentResolveList = null;
// Clear the value of mOtherProfile from previous call.
mOtherProfile = null;
@@ -186,6 +191,7 @@ public class ResolverListAdapter extends BaseAdapter {
mLastChosenPosition = -1;
mAllTargetsAreBrowsers = false;
mDisplayList.clear();
if (mBaseResolveList != null) {
currentResolveList = mUnfilteredResolveList = new ArrayList<>();
mResolverListController.addResolveListDedupe(currentResolveList,
@@ -198,7 +204,7 @@ public class ResolverListAdapter extends BaseAdapter {
mResolverListCommunicator.shouldGetActivityMetadata(),
mIntents);
if (currentResolveList == null) {
processSortedList(currentResolveList);
processSortedList(currentResolveList, doPostProcessing);
return true;
}
List<ResolvedComponentInfo> originalList =
@@ -256,22 +262,22 @@ public class ResolverListAdapter extends BaseAdapter {
--placeholderCount;
}
setPlaceholderCount(placeholderCount);
createSortingTask().execute(currentResolveList);
postListReadyRunnable();
createSortingTask(doPostProcessing).execute(currentResolveList);
postListReadyRunnable(doPostProcessing);
return false;
} else {
processSortedList(currentResolveList);
processSortedList(currentResolveList, doPostProcessing);
return true;
}
} else {
processSortedList(currentResolveList);
processSortedList(currentResolveList, doPostProcessing);
return true;
}
}
AsyncTask<List<ResolvedComponentInfo>,
Void,
List<ResolvedComponentInfo>> createSortingTask() {
List<ResolvedComponentInfo>> createSortingTask(boolean doPostProcessing) {
return new AsyncTask<List<ResolvedComponentInfo>,
Void,
List<ResolvedComponentInfo>>() {
@@ -283,15 +289,17 @@ public class ResolverListAdapter extends BaseAdapter {
}
@Override
protected void onPostExecute(List<ResolvedComponentInfo> sortedComponents) {
processSortedList(sortedComponents);
mResolverListCommunicator.updateProfileViewButton();
processSortedList(sortedComponents, doPostProcessing);
notifyDataSetChanged();
if (doPostProcessing) {
mResolverListCommunicator.updateProfileViewButton();
}
}
};
}
protected void processSortedList(List<ResolvedComponentInfo> sortedComponents) {
protected void processSortedList(List<ResolvedComponentInfo> sortedComponents,
boolean doPostProcessing) {
int n;
if (sortedComponents != null && (n = sortedComponents.size()) != 0) {
mAllTargetsAreBrowsers = mUseLayoutForBrowsables;
@@ -343,20 +351,23 @@ public class ResolverListAdapter extends BaseAdapter {
}
mResolverListCommunicator.sendVoiceChoicesIfNeeded();
postListReadyRunnable();
postListReadyRunnable(doPostProcessing);
}
/**
* Some necessary methods for creating the list are initiated in onCreate and will also
* determine the layout known. We therefore can't update the UI inline and post to the
* handler thread to update after the current task is finished.
* @param doPostProcessing Whether to update the UI and load additional direct share targets
* after the list has been rebuilt
*/
void postListReadyRunnable() {
void postListReadyRunnable(boolean doPostProcessing) {
if (mPostListReadyRunnable == null) {
mPostListReadyRunnable = new Runnable() {
@Override
public void run() {
mResolverListCommunicator.onPostListReady(ResolverListAdapter.this);
mResolverListCommunicator.onPostListReady(ResolverListAdapter.this,
doPostProcessing);
mPostListReadyRunnable = null;
}
};
@@ -590,6 +601,12 @@ public class ResolverListAdapter extends BaseAdapter {
return mResolverListController.getUserHandle();
}
protected List<ResolvedComponentInfo> getResolversForUser(UserHandle userHandle) {
return mResolverListController.getResolversForIntentAsUser(true,
mResolverListCommunicator.shouldGetActivityMetadata(),
mIntents, userHandle);
}
/**
* Necessary methods to communicate between {@link ResolverListAdapter}
* and {@link ResolverActivity}.
@@ -600,7 +617,7 @@ public class ResolverListAdapter extends BaseAdapter {
Intent getReplacementIntent(ActivityInfo activityInfo, Intent defIntent);
void onPostListReady(ResolverListAdapter listAdapter);
void onPostListReady(ResolverListAdapter listAdapter, boolean updateUi);
void sendVoiceChoicesIfNeeded();
@@ -612,7 +629,7 @@ public class ResolverListAdapter extends BaseAdapter {
Intent getTargetIntent();
void onHandlePackagesChanged();
void onHandlePackagesChanged(ResolverListAdapter listAdapter);
}
static class ViewHolder {

View File

@@ -111,6 +111,15 @@ public class ResolverListController {
boolean shouldGetResolvedFilter,
boolean shouldGetActivityMetadata,
List<Intent> intents) {
return getResolversForIntentAsUser(shouldGetResolvedFilter, shouldGetActivityMetadata,
intents, mUserHandle);
}
public List<ResolverActivity.ResolvedComponentInfo> getResolversForIntentAsUser(
boolean shouldGetResolvedFilter,
boolean shouldGetActivityMetadata,
List<Intent> intents,
UserHandle userHandle) {
List<ResolverActivity.ResolvedComponentInfo> resolvedComponents = null;
for (int i = 0, N = intents.size(); i < N; i++) {
final Intent intent = intents.get(i);
@@ -122,7 +131,7 @@ public class ResolverListController {
flags |= PackageManager.MATCH_INSTANT;
}
final List<ResolveInfo> infos = mpm.queryIntentActivitiesAsUser(intent, flags,
mUserHandle);
userHandle);
if (infos != null) {
if (resolvedComponents == null) {
resolvedComponents = new ArrayList<>();

View File

@@ -36,8 +36,10 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
private final ResolverProfileDescriptor[] mItems;
ResolverMultiProfilePagerAdapter(Context context,
ResolverListAdapter adapter) {
super(context, /* currentPage */ 0);
ResolverListAdapter adapter,
UserHandle personalProfileUserHandle,
UserHandle workProfileUserHandle) {
super(context, /* currentPage */ 0, personalProfileUserHandle, workProfileUserHandle);
mItems = new ResolverProfileDescriptor[] {
createProfileDescriptor(adapter)
};
@@ -46,8 +48,11 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
ResolverMultiProfilePagerAdapter(Context context,
ResolverListAdapter personalAdapter,
ResolverListAdapter workAdapter,
@Profile int defaultProfile) {
super(context, /* currentPage */ defaultProfile);
@Profile int defaultProfile,
UserHandle personalProfileUserHandle,
UserHandle workProfileUserHandle) {
super(context, /* currentPage */ defaultProfile, personalProfileUserHandle,
workProfileUserHandle);
mItems = new ResolverProfileDescriptor[] {
createProfileDescriptor(personalAdapter),
createProfileDescriptor(workAdapter)
@@ -115,6 +120,17 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
return getAdapterForIndex(1 - getCurrentPage());
}
@Override
public ResolverListAdapter getPersonalListAdapter() {
return getAdapterForIndex(PROFILE_PERSONAL);
}
@Override
@Nullable
public ResolverListAdapter getWorkListAdapter() {
return getAdapterForIndex(PROFILE_WORK);
}
@Override
ResolverListAdapter getCurrentRootAdapter() {
return getActiveListAdapter();

View File

@@ -27,12 +27,14 @@ import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static com.android.internal.app.MatcherUtils.first;
import static com.android.internal.app.ResolverDataProvider.createPackageManagerMockedInfo;
import static com.android.internal.app.ResolverDataProvider.createResolvedComponentInfoWithOtherId;
import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import android.content.Intent;
@@ -465,14 +467,33 @@ public class ResolverActivityTest {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
List<ResolvedComponentInfo> personalResolvedComponentInfos =
createResolvedComponentsForTest(3);
createResolvedComponentsForTestWithOtherProfile(3);
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
when(sOverrides.resolverListController.getResolversForIntentAsUser(
Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(personalResolvedComponentInfos);
Mockito.anyBoolean(),
Mockito.isA(List.class),
eq(UserHandle.SYSTEM))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
when(sOverrides.resolverListController.getResolversForIntentAsUser(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class),
eq(sOverrides.workProfileUserHandle))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class),
eq(sOverrides.workProfileUserHandle))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class),
eq(UserHandle.SYSTEM))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
Intent sendIntent = createSendImageIntent();
markWorkProfileUserAvailable();
@@ -502,9 +523,9 @@ public class ResolverActivityTest {
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
waitForIdle();
onView(withText(R.string.resolver_work_tab))
.perform(click());
waitForIdle();
assertThat(activity.getWorkListAdapter().getCount(), is(4));
}
@@ -553,10 +574,35 @@ public class ResolverActivityTest {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
markWorkProfileUserAvailable();
List<ResolvedComponentInfo> personalResolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(1);
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
when(sOverrides.resolverListController.getResolversForIntentAsUser(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class),
eq(UserHandle.SYSTEM))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
when(sOverrides.resolverListController.getResolversForIntentAsUser(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class),
eq(sOverrides.workProfileUserHandle))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class),
eq(sOverrides.workProfileUserHandle))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class),
eq(UserHandle.SYSTEM))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
Intent sendIntent = createSendImageIntent();
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);