diff options
14 files changed, 1007 insertions, 252 deletions
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java new file mode 100644 index 000000000000..2fd5bfd71656 --- /dev/null +++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.app; +import android.annotation.IntDef; +import android.content.Context; +import android.os.UserHandle; +import android.view.View; +import android.view.ViewGroup; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.widget.PagerAdapter; + +import com.android.internal.util.Preconditions; +import com.android.internal.widget.ViewPager; + +/** + * Skeletal {@link PagerAdapter} implementation of a work or personal profile page for + * intent resolution (including share sheet). + */ +public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { + + static final int PROFILE_PERSONAL = 0; + static final int PROFILE_WORK = 1; + @IntDef({PROFILE_PERSONAL, PROFILE_WORK}) + @interface Profile {} + + private final Context mContext; + private int mCurrentPage; + + AbstractMultiProfilePagerAdapter(Context context, int currentPage) { + mContext = Preconditions.checkNotNull(context); + mCurrentPage = currentPage; + } + + Context getContext() { + return mContext; + } + + /** + * Sets this instance of this class as {@link ViewPager}'s {@link PagerAdapter} and sets + * an {@link ViewPager.OnPageChangeListener} where it keeps track of the currently displayed + * page and rebuilds the list. + */ + void setupViewPager(ViewPager viewPager) { + viewPager.setCurrentItem(mCurrentPage); + viewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { + @Override + public void onPageSelected(int position) { + mCurrentPage = position; + getCurrentListAdapter().rebuildList(); + } + }); + viewPager.setAdapter(this); + } + + @Override + public ViewGroup instantiateItem(ViewGroup container, int position) { + final ProfileDescriptor profileDescriptor = getItem(position); + setupListAdapter(position); + container.addView(profileDescriptor.rootView); + return profileDescriptor.rootView; + } + + @Override + public void destroyItem(ViewGroup container, int position, Object view) { + container.removeView((View) view); + } + + @Override + public int getCount() { + return getItemCount(); + } + + protected int getCurrentPage() { + return mCurrentPage; + } + + UserHandle getCurrentUserHandle() { + return getCurrentListAdapter().mResolverListController.getUserHandle(); + } + + @Override + public boolean isViewFromObject(View view, Object object) { + return view == object; + } + + @Override + public CharSequence getPageTitle(int position) { + return null; + } + + /** + * Returns the {@link ProfileDescriptor} relevant to the given <code>pageIndex</code>. + * <ul> + * <li>For a device with only one user, <code>pageIndex</code> value of + * <code>0</code> would return the personal profile {@link ProfileDescriptor}.</li> + * <li>For a device with a work profile, <code>pageIndex</code> value of <code>0</code> would + * return the personal profile {@link ProfileDescriptor}, and <code>pageIndex</code> value of + * <code>1</code> would return the work profile {@link ProfileDescriptor}.</li> + * </ul> + */ + abstract ProfileDescriptor getItem(int pageIndex); + + /** + * Returns the number of {@link ProfileDescriptor} objects. + * <p>For a normal consumer device with only one user returns <code>1</code>. + * <p>For a device with a work profile returns <code>2</code>. + */ + abstract int getItemCount(); + + /** + * Responsible for assigning an adapter to the list view for the relevant page, specified by + * <code>pageIndex</code>, and other list view-related initialization procedures. + */ + abstract void setupListAdapter(int pageIndex); + + /** + * Returns the adapter of the list view for the relevant page specified by + * <code>pageIndex</code>. + * <p>This method is meant to be implemented with an implementation-specific return type + * depending on the adapter type. + */ + abstract Object getAdapterForIndex(int pageIndex); + + @VisibleForTesting + public abstract ResolverListAdapter getCurrentListAdapter(); + + abstract Object getCurrentRootAdapter(); + + abstract ViewGroup getCurrentAdapterView(); + + protected class ProfileDescriptor { + final ViewGroup rootView; + ProfileDescriptor(ViewGroup rootView) { + this.rootView = rootView; + } + } +}
\ No newline at end of file diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 183e9a6faf09..1af39265dbcb 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -98,6 +98,7 @@ import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; +import android.view.WindowInsets; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.widget.ImageView; @@ -173,7 +174,6 @@ public class ChooserActivity extends ResolverActivity implements @VisibleForTesting public static final int LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS = 250; - private static final int SINGLE_CELL_SPAN_SIZE = 1; private boolean mIsAppPredictorComponentAvailable; private AppPredictor mAppPredictor; @@ -229,9 +229,6 @@ public class ChooserActivity extends ResolverActivity implements private long mQueriedTargetServicesTimeMs; private long mQueriedSharingShortcutsTimeMs; - private RecyclerView mRecyclerView; - private ChooserListAdapter mChooserListAdapter; - private ChooserGridAdapter mChooserGridAdapter; private int mChooserRowServiceSpacing; private int mCurrAvailableWidth = 0; @@ -265,6 +262,9 @@ public class ChooserActivity extends ResolverActivity implements private ContentPreviewCoordinator mPreviewCoord; + @VisibleForTesting + protected ChooserMultiProfilePagerAdapter mChooserMultiProfilePagerAdapter; + private class ContentPreviewCoordinator { private static final int IMAGE_FADE_IN_MILLIS = 150; private static final int IMAGE_LOAD_TIMEOUT = 1; @@ -362,8 +362,8 @@ public class ChooserActivity extends ResolverActivity implements Log.i(TAG, "Hiding image preview area. Timed out waiting for preview to load" + " within " + mImageLoadTimeoutMillis + "ms."); collapseParentView(); - if (mChooserGridAdapter != null) { - mChooserGridAdapter.hideContentPreview(); + if (mChooserMultiProfilePagerAdapter.getCurrentRootAdapter() != null) { + mChooserMultiProfilePagerAdapter.getCurrentRootAdapter().hideContentPreview(); } mHideParentOnFail = false; } @@ -431,13 +431,14 @@ public class ChooserActivity extends ResolverActivity implements logDirectShareTargetReceived( MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_CHOOSER_SERVICE); sendVoiceChoicesIfNeeded(); - mChooserListAdapter.completeServiceTargetLoading(); + mChooserMultiProfilePagerAdapter.getCurrentListAdapter() + .completeServiceTargetLoading(); } } @Override public void handleMessage(Message msg) { - if (mChooserListAdapter == null || isDestroyed()) { + if (mChooserMultiProfilePagerAdapter.getCurrentListAdapter() == null || isDestroyed()) { return; } @@ -452,8 +453,10 @@ public class ChooserActivity extends ResolverActivity implements break; } if (sri.resultTargets != null) { - mChooserListAdapter.addServiceResults(sri.originalTarget, - sri.resultTargets, TARGET_TYPE_CHOOSER_TARGET); + // TODO(arangelov): Instead of using getCurrentListAdapter(), pass the + // profileId as part of the message. + mChooserMultiProfilePagerAdapter.getCurrentListAdapter().addServiceResults( + sri.originalTarget, sri.resultTargets, TARGET_TYPE_CHOOSER_TARGET); } unbindService(sri.connection); sri.connection.destroy(); @@ -476,15 +479,15 @@ public class ChooserActivity extends ResolverActivity implements Log.d(TAG, "LIST_VIEW_UPDATE_MESSAGE; "); } - mChooserListAdapter.refreshListView(); + mChooserMultiProfilePagerAdapter.getCurrentListAdapter().refreshListView(); break; case SHORTCUT_MANAGER_SHARE_TARGET_RESULT: if (DEBUG) Log.d(TAG, "SHORTCUT_MANAGER_SHARE_TARGET_RESULT"); final ServiceResultInfo resultInfo = (ServiceResultInfo) msg.obj; if (resultInfo.resultTargets != null) { - mChooserListAdapter.addServiceResults(resultInfo.originalTarget, - resultInfo.resultTargets, msg.arg1); + mChooserMultiProfilePagerAdapter.getCurrentListAdapter().addServiceResults( + resultInfo.originalTarget, resultInfo.resultTargets, msg.arg1); } break; @@ -504,6 +507,7 @@ public class ChooserActivity extends ResolverActivity implements protected void onCreate(Bundle savedInstanceState) { final long intentReceivedTime = System.currentTimeMillis(); // This is the only place this value is being set. Effectively final. + //TODO(arangelov) - should there be a mIsAppPredictorComponentAvailable flag for work tab? mIsAppPredictorComponentAvailable = isAppPredictionServiceAvailable(); mIsSuccessfullySelected = false; @@ -638,19 +642,24 @@ public class ChooserActivity extends ResolverActivity implements if (appPredictor != null) { mDirectShareAppTargetCache = new HashMap<>(); mAppPredictorCallback = resultList -> { + //TODO(arangelov) Take care of edge case when callback called after swiping tabs if (isFinishing() || isDestroyed()) { return; } - if (mChooserListAdapter.getCount() == 0) { + if (mChooserMultiProfilePagerAdapter.getCurrentListAdapter().getCount() == 0) { return; } if (resultList.isEmpty()) { // APS may be disabled, so try querying targets ourselves. - queryDirectShareTargets(mChooserListAdapter, true); + //TODO(arangelov) queryDirectShareTargets indirectly uses mIntents. + // Investigate implications for work tab. + queryDirectShareTargets( + mChooserMultiProfilePagerAdapter.getCurrentListAdapter(), true); return; } final List<DisplayResolveInfo> driList = - getDisplayResolveInfos(mChooserListAdapter); + getDisplayResolveInfos( + mChooserMultiProfilePagerAdapter.getCurrentListAdapter()); final List<ShortcutManager.ShareShortcutInfo> shareShortcutInfos = new ArrayList<>(); for (AppTarget appTarget : resultList) { @@ -684,21 +693,22 @@ public class ChooserActivity extends ResolverActivity implements final float chooserHeaderScrollElevation = getResources().getDimensionPixelSize(R.dimen.chooser_header_scroll_elevation); - mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { - public void onScrollStateChanged(RecyclerView view, int scrollState) { - } - - public void onScrolled(RecyclerView view, int dx, int dy) { - if (view.getChildCount() > 0) { - View child = view.getLayoutManager().findViewByPosition(0); - if (child == null || child.getTop() < 0) { - chooserHeader.setElevation(chooserHeaderScrollElevation); - return; + mChooserMultiProfilePagerAdapter.getCurrentAdapterView().addOnScrollListener( + new RecyclerView.OnScrollListener() { + public void onScrollStateChanged(RecyclerView view, int scrollState) { } - } - chooserHeader.setElevation(defaultElevation); - } + public void onScrolled(RecyclerView view, int dx, int dy) { + if (view.getChildCount() > 0) { + View child = view.getLayoutManager().findViewByPosition(0); + if (child == null || child.getTop() < 0) { + chooserHeader.setElevation(chooserHeaderScrollElevation); + return; + } + } + + chooserHeader.setElevation(defaultElevation); + } }); mResolverDrawerLayout.setOnCollapsedChangedListener( @@ -738,6 +748,71 @@ public class ChooserActivity extends ResolverActivity implements return context.getSharedPreferences(prefsFile, MODE_PRIVATE); } + @Override + protected AbstractMultiProfilePagerAdapter createMultiProfilePagerAdapter( + Intent[] initialIntents, + List<ResolveInfo> rList, + boolean filterLastUsed) { + if (hasWorkProfile() && ENABLE_TABBED_VIEW) { + mChooserMultiProfilePagerAdapter = createChooserMultiProfilePagerAdapterForTwoProfiles( + initialIntents, rList, filterLastUsed); + } else { + mChooserMultiProfilePagerAdapter = createChooserMultiProfilePagerAdapterForOneProfile( + initialIntents, rList, filterLastUsed); + } + return mChooserMultiProfilePagerAdapter; + } + + private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForOneProfile( + Intent[] initialIntents, + List<ResolveInfo> rList, + boolean filterLastUsed) { + ChooserGridAdapter adapter = createChooserGridAdapter( + /* context */ this, + /* payloadIntents */ mIntents, + initialIntents, + rList, + filterLastUsed, + mUseLayoutForBrowsables, + /* userHandle */ UserHandle.of(UserHandle.myUserId())); + return new ChooserMultiProfilePagerAdapter( + /* context */ this, + adapter); + } + + private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForTwoProfiles( + Intent[] initialIntents, + List<ResolveInfo> rList, + boolean filterLastUsed) { + ChooserGridAdapter personalAdapter = createChooserGridAdapter( + /* context */ this, + /* payloadIntents */ mIntents, + initialIntents, + rList, + filterLastUsed, + mUseLayoutForBrowsables, + /* userHandle */ getPersonalProfileUserHandle()); + ChooserGridAdapter workAdapter = createChooserGridAdapter( + /* context */ this, + /* payloadIntents */ mIntents, + initialIntents, + rList, + filterLastUsed, + mUseLayoutForBrowsables, + /* userHandle */ getWorkProfileUserHandle()); + return new ChooserMultiProfilePagerAdapter( + /* context */ this, + personalAdapter, + workAdapter, + /* defaultProfile */ getCurrentProfile()); + } + + @Override + protected boolean postRebuildList(boolean rebuildCompleted) { + mChooserMultiProfilePagerAdapter.getCurrentRootAdapter().maybeLogActionShareWithPreview(); + return postRebuildListInternal(rebuildCompleted); + } + /** * Returns true if app prediction service is defined and the component exists on device. */ @@ -776,7 +851,7 @@ public class ChooserActivity extends ResolverActivity implements * set up) */ protected boolean isWorkProfile() { - return ((UserManager) getSystemService(Context.USER_SERVICE)) + return getSystemService(UserManager.class) .getUserInfo(UserHandle.myUserId()).isManagedProfile(); } @@ -785,7 +860,9 @@ public class ChooserActivity extends ResolverActivity implements return new PackageMonitor() { @Override public void onSomePackagesChanged() { - mAdapter.handlePackagesChanged(); + // TODO(arangelov): Dispatch this to all adapters when we have the helper methods + // in a follow-up CL + mChooserMultiProfilePagerAdapter.getCurrentListAdapter().handlePackagesChanged(); updateProfileViewButton(); } }; @@ -1239,36 +1316,14 @@ public class ChooserActivity extends ResolverActivity implements } @Override - public void onPrepareAdapterView(ResolverListAdapter adapter, boolean isVisible) { - mRecyclerView = findViewById(R.id.resolver_list); - if (!isVisible) { - mRecyclerView.setVisibility(View.GONE); - return; - } - mRecyclerView.setVisibility(View.VISIBLE); + public void onPrepareAdapterView(ResolverListAdapter adapter) { + mChooserMultiProfilePagerAdapter.getCurrentAdapterView().setVisibility(View.VISIBLE); if (mCallerChooserTargets != null && mCallerChooserTargets.length > 0) { - mChooserListAdapter.addServiceResults(null, Lists.newArrayList(mCallerChooserTargets), + mChooserMultiProfilePagerAdapter.getCurrentListAdapter().addServiceResults( + /* origTarget */ null, + Lists.newArrayList(mCallerChooserTargets), TARGET_TYPE_DEFAULT); } - mChooserGridAdapter = new ChooserGridAdapter(mChooserListAdapter); - GridLayoutManager glm = (GridLayoutManager) mRecyclerView.getLayoutManager(); - glm.setSpanCount(mChooserGridAdapter.getMaxTargetsPerRow()); - glm.setSpanSizeLookup( - new GridLayoutManager.SpanSizeLookup() { - @Override - public int getSpanSize(int position) { - return mChooserGridAdapter.getItemViewType(position) - == ChooserGridAdapter.VIEW_TYPE_NORMAL - ? SINGLE_CELL_SPAN_SIZE - : glm.getSpanCount(); - } - }); - } - - @Override - protected boolean postRebuildList(boolean rebuildCompleted) { - mChooserListAdapter = (ChooserListAdapter) mAdapter; - return postRebuildListInternal(rebuildCompleted); } @Override @@ -1348,7 +1403,10 @@ public class ChooserActivity extends ResolverActivity implements @Override public void startSelected(int which, boolean always, boolean filtered) { - TargetInfo targetInfo = mChooserListAdapter.targetInfoForPosition(which, filtered); + ChooserListAdapter currentListAdapter = + mChooserMultiProfilePagerAdapter.getCurrentListAdapter(); + TargetInfo targetInfo = currentListAdapter + .targetInfoForPosition(which, filtered); if (targetInfo != null && targetInfo instanceof NotSelectableTargetInfo) { return; } @@ -1356,7 +1414,7 @@ public class ChooserActivity extends ResolverActivity implements final long selectionCost = System.currentTimeMillis() - mChooserShownTime; super.startSelected(which, always, filtered); - if (mChooserListAdapter.getCount() > 0) { + if (currentListAdapter.getCount() > 0) { // Log the index of which type of target the user picked. // Lower values mean the ranking was better. int cat = 0; @@ -1364,13 +1422,12 @@ public class ChooserActivity extends ResolverActivity implements int directTargetAlsoRanked = -1; int numCallerProvided = 0; HashedStringCache.HashResult directTargetHashed = null; - switch (mChooserListAdapter.getPositionTargetType(which)) { + switch (currentListAdapter.getPositionTargetType(which)) { case ChooserListAdapter.TARGET_SERVICE: cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SERVICE_TARGET; // Log the package name + target name to answer the question if most users // share to mostly the same person or to a bunch of different people. - ChooserTarget target = - mChooserListAdapter.getChooserTargetForValue(value); + ChooserTarget target = currentListAdapter.getChooserTargetForValue(value); directTargetHashed = HashedStringCache.getInstance().hashString( this, TAG, @@ -1386,8 +1443,8 @@ public class ChooserActivity extends ResolverActivity implements case ChooserListAdapter.TARGET_CALLER: case ChooserListAdapter.TARGET_STANDARD: cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_APP_TARGET; - value -= mChooserListAdapter.getSelectableServiceTargetCount(); - numCallerProvided = mChooserListAdapter.getCallerTargetCount(); + value -= currentListAdapter.getSelectableServiceTargetCount(); + numCallerProvided = currentListAdapter.getCallerTargetCount(); break; case ChooserListAdapter.TARGET_STANDARD_AZ: // A-Z targets are unranked standard targets; we use -1 to mark that they @@ -1429,11 +1486,13 @@ public class ChooserActivity extends ResolverActivity implements private int getRankedPosition(SelectableTargetInfo targetInfo) { String targetPackageName = targetInfo.getChooserTarget().getComponentName().getPackageName(); - int maxRankedResults = Math.min(mChooserListAdapter.mDisplayList.size(), - MAX_LOG_RANK_POSITION); + ChooserListAdapter currentListAdapter = + mChooserMultiProfilePagerAdapter.getCurrentListAdapter(); + int maxRankedResults = Math.min(currentListAdapter.mDisplayList.size(), + MAX_LOG_RANK_POSITION); for (int i = 0; i < maxRankedResults; i++) { - if (mChooserListAdapter.mDisplayList.get(i) + if (currentListAdapter.mDisplayList.get(i) .getResolveInfo().activityInfo.packageName.equals(targetPackageName)) { return i; } @@ -1577,6 +1636,7 @@ public class ChooserActivity extends ResolverActivity implements } } // Default to just querying ShortcutManager if AppPredictor not present. + //TODO(arangelov) we're using mIntents here, investicate possible implications on work tab final IntentFilter filter = getTargetIntentFilter(); if (filter == null) { return; @@ -1584,6 +1644,7 @@ public class ChooserActivity extends ResolverActivity implements final List<DisplayResolveInfo> driList = getDisplayResolveInfos(adapter); AsyncTask.execute(() -> { + //TODO(arangelov) use the selected probile tab's ShortcutManager ShortcutManager sm = (ShortcutManager) getSystemService(Context.SHORTCUT_SERVICE); List<ShortcutManager.ShareShortcutInfo> resultList = sm.getShareTargets(filter); sendShareShortcutInfoList(resultList, driList, null); @@ -1779,9 +1840,11 @@ public class ChooserActivity extends ResolverActivity implements final ResolveInfo ri = info.getResolveInfo(); Intent targetIntent = getTargetIntent(); if (ri != null && ri.activityInfo != null && targetIntent != null) { - if (mAdapter != null) { - mAdapter.updateModel(info.getResolvedComponentName()); - mAdapter.updateChooserCounts(ri.activityInfo.packageName, getUserId(), + ChooserListAdapter currentListAdapter = + mChooserMultiProfilePagerAdapter.getCurrentListAdapter(); + if (currentListAdapter != null) { + currentListAdapter.updateModel(info.getResolvedComponentName()); + currentListAdapter.updateChooserCounts(ri.activityInfo.packageName, getUserId(), targetIntent.getAction()); } if (DEBUG) { @@ -1956,8 +2019,9 @@ public class ChooserActivity extends ResolverActivity implements Intent targetIntent, String referrerPackageName, int launchedFromUid, + UserHandle userId, AbstractResolverComparator resolverComparator) { - super(context, pm, targetIntent, referrerPackageName, launchedFromUid, + super(context, pm, targetIntent, referrerPackageName, launchedFromUid, userId, resolverComparator); } @@ -1980,17 +2044,18 @@ public class ChooserActivity extends ResolverActivity implements } } - @Override - public ResolverListAdapter createAdapter(Context context, List<Intent> payloadIntents, - Intent[] initialIntents, List<ResolveInfo> rList, - boolean filterLastUsed, boolean useLayoutForBrowsables) { - return new ChooserListAdapter(context, payloadIntents, - initialIntents, rList, filterLastUsed, createListController(), - useLayoutForBrowsables, this, this); + @VisibleForTesting + public ChooserGridAdapter createChooserGridAdapter(Context context, + List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList, + boolean filterLastUsed, boolean useLayoutForBrowsables, UserHandle userHandle) { + return new ChooserGridAdapter( + new ChooserListAdapter(context, payloadIntents, initialIntents, rList, + filterLastUsed, createListController(userHandle), useLayoutForBrowsables, + this, this)); } @VisibleForTesting - protected ResolverListController createListController() { + protected ResolverListController createListController(UserHandle userHandle) { AppPredictor appPredictor = getAppPredictorForShareActivitesIfEnabled(); AbstractResolverComparator resolverComparator; if (appPredictor != null) { @@ -2008,6 +2073,7 @@ public class ChooserActivity extends ResolverActivity implements getTargetIntent(), getReferrerPackageName(), mLaunchedFromUid, + userHandle, resolverComparator); } @@ -2041,8 +2107,8 @@ public class ChooserActivity extends ResolverActivity implements } private void handleScroll(View view, int x, int y, int oldx, int oldy) { - if (mChooserGridAdapter != null) { - mChooserGridAdapter.handleScroll(view, y, oldy); + if (mChooserMultiProfilePagerAdapter.getCurrentRootAdapter() != null) { + mChooserMultiProfilePagerAdapter.getCurrentRootAdapter().handleScroll(view, y, oldy); } } @@ -2053,37 +2119,42 @@ public class ChooserActivity extends ResolverActivity implements */ private void handleLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { - if (mChooserGridAdapter == null || mRecyclerView == null) { + if (mChooserMultiProfilePagerAdapter == null) { + return; + } + RecyclerView recyclerView = mChooserMultiProfilePagerAdapter.getCurrentAdapterView(); + ChooserGridAdapter gridAdapter = mChooserMultiProfilePagerAdapter.getCurrentRootAdapter(); + if (gridAdapter == null || recyclerView == null) { return; } final int availableWidth = right - left - v.getPaddingLeft() - v.getPaddingRight(); - if (mChooserGridAdapter.consumeLayoutRequest() - || mChooserGridAdapter.calculateChooserTargetWidth(availableWidth) - || mRecyclerView.getAdapter() == null + if (gridAdapter.consumeLayoutRequest() + || gridAdapter.calculateChooserTargetWidth(availableWidth) + || recyclerView.getAdapter() == null || availableWidth != mCurrAvailableWidth) { mCurrAvailableWidth = availableWidth; - mRecyclerView.setAdapter(mChooserGridAdapter); - ((GridLayoutManager) mRecyclerView.getLayoutManager()) - .setSpanCount(mChooserGridAdapter.getMaxTargetsPerRow()); + recyclerView.setAdapter(gridAdapter); + ((GridLayoutManager) recyclerView.getLayoutManager()) + .setSpanCount(gridAdapter.getMaxTargetsPerRow()); getMainThreadHandler().post(() -> { - if (mResolverDrawerLayout == null || mChooserGridAdapter == null) { + if (mResolverDrawerLayout == null || gridAdapter == null) { return; } final int bottomInset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0; int offset = bottomInset; - int rowsToShow = mChooserGridAdapter.getContentPreviewRowCount() - + mChooserGridAdapter.getProfileRowCount() - + mChooserGridAdapter.getServiceTargetRowCount() - + mChooserGridAdapter.getCallerAndRankedTargetRowCount(); + int rowsToShow = gridAdapter.getContentPreviewRowCount() + + gridAdapter.getProfileRowCount() + + gridAdapter.getServiceTargetRowCount() + + gridAdapter.getCallerAndRankedTargetRowCount(); // then this is most likely not a SEND_* action, so check // the app target count if (rowsToShow == 0) { - rowsToShow = mChooserGridAdapter.getRowCount(); + rowsToShow = gridAdapter.getRowCount(); } // still zero? then use a default height and leave, which @@ -2097,9 +2168,9 @@ public class ChooserActivity extends ResolverActivity implements int directShareHeight = 0; rowsToShow = Math.min(4, rowsToShow); - for (int i = 0, childCount = mRecyclerView.getChildCount(); + for (int i = 0, childCount = recyclerView.getChildCount(); i < childCount && rowsToShow > 0; i++) { - View child = mRecyclerView.getChildAt(i); + View child = recyclerView.getChildAt(i); if (((GridLayoutManager.LayoutParams) child.getLayoutParams()).getSpanIndex() != 0) { continue; @@ -2107,9 +2178,9 @@ public class ChooserActivity extends ResolverActivity implements int height = child.getHeight(); offset += height; - if (mChooserGridAdapter.getTargetType( - mRecyclerView.getChildAdapterPosition(child)) - == mChooserListAdapter.TARGET_SERVICE) { + if (gridAdapter.getTargetType( + recyclerView.getChildAdapterPosition(child)) + == ChooserListAdapter.TARGET_SERVICE) { directShareHeight = height; } rowsToShow--; @@ -2145,13 +2216,13 @@ public class ChooserActivity extends ResolverActivity implements @Override // ResolverListCommunicator public void onHandlePackagesChanged() { mServicesRequested.clear(); - mAdapter.notifyDataSetChanged(); + mChooserMultiProfilePagerAdapter.getCurrentListAdapter().notifyDataSetChanged(); super.onHandlePackagesChanged(); } @Override // SelectableTargetInfoCommunicator public ActivityInfoPresentationGetter makePresentationGetter(ActivityInfo info) { - return mChooserListAdapter.makePresentationGetter(info); + return mChooserMultiProfilePagerAdapter.getCurrentListAdapter().makePresentationGetter(info); } @Override // SelectableTargetInfoCommunicator @@ -2161,9 +2232,9 @@ public class ChooserActivity extends ResolverActivity implements @Override // ChooserListCommunicator public int getMaxRankedTargets() { - return mChooserGridAdapter == null + return mChooserMultiProfilePagerAdapter.getCurrentRootAdapter() == null ? ChooserGridAdapter.MAX_TARGETS_PER_ROW_PORTRAIT - : mChooserGridAdapter.getMaxTargetsPerRow(); + : mChooserMultiProfilePagerAdapter.getCurrentRootAdapter().getMaxTargetsPerRow(); } @Override // ChooserListCommunicator @@ -2174,19 +2245,21 @@ public class ChooserActivity extends ResolverActivity implements @Override public void onListRebuilt() { - if (mChooserListAdapter.mDisplayList == null - || mChooserListAdapter.mDisplayList.isEmpty()) { - mChooserListAdapter.notifyDataSetChanged(); + final ChooserListAdapter currentListAdapter = + mChooserMultiProfilePagerAdapter.getCurrentListAdapter(); + if (currentListAdapter.mDisplayList == null + || currentListAdapter.mDisplayList.isEmpty()) { + currentListAdapter.notifyDataSetChanged(); } else { new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... voids) { - mChooserListAdapter.updateAlphabeticalList(); + currentListAdapter.updateAlphabeticalList(); return null; } @Override protected void onPostExecute(Void aVoid) { - mChooserListAdapter.notifyDataSetChanged(); + currentListAdapter.notifyDataSetChanged(); } }.execute(); } @@ -2202,14 +2275,14 @@ public class ChooserActivity extends ResolverActivity implements Log.d(TAG, "querying direct share targets from ShortcutManager"); } - queryDirectShareTargets(mChooserListAdapter, false); + queryDirectShareTargets(currentListAdapter, false); } if (USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS) { if (DEBUG) { Log.d(TAG, "List built querying services"); } - queryTargetServices(mChooserListAdapter); + queryTargetServices(currentListAdapter); } } @@ -2250,8 +2323,8 @@ public class ChooserActivity extends ResolverActivity implements false/* always */, true/* filterd */)); itemView.setOnLongClickListener(v -> { showTargetDetails( - mChooserListAdapter.resolveInfoForPosition( - mListPosition, true/* filtered */)); + mChooserMultiProfilePagerAdapter.getCurrentListAdapter() + .resolveInfoForPosition(mListPosition, /* filtered */ true)); return true; }); } @@ -2259,6 +2332,29 @@ public class ChooserActivity extends ResolverActivity implements } /** + * Intentionally override the {@link ResolverActivity} implementation as we only need that + * implementation for the intent resolver case. + */ + @Override + protected WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { + return insets.consumeSystemWindowInsets(); + } + + /** + * Intentionally override the {@link ResolverActivity} implementation as we only need that + * implementation for the intent resolver case. + */ + @Override + public void onButtonClick(View v) {} + + /** + * Intentionally override the {@link ResolverActivity} implementation as we only need that + * implementation for the intent resolver case. + */ + @Override + protected void resetButtonBar() {} + + /** * Adapter for all types of items and targets in ShareSheet. * Note that ranked sections like Direct Share - while appearing grid-like - are handled on the * row level by this adapter but not on the item level. Individual targets within the row are @@ -2329,12 +2425,11 @@ public class ChooserActivity extends ResolverActivity implements return false; } - private int getMaxTargetsPerRow() { + int getMaxTargetsPerRow() { int maxTargets = MAX_TARGETS_PER_ROW_PORTRAIT; if (shouldDisplayLandscape(getResources().getConfiguration().orientation)) { maxTargets = MAX_TARGETS_PER_ROW_LANDSCAPE; } - return maxTargets; } @@ -2477,10 +2572,6 @@ public class ChooserActivity extends ResolverActivity implements private ViewGroup createContentPreviewView(ViewGroup parent) { Intent targetIntent = getTargetIntent(); int previewType = findPreferredContentPreview(targetIntent, getContentResolver()); - - getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_SHARE_WITH_PREVIEW) - .setSubtype(previewType)); - return displayContentPreview(previewType, targetIntent, mLayoutInflater, parent); } @@ -2667,6 +2758,7 @@ public class ChooserActivity extends ResolverActivity implements for (int i = 0; i < columnCount; i++) { final View v = holder.getView(i); + if (start + i <= end) { holder.setViewVisibility(i, View.VISIBLE); holder.setItemIndex(i, start + i); @@ -2712,9 +2804,29 @@ public class ChooserActivity extends ResolverActivity implements && !isInMultiWindowMode(); if (mDirectShareViewHolder != null && canExpandDirectShare) { - mDirectShareViewHolder.handleScroll(mRecyclerView, y, oldy, getMaxTargetsPerRow()); + mDirectShareViewHolder.handleScroll( + mChooserMultiProfilePagerAdapter.getCurrentAdapterView(), y, oldy, + getMaxTargetsPerRow()); } } + + public ChooserListAdapter getListAdapter() { + return mChooserListAdapter; + } + + void maybeLogActionShareWithPreview() { + if (getContentPreviewRowCount() == 0) { + return; + } + Intent targetIntent = getTargetIntent(); + int previewType = findPreferredContentPreview(targetIntent, getContentResolver()); + getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_SHARE_WITH_PREVIEW) + .setSubtype(previewType)); + } + + boolean shouldCellSpan(int position) { + return getItemViewType(position) == VIEW_TYPE_NORMAL; + } } /** @@ -2898,7 +3010,8 @@ public class ChooserActivity extends ResolverActivity implements // only expand if we have more than maxTargetsPerRow, and delay that decision // until they start to scroll - if (mChooserListAdapter.getSelectableServiceTargetCount() <= maxTargetsPerRow) { + if (mChooserMultiProfilePagerAdapter.getCurrentListAdapter() + .getSelectableServiceTargetCount() <= maxTargetsPerRow) { mHideDirectShareExpansion = true; return; } diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java new file mode 100644 index 000000000000..aa8ab2865d92 --- /dev/null +++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.widget.GridLayoutManager; +import com.android.internal.widget.PagerAdapter; +import com.android.internal.widget.RecyclerView; + +/** + * A {@link PagerAdapter} which describes the work and personal profile share sheet screens. + */ +@VisibleForTesting +public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAdapter { + private static final int SINGLE_CELL_SPAN_SIZE = 1; + + private final ChooserProfileDescriptor[] mItems; + + ChooserMultiProfilePagerAdapter(Context context, + ChooserActivity.ChooserGridAdapter adapter) { + super(context, /* currentPage */ 0); + mItems = new ChooserProfileDescriptor[] { + createProfileDescriptor(adapter) + }; + } + + ChooserMultiProfilePagerAdapter(Context context, + ChooserActivity.ChooserGridAdapter personalAdapter, + ChooserActivity.ChooserGridAdapter workAdapter, + @Profile int defaultProfile) { + super(context, /* currentPage */ defaultProfile); + mItems = new ChooserProfileDescriptor[] { + createProfileDescriptor(personalAdapter), + createProfileDescriptor(workAdapter) + }; + } + + private ChooserProfileDescriptor createProfileDescriptor( + ChooserActivity.ChooserGridAdapter adapter) { + final LayoutInflater inflater = LayoutInflater.from(getContext()); + final ViewGroup rootView = + (ViewGroup) inflater.inflate(R.layout.chooser_list_per_profile, null, false); + return new ChooserProfileDescriptor(rootView, adapter); + } + + RecyclerView getListViewForIndex(int index) { + return getItem(index).recyclerView; + } + + @Override + ChooserProfileDescriptor getItem(int pageIndex) { + return mItems[pageIndex]; + } + + @Override + int getItemCount() { + return mItems.length; + } + + @Override + ChooserActivity.ChooserGridAdapter getAdapterForIndex(int pageIndex) { + return mItems[pageIndex].chooserGridAdapter; + } + + @Override + void setupListAdapter(int pageIndex) { + final RecyclerView recyclerView = getItem(pageIndex).recyclerView; + ChooserActivity.ChooserGridAdapter chooserGridAdapter = + getItem(pageIndex).chooserGridAdapter; + recyclerView.setAdapter(chooserGridAdapter); + GridLayoutManager glm = (GridLayoutManager) recyclerView.getLayoutManager(); + glm.setSpanCount(chooserGridAdapter.getMaxTargetsPerRow()); + glm.setSpanSizeLookup( + new GridLayoutManager.SpanSizeLookup() { + @Override + public int getSpanSize(int position) { + return chooserGridAdapter.shouldCellSpan(position) + ? SINGLE_CELL_SPAN_SIZE + : glm.getSpanCount(); + } + }); + } + + @Override + @VisibleForTesting + public ChooserListAdapter getCurrentListAdapter() { + return getAdapterForIndex(getCurrentPage()).getListAdapter(); + } + + @Override + ChooserActivity.ChooserGridAdapter getCurrentRootAdapter() { + return getAdapterForIndex(getCurrentPage()); + } + + @Override + RecyclerView getCurrentAdapterView() { + return getListViewForIndex(getCurrentPage()); + } + + class ChooserProfileDescriptor extends ProfileDescriptor { + private ChooserActivity.ChooserGridAdapter chooserGridAdapter; + private RecyclerView recyclerView; + ChooserProfileDescriptor(ViewGroup rootView, ChooserActivity.ChooserGridAdapter adapter) { + super(rootView); + chooserGridAdapter = adapter; + recyclerView = rootView.findViewById(R.id.resolver_list); + } + } +} diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 3c028d71b40d..9cf5e9f47e7f 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -18,11 +18,15 @@ package com.android.internal.app; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import static com.android.internal.app.AbstractMultiProfilePagerAdapter.PROFILE_PERSONAL; +import static com.android.internal.app.AbstractMultiProfilePagerAdapter.PROFILE_WORK; + import android.annotation.Nullable; import android.annotation.StringRes; import android.annotation.UiThread; import android.annotation.UnsupportedAppUsage; import android.app.Activity; +import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.ActivityThread; import android.app.VoiceInteractor.PickOptionRequest; @@ -72,6 +76,7 @@ import android.widget.Toast; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.AbstractMultiProfilePagerAdapter.Profile; import com.android.internal.app.chooser.DisplayResolveInfo; import com.android.internal.app.chooser.TargetInfo; import com.android.internal.content.PackageMonitor; @@ -99,10 +104,7 @@ public class ResolverActivity extends Activity implements public ResolverActivity() { } - @UnsupportedAppUsage - protected ResolverListAdapter mAdapter; private boolean mSafeForwardingMode; - private AbsListView mAdapterView; private Button mAlwaysButton; private Button mOnceButton; protected View mProfileView; @@ -143,8 +145,16 @@ public class ResolverActivity extends Activity implements private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key"; private static final String OPEN_LINKS_COMPONENT_KEY = "app_link_state"; + /** + * TODO(arangelov): Remove a couple of weeks after work/personal tabs are finalized. + */ + static final boolean ENABLE_TABBED_VIEW = false; + private final PackageMonitor mPackageMonitor = createPackageMonitor(); + @VisibleForTesting + protected AbstractMultiProfilePagerAdapter mMultiProfilePagerAdapter; + // Intent extra for connected audio devices public static final String EXTRA_IS_AUDIO_CAPTURE_DEVICE = "is_audio_capture_device"; @@ -230,7 +240,7 @@ public class ResolverActivity extends Activity implements return new PackageMonitor() { @Override public void onSomePackagesChanged() { - mAdapter.handlePackagesChanged(); + mMultiProfilePagerAdapter.getCurrentListAdapter().handlePackagesChanged(); updateProfileViewButton(); } @@ -325,15 +335,13 @@ public class ResolverActivity extends Activity implements mSupportsAlwaysUseOption = supportsAlwaysUseOption; - // The last argument of createAdapter is whether to do special handling + // The last argument of createResolverListAdapter is whether to do special handling // of the last used choice to highlight it in the list. We need to always // turn this off when running under voice interaction, since it results in // a more complicated UI that the current voice interaction flow is not able // to handle. boolean filterLastUsed = mSupportsAlwaysUseOption && !isVoiceInteraction(); - mAdapter = createAdapter(this, mIntents, initialIntents, rList, - filterLastUsed, mUseLayoutForBrowsables); - + mMultiProfilePagerAdapter = createMultiProfilePagerAdapter(initialIntents, rList, filterLastUsed); if (configureContentView()) { return; } @@ -364,15 +372,96 @@ public class ResolverActivity extends Activity implements } final Set<String> categories = intent.getCategories(); - MetricsLogger.action(this, mAdapter.hasFilteredItem() + MetricsLogger.action(this, mMultiProfilePagerAdapter.getCurrentListAdapter().hasFilteredItem() ? MetricsProto.MetricsEvent.ACTION_SHOW_APP_DISAMBIG_APP_FEATURED : MetricsProto.MetricsEvent.ACTION_SHOW_APP_DISAMBIG_NONE_FEATURED, intent.getAction() + ":" + intent.getType() + ":" + (categories != null ? Arrays.toString(categories.toArray()) : "")); } + protected AbstractMultiProfilePagerAdapter createMultiProfilePagerAdapter( + Intent[] initialIntents, + List<ResolveInfo> rList, + boolean filterLastUsed) { + AbstractMultiProfilePagerAdapter resolverMultiProfilePagerAdapter = null; + if (hasWorkProfile() && ENABLE_TABBED_VIEW) { + resolverMultiProfilePagerAdapter = + createResolverMultiProfilePagerAdapterForTwoProfiles( + initialIntents, rList, filterLastUsed); + } else { + resolverMultiProfilePagerAdapter = createResolverMultiProfilePagerAdapterForOneProfile( + initialIntents, rList, filterLastUsed); + } + return resolverMultiProfilePagerAdapter; + } + + private ResolverMultiProfilePagerAdapter createResolverMultiProfilePagerAdapterForOneProfile( + Intent[] initialIntents, + List<ResolveInfo> rList, boolean filterLastUsed) { + ResolverListAdapter adapter = createResolverListAdapter( + /* context */ this, + /* payloadIntents */ mIntents, + initialIntents, + rList, + filterLastUsed, + mUseLayoutForBrowsables, + /* userHandle */ UserHandle.of(UserHandle.myUserId())); + return new ResolverMultiProfilePagerAdapter( + /* context */ this, + adapter); + } + + private ResolverMultiProfilePagerAdapter createResolverMultiProfilePagerAdapterForTwoProfiles( + Intent[] initialIntents, + List<ResolveInfo> rList, + boolean filterLastUsed) { + ResolverListAdapter personalAdapter = createResolverListAdapter( + /* context */ this, + /* payloadIntents */ mIntents, + initialIntents, + rList, + filterLastUsed, + mUseLayoutForBrowsables, + /* userHandle */ getPersonalProfileUserHandle()); + ResolverListAdapter workAdapter = createResolverListAdapter( + /* context */ this, + /* payloadIntents */ mIntents, + initialIntents, + rList, + filterLastUsed, + mUseLayoutForBrowsables, + /* userHandle */ getWorkProfileUserHandle()); + return new ResolverMultiProfilePagerAdapter( + /* context */ this, + personalAdapter, + workAdapter, + /* defaultProfile */ getCurrentProfile()); + } + + protected @Profile int getCurrentProfile() { + return (UserHandle.myUserId() == UserHandle.USER_SYSTEM ? PROFILE_PERSONAL : PROFILE_WORK); + } + + protected UserHandle getPersonalProfileUserHandle() { + return UserHandle.of(ActivityManager.getCurrentUser()); + } + protected @Nullable UserHandle getWorkProfileUserHandle() { + UserManager userManager = getSystemService(UserManager.class); + for (final UserInfo userInfo : userManager.getProfiles(ActivityManager.getCurrentUser())) { + if (userInfo.isManagedProfile()) { + return userInfo.getUserHandle(); + } + } + return null; + } + + protected boolean hasWorkProfile() { + return getWorkProfileUserHandle() != null; + } + protected void onProfileClick(View v) { - final DisplayResolveInfo dri = mAdapter.getOtherProfile(); + final DisplayResolveInfo dri = + mMultiProfilePagerAdapter.getCurrentListAdapter().getOtherProfile(); if (dri == null) { return; } @@ -395,11 +484,13 @@ public class ResolverActivity extends Activity implements if (mFooterSpacer == null) { mFooterSpacer = new Space(getApplicationContext()); } else { - ((ListView) mAdapterView).removeFooterView(mFooterSpacer); + ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter) + .getCurrentAdapterView().removeFooterView(mFooterSpacer); } mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT, mSystemWindowInsets.bottom)); - ((ListView) mAdapterView).addFooterView(mFooterSpacer); + ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter) + .getCurrentAdapterView().addFooterView(mFooterSpacer); } else { View emptyView = findViewById(R.id.empty); if (emptyView != null) { @@ -417,7 +508,7 @@ public class ResolverActivity extends Activity implements @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - mAdapter.handlePackagesChanged(); + mMultiProfilePagerAdapter.getCurrentListAdapter().handlePackagesChanged(); if (mSystemWindowInsets != null) { mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top, @@ -432,9 +523,10 @@ public class ResolverActivity extends Activity implements return; } - final Option[] options = new Option[mAdapter.getCount()]; + int count = mMultiProfilePagerAdapter.getCurrentListAdapter().getCount(); + final Option[] options = new Option[count]; for (int i = 0, N = options.length; i < N; i++) { - TargetInfo target = mAdapter.getItem(i); + TargetInfo target = mMultiProfilePagerAdapter.getCurrentListAdapter().getItem(i); if (target == null) { // If this occurs, a new set of targets is being loaded. Let that complete, // and have the next call to send voice choices proceed instead. @@ -483,8 +575,9 @@ public class ResolverActivity extends Activity implements return; } - final DisplayResolveInfo dri = mAdapter.getOtherProfile(); - if (dri != null) { + final DisplayResolveInfo dri = + mMultiProfilePagerAdapter.getCurrentListAdapter().getOtherProfile(); + if (dri != null && !ENABLE_TABBED_VIEW) { mProfileView.setVisibility(View.VISIBLE); View text = mProfileView.findViewById(R.id.profile_button); if (!(text instanceof TextView)) { @@ -535,7 +628,8 @@ public class ResolverActivity extends Activity implements // While there may already be a filtered item, we can only use it in the title if the list // is already sorted and all information relevant to it is already in the list. - final boolean named = mAdapter.getFilteredPosition() >= 0; + final boolean named = + mMultiProfilePagerAdapter.getCurrentListAdapter().getFilteredPosition() >= 0; if (title == ActionTitle.DEFAULT && defaultTitleRes != 0) { return getString(defaultTitleRes); } else if (isHttpSchemeAndViewAction(intent)) { @@ -544,12 +638,14 @@ public class ResolverActivity extends Activity implements String dialogTitle = null; if (named && !mUseLayoutForBrowsables) { dialogTitle = getString(ActionTitle.BROWSABLE_APP_TITLE_RES, - mAdapter.getFilteredItem().getDisplayLabel()); + mMultiProfilePagerAdapter.getCurrentListAdapter().getFilteredItem() + .getDisplayLabel()); } else if (named && mUseLayoutForBrowsables) { dialogTitle = getString(ActionTitle.BROWSABLE_HOST_APP_TITLE_RES, intent.getData().getHost(), - mAdapter.getFilteredItem().getDisplayLabel()); - } else if (mAdapter.areAllTargetsBrowsers()) { + mMultiProfilePagerAdapter.getCurrentListAdapter().getFilteredItem() + .getDisplayLabel()); + } else if (mMultiProfilePagerAdapter.getCurrentListAdapter().areAllTargetsBrowsers()) { dialogTitle = getString(ActionTitle.BROWSABLE_TITLE_RES); } else { dialogTitle = getString(ActionTitle.BROWSABLE_HOST_TITLE_RES, @@ -558,7 +654,8 @@ public class ResolverActivity extends Activity implements return dialogTitle; } else { return named - ? getString(title.namedTitleRes, mAdapter.getFilteredItem().getDisplayLabel()) + ? getString(title.namedTitleRes, mMultiProfilePagerAdapter + .getCurrentListAdapter().getFilteredItem().getDisplayLabel()) : getString(title.titleRes); } } @@ -576,7 +673,7 @@ public class ResolverActivity extends Activity implements mPackageMonitor.register(this, getMainLooper(), false); mRegistered = true; } - mAdapter.handlePackagesChanged(); + mMultiProfilePagerAdapter.getCurrentListAdapter().handlePackagesChanged(); updateProfileViewButton(); } @@ -609,8 +706,8 @@ public class ResolverActivity extends Activity implements if (!isChangingConfigurations() && mPickOptionRequest != null) { mPickOptionRequest.cancel(); } - if (mAdapter != null) { - mAdapter.onDestroy(); + if (mMultiProfilePagerAdapter.getCurrentListAdapter() != null) { + mMultiProfilePagerAdapter.getCurrentListAdapter().onDestroy(); } } @@ -660,7 +757,8 @@ public class ResolverActivity extends Activity implements boolean enabled = false; ResolveInfo ri = null; if (hasValidSelection) { - ri = mAdapter.resolveInfoForPosition(checkedPos, filtered); + ri = mMultiProfilePagerAdapter.getCurrentListAdapter() + .resolveInfoForPosition(checkedPos, filtered); if (ri == null) { Log.e(TAG, "Invalid position supplied to setAlwaysButtonEnabled"); return; @@ -701,11 +799,13 @@ public class ResolverActivity extends Activity implements public void onButtonClick(View v) { final int id = v.getId(); - int which = mAdapter.hasFilteredItem() - ? mAdapter.getFilteredPosition() - : mAdapterView.getCheckedItemPosition(); - boolean hasIndexBeenFiltered = !mAdapter.hasFilteredItem(); - ResolveInfo ri = mAdapter.resolveInfoForPosition(which, hasIndexBeenFiltered); + ListView listView = (ListView) mMultiProfilePagerAdapter.getCurrentAdapterView(); + ResolverListAdapter currentListAdapter = mMultiProfilePagerAdapter.getCurrentListAdapter(); + int which = currentListAdapter.hasFilteredItem() + ? currentListAdapter.getFilteredPosition() + : listView.getCheckedItemPosition(); + boolean hasIndexBeenFiltered = !currentListAdapter.hasFilteredItem(); + ResolveInfo ri = currentListAdapter.resolveInfoForPosition(which, hasIndexBeenFiltered); if (mUseLayoutForBrowsables && !ri.handleAllWebDataURI && id == R.id.button_always) { showSettingsForSelected(ri); @@ -736,7 +836,8 @@ public class ResolverActivity extends Activity implements if (isFinishing()) { return; } - ResolveInfo ri = mAdapter.resolveInfoForPosition(which, hasIndexBeenFiltered); + ResolveInfo ri = mMultiProfilePagerAdapter.getCurrentListAdapter() + .resolveInfoForPosition(which, hasIndexBeenFiltered); if (mResolvingHome && hasManagedProfile() && !supportsManagedProfiles(ri)) { Toast.makeText(this, String.format(getResources().getString( com.android.internal.R.string.activity_resolver_work_profiles_support), @@ -745,7 +846,8 @@ public class ResolverActivity extends Activity implements return; } - TargetInfo target = mAdapter.targetInfoForPosition(which, hasIndexBeenFiltered); + TargetInfo target = mMultiProfilePagerAdapter.getCurrentListAdapter() + .targetInfoForPosition(which, hasIndexBeenFiltered); if (target == null) { return; } @@ -760,7 +862,8 @@ public class ResolverActivity extends Activity implements MetricsLogger.action( this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_TAP); } - MetricsLogger.action(this, mAdapter.hasFilteredItem() + MetricsLogger.action(this, + mMultiProfilePagerAdapter.getCurrentListAdapter().hasFilteredItem() ? MetricsProto.MetricsEvent.ACTION_HIDE_APP_DISAMBIG_APP_FEATURED : MetricsProto.MetricsEvent.ACTION_HIDE_APP_DISAMBIG_NONE_FEATURED); finish(); @@ -783,10 +886,11 @@ public class ResolverActivity extends Activity implements } protected void onListRebuilt() { - int count = mAdapter.getUnfilteredCount(); - if (count == 1 && mAdapter.getOtherProfile() == null) { + int count = mMultiProfilePagerAdapter.getCurrentListAdapter().getUnfilteredCount(); + if (count == 1 && mMultiProfilePagerAdapter.getCurrentListAdapter().getOtherProfile() == null) { // Only one target, so we're a candidate to auto-launch! - final TargetInfo target = mAdapter.targetInfoForPosition(0, false); + final TargetInfo target = + mMultiProfilePagerAdapter.getCurrentListAdapter().targetInfoForPosition(0, false); if (shouldAutoLaunchSingleChoice(target)) { safelyStartActivity(target); finish(); @@ -798,8 +902,9 @@ public class ResolverActivity extends Activity implements final ResolveInfo ri = target.getResolveInfo(); final Intent intent = target != null ? target.getResolvedIntent() : null; - if (intent != null && (mSupportsAlwaysUseOption || mAdapter.hasFilteredItem()) - && mAdapter.mUnfilteredResolveList != null) { + if (intent != null && (mSupportsAlwaysUseOption + || mMultiProfilePagerAdapter.getCurrentListAdapter().hasFilteredItem()) + && mMultiProfilePagerAdapter.getCurrentListAdapter().getUnfilteredResolveList() != null) { // Build a reasonable intent filter, based on what matched. IntentFilter filter = new IntentFilter(); Intent filterIntent; @@ -884,13 +989,14 @@ public class ResolverActivity extends Activity implements } if (filter != null) { - final int N = mAdapter.mUnfilteredResolveList.size(); + final int N = mMultiProfilePagerAdapter.getCurrentListAdapter() + .getUnfilteredResolveList().size(); ComponentName[] set; // If we don't add back in the component for forwarding the intent to a managed // profile, the preferred activity may not be updated correctly (as the set of // components we tell it we knew about will have changed). final boolean needToAddBackProfileForwardingComponent = - mAdapter.getOtherProfile() != null; + mMultiProfilePagerAdapter.getCurrentListAdapter().getOtherProfile() != null; if (!needToAddBackProfileForwardingComponent) { set = new ComponentName[N]; } else { @@ -899,15 +1005,18 @@ public class ResolverActivity extends Activity implements int bestMatch = 0; for (int i=0; i<N; i++) { - ResolveInfo r = mAdapter.mUnfilteredResolveList.get(i).getResolveInfoAt(0); + ResolveInfo r = mMultiProfilePagerAdapter.getCurrentListAdapter() + .getUnfilteredResolveList().get(i).getResolveInfoAt(0); set[i] = new ComponentName(r.activityInfo.packageName, r.activityInfo.name); if (r.match > bestMatch) bestMatch = r.match; } if (needToAddBackProfileForwardingComponent) { - set[N] = mAdapter.getOtherProfile().getResolvedComponentName(); - final int otherProfileMatch = mAdapter.getOtherProfile().getResolveInfo().match; + set[N] = mMultiProfilePagerAdapter.getCurrentListAdapter() + .getOtherProfile().getResolvedComponentName(); + final int otherProfileMatch = mMultiProfilePagerAdapter.getCurrentListAdapter() + .getOtherProfile().getResolveInfo().match; if (otherProfileMatch > bestMatch) bestMatch = otherProfileMatch; } @@ -946,7 +1055,8 @@ public class ResolverActivity extends Activity implements } } else { try { - mAdapter.mResolverListController.setLastChosen(intent, filter, bestMatch); + mMultiProfilePagerAdapter.getCurrentListAdapter() + .mResolverListController.setLastChosen(intent, filter, bestMatch); } catch (RemoteException re) { Log.d(TAG, "Error calling setLastChosenActivity\n" + re); } @@ -984,14 +1094,15 @@ public class ResolverActivity extends Activity implements if (mProfileSwitchMessageId != -1) { Toast.makeText(this, getString(mProfileSwitchMessageId), Toast.LENGTH_LONG).show(); } + UserHandle currentUserHandle = mMultiProfilePagerAdapter.getCurrentUserHandle(); if (!mSafeForwardingMode) { - if (cti.start(this, null)) { + if (cti.startAsUser(this, null, currentUserHandle)) { onActivityStarted(cti); } return; } try { - if (cti.startAsCaller(this, null, UserHandle.USER_NULL)) { + if (cti.startAsCaller(this, null, currentUserHandle.getIdentifier())) { onActivityStarted(cti); } } catch (RuntimeException e) { @@ -1061,26 +1172,27 @@ public class ResolverActivity extends Activity implements startActivity(in); } - public ResolverListAdapter createAdapter(Context context, List<Intent> payloadIntents, - Intent[] initialIntents, List<ResolveInfo> rList, - boolean filterLastUsed, boolean useLayoutForBrowsables) { - + @VisibleForTesting + protected ResolverListAdapter createResolverListAdapter(Context context, + List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList, + boolean filterLastUsed, boolean useLayoutForBrowsables, UserHandle userHandle) { Intent startIntent = getIntent(); boolean isAudioCaptureDevice = startIntent.getBooleanExtra(EXTRA_IS_AUDIO_CAPTURE_DEVICE, false); return new ResolverListAdapter(context, payloadIntents, initialIntents, rList, - filterLastUsed, createListController(), useLayoutForBrowsables, this, + filterLastUsed, createListController(userHandle), useLayoutForBrowsables, this, isAudioCaptureDevice); } @VisibleForTesting - protected ResolverListController createListController() { + protected ResolverListController createListController(UserHandle userHandle) { return new ResolverListController( this, mPm, getTargetIntent(), getReferrerPackageName(), - mLaunchedFromUid); + mLaunchedFromUid, + userHandle); } /** @@ -1088,16 +1200,17 @@ public class ResolverActivity extends Activity implements * @return <code>true</code> if the activity is finishing and creation should halt. */ private boolean configureContentView() { - if (mAdapter == null) { + if (mMultiProfilePagerAdapter.getCurrentListAdapter() == null) { throw new IllegalStateException("mAdapter cannot be null."); } - boolean rebuildCompleted = mAdapter.rebuildList(); + boolean rebuildCompleted = mMultiProfilePagerAdapter.getCurrentListAdapter().rebuildList(); if (useLayoutWithDefault()) { mLayoutId = R.layout.resolver_list_with_default; } else { mLayoutId = getLayoutResource(); } setContentView(mLayoutId); + mMultiProfilePagerAdapter.setupViewPager(findViewById(R.id.profile_pager)); return postRebuildList(rebuildCompleted); } @@ -1118,14 +1231,16 @@ public class ResolverActivity extends Activity implements */ final boolean postRebuildListInternal(boolean rebuildCompleted) { - int count = mAdapter.getUnfilteredCount(); + int count = mMultiProfilePagerAdapter.getCurrentListAdapter().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 && mAdapter.getOtherProfile() == null) { + if (count == 1 + && mMultiProfilePagerAdapter.getCurrentListAdapter().getOtherProfile() == null) { // Only one target, so we're a candidate to auto-launch! - final TargetInfo target = mAdapter.targetInfoForPosition(0, false); + final TargetInfo target = mMultiProfilePagerAdapter.getCurrentListAdapter() + .targetInfoForPosition(0, false); if (shouldAutoLaunchSingleChoice(target)) { safelyStartActivity(target); mPackageMonitor.unregister(); @@ -1136,37 +1251,32 @@ public class ResolverActivity extends Activity implements } } - boolean isAdapterViewVisible = true; - if (count == 0 && mAdapter.getPlaceholderCount() == 0) { + setupViewVisibilities(count); + return false; + } + + private void setupViewVisibilities(int count) { + if (count == 0 + && mMultiProfilePagerAdapter.getCurrentListAdapter().getPlaceholderCount() == 0) { final TextView emptyView = findViewById(R.id.empty); emptyView.setVisibility(View.VISIBLE); - isAdapterViewVisible = false; + findViewById(R.id.profile_pager).setVisibility(View.GONE); + } else { + onPrepareAdapterView(mMultiProfilePagerAdapter.getCurrentListAdapter()); } - - onPrepareAdapterView(mAdapter, isAdapterViewVisible); - return false; } /** * Prepare the scrollable view which consumes data in the list adapter. * @param adapter The adapter used to provide data to item views. - * @param isVisible True if the scrollable view should be visible; false, otherwise. */ - public void onPrepareAdapterView(ResolverListAdapter adapter, boolean isVisible) { - mAdapterView = findViewById(R.id.resolver_list); - if (!isVisible) { - mAdapterView.setVisibility(View.GONE); - return; - } - mAdapterView.setVisibility(View.VISIBLE); + public void onPrepareAdapterView(ResolverListAdapter adapter) { + mMultiProfilePagerAdapter.getCurrentAdapterView().setVisibility(View.VISIBLE); final boolean useHeader = adapter.hasFilteredItem(); - final ListView listView = mAdapterView instanceof ListView ? (ListView) mAdapterView : null; - - mAdapterView.setAdapter(mAdapter); - + final ListView listView = (ListView) mMultiProfilePagerAdapter.getCurrentAdapterView(); final ItemClickListener listener = new ItemClickListener(); - mAdapterView.setOnItemClickListener(listener); - mAdapterView.setOnItemLongClickListener(listener); + listView.setOnItemClickListener(listener); + listView.setOnItemLongClickListener(listener); if (mSupportsAlwaysUseOption || mUseLayoutForBrowsables) { listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE); @@ -1185,7 +1295,8 @@ public class ResolverActivity extends Activity implements * Configure the area above the app selection list (title, content preview, etc). */ public void setHeader() { - if (mAdapter.getCount() == 0 && mAdapter.getPlaceholderCount() == 0) { + if (mMultiProfilePagerAdapter.getCurrentListAdapter().getCount() == 0 + && mMultiProfilePagerAdapter.getCurrentListAdapter().getPlaceholderCount() == 0) { final TextView titleView = findViewById(R.id.title); if (titleView != null) { titleView.setVisibility(View.GONE); @@ -1206,11 +1317,11 @@ public class ResolverActivity extends Activity implements final ImageView iconView = findViewById(R.id.icon); if (iconView != null) { - mAdapter.loadFilteredItemIconTaskAsync(iconView); + mMultiProfilePagerAdapter.getCurrentListAdapter().loadFilteredItemIconTaskAsync(iconView); } } - private void resetButtonBar() { + protected void resetButtonBar() { if (!mSupportsAlwaysUseOption && !mUseLayoutForBrowsables) { return; } @@ -1234,24 +1345,27 @@ public class ResolverActivity extends Activity implements } private void resetAlwaysOrOnceButtonBar() { - if (useLayoutWithDefault() - && mAdapter.getFilteredPosition() != ListView.INVALID_POSITION) { - setAlwaysButtonEnabled(true, mAdapter.getFilteredPosition(), false); + int filteredPosition = mMultiProfilePagerAdapter.getCurrentListAdapter() + .getFilteredPosition(); + if (useLayoutWithDefault() && filteredPosition != ListView.INVALID_POSITION) { + setAlwaysButtonEnabled(true, filteredPosition, false); mOnceButton.setEnabled(true); return; } // When the items load in, if an item was already selected, enable the buttons - if (mAdapterView != null - && mAdapterView.getCheckedItemPosition() != ListView.INVALID_POSITION) { - setAlwaysButtonEnabled(true, mAdapterView.getCheckedItemPosition(), true); + ListView currentAdapterView = (ListView) mMultiProfilePagerAdapter.getCurrentAdapterView(); + if (currentAdapterView != null + && currentAdapterView.getCheckedItemPosition() != ListView.INVALID_POSITION) { + setAlwaysButtonEnabled(true, currentAdapterView.getCheckedItemPosition(), true); mOnceButton.setEnabled(true); } } @Override // ResolverListCommunicator public boolean useLayoutWithDefault() { - return mSupportsAlwaysUseOption && mAdapter.hasFilteredItem(); + return mSupportsAlwaysUseOption + && mMultiProfilePagerAdapter.getCurrentListAdapter().hasFilteredItem(); } /** @@ -1275,7 +1389,7 @@ public class ResolverActivity extends Activity implements @Override // ResolverListCommunicator public void onHandlePackagesChanged() { - if (mAdapter.getCount() == 0) { + if (mMultiProfilePagerAdapter.getCurrentListAdapter().getCount() == 0) { // We no longer have any items... just finish the activity. finish(); } @@ -1350,11 +1464,14 @@ public class ResolverActivity extends Activity implements return; } // If we're still loading, we can't yet enable the buttons. - if (mAdapter.resolveInfoForPosition(position, true) == null) { + if (mMultiProfilePagerAdapter.getCurrentListAdapter() + .resolveInfoForPosition(position, true) == null) { return; } - final int checkedPos = mAdapterView.getCheckedItemPosition(); + ListView currentAdapterView = + (ListView) mMultiProfilePagerAdapter.getCurrentAdapterView(); + final int checkedPos = currentAdapterView.getCheckedItemPosition(); final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION; if (!useLayoutWithDefault() && (!hasValidSelection || mLastSelected != checkedPos) @@ -1362,7 +1479,7 @@ public class ResolverActivity extends Activity implements setAlwaysButtonEnabled(hasValidSelection, checkedPos, true); mOnceButton.setEnabled(hasValidSelection); if (hasValidSelection) { - mAdapterView.smoothScrollToPosition(checkedPos); + currentAdapterView.smoothScrollToPosition(checkedPos); } mLastSelected = checkedPos; } else { @@ -1380,7 +1497,8 @@ public class ResolverActivity extends Activity implements // Header views don't count. return false; } - ResolveInfo ri = mAdapter.resolveInfoForPosition(position, true); + ResolveInfo ri = mMultiProfilePagerAdapter.getCurrentListAdapter() + .resolveInfoForPosition(position, true); showTargetDetails(ri); return true; } @@ -1420,7 +1538,8 @@ public class ResolverActivity extends Activity implements final ResolverActivity ra = (ResolverActivity) getActivity(); if (ra != null) { - final TargetInfo ti = ra.mAdapter.getItem(selections[0].getIndex()); + final TargetInfo ti = ra.mMultiProfilePagerAdapter.getCurrentListAdapter() + .getItem(selections[0].getIndex()); if (ra.onTargetSelected(ti, false)) { ra.mPickOptionRequest = null; ra.finish(); diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java index bb7ca358f815..48064da7c35c 100644 --- a/core/java/com/android/internal/app/ResolverListAdapter.java +++ b/core/java/com/android/internal/app/ResolverListAdapter.java @@ -37,7 +37,6 @@ import android.graphics.ColorMatrixColorFilter; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask; -import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; @@ -81,7 +80,7 @@ public class ResolverListAdapter extends BaseAdapter { // This one is the list that the Adapter will actually present. List<DisplayResolveInfo> mDisplayList; - List<ResolvedComponentInfo> mUnfilteredResolveList; + private List<ResolvedComponentInfo> mUnfilteredResolveList; private int mLastChosenPosition = -1; private boolean mFilterLastUsed; @@ -162,6 +161,10 @@ public class ResolverListAdapter extends BaseAdapter { mResolverListController.updateChooserCounts(packageName, userId, action); } + List<ResolvedComponentInfo> getUnfilteredResolveList() { + return mUnfilteredResolveList; + } + /** * @return true if all items in the display list are defined as browsers by * ResolveInfo.handleAllWebDataURI @@ -576,7 +579,7 @@ public class ResolverListAdapter extends BaseAdapter { Drawable loadIconForResolveInfo(ResolveInfo ri) { // Load icons based on the current process. If in work profile icons should be badged. - return makePresentationGetter(ri).getIcon(Process.myUserHandle()); + return makePresentationGetter(ri).getIcon(mResolverListController.getUserHandle()); } void loadFilteredItemIconTaskAsync(@NonNull ImageView iconView) { diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java index b456ca00fe2b..abd3eb2453df 100644 --- a/core/java/com/android/internal/app/ResolverListController.java +++ b/core/java/com/android/internal/app/ResolverListController.java @@ -28,6 +28,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -55,6 +56,7 @@ public class ResolverListController { private static final String TAG = "ResolverListController"; private static final boolean DEBUG = false; + private final UserHandle mUserHandle; private AbstractResolverComparator mResolverComparator; private boolean isComputed = false; @@ -64,8 +66,9 @@ public class ResolverListController { PackageManager pm, Intent targetIntent, String referrerPackage, - int launchedFromUid) { - this(context, pm, targetIntent, referrerPackage, launchedFromUid, + int launchedFromUid, + UserHandle userHandle) { + this(context, pm, targetIntent, referrerPackage, launchedFromUid, userHandle, new ResolverRankerServiceResolverComparator( context, targetIntent, referrerPackage, null)); } @@ -76,12 +79,14 @@ public class ResolverListController { Intent targetIntent, String referrerPackage, int launchedFromUid, + UserHandle userHandle, AbstractResolverComparator resolverComparator) { mContext = context; mpm = pm; mLaunchedFromUid = launchedFromUid; mTargetIntent = targetIntent; mReferrerPackage = referrerPackage; + mUserHandle = userHandle; mResolverComparator = resolverComparator; } @@ -116,7 +121,8 @@ public class ResolverListController { || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) { flags |= PackageManager.MATCH_INSTANT; } - final List<ResolveInfo> infos = mpm.queryIntentActivities(intent, flags); + final List<ResolveInfo> infos = mpm.queryIntentActivitiesAsUser(intent, flags, + mUserHandle); if (infos != null) { if (resolvedComponents == null) { resolvedComponents = new ArrayList<>(); @@ -127,6 +133,10 @@ public class ResolverListController { return resolvedComponents; } + UserHandle getUserHandle() { + return mUserHandle; + } + @VisibleForTesting public void addResolveListDedupe(List<ResolverActivity.ResolvedComponentInfo> into, Intent intent, diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java new file mode 100644 index 000000000000..9e814ab5d0aa --- /dev/null +++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.widget.ListView; + +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.widget.PagerAdapter; + +/** + * A {@link PagerAdapter} which describes the work and personal profile intent resolver screens. + */ +@VisibleForTesting +public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerAdapter { + + private final ResolverProfileDescriptor[] mItems; + + ResolverMultiProfilePagerAdapter(Context context, + ResolverListAdapter adapter) { + super(context, /* currentPage */ 0); + mItems = new ResolverProfileDescriptor[] { + createProfileDescriptor(adapter) + }; + } + + ResolverMultiProfilePagerAdapter(Context context, + ResolverListAdapter personalAdapter, + ResolverListAdapter workAdapter, + @Profile int defaultProfile) { + super(context, /* currentPage */ defaultProfile); + mItems = new ResolverProfileDescriptor[] { + createProfileDescriptor(personalAdapter), + createProfileDescriptor(workAdapter) + }; + } + + private ResolverProfileDescriptor createProfileDescriptor( + ResolverListAdapter adapter) { + final LayoutInflater inflater = LayoutInflater.from(getContext()); + final ViewGroup rootView = + (ViewGroup) inflater.inflate(R.layout.resolver_list_per_profile, null, false); + return new ResolverProfileDescriptor(rootView, adapter); + } + + ListView getListViewForIndex(int index) { + return getItem(index).listView; + } + + @Override + ResolverProfileDescriptor getItem(int pageIndex) { + return mItems[pageIndex]; + } + + @Override + int getItemCount() { + return mItems.length; + } + + @Override + void setupListAdapter(int pageIndex) { + final ListView listView = getItem(pageIndex).listView; + listView.setAdapter(getItem(pageIndex).resolverListAdapter); + } + + @Override + ResolverListAdapter getAdapterForIndex(int pageIndex) { + return mItems[pageIndex].resolverListAdapter; + } + + @Override + @VisibleForTesting + public ResolverListAdapter getCurrentListAdapter() { + return getAdapterForIndex(getCurrentPage()); + } + + @Override + ResolverListAdapter getCurrentRootAdapter() { + return getCurrentListAdapter(); + } + + @Override + ListView getCurrentAdapterView() { + return getListViewForIndex(getCurrentPage()); + } + + class ResolverProfileDescriptor extends ProfileDescriptor { + private ResolverListAdapter resolverListAdapter; + final ListView listView; + ResolverProfileDescriptor(ViewGroup rootView, ResolverListAdapter adapter) { + super(rootView); + resolverListAdapter = adapter; + listView = rootView.findViewById(R.id.resolver_list); + } + } +} diff --git a/core/java/com/android/internal/app/WrapHeightViewPager.java b/core/java/com/android/internal/app/WrapHeightViewPager.java new file mode 100644 index 000000000000..b017bb44d751 --- /dev/null +++ b/core/java/com/android/internal/app/WrapHeightViewPager.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; + +import com.android.internal.widget.ViewPager; + +/** + * A {@link ViewPager} which wraps around its first child's height. + * <p>Normally {@link ViewPager} instances expand their height to cover all remaining space in + * the layout. + * <p>This class is used for the intent resolver picker's tabbed view to maintain + * consistency with the previous behavior. + */ +public class WrapHeightViewPager extends ViewPager { + + public WrapHeightViewPager(Context context) { + super(context); + } + + public WrapHeightViewPager(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public WrapHeightViewPager(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public WrapHeightViewPager(Context context, AttributeSet attrs, + int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + // TODO(arangelov): When we have multiple pages, the height should wrap to the currently + // displayed page. Investigate whether onMeasure is called when changing a page, and instead + // of getChildAt(0), use the currently displayed one. + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.AT_MOST) { + return; + } + widthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY); + int height = getMeasuredHeight(); + if (getChildCount() > 0) { + View firstChild = getChildAt(0); + firstChild.measure(widthMeasureSpec, + MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST)); + height = firstChild.getMeasuredHeight(); + } + heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } +} diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml index 6807f9af3980..9f296f8f7c08 100644 --- a/core/res/res/layout/chooser_grid.xml +++ b/core/res/res/layout/chooser_grid.xml @@ -29,7 +29,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alwaysShow="true" - android:elevation="1dp" + android:elevation="0dp" android:background="@drawable/bottomsheet_background"> <ImageView @@ -55,16 +55,10 @@ android:layout_centerHorizontal="true"/> </RelativeLayout> - <com.android.internal.widget.RecyclerView + <com.android.internal.widget.ViewPager + android:id="@+id/profile_pager" android:layout_width="match_parent" - android:layout_height="match_parent" - android:layoutManager="com.android.internal.widget.GridLayoutManager" - android:id="@+id/resolver_list" - android:clipToPadding="false" - android:background="?attr/colorBackgroundFloating" - android:scrollbars="none" - android:elevation="1dp" - android:nestedScrollingEnabled="true"/> + android:layout_height="wrap_content"/> <TextView android:id="@+id/empty" android:layout_width="match_parent" diff --git a/core/res/res/layout/chooser_list_per_profile.xml b/core/res/res/layout/chooser_list_per_profile.xml new file mode 100644 index 000000000000..212813f10bd4 --- /dev/null +++ b/core/res/res/layout/chooser_list_per_profile.xml @@ -0,0 +1,27 @@ +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<com.android.internal.widget.RecyclerView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layoutManager="com.android.internal.widget.GridLayoutManager" + android:id="@+id/resolver_list" + android:clipToPadding="false" + android:background="?attr/colorBackgroundFloating" + android:scrollbars="none" + android:elevation="1dp" + android:nestedScrollingEnabled="true"/>
\ No newline at end of file diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml index 6e45e7a4c509..c5d891254227 100644 --- a/core/res/res/layout/resolver_list.xml +++ b/core/res/res/layout/resolver_list.xml @@ -63,25 +63,22 @@ </RelativeLayout> <View + android:id="@+id/divider" android:layout_alwaysShow="true" android:layout_width="match_parent" android:layout_height="1dp" android:background="?attr/colorBackgroundFloating" android:foreground="?attr/dividerVertical" /> - <ListView + + <com.android.internal.app.WrapHeightViewPager + android:id="@+id/profile_pager" android:layout_width="match_parent" android:layout_height="wrap_content" - android:id="@+id/resolver_list" - android:clipToPadding="false" - android:background="?attr/colorBackgroundFloating" - android:elevation="@dimen/resolver_elevation" - android:nestedScrollingEnabled="true" - android:scrollbarStyle="outsideOverlay" - android:scrollIndicators="top|bottom" android:divider="?attr/dividerVertical" android:footerDividersEnabled="false" android:headerDividersEnabled="false" - android:dividerHeight="1dp" /> + android:dividerHeight="1dp"/> + <View android:layout_alwaysShow="true" android:layout_width="match_parent" @@ -89,7 +86,6 @@ android:background="?attr/colorBackgroundFloating" android:foreground="?attr/dividerVertical" /> - <TextView android:id="@+id/empty" android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/core/res/res/layout/resolver_list_per_profile.xml b/core/res/res/layout/resolver_list_per_profile.xml new file mode 100644 index 000000000000..68b991755e73 --- /dev/null +++ b/core/res/res/layout/resolver_list_per_profile.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<ListView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:id="@+id/resolver_list" + android:clipToPadding="false" + android:background="?attr/colorBackgroundFloating" + android:elevation="@dimen/resolver_elevation" + android:nestedScrollingEnabled="true" + android:scrollbarStyle="outsideOverlay" + android:scrollIndicators="top|bottom" + android:divider="?attr/dividerVertical" + android:footerDividersEnabled="false" + android:headerDividersEnabled="false" + android:dividerHeight="1dp" />
\ No newline at end of file diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml index dbba0b7bcc25..5b3d929d23a5 100644 --- a/core/res/res/layout/resolver_list_with_default.xml +++ b/core/res/res/layout/resolver_list_with_default.xml @@ -144,25 +144,21 @@ </LinearLayout> <View + android:id="@+id/divider" android:layout_alwaysShow="true" android:layout_width="match_parent" android:layout_height="1dp" android:background="?attr/colorBackgroundFloating" android:foreground="?attr/dividerVertical" /> - <ListView + + <com.android.internal.app.WrapHeightViewPager + android:id="@+id/profile_pager" android:layout_width="match_parent" android:layout_height="wrap_content" - android:id="@+id/resolver_list" - android:clipToPadding="false" - android:background="?attr/colorBackgroundFloating" - android:elevation="@dimen/resolver_elevation" - android:nestedScrollingEnabled="true" - android:scrollbarStyle="outsideOverlay" - android:scrollIndicators="top|bottom" + android:dividerHeight="1dp" android:divider="?attr/dividerVertical" android:footerDividersEnabled="false" - android:headerDividersEnabled="false" - android:dividerHeight="1dp" /> + android:headerDividersEnabled="false"/> <View android:layout_alwaysShow="true" android:layout_width="match_parent" diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index fc1c35879a50..90343e07d4e4 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -247,6 +247,7 @@ <java-symbol type="id" name="mic" /> <java-symbol type="id" name="overlay" /> <java-symbol type="id" name="app_ops" /> + <java-symbol type="id" name="profile_pager" /> <java-symbol type="attr" name="actionModeShareDrawable" /> <java-symbol type="attr" name="alertDialogCenterButtons" /> @@ -1533,6 +1534,8 @@ <java-symbol type="layout" name="user_switching_dialog" /> <java-symbol type="layout" name="common_tab_settings" /> <java-symbol type="layout" name="notification_material_media_seekbar" /> + <java-symbol type="layout" name="resolver_list_per_profile" /> + <java-symbol type="layout" name="chooser_list_per_profile" /> <java-symbol type="anim" name="slide_in_child_bottom" /> <java-symbol type="anim" name="slide_in_right" /> |