Merge "[Partial Screensharing] Add abstraction to show custom device policy blockers in ChooserActivity" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
814ef64f49
@@ -17,18 +17,15 @@ package com.android.internal.app;
|
|||||||
|
|
||||||
import android.annotation.IntDef;
|
import android.annotation.IntDef;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.UserIdInt;
|
||||||
import android.app.AppGlobals;
|
import android.app.AppGlobals;
|
||||||
import android.app.admin.DevicePolicyEventLogger;
|
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.IPackageManager;
|
import android.content.pm.IPackageManager;
|
||||||
import android.content.pm.ResolveInfo;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Trace;
|
import android.os.Trace;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
|
||||||
import android.stats.devicepolicy.DevicePolicyEnums;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
@@ -60,73 +57,31 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
|
|||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private int mCurrentPage;
|
private int mCurrentPage;
|
||||||
private OnProfileSelectedListener mOnProfileSelectedListener;
|
private OnProfileSelectedListener mOnProfileSelectedListener;
|
||||||
private OnSwitchOnWorkSelectedListener mOnSwitchOnWorkSelectedListener;
|
|
||||||
private Set<Integer> mLoadedPages;
|
private Set<Integer> mLoadedPages;
|
||||||
private final UserHandle mPersonalProfileUserHandle;
|
private final EmptyStateProvider mEmptyStateProvider;
|
||||||
private final UserHandle mWorkProfileUserHandle;
|
private final UserHandle mWorkProfileUserHandle;
|
||||||
private Injector mInjector;
|
private final QuietModeManager mQuietModeManager;
|
||||||
private boolean mIsWaitingToEnableWorkProfile;
|
|
||||||
|
|
||||||
AbstractMultiProfilePagerAdapter(Context context, int currentPage,
|
AbstractMultiProfilePagerAdapter(Context context, int currentPage,
|
||||||
UserHandle personalProfileUserHandle,
|
EmptyStateProvider emptyStateProvider,
|
||||||
|
QuietModeManager quietModeManager,
|
||||||
UserHandle workProfileUserHandle) {
|
UserHandle workProfileUserHandle) {
|
||||||
mContext = Objects.requireNonNull(context);
|
mContext = Objects.requireNonNull(context);
|
||||||
mCurrentPage = currentPage;
|
mCurrentPage = currentPage;
|
||||||
mLoadedPages = new HashSet<>();
|
mLoadedPages = new HashSet<>();
|
||||||
mPersonalProfileUserHandle = personalProfileUserHandle;
|
|
||||||
mWorkProfileUserHandle = workProfileUserHandle;
|
mWorkProfileUserHandle = workProfileUserHandle;
|
||||||
UserManager userManager = context.getSystemService(UserManager.class);
|
mEmptyStateProvider = emptyStateProvider;
|
||||||
mInjector = new Injector() {
|
mQuietModeManager = quietModeManager;
|
||||||
@Override
|
|
||||||
public boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId,
|
|
||||||
int targetUserId) {
|
|
||||||
return AbstractMultiProfilePagerAdapter.this
|
|
||||||
.hasCrossProfileIntents(intents, sourceUserId, targetUserId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
|
||||||
public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
|
return mQuietModeManager.isQuietModeEnabled(workProfileUserHandle);
|
||||||
return userManager.isQuietModeEnabled(workProfileUserHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void requestQuietModeEnabled(boolean enabled, UserHandle workProfileUserHandle) {
|
|
||||||
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
|
||||||
userManager.requestQuietModeEnabled(enabled, workProfileUserHandle);
|
|
||||||
});
|
|
||||||
mIsWaitingToEnableWorkProfile = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void markWorkProfileEnabledBroadcastReceived() {
|
|
||||||
mIsWaitingToEnableWorkProfile = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isWaitingToEnableWorkProfile() {
|
|
||||||
return mIsWaitingToEnableWorkProfile;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides the default {@link Injector} for testing purposes.
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
public void setInjector(Injector injector) {
|
|
||||||
mInjector = injector;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
|
|
||||||
return mInjector.isQuietModeEnabled(workProfileUserHandle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setOnProfileSelectedListener(OnProfileSelectedListener listener) {
|
void setOnProfileSelectedListener(OnProfileSelectedListener listener) {
|
||||||
mOnProfileSelectedListener = listener;
|
mOnProfileSelectedListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setOnSwitchOnWorkSelectedListener(OnSwitchOnWorkSelectedListener listener) {
|
|
||||||
mOnSwitchOnWorkSelectedListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
Context getContext() {
|
Context getContext() {
|
||||||
return mContext;
|
return mContext;
|
||||||
}
|
}
|
||||||
@@ -280,8 +235,6 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
|
|||||||
|
|
||||||
abstract @Nullable ViewGroup getInactiveAdapterView();
|
abstract @Nullable ViewGroup getInactiveAdapterView();
|
||||||
|
|
||||||
abstract String getMetricsCategory();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rebuilds the tab that is currently visible to the user.
|
* Rebuilds the tab that is currently visible to the user.
|
||||||
* <p>Returns {@code true} if rebuild has completed.
|
* <p>Returns {@code true} if rebuild has completed.
|
||||||
@@ -317,41 +270,18 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean rebuildTab(ResolverListAdapter activeListAdapter, boolean doPostProcessing) {
|
private boolean rebuildTab(ResolverListAdapter activeListAdapter, boolean doPostProcessing) {
|
||||||
if (shouldShowNoCrossProfileIntentsEmptyState(activeListAdapter)) {
|
if (shouldSkipRebuild(activeListAdapter)) {
|
||||||
activeListAdapter.postListReadyRunnable(doPostProcessing, /* rebuildCompleted */ true);
|
activeListAdapter.postListReadyRunnable(doPostProcessing, /* rebuildCompleted */ true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return activeListAdapter.rebuildList(doPostProcessing);
|
return activeListAdapter.rebuildList(doPostProcessing);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldShowNoCrossProfileIntentsEmptyState(
|
private boolean shouldSkipRebuild(ResolverListAdapter activeListAdapter) {
|
||||||
ResolverListAdapter activeListAdapter) {
|
EmptyState emptyState = mEmptyStateProvider.getEmptyState(activeListAdapter);
|
||||||
UserHandle listUserHandle = activeListAdapter.getUserHandle();
|
return emptyState != null && emptyState.shouldSkipDataRebuild();
|
||||||
return UserHandle.myUserId() != listUserHandle.getIdentifier()
|
|
||||||
&& allowShowNoCrossProfileIntentsEmptyState()
|
|
||||||
&& !mInjector.hasCrossProfileIntents(activeListAdapter.getIntents(),
|
|
||||||
UserHandle.myUserId(), listUserHandle.getIdentifier());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean allowShowNoCrossProfileIntentsEmptyState() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void showWorkProfileOffEmptyState(
|
|
||||||
ResolverListAdapter activeListAdapter, View.OnClickListener listener);
|
|
||||||
|
|
||||||
protected abstract void showNoPersonalToWorkIntentsEmptyState(
|
|
||||||
ResolverListAdapter activeListAdapter);
|
|
||||||
|
|
||||||
protected abstract void showNoPersonalAppsAvailableEmptyState(
|
|
||||||
ResolverListAdapter activeListAdapter);
|
|
||||||
|
|
||||||
protected abstract void showNoWorkAppsAvailableEmptyState(
|
|
||||||
ResolverListAdapter activeListAdapter);
|
|
||||||
|
|
||||||
protected abstract void showNoWorkToPersonalIntentsEmptyState(
|
|
||||||
ResolverListAdapter activeListAdapter);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The empty state screens are shown according to their priority:
|
* The empty state screens are shown according to their priority:
|
||||||
* <ol>
|
* <ol>
|
||||||
@@ -366,103 +296,88 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
|
|||||||
* anyway.
|
* anyway.
|
||||||
*/
|
*/
|
||||||
void showEmptyResolverListEmptyState(ResolverListAdapter listAdapter) {
|
void showEmptyResolverListEmptyState(ResolverListAdapter listAdapter) {
|
||||||
if (maybeShowNoCrossProfileIntentsEmptyState(listAdapter)) {
|
final EmptyState emptyState = mEmptyStateProvider.getEmptyState(listAdapter);
|
||||||
|
|
||||||
|
if (emptyState == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (maybeShowWorkProfileOffEmptyState(listAdapter)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
maybeShowNoAppsAvailableEmptyState(listAdapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean maybeShowNoCrossProfileIntentsEmptyState(ResolverListAdapter listAdapter) {
|
emptyState.onEmptyStateShown();
|
||||||
if (!shouldShowNoCrossProfileIntentsEmptyState(listAdapter)) {
|
|
||||||
return false;
|
View.OnClickListener clickListener = null;
|
||||||
|
|
||||||
|
if (emptyState.getButtonClickListener() != null) {
|
||||||
|
clickListener = v -> emptyState.getButtonClickListener().onClick(() -> {
|
||||||
|
ProfileDescriptor descriptor = getItem(
|
||||||
|
userHandleToPageIndex(listAdapter.getUserHandle()));
|
||||||
|
AbstractMultiProfilePagerAdapter.this.showSpinner(descriptor.getEmptyStateView());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (listAdapter.getUserHandle().equals(mPersonalProfileUserHandle)) {
|
|
||||||
DevicePolicyEventLogger.createEvent(
|
showEmptyState(listAdapter, emptyState, clickListener);
|
||||||
DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL)
|
|
||||||
.setStrings(getMetricsCategory())
|
|
||||||
.write();
|
|
||||||
showNoWorkToPersonalIntentsEmptyState(listAdapter);
|
|
||||||
} else {
|
|
||||||
DevicePolicyEventLogger.createEvent(
|
|
||||||
DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK)
|
|
||||||
.setStrings(getMetricsCategory())
|
|
||||||
.write();
|
|
||||||
showNoPersonalToWorkIntentsEmptyState(listAdapter);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@code true} if the work profile off empty state screen is shown.
|
* Class to get user id of the current process
|
||||||
*/
|
*/
|
||||||
private boolean maybeShowWorkProfileOffEmptyState(ResolverListAdapter listAdapter) {
|
public static class MyUserIdProvider {
|
||||||
UserHandle listUserHandle = listAdapter.getUserHandle();
|
/**
|
||||||
if (!listUserHandle.equals(mWorkProfileUserHandle)
|
* @return user id of the current process
|
||||||
|| !mInjector.isQuietModeEnabled(mWorkProfileUserHandle)
|
*/
|
||||||
|| listAdapter.getCount() == 0) {
|
public int getMyUserId() {
|
||||||
return false;
|
return UserHandle.myUserId();
|
||||||
}
|
|
||||||
DevicePolicyEventLogger
|
|
||||||
.createEvent(DevicePolicyEnums.RESOLVER_EMPTY_STATE_WORK_APPS_DISABLED)
|
|
||||||
.setStrings(getMetricsCategory())
|
|
||||||
.write();
|
|
||||||
showWorkProfileOffEmptyState(listAdapter,
|
|
||||||
v -> {
|
|
||||||
ProfileDescriptor descriptor = getItem(
|
|
||||||
userHandleToPageIndex(listAdapter.getUserHandle()));
|
|
||||||
showSpinner(descriptor.getEmptyStateView());
|
|
||||||
if (mOnSwitchOnWorkSelectedListener != null) {
|
|
||||||
mOnSwitchOnWorkSelectedListener.onSwitchOnWorkSelected();
|
|
||||||
}
|
|
||||||
mInjector.requestQuietModeEnabled(false, mWorkProfileUserHandle);
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void maybeShowNoAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
|
|
||||||
UserHandle listUserHandle = listAdapter.getUserHandle();
|
|
||||||
if (mWorkProfileUserHandle != null
|
|
||||||
&& (UserHandle.myUserId() == listUserHandle.getIdentifier()
|
|
||||||
|| !hasAppsInOtherProfile(listAdapter))) {
|
|
||||||
DevicePolicyEventLogger.createEvent(
|
|
||||||
DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_APPS_RESOLVED)
|
|
||||||
.setStrings(getMetricsCategory())
|
|
||||||
.setBoolean(/*isPersonalProfile*/ listUserHandle == mPersonalProfileUserHandle)
|
|
||||||
.write();
|
|
||||||
if (listUserHandle == mPersonalProfileUserHandle) {
|
|
||||||
showNoPersonalAppsAvailableEmptyState(listAdapter);
|
|
||||||
} else {
|
|
||||||
showNoWorkAppsAvailableEmptyState(listAdapter);
|
|
||||||
}
|
|
||||||
} else if (mWorkProfileUserHandle == null) {
|
|
||||||
showConsumerUserNoAppsAvailableEmptyState(listAdapter);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void showEmptyState(ResolverListAdapter activeListAdapter, String title,
|
/**
|
||||||
String subtitle) {
|
* Utility class to check if there are cross profile intents, it is in a separate class so
|
||||||
showEmptyState(activeListAdapter, title, subtitle, /* buttonOnClick */ null);
|
* it could be mocked in tests
|
||||||
|
*/
|
||||||
|
public static class CrossProfileIntentsChecker {
|
||||||
|
|
||||||
|
private final ContentResolver mContentResolver;
|
||||||
|
|
||||||
|
public CrossProfileIntentsChecker(@NonNull ContentResolver contentResolver) {
|
||||||
|
mContentResolver = contentResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void showEmptyState(ResolverListAdapter activeListAdapter,
|
/**
|
||||||
String title, String subtitle, View.OnClickListener buttonOnClick) {
|
* Returns {@code true} if at least one of the provided {@code intents} can be forwarded
|
||||||
|
* from {@code source} (user id) to {@code target} (user id).
|
||||||
|
*/
|
||||||
|
public boolean hasCrossProfileIntents(List<Intent> intents, @UserIdInt int source,
|
||||||
|
@UserIdInt int target) {
|
||||||
|
IPackageManager packageManager = AppGlobals.getPackageManager();
|
||||||
|
|
||||||
|
return intents.stream().anyMatch(intent ->
|
||||||
|
null != IntentForwarderActivity.canForward(intent, source, target,
|
||||||
|
packageManager, mContentResolver));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void showEmptyState(ResolverListAdapter activeListAdapter, EmptyState emptyState,
|
||||||
|
View.OnClickListener buttonOnClick) {
|
||||||
ProfileDescriptor descriptor = getItem(
|
ProfileDescriptor descriptor = getItem(
|
||||||
userHandleToPageIndex(activeListAdapter.getUserHandle()));
|
userHandleToPageIndex(activeListAdapter.getUserHandle()));
|
||||||
descriptor.rootView.findViewById(R.id.resolver_list).setVisibility(View.GONE);
|
descriptor.rootView.findViewById(R.id.resolver_list).setVisibility(View.GONE);
|
||||||
ViewGroup emptyStateView = descriptor.getEmptyStateView();
|
ViewGroup emptyStateView = descriptor.getEmptyStateView();
|
||||||
resetViewVisibilitiesForWorkProfileEmptyState(emptyStateView);
|
resetViewVisibilitiesForEmptyState(emptyStateView);
|
||||||
emptyStateView.setVisibility(View.VISIBLE);
|
emptyStateView.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
View container = emptyStateView.findViewById(R.id.resolver_empty_state_container);
|
View container = emptyStateView.findViewById(R.id.resolver_empty_state_container);
|
||||||
setupContainerPadding(container);
|
setupContainerPadding(container);
|
||||||
|
|
||||||
TextView titleView = emptyStateView.findViewById(R.id.resolver_empty_state_title);
|
TextView titleView = emptyStateView.findViewById(R.id.resolver_empty_state_title);
|
||||||
|
String title = emptyState.getTitle();
|
||||||
|
if (title != null) {
|
||||||
|
titleView.setVisibility(View.VISIBLE);
|
||||||
titleView.setText(title);
|
titleView.setText(title);
|
||||||
|
} else {
|
||||||
|
titleView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
TextView subtitleView = emptyStateView.findViewById(R.id.resolver_empty_state_subtitle);
|
TextView subtitleView = emptyStateView.findViewById(R.id.resolver_empty_state_subtitle);
|
||||||
|
String subtitle = emptyState.getSubtitle();
|
||||||
if (subtitle != null) {
|
if (subtitle != null) {
|
||||||
subtitleView.setVisibility(View.VISIBLE);
|
subtitleView.setVisibility(View.VISIBLE);
|
||||||
subtitleView.setText(subtitle);
|
subtitleView.setText(subtitle);
|
||||||
@@ -470,6 +385,9 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
|
|||||||
subtitleView.setVisibility(View.GONE);
|
subtitleView.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
View defaultEmptyText = emptyStateView.findViewById(R.id.empty);
|
||||||
|
defaultEmptyText.setVisibility(emptyState.useDefaultEmptyView() ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
Button button = emptyStateView.findViewById(R.id.resolver_empty_state_button);
|
Button button = emptyStateView.findViewById(R.id.resolver_empty_state_button);
|
||||||
button.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE);
|
button.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE);
|
||||||
button.setOnClickListener(buttonOnClick);
|
button.setOnClickListener(buttonOnClick);
|
||||||
@@ -483,22 +401,6 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
|
|||||||
*/
|
*/
|
||||||
protected void setupContainerPadding(View container) {}
|
protected void setupContainerPadding(View container) {}
|
||||||
|
|
||||||
private void showConsumerUserNoAppsAvailableEmptyState(ResolverListAdapter activeListAdapter) {
|
|
||||||
ProfileDescriptor descriptor = getItem(
|
|
||||||
userHandleToPageIndex(activeListAdapter.getUserHandle()));
|
|
||||||
descriptor.rootView.findViewById(R.id.resolver_list).setVisibility(View.GONE);
|
|
||||||
View emptyStateView = descriptor.getEmptyStateView();
|
|
||||||
resetViewVisibilitiesForConsumerUserEmptyState(emptyStateView);
|
|
||||||
emptyStateView.setVisibility(View.VISIBLE);
|
|
||||||
|
|
||||||
activeListAdapter.markTabLoaded();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isSpinnerShowing(View emptyStateView) {
|
|
||||||
return emptyStateView.findViewById(R.id.resolver_empty_state_progress).getVisibility()
|
|
||||||
== View.VISIBLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showSpinner(View emptyStateView) {
|
private void showSpinner(View emptyStateView) {
|
||||||
emptyStateView.findViewById(R.id.resolver_empty_state_title).setVisibility(View.INVISIBLE);
|
emptyStateView.findViewById(R.id.resolver_empty_state_title).setVisibility(View.INVISIBLE);
|
||||||
emptyStateView.findViewById(R.id.resolver_empty_state_button).setVisibility(View.INVISIBLE);
|
emptyStateView.findViewById(R.id.resolver_empty_state_button).setVisibility(View.INVISIBLE);
|
||||||
@@ -506,7 +408,7 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
|
|||||||
emptyStateView.findViewById(R.id.empty).setVisibility(View.GONE);
|
emptyStateView.findViewById(R.id.empty).setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetViewVisibilitiesForWorkProfileEmptyState(View emptyStateView) {
|
private void resetViewVisibilitiesForEmptyState(View emptyStateView) {
|
||||||
emptyStateView.findViewById(R.id.resolver_empty_state_title).setVisibility(View.VISIBLE);
|
emptyStateView.findViewById(R.id.resolver_empty_state_title).setVisibility(View.VISIBLE);
|
||||||
emptyStateView.findViewById(R.id.resolver_empty_state_subtitle).setVisibility(View.VISIBLE);
|
emptyStateView.findViewById(R.id.resolver_empty_state_subtitle).setVisibility(View.VISIBLE);
|
||||||
emptyStateView.findViewById(R.id.resolver_empty_state_button).setVisibility(View.INVISIBLE);
|
emptyStateView.findViewById(R.id.resolver_empty_state_button).setVisibility(View.INVISIBLE);
|
||||||
@@ -514,14 +416,6 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
|
|||||||
emptyStateView.findViewById(R.id.empty).setVisibility(View.GONE);
|
emptyStateView.findViewById(R.id.empty).setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetViewVisibilitiesForConsumerUserEmptyState(View emptyStateView) {
|
|
||||||
emptyStateView.findViewById(R.id.resolver_empty_state_title).setVisibility(View.GONE);
|
|
||||||
emptyStateView.findViewById(R.id.resolver_empty_state_subtitle).setVisibility(View.GONE);
|
|
||||||
emptyStateView.findViewById(R.id.resolver_empty_state_button).setVisibility(View.GONE);
|
|
||||||
emptyStateView.findViewById(R.id.resolver_empty_state_progress).setVisibility(View.GONE);
|
|
||||||
emptyStateView.findViewById(R.id.empty).setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void showListView(ResolverListAdapter activeListAdapter) {
|
protected void showListView(ResolverListAdapter activeListAdapter) {
|
||||||
ProfileDescriptor descriptor = getItem(
|
ProfileDescriptor descriptor = getItem(
|
||||||
userHandleToPageIndex(activeListAdapter.getUserHandle()));
|
userHandleToPageIndex(activeListAdapter.getUserHandle()));
|
||||||
@@ -530,33 +424,6 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
|
|||||||
emptyStateView.setVisibility(View.GONE);
|
emptyStateView.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasCrossProfileIntents(List<Intent> intents, int source, int target) {
|
|
||||||
IPackageManager packageManager = AppGlobals.getPackageManager();
|
|
||||||
ContentResolver contentResolver = mContext.getContentResolver();
|
|
||||||
for (Intent intent : intents) {
|
|
||||||
if (IntentForwarderActivity.canForward(intent, source, target, packageManager,
|
|
||||||
contentResolver) != null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean shouldShowEmptyStateScreen(ResolverListAdapter listAdapter) {
|
boolean shouldShowEmptyStateScreen(ResolverListAdapter listAdapter) {
|
||||||
int count = listAdapter.getUnfilteredCount();
|
int count = listAdapter.getUnfilteredCount();
|
||||||
return (count == 0 && listAdapter.getPlaceholderCount() == 0)
|
return (count == 0 && listAdapter.getPlaceholderCount() == 0)
|
||||||
@@ -599,6 +466,98 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
|
|||||||
void onProfilePageStateChanged(int state);
|
void onProfilePageStateChanged(int state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an empty state to show for the current profile page (tab) if necessary.
|
||||||
|
* This could be used e.g. to show a blocker on a tab if device management policy doesn't
|
||||||
|
* allow to use it or there are no apps available.
|
||||||
|
*/
|
||||||
|
public interface EmptyStateProvider {
|
||||||
|
/**
|
||||||
|
* When a non-null empty state is returned the corresponding profile page will show
|
||||||
|
* this empty state
|
||||||
|
* @param resolverListAdapter the current adapter
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
default EmptyState getEmptyState(ResolverListAdapter resolverListAdapter) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empty state provider that combines multiple providers. Providers earlier in the list have
|
||||||
|
* priority, that is if there is a provider that returns non-null empty state then all further
|
||||||
|
* providers will be ignored.
|
||||||
|
*/
|
||||||
|
public static class CompositeEmptyStateProvider implements EmptyStateProvider {
|
||||||
|
|
||||||
|
private final EmptyStateProvider[] mProviders;
|
||||||
|
|
||||||
|
public CompositeEmptyStateProvider(EmptyStateProvider... providers) {
|
||||||
|
mProviders = providers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public EmptyState getEmptyState(ResolverListAdapter resolverListAdapter) {
|
||||||
|
for (EmptyStateProvider provider : mProviders) {
|
||||||
|
EmptyState emptyState = provider.getEmptyState(resolverListAdapter);
|
||||||
|
if (emptyState != null) {
|
||||||
|
return emptyState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes how the blocked empty state should look like for a profile tab
|
||||||
|
*/
|
||||||
|
public interface EmptyState {
|
||||||
|
/**
|
||||||
|
* Title that will be shown on the empty state
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
default String getTitle() { return null; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtitle that will be shown underneath the title on the empty state
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
default String getSubtitle() { return null; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If non-null then a button will be shown and this listener will be called
|
||||||
|
* when the button is clicked
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
default ClickListener getButtonClickListener() { return null; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true then default text ('No apps can perform this action') and style for the empty
|
||||||
|
* state will be applied, title and subtitle will be ignored.
|
||||||
|
*/
|
||||||
|
default boolean useDefaultEmptyView() { return false; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if for this empty state we should skip rebuilding of the apps list
|
||||||
|
* for this tab.
|
||||||
|
*/
|
||||||
|
default boolean shouldSkipDataRebuild() { return false; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when empty state is shown, could be used e.g. to track analytics events
|
||||||
|
*/
|
||||||
|
default void onEmptyStateShown() {}
|
||||||
|
|
||||||
|
interface ClickListener {
|
||||||
|
void onClick(TabControl currentTab);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TabControl {
|
||||||
|
void showSpinner();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listener for when the user switches on the work profile from the work tab.
|
* Listener for when the user switches on the work profile from the work tab.
|
||||||
*/
|
*/
|
||||||
@@ -612,14 +571,7 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
|
|||||||
/**
|
/**
|
||||||
* Describes an injector to be used for cross profile functionality. Overridable for testing.
|
* Describes an injector to be used for cross profile functionality. Overridable for testing.
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
public interface QuietModeManager {
|
||||||
public interface Injector {
|
|
||||||
/**
|
|
||||||
* Returns {@code true} if at least one of the provided {@code intents} can be forwarded
|
|
||||||
* from {@code sourceUserId} to {@code targetUserId}.
|
|
||||||
*/
|
|
||||||
boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId, int targetUserId);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the given profile is in quiet mode or not.
|
* Returns whether the given profile is in quiet mode or not.
|
||||||
*/
|
*/
|
||||||
@@ -629,5 +581,15 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
|
|||||||
* Enables or disables quiet mode for a managed profile.
|
* Enables or disables quiet mode for a managed profile.
|
||||||
*/
|
*/
|
||||||
void requestQuietModeEnabled(boolean enabled, UserHandle workProfileUserHandle);
|
void requestQuietModeEnabled(boolean enabled, UserHandle workProfileUserHandle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should be called when the work profile enabled broadcast received
|
||||||
|
*/
|
||||||
|
void markWorkProfileEnabledBroadcastReceived();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if enabling of work profile is in progress
|
||||||
|
*/
|
||||||
|
boolean isWaitingToEnableWorkProfile();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,6 +16,14 @@
|
|||||||
|
|
||||||
package com.android.internal.app;
|
package com.android.internal.app;
|
||||||
|
|
||||||
|
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_PERSONAL;
|
||||||
|
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_WORK;
|
||||||
|
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_PERSONAL;
|
||||||
|
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_WORK;
|
||||||
|
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
|
||||||
|
import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL;
|
||||||
|
import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK;
|
||||||
|
|
||||||
import static com.android.internal.util.LatencyTracker.ACTION_LOAD_SHARE_SHEET;
|
import static com.android.internal.util.LatencyTracker.ACTION_LOAD_SHARE_SHEET;
|
||||||
|
|
||||||
import static java.lang.annotation.RetentionPolicy.SOURCE;
|
import static java.lang.annotation.RetentionPolicy.SOURCE;
|
||||||
@@ -114,6 +122,9 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import com.android.internal.R;
|
import com.android.internal.R;
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyState;
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyStateProvider;
|
||||||
|
import com.android.internal.app.NoCrossProfileEmptyStateProvider.DevicePolicyBlockerEmptyState;
|
||||||
import com.android.internal.app.ResolverListAdapter.ActivityInfoPresentationGetter;
|
import com.android.internal.app.ResolverListAdapter.ActivityInfoPresentationGetter;
|
||||||
import com.android.internal.app.ResolverListAdapter.ViewHolder;
|
import com.android.internal.app.ResolverListAdapter.ViewHolder;
|
||||||
import com.android.internal.app.chooser.ChooserTargetInfo;
|
import com.android.internal.app.chooser.ChooserTargetInfo;
|
||||||
@@ -830,6 +841,41 @@ public class ChooserActivity extends ResolverActivity implements
|
|||||||
return mChooserMultiProfilePagerAdapter;
|
return mChooserMultiProfilePagerAdapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected EmptyStateProvider createBlockerEmptyStateProvider() {
|
||||||
|
final boolean isSendAction = isSendAction(getTargetIntent());
|
||||||
|
|
||||||
|
final EmptyState noWorkToPersonalEmptyState =
|
||||||
|
new DevicePolicyBlockerEmptyState(
|
||||||
|
/* context= */ this,
|
||||||
|
/* devicePolicyStringTitleId= */ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE,
|
||||||
|
/* defaultTitleResource= */ R.string.resolver_cross_profile_blocked,
|
||||||
|
/* devicePolicyStringSubtitleId= */
|
||||||
|
isSendAction ? RESOLVER_CANT_SHARE_WITH_PERSONAL : RESOLVER_CANT_ACCESS_PERSONAL,
|
||||||
|
/* defaultSubtitleResource= */
|
||||||
|
isSendAction ? R.string.resolver_cant_share_with_personal_apps_explanation
|
||||||
|
: R.string.resolver_cant_access_personal_apps_explanation,
|
||||||
|
/* devicePolicyEventId= */ RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL,
|
||||||
|
/* devicePolicyEventCategory= */ ResolverActivity.METRICS_CATEGORY_CHOOSER);
|
||||||
|
|
||||||
|
final EmptyState noPersonalToWorkEmptyState =
|
||||||
|
new DevicePolicyBlockerEmptyState(
|
||||||
|
/* context= */ this,
|
||||||
|
/* devicePolicyStringTitleId= */ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE,
|
||||||
|
/* defaultTitleResource= */ R.string.resolver_cross_profile_blocked,
|
||||||
|
/* devicePolicyStringSubtitleId= */
|
||||||
|
isSendAction ? RESOLVER_CANT_SHARE_WITH_WORK : RESOLVER_CANT_ACCESS_WORK,
|
||||||
|
/* defaultSubtitleResource= */
|
||||||
|
isSendAction ? R.string.resolver_cant_share_with_work_apps_explanation
|
||||||
|
: R.string.resolver_cant_access_work_apps_explanation,
|
||||||
|
/* devicePolicyEventId= */ RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK,
|
||||||
|
/* devicePolicyEventCategory= */ ResolverActivity.METRICS_CATEGORY_CHOOSER);
|
||||||
|
|
||||||
|
return new NoCrossProfileEmptyStateProvider(getPersonalProfileUserHandle(),
|
||||||
|
noWorkToPersonalEmptyState, noPersonalToWorkEmptyState,
|
||||||
|
createCrossProfileIntentsChecker(), createMyUserIdProvider());
|
||||||
|
}
|
||||||
|
|
||||||
private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForOneProfile(
|
private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForOneProfile(
|
||||||
Intent[] initialIntents,
|
Intent[] initialIntents,
|
||||||
List<ResolveInfo> rList,
|
List<ResolveInfo> rList,
|
||||||
@@ -844,9 +890,10 @@ public class ChooserActivity extends ResolverActivity implements
|
|||||||
return new ChooserMultiProfilePagerAdapter(
|
return new ChooserMultiProfilePagerAdapter(
|
||||||
/* context */ this,
|
/* context */ this,
|
||||||
adapter,
|
adapter,
|
||||||
getPersonalProfileUserHandle(),
|
createEmptyStateProvider(/* workProfileUserHandle= */ null),
|
||||||
|
mQuietModeManager,
|
||||||
/* workProfileUserHandle= */ null,
|
/* workProfileUserHandle= */ null,
|
||||||
isSendAction(getTargetIntent()), mMaxTargetsPerRow);
|
mMaxTargetsPerRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForTwoProfiles(
|
private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForTwoProfiles(
|
||||||
@@ -872,10 +919,11 @@ public class ChooserActivity extends ResolverActivity implements
|
|||||||
/* context */ this,
|
/* context */ this,
|
||||||
personalAdapter,
|
personalAdapter,
|
||||||
workAdapter,
|
workAdapter,
|
||||||
|
createEmptyStateProvider(/* workProfileUserHandle= */ getWorkProfileUserHandle()),
|
||||||
|
mQuietModeManager,
|
||||||
selectedProfile,
|
selectedProfile,
|
||||||
getPersonalProfileUserHandle(),
|
|
||||||
getWorkProfileUserHandle(),
|
getWorkProfileUserHandle(),
|
||||||
isSendAction(getTargetIntent()), mMaxTargetsPerRow);
|
mMaxTargetsPerRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int findSelectedProfile() {
|
private int findSelectedProfile() {
|
||||||
|
|||||||
@@ -16,17 +16,7 @@
|
|||||||
|
|
||||||
package com.android.internal.app;
|
package com.android.internal.app;
|
||||||
|
|
||||||
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_PERSONAL;
|
|
||||||
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_WORK;
|
|
||||||
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_PERSONAL;
|
|
||||||
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_WORK;
|
|
||||||
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
|
|
||||||
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_PERSONAL_APPS;
|
|
||||||
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_WORK_APPS;
|
|
||||||
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PAUSED_TITLE;
|
|
||||||
|
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.app.admin.DevicePolicyManager;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -47,37 +37,37 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
|
|||||||
private static final int SINGLE_CELL_SPAN_SIZE = 1;
|
private static final int SINGLE_CELL_SPAN_SIZE = 1;
|
||||||
|
|
||||||
private final ChooserProfileDescriptor[] mItems;
|
private final ChooserProfileDescriptor[] mItems;
|
||||||
private final boolean mIsSendAction;
|
|
||||||
private int mBottomOffset;
|
private int mBottomOffset;
|
||||||
private int mMaxTargetsPerRow;
|
private int mMaxTargetsPerRow;
|
||||||
|
|
||||||
ChooserMultiProfilePagerAdapter(Context context,
|
ChooserMultiProfilePagerAdapter(Context context,
|
||||||
ChooserActivity.ChooserGridAdapter adapter,
|
ChooserActivity.ChooserGridAdapter adapter,
|
||||||
UserHandle personalProfileUserHandle,
|
EmptyStateProvider emptyStateProvider,
|
||||||
|
QuietModeManager quietModeManager,
|
||||||
UserHandle workProfileUserHandle,
|
UserHandle workProfileUserHandle,
|
||||||
boolean isSendAction, int maxTargetsPerRow) {
|
int maxTargetsPerRow) {
|
||||||
super(context, /* currentPage */ 0, personalProfileUserHandle, workProfileUserHandle);
|
super(context, /* currentPage */ 0, emptyStateProvider, quietModeManager,
|
||||||
|
workProfileUserHandle);
|
||||||
mItems = new ChooserProfileDescriptor[] {
|
mItems = new ChooserProfileDescriptor[] {
|
||||||
createProfileDescriptor(adapter)
|
createProfileDescriptor(adapter)
|
||||||
};
|
};
|
||||||
mIsSendAction = isSendAction;
|
|
||||||
mMaxTargetsPerRow = maxTargetsPerRow;
|
mMaxTargetsPerRow = maxTargetsPerRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChooserMultiProfilePagerAdapter(Context context,
|
ChooserMultiProfilePagerAdapter(Context context,
|
||||||
ChooserActivity.ChooserGridAdapter personalAdapter,
|
ChooserActivity.ChooserGridAdapter personalAdapter,
|
||||||
ChooserActivity.ChooserGridAdapter workAdapter,
|
ChooserActivity.ChooserGridAdapter workAdapter,
|
||||||
|
EmptyStateProvider emptyStateProvider,
|
||||||
|
QuietModeManager quietModeManager,
|
||||||
@Profile int defaultProfile,
|
@Profile int defaultProfile,
|
||||||
UserHandle personalProfileUserHandle,
|
|
||||||
UserHandle workProfileUserHandle,
|
UserHandle workProfileUserHandle,
|
||||||
boolean isSendAction, int maxTargetsPerRow) {
|
int maxTargetsPerRow) {
|
||||||
super(context, /* currentPage */ defaultProfile, personalProfileUserHandle,
|
super(context, /* currentPage */ defaultProfile, emptyStateProvider,
|
||||||
workProfileUserHandle);
|
quietModeManager, workProfileUserHandle);
|
||||||
mItems = new ChooserProfileDescriptor[] {
|
mItems = new ChooserProfileDescriptor[] {
|
||||||
createProfileDescriptor(personalAdapter),
|
createProfileDescriptor(personalAdapter),
|
||||||
createProfileDescriptor(workAdapter)
|
createProfileDescriptor(workAdapter)
|
||||||
};
|
};
|
||||||
mIsSendAction = isSendAction;
|
|
||||||
mMaxTargetsPerRow = maxTargetsPerRow;
|
mMaxTargetsPerRow = maxTargetsPerRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,112 +182,6 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
|
|||||||
return getListViewForIndex(1 - getCurrentPage());
|
return getListViewForIndex(1 - getCurrentPage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
String getMetricsCategory() {
|
|
||||||
return ResolverActivity.METRICS_CATEGORY_CHOOSER;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void showWorkProfileOffEmptyState(ResolverListAdapter activeListAdapter,
|
|
||||||
View.OnClickListener listener) {
|
|
||||||
showEmptyState(activeListAdapter,
|
|
||||||
getWorkAppPausedTitle(),
|
|
||||||
/* subtitle = */ null,
|
|
||||||
listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void showNoPersonalToWorkIntentsEmptyState(ResolverListAdapter activeListAdapter) {
|
|
||||||
if (mIsSendAction) {
|
|
||||||
showEmptyState(activeListAdapter,
|
|
||||||
getCrossProfileBlockedTitle(),
|
|
||||||
getCantShareWithWorkMessage());
|
|
||||||
} else {
|
|
||||||
showEmptyState(activeListAdapter,
|
|
||||||
getCrossProfileBlockedTitle(),
|
|
||||||
getCantAccessWorkMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void showNoWorkToPersonalIntentsEmptyState(ResolverListAdapter activeListAdapter) {
|
|
||||||
if (mIsSendAction) {
|
|
||||||
showEmptyState(activeListAdapter,
|
|
||||||
getCrossProfileBlockedTitle(),
|
|
||||||
getCantShareWithPersonalMessage());
|
|
||||||
} else {
|
|
||||||
showEmptyState(activeListAdapter,
|
|
||||||
getCrossProfileBlockedTitle(),
|
|
||||||
getCantAccessPersonalMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void showNoPersonalAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
|
|
||||||
showEmptyState(listAdapter, getNoPersonalAppsAvailableMessage(), /* subtitle= */ null);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void showNoWorkAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
|
|
||||||
showEmptyState(listAdapter, getNoWorkAppsAvailableMessage(), /* subtitle = */ null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getWorkAppPausedTitle() {
|
|
||||||
return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
|
|
||||||
RESOLVER_WORK_PAUSED_TITLE,
|
|
||||||
() -> getContext().getString(R.string.resolver_turn_on_work_apps));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getCrossProfileBlockedTitle() {
|
|
||||||
return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
|
|
||||||
RESOLVER_CROSS_PROFILE_BLOCKED_TITLE,
|
|
||||||
() -> getContext().getString(R.string.resolver_cross_profile_blocked));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getCantShareWithWorkMessage() {
|
|
||||||
return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
|
|
||||||
RESOLVER_CANT_SHARE_WITH_WORK,
|
|
||||||
() -> getContext().getString(
|
|
||||||
R.string.resolver_cant_share_with_work_apps_explanation));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getCantShareWithPersonalMessage() {
|
|
||||||
return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
|
|
||||||
RESOLVER_CANT_SHARE_WITH_PERSONAL,
|
|
||||||
() -> getContext().getString(
|
|
||||||
R.string.resolver_cant_share_with_personal_apps_explanation));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getCantAccessWorkMessage() {
|
|
||||||
return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
|
|
||||||
RESOLVER_CANT_ACCESS_WORK,
|
|
||||||
() -> getContext().getString(
|
|
||||||
R.string.resolver_cant_access_work_apps_explanation));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getCantAccessPersonalMessage() {
|
|
||||||
return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
|
|
||||||
RESOLVER_CANT_ACCESS_PERSONAL,
|
|
||||||
() -> getContext().getString(
|
|
||||||
R.string.resolver_cant_access_personal_apps_explanation));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getNoWorkAppsAvailableMessage() {
|
|
||||||
return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
|
|
||||||
RESOLVER_NO_WORK_APPS,
|
|
||||||
() -> getContext().getString(
|
|
||||||
R.string.resolver_no_work_apps_available));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getNoPersonalAppsAvailableMessage() {
|
|
||||||
return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
|
|
||||||
RESOLVER_NO_PERSONAL_APPS,
|
|
||||||
() -> getContext().getString(
|
|
||||||
R.string.resolver_no_personal_apps_available));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void setEmptyStateBottomOffset(int bottomOffset) {
|
void setEmptyStateBottomOffset(int bottomOffset) {
|
||||||
mBottomOffset = bottomOffset;
|
mBottomOffset = bottomOffset;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_PERSONAL_APPS;
|
||||||
|
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_WORK_APPS;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.app.admin.DevicePolicyEventLogger;
|
||||||
|
import android.app.admin.DevicePolicyManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.stats.devicepolicy.nano.DevicePolicyEnums;
|
||||||
|
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyState;
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyStateProvider;
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.MyUserIdProvider;
|
||||||
|
import com.android.internal.R;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chooser/ResolverActivity empty state provider that returns empty state which is shown when
|
||||||
|
* there are no apps available.
|
||||||
|
*/
|
||||||
|
public class NoAppsAvailableEmptyStateProvider implements EmptyStateProvider {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private final Context mContext;
|
||||||
|
@Nullable
|
||||||
|
private final UserHandle mWorkProfileUserHandle;
|
||||||
|
@Nullable
|
||||||
|
private final UserHandle mPersonalProfileUserHandle;
|
||||||
|
@NonNull
|
||||||
|
private final String mMetricsCategory;
|
||||||
|
@NonNull
|
||||||
|
private final MyUserIdProvider mMyUserIdProvider;
|
||||||
|
|
||||||
|
public NoAppsAvailableEmptyStateProvider(Context context, UserHandle workProfileUserHandle,
|
||||||
|
UserHandle personalProfileUserHandle, String metricsCategory,
|
||||||
|
MyUserIdProvider myUserIdProvider) {
|
||||||
|
mContext = context;
|
||||||
|
mWorkProfileUserHandle = workProfileUserHandle;
|
||||||
|
mPersonalProfileUserHandle = personalProfileUserHandle;
|
||||||
|
mMetricsCategory = metricsCategory;
|
||||||
|
mMyUserIdProvider = myUserIdProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("ReferenceEquality")
|
||||||
|
public EmptyState getEmptyState(ResolverListAdapter resolverListAdapter) {
|
||||||
|
UserHandle listUserHandle = resolverListAdapter.getUserHandle();
|
||||||
|
|
||||||
|
if (mWorkProfileUserHandle != null
|
||||||
|
&& (mMyUserIdProvider.getMyUserId() == listUserHandle.getIdentifier()
|
||||||
|
|| !hasAppsInOtherProfile(resolverListAdapter))) {
|
||||||
|
|
||||||
|
String title;
|
||||||
|
if (listUserHandle == mPersonalProfileUserHandle) {
|
||||||
|
title = mContext.getSystemService(
|
||||||
|
DevicePolicyManager.class).getResources().getString(
|
||||||
|
RESOLVER_NO_PERSONAL_APPS,
|
||||||
|
() -> mContext.getString(R.string.resolver_no_personal_apps_available));
|
||||||
|
} else {
|
||||||
|
title = mContext.getSystemService(
|
||||||
|
DevicePolicyManager.class).getResources().getString(
|
||||||
|
RESOLVER_NO_WORK_APPS,
|
||||||
|
() -> mContext.getString(R.string.resolver_no_work_apps_available));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new NoAppsAvailableEmptyState(
|
||||||
|
title, mMetricsCategory,
|
||||||
|
/* isPersonalProfile= */ listUserHandle == mPersonalProfileUserHandle
|
||||||
|
);
|
||||||
|
} else if (mWorkProfileUserHandle == null) {
|
||||||
|
// Return default empty state without tracking
|
||||||
|
return new DefaultEmptyState();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasAppsInOtherProfile(ResolverListAdapter adapter) {
|
||||||
|
if (mWorkProfileUserHandle == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
List<ResolverActivity.ResolvedComponentInfo> resolversForIntent =
|
||||||
|
adapter.getResolversForUser(UserHandle.of(mMyUserIdProvider.getMyUserId()));
|
||||||
|
for (ResolverActivity.ResolvedComponentInfo info : resolversForIntent) {
|
||||||
|
ResolveInfo resolveInfo = info.getResolveInfoAt(0);
|
||||||
|
if (resolveInfo.targetUserId != UserHandle.USER_CURRENT) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DefaultEmptyState implements EmptyState {
|
||||||
|
@Override
|
||||||
|
public boolean useDefaultEmptyView() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NoAppsAvailableEmptyState implements EmptyState {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private String mTitle;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private String mMetricsCategory;
|
||||||
|
|
||||||
|
private boolean mIsPersonalProfile;
|
||||||
|
|
||||||
|
public NoAppsAvailableEmptyState(String title, String metricsCategory,
|
||||||
|
boolean isPersonalProfile) {
|
||||||
|
mTitle = title;
|
||||||
|
mMetricsCategory = metricsCategory;
|
||||||
|
mIsPersonalProfile = isPersonalProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getTitle() {
|
||||||
|
return mTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEmptyStateShown() {
|
||||||
|
DevicePolicyEventLogger.createEvent(
|
||||||
|
DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_APPS_RESOLVED)
|
||||||
|
.setStrings(mMetricsCategory)
|
||||||
|
.setBoolean(/*isPersonalProfile*/ mIsPersonalProfile)
|
||||||
|
.write();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.annotation.StringRes;
|
||||||
|
import android.app.admin.DevicePolicyEventLogger;
|
||||||
|
import android.app.admin.DevicePolicyManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.CrossProfileIntentsChecker;
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyState;
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyStateProvider;
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.MyUserIdProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empty state provider that does not allow cross profile sharing, it will return a blocker
|
||||||
|
* in case if the profile of the current tab is not the same as the profile of the calling app.
|
||||||
|
*/
|
||||||
|
public class NoCrossProfileEmptyStateProvider implements EmptyStateProvider {
|
||||||
|
|
||||||
|
private final UserHandle mPersonalProfileUserHandle;
|
||||||
|
private final EmptyState mNoWorkToPersonalEmptyState;
|
||||||
|
private final EmptyState mNoPersonalToWorkEmptyState;
|
||||||
|
private final CrossProfileIntentsChecker mCrossProfileIntentsChecker;
|
||||||
|
private final MyUserIdProvider mUserIdProvider;
|
||||||
|
|
||||||
|
public NoCrossProfileEmptyStateProvider(UserHandle personalUserHandle,
|
||||||
|
EmptyState noWorkToPersonalEmptyState,
|
||||||
|
EmptyState noPersonalToWorkEmptyState,
|
||||||
|
CrossProfileIntentsChecker crossProfileIntentsChecker,
|
||||||
|
MyUserIdProvider myUserIdProvider) {
|
||||||
|
mPersonalProfileUserHandle = personalUserHandle;
|
||||||
|
mNoWorkToPersonalEmptyState = noWorkToPersonalEmptyState;
|
||||||
|
mNoPersonalToWorkEmptyState = noPersonalToWorkEmptyState;
|
||||||
|
mCrossProfileIntentsChecker = crossProfileIntentsChecker;
|
||||||
|
mUserIdProvider = myUserIdProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public EmptyState getEmptyState(ResolverListAdapter resolverListAdapter) {
|
||||||
|
boolean shouldShowBlocker =
|
||||||
|
mUserIdProvider.getMyUserId() != resolverListAdapter.getUserHandle().getIdentifier()
|
||||||
|
&& !mCrossProfileIntentsChecker
|
||||||
|
.hasCrossProfileIntents(resolverListAdapter.getIntents(),
|
||||||
|
mUserIdProvider.getMyUserId(),
|
||||||
|
resolverListAdapter.getUserHandle().getIdentifier());
|
||||||
|
|
||||||
|
if (!shouldShowBlocker) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolverListAdapter.getUserHandle().equals(mPersonalProfileUserHandle)) {
|
||||||
|
return mNoWorkToPersonalEmptyState;
|
||||||
|
} else {
|
||||||
|
return mNoPersonalToWorkEmptyState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empty state that gets strings from the device policy manager and tracks events into
|
||||||
|
* event logger of the device policy events.
|
||||||
|
*/
|
||||||
|
public static class DevicePolicyBlockerEmptyState implements EmptyState {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private final Context mContext;
|
||||||
|
private final String mDevicePolicyStringTitleId;
|
||||||
|
@StringRes
|
||||||
|
private final int mDefaultTitleResource;
|
||||||
|
private final String mDevicePolicyStringSubtitleId;
|
||||||
|
@StringRes
|
||||||
|
private final int mDefaultSubtitleResource;
|
||||||
|
private final int mEventId;
|
||||||
|
@NonNull
|
||||||
|
private final String mEventCategory;
|
||||||
|
|
||||||
|
public DevicePolicyBlockerEmptyState(Context context, String devicePolicyStringTitleId,
|
||||||
|
@StringRes int defaultTitleResource, String devicePolicyStringSubtitleId,
|
||||||
|
@StringRes int defaultSubtitleResource,
|
||||||
|
int devicePolicyEventId, String devicePolicyEventCategory) {
|
||||||
|
mContext = context;
|
||||||
|
mDevicePolicyStringTitleId = devicePolicyStringTitleId;
|
||||||
|
mDefaultTitleResource = defaultTitleResource;
|
||||||
|
mDevicePolicyStringSubtitleId = devicePolicyStringSubtitleId;
|
||||||
|
mDefaultSubtitleResource = defaultSubtitleResource;
|
||||||
|
mEventId = devicePolicyEventId;
|
||||||
|
mEventCategory = devicePolicyEventCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getTitle() {
|
||||||
|
return mContext.getSystemService(DevicePolicyManager.class).getResources().getString(
|
||||||
|
mDevicePolicyStringTitleId,
|
||||||
|
() -> mContext.getString(mDefaultTitleResource));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getSubtitle() {
|
||||||
|
return mContext.getSystemService(DevicePolicyManager.class).getResources().getString(
|
||||||
|
mDevicePolicyStringSubtitleId,
|
||||||
|
() -> mContext.getString(mDefaultSubtitleResource));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEmptyStateShown() {
|
||||||
|
DevicePolicyEventLogger.createEvent(mEventId)
|
||||||
|
.setStrings(mEventCategory)
|
||||||
|
.write();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldSkipDataRebuild() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,9 @@ package com.android.internal.app;
|
|||||||
import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
|
import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
|
||||||
import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_PERSONAL;
|
import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_PERSONAL;
|
||||||
import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_WORK;
|
import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_WORK;
|
||||||
|
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_PERSONAL;
|
||||||
|
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_WORK;
|
||||||
|
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
|
||||||
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERSONAL_TAB;
|
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERSONAL_TAB;
|
||||||
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERSONAL_TAB_ACCESSIBILITY;
|
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERSONAL_TAB_ACCESSIBILITY;
|
||||||
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PROFILE_NOT_SUPPORTED;
|
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PROFILE_NOT_SUPPORTED;
|
||||||
@@ -26,6 +29,8 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK
|
|||||||
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB_ACCESSIBILITY;
|
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB_ACCESSIBILITY;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||||
import static android.content.PermissionChecker.PID_UNKNOWN;
|
import static android.content.PermissionChecker.PID_UNKNOWN;
|
||||||
|
import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL;
|
||||||
|
import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK;
|
||||||
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
|
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
|
||||||
|
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
@@ -57,6 +62,7 @@ import android.content.res.TypedArray;
|
|||||||
import android.graphics.Insets;
|
import android.graphics.Insets;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.AsyncTask;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.PatternMatcher;
|
import android.os.PatternMatcher;
|
||||||
@@ -93,7 +99,14 @@ import android.widget.Toast;
|
|||||||
|
|
||||||
import com.android.internal.R;
|
import com.android.internal.R;
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.CompositeEmptyStateProvider;
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.CrossProfileIntentsChecker;
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyStateProvider;
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.MyUserIdProvider;
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.OnSwitchOnWorkSelectedListener;
|
||||||
import com.android.internal.app.AbstractMultiProfilePagerAdapter.Profile;
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.Profile;
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.QuietModeManager;
|
||||||
|
import com.android.internal.app.NoCrossProfileEmptyStateProvider.DevicePolicyBlockerEmptyState;
|
||||||
import com.android.internal.app.chooser.ChooserTargetInfo;
|
import com.android.internal.app.chooser.ChooserTargetInfo;
|
||||||
import com.android.internal.app.chooser.DisplayResolveInfo;
|
import com.android.internal.app.chooser.DisplayResolveInfo;
|
||||||
import com.android.internal.app.chooser.TargetInfo;
|
import com.android.internal.app.chooser.TargetInfo;
|
||||||
@@ -186,6 +199,8 @@ public class ResolverActivity extends Activity implements
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
protected AbstractMultiProfilePagerAdapter mMultiProfilePagerAdapter;
|
protected AbstractMultiProfilePagerAdapter mMultiProfilePagerAdapter;
|
||||||
|
|
||||||
|
protected QuietModeManager mQuietModeManager;
|
||||||
|
|
||||||
// Intent extra for connected audio devices
|
// Intent extra for connected audio devices
|
||||||
public static final String EXTRA_IS_AUDIO_CAPTURE_DEVICE = "is_audio_capture_device";
|
public static final String EXTRA_IS_AUDIO_CAPTURE_DEVICE = "is_audio_capture_device";
|
||||||
|
|
||||||
@@ -217,6 +232,9 @@ public class ResolverActivity extends Activity implements
|
|||||||
|
|
||||||
private UserHandle mWorkProfileUserHandle;
|
private UserHandle mWorkProfileUserHandle;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private OnSwitchOnWorkSelectedListener mOnSwitchOnWorkSelectedListener;
|
||||||
|
|
||||||
protected final LatencyTracker mLatencyTracker = getLatencyTracker();
|
protected final LatencyTracker mLatencyTracker = getLatencyTracker();
|
||||||
|
|
||||||
private LatencyTracker getLatencyTracker() {
|
private LatencyTracker getLatencyTracker() {
|
||||||
@@ -375,6 +393,8 @@ public class ResolverActivity extends Activity implements
|
|||||||
setTheme(appliedThemeResId());
|
setTheme(appliedThemeResId());
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
mQuietModeManager = createQuietModeManager();
|
||||||
|
|
||||||
// Determine whether we should show that intent is forwarded
|
// Determine whether we should show that intent is forwarded
|
||||||
// from managed profile to owner or other way around.
|
// from managed profile to owner or other way around.
|
||||||
setProfileSwitchMessage(intent.getContentUserHint());
|
setProfileSwitchMessage(intent.getContentUserHint());
|
||||||
@@ -475,6 +495,111 @@ public class ResolverActivity extends Activity implements
|
|||||||
return resolverMultiProfilePagerAdapter;
|
return resolverMultiProfilePagerAdapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
protected MyUserIdProvider createMyUserIdProvider() {
|
||||||
|
return new MyUserIdProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
protected CrossProfileIntentsChecker createCrossProfileIntentsChecker() {
|
||||||
|
return new CrossProfileIntentsChecker(getContentResolver());
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
protected QuietModeManager createQuietModeManager() {
|
||||||
|
UserManager userManager = getSystemService(UserManager.class);
|
||||||
|
return new QuietModeManager() {
|
||||||
|
|
||||||
|
private boolean mIsWaitingToEnableWorkProfile = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
|
||||||
|
return userManager.isQuietModeEnabled(workProfileUserHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void requestQuietModeEnabled(boolean enabled, UserHandle workProfileUserHandle) {
|
||||||
|
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
||||||
|
userManager.requestQuietModeEnabled(enabled, workProfileUserHandle);
|
||||||
|
});
|
||||||
|
mIsWaitingToEnableWorkProfile = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markWorkProfileEnabledBroadcastReceived() {
|
||||||
|
mIsWaitingToEnableWorkProfile = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isWaitingToEnableWorkProfile() {
|
||||||
|
return mIsWaitingToEnableWorkProfile;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected EmptyStateProvider createBlockerEmptyStateProvider() {
|
||||||
|
final boolean shouldShowNoCrossProfileIntentsEmptyState = getUser().equals(getIntentUser());
|
||||||
|
|
||||||
|
if (!shouldShowNoCrossProfileIntentsEmptyState) {
|
||||||
|
// Implementation that doesn't show any blockers
|
||||||
|
return new EmptyStateProvider() {};
|
||||||
|
}
|
||||||
|
|
||||||
|
final AbstractMultiProfilePagerAdapter.EmptyState
|
||||||
|
noWorkToPersonalEmptyState =
|
||||||
|
new DevicePolicyBlockerEmptyState(/* context= */ this,
|
||||||
|
/* devicePolicyStringTitleId= */ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE,
|
||||||
|
/* defaultTitleResource= */ R.string.resolver_cross_profile_blocked,
|
||||||
|
/* devicePolicyStringSubtitleId= */ RESOLVER_CANT_ACCESS_PERSONAL,
|
||||||
|
/* defaultSubtitleResource= */
|
||||||
|
R.string.resolver_cant_access_personal_apps_explanation,
|
||||||
|
/* devicePolicyEventId= */ RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL,
|
||||||
|
/* devicePolicyEventCategory= */ ResolverActivity.METRICS_CATEGORY_RESOLVER);
|
||||||
|
|
||||||
|
final AbstractMultiProfilePagerAdapter.EmptyState noPersonalToWorkEmptyState =
|
||||||
|
new DevicePolicyBlockerEmptyState(/* context= */ this,
|
||||||
|
/* devicePolicyStringTitleId= */ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE,
|
||||||
|
/* defaultTitleResource= */ R.string.resolver_cross_profile_blocked,
|
||||||
|
/* devicePolicyStringSubtitleId= */ RESOLVER_CANT_ACCESS_WORK,
|
||||||
|
/* defaultSubtitleResource= */
|
||||||
|
R.string.resolver_cant_access_work_apps_explanation,
|
||||||
|
/* devicePolicyEventId= */ RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK,
|
||||||
|
/* devicePolicyEventCategory= */ ResolverActivity.METRICS_CATEGORY_RESOLVER);
|
||||||
|
|
||||||
|
return new NoCrossProfileEmptyStateProvider(getPersonalProfileUserHandle(),
|
||||||
|
noWorkToPersonalEmptyState, noPersonalToWorkEmptyState,
|
||||||
|
createCrossProfileIntentsChecker(), createMyUserIdProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected EmptyStateProvider createEmptyStateProvider(
|
||||||
|
@Nullable UserHandle workProfileUserHandle) {
|
||||||
|
final EmptyStateProvider blockerEmptyStateProvider = createBlockerEmptyStateProvider();
|
||||||
|
|
||||||
|
final EmptyStateProvider workProfileOffEmptyStateProvider =
|
||||||
|
new WorkProfilePausedEmptyStateProvider(this, workProfileUserHandle,
|
||||||
|
mQuietModeManager,
|
||||||
|
/* onSwitchOnWorkSelectedListener= */
|
||||||
|
() -> { if (mOnSwitchOnWorkSelectedListener != null) {
|
||||||
|
mOnSwitchOnWorkSelectedListener.onSwitchOnWorkSelected();
|
||||||
|
}},
|
||||||
|
getMetricsCategory());
|
||||||
|
|
||||||
|
final EmptyStateProvider noAppsEmptyStateProvider = new NoAppsAvailableEmptyStateProvider(
|
||||||
|
this,
|
||||||
|
workProfileUserHandle,
|
||||||
|
getPersonalProfileUserHandle(),
|
||||||
|
getMetricsCategory(),
|
||||||
|
createMyUserIdProvider()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Return composite provider, the order matters (the higher, the more priority)
|
||||||
|
return new CompositeEmptyStateProvider(
|
||||||
|
blockerEmptyStateProvider,
|
||||||
|
workProfileOffEmptyStateProvider,
|
||||||
|
noAppsEmptyStateProvider
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private ResolverMultiProfilePagerAdapter createResolverMultiProfilePagerAdapterForOneProfile(
|
private ResolverMultiProfilePagerAdapter createResolverMultiProfilePagerAdapterForOneProfile(
|
||||||
Intent[] initialIntents,
|
Intent[] initialIntents,
|
||||||
List<ResolveInfo> rList, boolean filterLastUsed) {
|
List<ResolveInfo> rList, boolean filterLastUsed) {
|
||||||
@@ -485,10 +610,12 @@ public class ResolverActivity extends Activity implements
|
|||||||
rList,
|
rList,
|
||||||
filterLastUsed,
|
filterLastUsed,
|
||||||
/* userHandle */ UserHandle.of(UserHandle.myUserId()));
|
/* userHandle */ UserHandle.of(UserHandle.myUserId()));
|
||||||
|
QuietModeManager quietModeManager = createQuietModeManager();
|
||||||
return new ResolverMultiProfilePagerAdapter(
|
return new ResolverMultiProfilePagerAdapter(
|
||||||
/* context */ this,
|
/* context */ this,
|
||||||
adapter,
|
adapter,
|
||||||
getPersonalProfileUserHandle(),
|
createEmptyStateProvider(/* workProfileUserHandle= */ null),
|
||||||
|
quietModeManager,
|
||||||
/* workProfileUserHandle= */ null);
|
/* workProfileUserHandle= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,14 +666,15 @@ public class ResolverActivity extends Activity implements
|
|||||||
(filterLastUsed && UserHandle.myUserId()
|
(filterLastUsed && UserHandle.myUserId()
|
||||||
== workProfileUserHandle.getIdentifier()),
|
== workProfileUserHandle.getIdentifier()),
|
||||||
/* userHandle */ workProfileUserHandle);
|
/* userHandle */ workProfileUserHandle);
|
||||||
|
QuietModeManager quietModeManager = createQuietModeManager();
|
||||||
return new ResolverMultiProfilePagerAdapter(
|
return new ResolverMultiProfilePagerAdapter(
|
||||||
/* context */ this,
|
/* context */ this,
|
||||||
personalAdapter,
|
personalAdapter,
|
||||||
workAdapter,
|
workAdapter,
|
||||||
|
createEmptyStateProvider(getWorkProfileUserHandle()),
|
||||||
|
quietModeManager,
|
||||||
selectedProfile,
|
selectedProfile,
|
||||||
getPersonalProfileUserHandle(),
|
getWorkProfileUserHandle());
|
||||||
getWorkProfileUserHandle(),
|
|
||||||
/* shouldShowNoCrossProfileIntentsEmptyState= */ getUser().equals(intentUser));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int appliedThemeResId() {
|
protected int appliedThemeResId() {
|
||||||
@@ -853,9 +981,9 @@ public class ResolverActivity extends Activity implements
|
|||||||
}
|
}
|
||||||
mRegistered = true;
|
mRegistered = true;
|
||||||
}
|
}
|
||||||
if (shouldShowTabs() && mMultiProfilePagerAdapter.isWaitingToEnableWorkProfile()) {
|
if (shouldShowTabs() && mQuietModeManager.isWaitingToEnableWorkProfile()) {
|
||||||
if (mMultiProfilePagerAdapter.isQuietModeEnabled(getWorkProfileUserHandle())) {
|
if (mQuietModeManager.isQuietModeEnabled(getWorkProfileUserHandle())) {
|
||||||
mMultiProfilePagerAdapter.markWorkProfileEnabledBroadcastReceived();
|
mQuietModeManager.markWorkProfileEnabledBroadcastReceived();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged();
|
mMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged();
|
||||||
@@ -1815,13 +1943,12 @@ public class ResolverActivity extends Activity implements
|
|||||||
onHorizontalSwipeStateChanged(state);
|
onHorizontalSwipeStateChanged(state);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
mMultiProfilePagerAdapter.setOnSwitchOnWorkSelectedListener(
|
mOnSwitchOnWorkSelectedListener = () -> {
|
||||||
() -> {
|
|
||||||
final View workTab = tabHost.getTabWidget().getChildAt(1);
|
final View workTab = tabHost.getTabWidget().getChildAt(1);
|
||||||
workTab.setFocusable(true);
|
workTab.setFocusable(true);
|
||||||
workTab.setFocusableInTouchMode(true);
|
workTab.setFocusableInTouchMode(true);
|
||||||
workTab.requestFocus();
|
workTab.requestFocus();
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getPersonalTabLabel() {
|
private String getPersonalTabLabel() {
|
||||||
@@ -2082,7 +2209,7 @@ public class ResolverActivity extends Activity implements
|
|||||||
public void onHandlePackagesChanged(ResolverListAdapter listAdapter) {
|
public void onHandlePackagesChanged(ResolverListAdapter listAdapter) {
|
||||||
if (listAdapter == mMultiProfilePagerAdapter.getActiveListAdapter()) {
|
if (listAdapter == mMultiProfilePagerAdapter.getActiveListAdapter()) {
|
||||||
if (listAdapter.getUserHandle().equals(getWorkProfileUserHandle())
|
if (listAdapter.getUserHandle().equals(getWorkProfileUserHandle())
|
||||||
&& mMultiProfilePagerAdapter.isWaitingToEnableWorkProfile()) {
|
&& mQuietModeManager.isWaitingToEnableWorkProfile()) {
|
||||||
// We have just turned on the work profile and entered the pass code to start it,
|
// We have just turned on the work profile and entered the pass code to start it,
|
||||||
// now we are waiting to receive the ACTION_USER_UNLOCKED broadcast. There is no
|
// now we are waiting to receive the ACTION_USER_UNLOCKED broadcast. There is no
|
||||||
// point in reloading the list now, since the work profile user is still
|
// point in reloading the list now, since the work profile user is still
|
||||||
@@ -2134,7 +2261,7 @@ public class ResolverActivity extends Activity implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
mWorkProfileHasBeenEnabled = true;
|
mWorkProfileHasBeenEnabled = true;
|
||||||
mMultiProfilePagerAdapter.markWorkProfileEnabledBroadcastReceived();
|
mQuietModeManager.markWorkProfileEnabledBroadcastReceived();
|
||||||
} else {
|
} else {
|
||||||
// Must be an UNAVAILABLE broadcast, so we watch for the next availability
|
// Must be an UNAVAILABLE broadcast, so we watch for the next availability
|
||||||
mWorkProfileHasBeenEnabled = false;
|
mWorkProfileHasBeenEnabled = false;
|
||||||
@@ -2150,7 +2277,6 @@ public class ResolverActivity extends Activity implements
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public static final class ResolvedComponentInfo {
|
public static final class ResolvedComponentInfo {
|
||||||
public final ComponentName name;
|
public final ComponentName name;
|
||||||
private final List<Intent> mIntents = new ArrayList<>();
|
private final List<Intent> mIntents = new ArrayList<>();
|
||||||
|
|||||||
@@ -16,15 +16,7 @@
|
|||||||
|
|
||||||
package com.android.internal.app;
|
package com.android.internal.app;
|
||||||
|
|
||||||
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_PERSONAL;
|
|
||||||
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_WORK;
|
|
||||||
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
|
|
||||||
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_PERSONAL_APPS;
|
|
||||||
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_WORK_APPS;
|
|
||||||
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PAUSED_TITLE;
|
|
||||||
|
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.app.admin.DevicePolicyManager;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -43,34 +35,33 @@ import com.android.internal.widget.PagerAdapter;
|
|||||||
public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerAdapter {
|
public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerAdapter {
|
||||||
|
|
||||||
private final ResolverProfileDescriptor[] mItems;
|
private final ResolverProfileDescriptor[] mItems;
|
||||||
private final boolean mShouldShowNoCrossProfileIntentsEmptyState;
|
|
||||||
private boolean mUseLayoutWithDefault;
|
private boolean mUseLayoutWithDefault;
|
||||||
|
|
||||||
ResolverMultiProfilePagerAdapter(Context context,
|
ResolverMultiProfilePagerAdapter(Context context,
|
||||||
ResolverListAdapter adapter,
|
ResolverListAdapter adapter,
|
||||||
UserHandle personalProfileUserHandle,
|
EmptyStateProvider emptyStateProvider,
|
||||||
|
QuietModeManager quietModeManager,
|
||||||
UserHandle workProfileUserHandle) {
|
UserHandle workProfileUserHandle) {
|
||||||
super(context, /* currentPage */ 0, personalProfileUserHandle, workProfileUserHandle);
|
super(context, /* currentPage */ 0, emptyStateProvider, quietModeManager,
|
||||||
|
workProfileUserHandle);
|
||||||
mItems = new ResolverProfileDescriptor[] {
|
mItems = new ResolverProfileDescriptor[] {
|
||||||
createProfileDescriptor(adapter)
|
createProfileDescriptor(adapter)
|
||||||
};
|
};
|
||||||
mShouldShowNoCrossProfileIntentsEmptyState = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ResolverMultiProfilePagerAdapter(Context context,
|
ResolverMultiProfilePagerAdapter(Context context,
|
||||||
ResolverListAdapter personalAdapter,
|
ResolverListAdapter personalAdapter,
|
||||||
ResolverListAdapter workAdapter,
|
ResolverListAdapter workAdapter,
|
||||||
|
EmptyStateProvider emptyStateProvider,
|
||||||
|
QuietModeManager quietModeManager,
|
||||||
@Profile int defaultProfile,
|
@Profile int defaultProfile,
|
||||||
UserHandle personalProfileUserHandle,
|
UserHandle workProfileUserHandle) {
|
||||||
UserHandle workProfileUserHandle,
|
super(context, /* currentPage */ defaultProfile, emptyStateProvider, quietModeManager,
|
||||||
boolean shouldShowNoCrossProfileIntentsEmptyState) {
|
|
||||||
super(context, /* currentPage */ defaultProfile, personalProfileUserHandle,
|
|
||||||
workProfileUserHandle);
|
workProfileUserHandle);
|
||||||
mItems = new ResolverProfileDescriptor[] {
|
mItems = new ResolverProfileDescriptor[] {
|
||||||
createProfileDescriptor(personalAdapter),
|
createProfileDescriptor(personalAdapter),
|
||||||
createProfileDescriptor(workAdapter)
|
createProfileDescriptor(workAdapter)
|
||||||
};
|
};
|
||||||
mShouldShowNoCrossProfileIntentsEmptyState = shouldShowNoCrossProfileIntentsEmptyState;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResolverProfileDescriptor createProfileDescriptor(
|
private ResolverProfileDescriptor createProfileDescriptor(
|
||||||
@@ -170,93 +161,6 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
|
|||||||
return getListViewForIndex(1 - getCurrentPage());
|
return getListViewForIndex(1 - getCurrentPage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
String getMetricsCategory() {
|
|
||||||
return ResolverActivity.METRICS_CATEGORY_RESOLVER;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean allowShowNoCrossProfileIntentsEmptyState() {
|
|
||||||
return mShouldShowNoCrossProfileIntentsEmptyState;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void showWorkProfileOffEmptyState(ResolverListAdapter activeListAdapter,
|
|
||||||
View.OnClickListener listener) {
|
|
||||||
showEmptyState(activeListAdapter,
|
|
||||||
getWorkAppPausedTitle(),
|
|
||||||
/* subtitle = */ null,
|
|
||||||
listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void showNoPersonalToWorkIntentsEmptyState(ResolverListAdapter activeListAdapter) {
|
|
||||||
showEmptyState(activeListAdapter,
|
|
||||||
getCrossProfileBlockedTitle(),
|
|
||||||
getCantAccessWorkMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void showNoWorkToPersonalIntentsEmptyState(ResolverListAdapter activeListAdapter) {
|
|
||||||
showEmptyState(activeListAdapter,
|
|
||||||
getCrossProfileBlockedTitle(),
|
|
||||||
getCantAccessPersonalMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void showNoPersonalAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
|
|
||||||
showEmptyState(listAdapter,
|
|
||||||
getNoPersonalAppsAvailableMessage(),
|
|
||||||
/* subtitle = */ null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void showNoWorkAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
|
|
||||||
showEmptyState(listAdapter,
|
|
||||||
getNoWorkAppsAvailableMessage(),
|
|
||||||
/* subtitle= */ null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getWorkAppPausedTitle() {
|
|
||||||
return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
|
|
||||||
RESOLVER_WORK_PAUSED_TITLE,
|
|
||||||
() -> getContext().getString(R.string.resolver_turn_on_work_apps));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getCrossProfileBlockedTitle() {
|
|
||||||
return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
|
|
||||||
RESOLVER_CROSS_PROFILE_BLOCKED_TITLE,
|
|
||||||
() -> getContext().getString(R.string.resolver_cross_profile_blocked));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getCantAccessWorkMessage() {
|
|
||||||
return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
|
|
||||||
RESOLVER_CANT_ACCESS_WORK,
|
|
||||||
() -> getContext().getString(
|
|
||||||
R.string.resolver_cant_access_work_apps_explanation));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getCantAccessPersonalMessage() {
|
|
||||||
return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
|
|
||||||
RESOLVER_CANT_ACCESS_PERSONAL,
|
|
||||||
() -> getContext().getString(
|
|
||||||
R.string.resolver_cant_access_personal_apps_explanation));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getNoWorkAppsAvailableMessage() {
|
|
||||||
return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
|
|
||||||
RESOLVER_NO_WORK_APPS,
|
|
||||||
() -> getContext().getString(
|
|
||||||
R.string.resolver_no_work_apps_available));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getNoPersonalAppsAvailableMessage() {
|
|
||||||
return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
|
|
||||||
RESOLVER_NO_PERSONAL_APPS,
|
|
||||||
() -> getContext().getString(
|
|
||||||
R.string.resolver_no_personal_apps_available));
|
|
||||||
}
|
|
||||||
|
|
||||||
void setUseLayoutWithDefault(boolean useLayoutWithDefault) {
|
void setUseLayoutWithDefault(boolean useLayoutWithDefault) {
|
||||||
mUseLayoutWithDefault = useLayoutWithDefault;
|
mUseLayoutWithDefault = useLayoutWithDefault;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PAUSED_TITLE;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.app.admin.DevicePolicyEventLogger;
|
||||||
|
import android.app.admin.DevicePolicyManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.stats.devicepolicy.nano.DevicePolicyEnums;
|
||||||
|
|
||||||
|
import com.android.internal.R;
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyState;
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyStateProvider;
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.OnSwitchOnWorkSelectedListener;
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.QuietModeManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chooser/ResolverActivity empty state provider that returns empty state which is shown when
|
||||||
|
* work profile is paused and we need to show a button to enable it.
|
||||||
|
*/
|
||||||
|
public class WorkProfilePausedEmptyStateProvider implements EmptyStateProvider {
|
||||||
|
|
||||||
|
private final UserHandle mWorkProfileUserHandle;
|
||||||
|
private final QuietModeManager mQuietModeManager;
|
||||||
|
private final String mMetricsCategory;
|
||||||
|
private final OnSwitchOnWorkSelectedListener mOnSwitchOnWorkSelectedListener;
|
||||||
|
private final Context mContext;
|
||||||
|
|
||||||
|
public WorkProfilePausedEmptyStateProvider(@NonNull Context context,
|
||||||
|
@Nullable UserHandle workProfileUserHandle,
|
||||||
|
@NonNull QuietModeManager quietModeManager,
|
||||||
|
@Nullable OnSwitchOnWorkSelectedListener onSwitchOnWorkSelectedListener,
|
||||||
|
@NonNull String metricsCategory) {
|
||||||
|
mContext = context;
|
||||||
|
mWorkProfileUserHandle = workProfileUserHandle;
|
||||||
|
mQuietModeManager = quietModeManager;
|
||||||
|
mMetricsCategory = metricsCategory;
|
||||||
|
mOnSwitchOnWorkSelectedListener = onSwitchOnWorkSelectedListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public EmptyState getEmptyState(ResolverListAdapter resolverListAdapter) {
|
||||||
|
if (!resolverListAdapter.getUserHandle().equals(mWorkProfileUserHandle)
|
||||||
|
|| !mQuietModeManager.isQuietModeEnabled(mWorkProfileUserHandle)
|
||||||
|
|| resolverListAdapter.getCount() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String title = mContext.getSystemService(DevicePolicyManager.class)
|
||||||
|
.getResources().getString(RESOLVER_WORK_PAUSED_TITLE,
|
||||||
|
() -> mContext.getString(R.string.resolver_turn_on_work_apps));
|
||||||
|
|
||||||
|
return new WorkProfileOffEmptyState(title, (tab) -> {
|
||||||
|
tab.showSpinner();
|
||||||
|
if (mOnSwitchOnWorkSelectedListener != null) {
|
||||||
|
mOnSwitchOnWorkSelectedListener.onSwitchOnWorkSelected();
|
||||||
|
}
|
||||||
|
mQuietModeManager.requestQuietModeEnabled(false, mWorkProfileUserHandle);
|
||||||
|
}, mMetricsCategory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WorkProfileOffEmptyState implements EmptyState {
|
||||||
|
|
||||||
|
private final String mTitle;
|
||||||
|
private final ClickListener mOnClick;
|
||||||
|
private final String mMetricsCategory;
|
||||||
|
|
||||||
|
public WorkProfileOffEmptyState(String title, @NonNull ClickListener onClick,
|
||||||
|
@NonNull String metricsCategory) {
|
||||||
|
mTitle = title;
|
||||||
|
mOnClick = onClick;
|
||||||
|
mMetricsCategory = metricsCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getTitle() {
|
||||||
|
return mTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public ClickListener getButtonClickListener() {
|
||||||
|
return mOnClick;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEmptyStateShown() {
|
||||||
|
DevicePolicyEventLogger
|
||||||
|
.createEvent(DevicePolicyEnums.RESOLVER_EMPTY_STATE_WORK_APPS_DISABLED)
|
||||||
|
.setStrings(mMetricsCategory)
|
||||||
|
.write();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,9 +16,11 @@
|
|||||||
|
|
||||||
package com.android.internal.app;
|
package com.android.internal.app;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
@@ -26,10 +28,12 @@ import android.graphics.Bitmap;
|
|||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.CrossProfileIntentsChecker;
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.MyUserIdProvider;
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.QuietModeManager;
|
||||||
import com.android.internal.app.chooser.TargetInfo;
|
import com.android.internal.app.chooser.TargetInfo;
|
||||||
import com.android.internal.logging.MetricsLogger;
|
import com.android.internal.logging.MetricsLogger;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
@@ -71,7 +75,10 @@ public class ChooserActivityOverrideData {
|
|||||||
public boolean isQuietModeEnabled;
|
public boolean isQuietModeEnabled;
|
||||||
public boolean isWorkProfileUserRunning;
|
public boolean isWorkProfileUserRunning;
|
||||||
public boolean isWorkProfileUserUnlocked;
|
public boolean isWorkProfileUserUnlocked;
|
||||||
public AbstractMultiProfilePagerAdapter.Injector multiPagerAdapterInjector;
|
public Integer myUserId;
|
||||||
|
public QuietModeManager mQuietModeManager;
|
||||||
|
public MyUserIdProvider mMyUserIdProvider;
|
||||||
|
public CrossProfileIntentsChecker mCrossProfileIntentsChecker;
|
||||||
public PackageManager packageManager;
|
public PackageManager packageManager;
|
||||||
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
@@ -95,14 +102,9 @@ public class ChooserActivityOverrideData {
|
|||||||
isQuietModeEnabled = false;
|
isQuietModeEnabled = false;
|
||||||
isWorkProfileUserRunning = true;
|
isWorkProfileUserRunning = true;
|
||||||
isWorkProfileUserUnlocked = true;
|
isWorkProfileUserUnlocked = true;
|
||||||
|
myUserId = null;
|
||||||
packageManager = null;
|
packageManager = null;
|
||||||
multiPagerAdapterInjector = new AbstractMultiProfilePagerAdapter.Injector() {
|
mQuietModeManager = new QuietModeManager() {
|
||||||
@Override
|
|
||||||
public boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId,
|
|
||||||
int targetUserId) {
|
|
||||||
return hasCrossProfileIntents;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
|
public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
|
||||||
return isQuietModeEnabled;
|
return isQuietModeEnabled;
|
||||||
@@ -113,7 +115,27 @@ public class ChooserActivityOverrideData {
|
|||||||
UserHandle workProfileUserHandle) {
|
UserHandle workProfileUserHandle) {
|
||||||
isQuietModeEnabled = enabled;
|
isQuietModeEnabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markWorkProfileEnabledBroadcastReceived() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isWaitingToEnableWorkProfile() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mMyUserIdProvider = new MyUserIdProvider() {
|
||||||
|
@Override
|
||||||
|
public int getMyUserId() {
|
||||||
|
return myUserId != null ? myUserId : UserHandle.myUserId();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mCrossProfileIntentsChecker = mock(CrossProfileIntentsChecker.class);
|
||||||
|
when(mCrossProfileIntentsChecker.hasCrossProfileIntents(any(), anyInt(), anyInt()))
|
||||||
|
.thenAnswer(invocation -> hasCrossProfileIntents);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChooserActivityOverrideData() {}
|
private ChooserActivityOverrideData() {}
|
||||||
|
|||||||
@@ -0,0 +1,450 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 static androidx.test.espresso.Espresso.onView;
|
||||||
|
import static androidx.test.espresso.action.ViewActions.click;
|
||||||
|
import static androidx.test.espresso.action.ViewActions.swipeUp;
|
||||||
|
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||||
|
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||||
|
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||||
|
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||||
|
|
||||||
|
import static com.android.internal.app.ChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.NO_BLOCKER;
|
||||||
|
import static com.android.internal.app.ChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.PERSONAL_PROFILE_ACCESS_BLOCKER;
|
||||||
|
import static com.android.internal.app.ChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.PERSONAL_PROFILE_SHARE_BLOCKER;
|
||||||
|
import static com.android.internal.app.ChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.WORK_PROFILE_ACCESS_BLOCKER;
|
||||||
|
import static com.android.internal.app.ChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.WORK_PROFILE_SHARE_BLOCKER;
|
||||||
|
import static com.android.internal.app.ChooserActivityWorkProfileTest.TestCase.Tab.PERSONAL;
|
||||||
|
import static com.android.internal.app.ChooserActivityWorkProfileTest.TestCase.Tab.WORK;
|
||||||
|
import static com.android.internal.app.ChooserWrapperActivity.sOverrides;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.not;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.companion.DeviceFilter;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
|
||||||
|
import androidx.test.InstrumentationRegistry;
|
||||||
|
import androidx.test.espresso.NoMatchingViewException;
|
||||||
|
import androidx.test.rule.ActivityTestRule;
|
||||||
|
|
||||||
|
import com.android.internal.R;
|
||||||
|
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
|
||||||
|
import com.android.internal.app.ChooserActivityWorkProfileTest.TestCase.Tab;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@DeviceFilter.MediumType
|
||||||
|
@RunWith(Parameterized.class)
|
||||||
|
public class ChooserActivityWorkProfileTest {
|
||||||
|
|
||||||
|
private static final UserHandle PERSONAL_USER_HANDLE = InstrumentationRegistry
|
||||||
|
.getInstrumentation().getTargetContext().getUser();
|
||||||
|
private static final UserHandle WORK_USER_HANDLE = UserHandle.of(10);
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public ActivityTestRule<ChooserWrapperActivity> mActivityRule =
|
||||||
|
new ActivityTestRule<>(ChooserWrapperActivity.class, false,
|
||||||
|
false);
|
||||||
|
private final TestCase mTestCase;
|
||||||
|
|
||||||
|
public ChooserActivityWorkProfileTest(TestCase testCase) {
|
||||||
|
mTestCase = testCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void cleanOverrideData() {
|
||||||
|
sOverrides.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBlocker() {
|
||||||
|
setUpPersonalAndWorkComponentInfos();
|
||||||
|
sOverrides.hasCrossProfileIntents = mTestCase.hasCrossProfileIntents();
|
||||||
|
sOverrides.myUserId = mTestCase.getMyUserHandle().getIdentifier();
|
||||||
|
|
||||||
|
launchActivity(mTestCase.getIsSendAction());
|
||||||
|
switchToTab(mTestCase.getTab());
|
||||||
|
|
||||||
|
switch (mTestCase.getExpectedBlocker()) {
|
||||||
|
case NO_BLOCKER:
|
||||||
|
assertNoBlockerDisplayed();
|
||||||
|
break;
|
||||||
|
case PERSONAL_PROFILE_SHARE_BLOCKER:
|
||||||
|
assertCantSharePersonalAppsBlockerDisplayed();
|
||||||
|
break;
|
||||||
|
case WORK_PROFILE_SHARE_BLOCKER:
|
||||||
|
assertCantShareWorkAppsBlockerDisplayed();
|
||||||
|
break;
|
||||||
|
case PERSONAL_PROFILE_ACCESS_BLOCKER:
|
||||||
|
assertCantAccessPersonalAppsBlockerDisplayed();
|
||||||
|
break;
|
||||||
|
case WORK_PROFILE_ACCESS_BLOCKER:
|
||||||
|
assertCantAccessWorkAppsBlockerDisplayed();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parameterized.Parameters(name = "{0}")
|
||||||
|
public static Collection tests() {
|
||||||
|
return Arrays.asList(
|
||||||
|
new TestCase(
|
||||||
|
/* isSendAction= */ true,
|
||||||
|
/* hasCrossProfileIntents= */ true,
|
||||||
|
/* myUserHandle= */ WORK_USER_HANDLE,
|
||||||
|
/* tab= */ WORK,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
// TODO(b/256869196) ChooserActivity goes into requestLayout loop
|
||||||
|
// new TestCase(
|
||||||
|
// /* isSendAction= */ true,
|
||||||
|
// /* hasCrossProfileIntents= */ false,
|
||||||
|
// /* myUserHandle= */ WORK_USER_HANDLE,
|
||||||
|
// /* tab= */ WORK,
|
||||||
|
// /* expectedBlocker= */ NO_BLOCKER
|
||||||
|
// ),
|
||||||
|
new TestCase(
|
||||||
|
/* isSendAction= */ true,
|
||||||
|
/* hasCrossProfileIntents= */ true,
|
||||||
|
/* myUserHandle= */ PERSONAL_USER_HANDLE,
|
||||||
|
/* tab= */ WORK,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* isSendAction= */ true,
|
||||||
|
/* hasCrossProfileIntents= */ false,
|
||||||
|
/* myUserHandle= */ PERSONAL_USER_HANDLE,
|
||||||
|
/* tab= */ WORK,
|
||||||
|
/* expectedBlocker= */ WORK_PROFILE_SHARE_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* isSendAction= */ true,
|
||||||
|
/* hasCrossProfileIntents= */ true,
|
||||||
|
/* myUserHandle= */ WORK_USER_HANDLE,
|
||||||
|
/* tab= */ PERSONAL,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
// TODO(b/256869196) ChooserActivity goes into requestLayout loop
|
||||||
|
// new TestCase(
|
||||||
|
// /* isSendAction= */ true,
|
||||||
|
// /* hasCrossProfileIntents= */ false,
|
||||||
|
// /* myUserHandle= */ WORK_USER_HANDLE,
|
||||||
|
// /* tab= */ PERSONAL,
|
||||||
|
// /* expectedBlocker= */ PERSONAL_PROFILE_SHARE_BLOCKER
|
||||||
|
// ),
|
||||||
|
new TestCase(
|
||||||
|
/* isSendAction= */ true,
|
||||||
|
/* hasCrossProfileIntents= */ true,
|
||||||
|
/* myUserHandle= */ PERSONAL_USER_HANDLE,
|
||||||
|
/* tab= */ PERSONAL,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* isSendAction= */ true,
|
||||||
|
/* hasCrossProfileIntents= */ false,
|
||||||
|
/* myUserHandle= */ PERSONAL_USER_HANDLE,
|
||||||
|
/* tab= */ PERSONAL,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* isSendAction= */ false,
|
||||||
|
/* hasCrossProfileIntents= */ true,
|
||||||
|
/* myUserHandle= */ WORK_USER_HANDLE,
|
||||||
|
/* tab= */ WORK,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* isSendAction= */ false,
|
||||||
|
/* hasCrossProfileIntents= */ false,
|
||||||
|
/* myUserHandle= */ WORK_USER_HANDLE,
|
||||||
|
/* tab= */ WORK,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* isSendAction= */ false,
|
||||||
|
/* hasCrossProfileIntents= */ true,
|
||||||
|
/* myUserHandle= */ PERSONAL_USER_HANDLE,
|
||||||
|
/* tab= */ WORK,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* isSendAction= */ false,
|
||||||
|
/* hasCrossProfileIntents= */ false,
|
||||||
|
/* myUserHandle= */ PERSONAL_USER_HANDLE,
|
||||||
|
/* tab= */ WORK,
|
||||||
|
/* expectedBlocker= */ WORK_PROFILE_ACCESS_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* isSendAction= */ false,
|
||||||
|
/* hasCrossProfileIntents= */ true,
|
||||||
|
/* myUserHandle= */ WORK_USER_HANDLE,
|
||||||
|
/* tab= */ PERSONAL,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* isSendAction= */ false,
|
||||||
|
/* hasCrossProfileIntents= */ false,
|
||||||
|
/* myUserHandle= */ WORK_USER_HANDLE,
|
||||||
|
/* tab= */ PERSONAL,
|
||||||
|
/* expectedBlocker= */ PERSONAL_PROFILE_ACCESS_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* isSendAction= */ false,
|
||||||
|
/* hasCrossProfileIntents= */ true,
|
||||||
|
/* myUserHandle= */ PERSONAL_USER_HANDLE,
|
||||||
|
/* tab= */ PERSONAL,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* isSendAction= */ false,
|
||||||
|
/* hasCrossProfileIntents= */ false,
|
||||||
|
/* myUserHandle= */ PERSONAL_USER_HANDLE,
|
||||||
|
/* tab= */ PERSONAL,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile(
|
||||||
|
int numberOfResults, int userId) {
|
||||||
|
List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
|
||||||
|
for (int i = 0; i < numberOfResults; i++) {
|
||||||
|
infoList.add(
|
||||||
|
ResolverDataProvider.createResolvedComponentInfoWithOtherId(i, userId));
|
||||||
|
}
|
||||||
|
return infoList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) {
|
||||||
|
List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
|
||||||
|
for (int i = 0; i < numberOfResults; i++) {
|
||||||
|
infoList.add(ResolverDataProvider.createResolvedComponentInfo(i));
|
||||||
|
}
|
||||||
|
return infoList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUpPersonalAndWorkComponentInfos() {
|
||||||
|
// enable the work tab feature flag
|
||||||
|
ResolverActivity.ENABLE_TABBED_VIEW = true;
|
||||||
|
markWorkProfileUserAvailable();
|
||||||
|
int workProfileTargets = 4;
|
||||||
|
List<ResolvedComponentInfo> personalResolvedComponentInfos =
|
||||||
|
createResolvedComponentsForTestWithOtherProfile(3,
|
||||||
|
/* userId */ WORK_USER_HANDLE.getIdentifier());
|
||||||
|
List<ResolvedComponentInfo> workResolvedComponentInfos =
|
||||||
|
createResolvedComponentsForTest(workProfileTargets);
|
||||||
|
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupResolverControllers(
|
||||||
|
List<ResolvedComponentInfo> personalResolvedComponentInfos,
|
||||||
|
List<ResolvedComponentInfo> workResolvedComponentInfos) {
|
||||||
|
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
|
||||||
|
Mockito.anyBoolean(),
|
||||||
|
Mockito.anyBoolean(),
|
||||||
|
Mockito.isA(List.class)))
|
||||||
|
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
|
||||||
|
when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
|
||||||
|
Mockito.anyBoolean(),
|
||||||
|
Mockito.anyBoolean(),
|
||||||
|
Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
|
||||||
|
when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
|
||||||
|
Mockito.anyBoolean(),
|
||||||
|
Mockito.anyBoolean(),
|
||||||
|
Mockito.isA(List.class),
|
||||||
|
eq(UserHandle.SYSTEM)))
|
||||||
|
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void waitForIdle() {
|
||||||
|
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markWorkProfileUserAvailable() {
|
||||||
|
ChooserWrapperActivity.sOverrides.workProfileUserHandle = WORK_USER_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertCantAccessWorkAppsBlockerDisplayed() {
|
||||||
|
onView(withText(R.string.resolver_cross_profile_blocked))
|
||||||
|
.check(matches(isDisplayed()));
|
||||||
|
onView(withText(R.string.resolver_cant_access_work_apps_explanation))
|
||||||
|
.check(matches(isDisplayed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertCantAccessPersonalAppsBlockerDisplayed() {
|
||||||
|
onView(withText(R.string.resolver_cross_profile_blocked))
|
||||||
|
.check(matches(isDisplayed()));
|
||||||
|
onView(withText(R.string.resolver_cant_access_personal_apps_explanation))
|
||||||
|
.check(matches(isDisplayed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertCantShareWorkAppsBlockerDisplayed() {
|
||||||
|
onView(withText(R.string.resolver_cross_profile_blocked))
|
||||||
|
.check(matches(isDisplayed()));
|
||||||
|
onView(withText(R.string.resolver_cant_share_with_work_apps_explanation))
|
||||||
|
.check(matches(isDisplayed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertCantSharePersonalAppsBlockerDisplayed() {
|
||||||
|
onView(withText(R.string.resolver_cross_profile_blocked))
|
||||||
|
.check(matches(isDisplayed()));
|
||||||
|
onView(withText(R.string.resolver_cant_share_with_personal_apps_explanation))
|
||||||
|
.check(matches(isDisplayed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertNoBlockerDisplayed() {
|
||||||
|
try {
|
||||||
|
onView(withText(R.string.resolver_cross_profile_blocked))
|
||||||
|
.check(matches(not(isDisplayed())));
|
||||||
|
} catch (NoMatchingViewException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void switchToTab(Tab tab) {
|
||||||
|
final int stringId = tab == Tab.WORK ? R.string.resolver_work_tab
|
||||||
|
: R.string.resolver_personal_tab;
|
||||||
|
|
||||||
|
onView(withText(stringId)).perform(click());
|
||||||
|
waitForIdle();
|
||||||
|
|
||||||
|
onView(withId(R.id.contentPanel))
|
||||||
|
.perform(swipeUp());
|
||||||
|
waitForIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Intent createTextIntent(boolean isSendAction) {
|
||||||
|
Intent sendIntent = new Intent();
|
||||||
|
if (isSendAction) {
|
||||||
|
sendIntent.setAction(Intent.ACTION_SEND);
|
||||||
|
}
|
||||||
|
sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
|
||||||
|
sendIntent.setType("text/plain");
|
||||||
|
return sendIntent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void launchActivity(boolean isSendAction) {
|
||||||
|
Intent sendIntent = createTextIntent(isSendAction);
|
||||||
|
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "Test"));
|
||||||
|
waitForIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TestCase {
|
||||||
|
private final boolean mIsSendAction;
|
||||||
|
private final boolean mHasCrossProfileIntents;
|
||||||
|
private final UserHandle mMyUserHandle;
|
||||||
|
private final Tab mTab;
|
||||||
|
private final ExpectedBlocker mExpectedBlocker;
|
||||||
|
|
||||||
|
public enum ExpectedBlocker {
|
||||||
|
NO_BLOCKER,
|
||||||
|
PERSONAL_PROFILE_SHARE_BLOCKER,
|
||||||
|
WORK_PROFILE_SHARE_BLOCKER,
|
||||||
|
PERSONAL_PROFILE_ACCESS_BLOCKER,
|
||||||
|
WORK_PROFILE_ACCESS_BLOCKER
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Tab {
|
||||||
|
WORK,
|
||||||
|
PERSONAL
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestCase(boolean isSendAction, boolean hasCrossProfileIntents,
|
||||||
|
UserHandle myUserHandle, Tab tab, ExpectedBlocker expectedBlocker) {
|
||||||
|
mIsSendAction = isSendAction;
|
||||||
|
mHasCrossProfileIntents = hasCrossProfileIntents;
|
||||||
|
mMyUserHandle = myUserHandle;
|
||||||
|
mTab = tab;
|
||||||
|
mExpectedBlocker = expectedBlocker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getIsSendAction() {
|
||||||
|
return mIsSendAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasCrossProfileIntents() {
|
||||||
|
return mHasCrossProfileIntents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserHandle getMyUserHandle() {
|
||||||
|
return mMyUserHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tab getTab() {
|
||||||
|
return mTab;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExpectedBlocker getExpectedBlocker() {
|
||||||
|
return mExpectedBlocker;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder result = new StringBuilder("test");
|
||||||
|
|
||||||
|
if (mTab == WORK) {
|
||||||
|
result.append("WorkTab_");
|
||||||
|
} else {
|
||||||
|
result.append("PersonalTab_");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mIsSendAction) {
|
||||||
|
result.append("sendAction_");
|
||||||
|
} else {
|
||||||
|
result.append("notSendAction_");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mHasCrossProfileIntents) {
|
||||||
|
result.append("hasCrossProfileIntents_");
|
||||||
|
} else {
|
||||||
|
result.append("doesNotHaveCrossProfileIntents_");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mMyUserHandle.equals(PERSONAL_USER_HANDLE)) {
|
||||||
|
result.append("myUserIsPersonal_");
|
||||||
|
} else {
|
||||||
|
result.append("myUserIsWork_");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mExpectedBlocker == ExpectedBlocker.NO_BLOCKER) {
|
||||||
|
result.append("thenNoBlocker");
|
||||||
|
} else if (mExpectedBlocker == PERSONAL_PROFILE_ACCESS_BLOCKER) {
|
||||||
|
result.append("thenAccessBlockerOnPersonalProfile");
|
||||||
|
} else if (mExpectedBlocker == PERSONAL_PROFILE_SHARE_BLOCKER) {
|
||||||
|
result.append("thenShareBlockerOnPersonalProfile");
|
||||||
|
} else if (mExpectedBlocker == WORK_PROFILE_ACCESS_BLOCKER) {
|
||||||
|
result.append("thenAccessBlockerOnWorkProfile");
|
||||||
|
} else if (mExpectedBlocker == WORK_PROFILE_SHARE_BLOCKER) {
|
||||||
|
result.append("thenShareBlockerOnWorkProfile");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,10 @@
|
|||||||
|
|
||||||
package com.android.internal.app;
|
package com.android.internal.app;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.Mockito.doAnswer;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
@@ -34,6 +38,8 @@ import android.os.UserHandle;
|
|||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.util.Size;
|
import android.util.Size;
|
||||||
|
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.CrossProfileIntentsChecker;
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.MyUserIdProvider;
|
||||||
import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter;
|
import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter;
|
||||||
import com.android.internal.app.chooser.DisplayResolveInfo;
|
import com.android.internal.app.chooser.DisplayResolveInfo;
|
||||||
import com.android.internal.app.chooser.TargetInfo;
|
import com.android.internal.app.chooser.TargetInfo;
|
||||||
@@ -59,15 +65,6 @@ public class ChooserWrapperActivity extends ChooserActivity implements IChooserW
|
|||||||
return 1234;
|
return 1234;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected AbstractMultiProfilePagerAdapter createMultiProfilePagerAdapter(
|
|
||||||
Intent[] initialIntents, List<ResolveInfo> rList, boolean filterLastUsed) {
|
|
||||||
AbstractMultiProfilePagerAdapter multiProfilePagerAdapter =
|
|
||||||
super.createMultiProfilePagerAdapter(initialIntents, rList, filterLastUsed);
|
|
||||||
multiProfilePagerAdapter.setInjector(sOverrides.multiPagerAdapterInjector);
|
|
||||||
return multiProfilePagerAdapter;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChooserListAdapter createChooserListAdapter(Context context, List<Intent> payloadIntents,
|
public ChooserListAdapter createChooserListAdapter(Context context, List<Intent> payloadIntents,
|
||||||
Intent[] initialIntents, List<ResolveInfo> rList, boolean filterLastUsed,
|
Intent[] initialIntents, List<ResolveInfo> rList, boolean filterLastUsed,
|
||||||
@@ -134,6 +131,30 @@ public class ChooserWrapperActivity extends ChooserActivity implements IChooserW
|
|||||||
return super.isVoiceInteraction();
|
return super.isVoiceInteraction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MyUserIdProvider createMyUserIdProvider() {
|
||||||
|
if (sOverrides.mMyUserIdProvider != null) {
|
||||||
|
return sOverrides.mMyUserIdProvider;
|
||||||
|
}
|
||||||
|
return super.createMyUserIdProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CrossProfileIntentsChecker createCrossProfileIntentsChecker() {
|
||||||
|
if (sOverrides.mCrossProfileIntentsChecker != null) {
|
||||||
|
return sOverrides.mCrossProfileIntentsChecker;
|
||||||
|
}
|
||||||
|
return super.createCrossProfileIntentsChecker();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AbstractMultiProfilePagerAdapter.QuietModeManager createQuietModeManager() {
|
||||||
|
if (sOverrides.mQuietModeManager != null) {
|
||||||
|
return sOverrides.mQuietModeManager;
|
||||||
|
}
|
||||||
|
return super.createQuietModeManager();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void safelyStartActivity(com.android.internal.app.chooser.TargetInfo cti) {
|
public void safelyStartActivity(com.android.internal.app.chooser.TargetInfo cti) {
|
||||||
if (sOverrides.onSafelyStartCallback != null &&
|
if (sOverrides.onSafelyStartCallback != null &&
|
||||||
|
|||||||
@@ -0,0 +1,429 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 static androidx.test.espresso.Espresso.onView;
|
||||||
|
import static androidx.test.espresso.action.ViewActions.click;
|
||||||
|
import static androidx.test.espresso.action.ViewActions.swipeUp;
|
||||||
|
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||||
|
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||||
|
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||||
|
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||||
|
|
||||||
|
import static com.android.internal.app.ResolverActivityWorkProfileTest.TestCase.ExpectedBlocker.NO_BLOCKER;
|
||||||
|
import static com.android.internal.app.ResolverActivityWorkProfileTest.TestCase.ExpectedBlocker.PERSONAL_PROFILE_BLOCKER;
|
||||||
|
import static com.android.internal.app.ResolverActivityWorkProfileTest.TestCase.ExpectedBlocker.WORK_PROFILE_BLOCKER;
|
||||||
|
import static com.android.internal.app.ResolverActivityWorkProfileTest.TestCase.Tab.PERSONAL;
|
||||||
|
import static com.android.internal.app.ResolverActivityWorkProfileTest.TestCase.Tab.WORK;
|
||||||
|
import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.not;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.companion.DeviceFilter;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
|
||||||
|
import androidx.test.InstrumentationRegistry;
|
||||||
|
import androidx.test.espresso.NoMatchingViewException;
|
||||||
|
import androidx.test.rule.ActivityTestRule;
|
||||||
|
|
||||||
|
import com.android.internal.R;
|
||||||
|
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
|
||||||
|
import com.android.internal.app.ResolverActivityWorkProfileTest.TestCase.Tab;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@DeviceFilter.MediumType
|
||||||
|
@RunWith(Parameterized.class)
|
||||||
|
public class ResolverActivityWorkProfileTest {
|
||||||
|
|
||||||
|
private static final UserHandle PERSONAL_USER_HANDLE = InstrumentationRegistry
|
||||||
|
.getInstrumentation().getTargetContext().getUser();
|
||||||
|
private static final UserHandle WORK_USER_HANDLE = UserHandle.of(10);
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public ActivityTestRule<ResolverWrapperActivity> mActivityRule =
|
||||||
|
new ActivityTestRule<>(ResolverWrapperActivity.class, false,
|
||||||
|
false);
|
||||||
|
private final TestCase mTestCase;
|
||||||
|
|
||||||
|
public ResolverActivityWorkProfileTest(TestCase testCase) {
|
||||||
|
mTestCase = testCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void cleanOverrideData() {
|
||||||
|
sOverrides.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBlocker() {
|
||||||
|
setUpPersonalAndWorkComponentInfos();
|
||||||
|
sOverrides.hasCrossProfileIntents = mTestCase.hasCrossProfileIntents();
|
||||||
|
sOverrides.myUserId = mTestCase.getMyUserHandle().getIdentifier();
|
||||||
|
|
||||||
|
launchActivity(/* callingUser= */ mTestCase.getExtraCallingUser());
|
||||||
|
switchToTab(mTestCase.getTab());
|
||||||
|
|
||||||
|
switch (mTestCase.getExpectedBlocker()) {
|
||||||
|
case NO_BLOCKER:
|
||||||
|
assertNoBlockerDisplayed();
|
||||||
|
break;
|
||||||
|
case PERSONAL_PROFILE_BLOCKER:
|
||||||
|
assertCantAccessPersonalAppsBlockerDisplayed();
|
||||||
|
break;
|
||||||
|
case WORK_PROFILE_BLOCKER:
|
||||||
|
assertCantAccessWorkAppsBlockerDisplayed();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parameterized.Parameters(name = "{0}")
|
||||||
|
public static Collection tests() {
|
||||||
|
return Arrays.asList(
|
||||||
|
new TestCase(
|
||||||
|
/* extraCallingUser= */ null,
|
||||||
|
/* hasCrossProfileIntents= */ true,
|
||||||
|
/* myUserHandle= */ WORK_USER_HANDLE,
|
||||||
|
/* tab= */ WORK,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* extraCallingUser= */ null,
|
||||||
|
/* hasCrossProfileIntents= */ false,
|
||||||
|
/* myUserHandle= */ WORK_USER_HANDLE,
|
||||||
|
/* tab= */ WORK,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* extraCallingUser= */ null,
|
||||||
|
/* hasCrossProfileIntents= */ true,
|
||||||
|
/* myUserHandle= */ PERSONAL_USER_HANDLE,
|
||||||
|
/* tab= */ WORK,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* extraCallingUser= */ null,
|
||||||
|
/* hasCrossProfileIntents= */ false,
|
||||||
|
/* myUserHandle= */ PERSONAL_USER_HANDLE,
|
||||||
|
/* tab= */ WORK,
|
||||||
|
/* expectedBlocker= */ WORK_PROFILE_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* extraCallingUser= */ null,
|
||||||
|
/* hasCrossProfileIntents= */ true,
|
||||||
|
/* myUserHandle= */ WORK_USER_HANDLE,
|
||||||
|
/* tab= */ PERSONAL,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* extraCallingUser= */ null,
|
||||||
|
/* hasCrossProfileIntents= */ false,
|
||||||
|
/* myUserHandle= */ WORK_USER_HANDLE,
|
||||||
|
/* tab= */ PERSONAL,
|
||||||
|
/* expectedBlocker= */ PERSONAL_PROFILE_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* extraCallingUser= */ null,
|
||||||
|
/* hasCrossProfileIntents= */ true,
|
||||||
|
/* myUserHandle= */ PERSONAL_USER_HANDLE,
|
||||||
|
/* tab= */ PERSONAL,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* extraCallingUser= */ null,
|
||||||
|
/* hasCrossProfileIntents= */ false,
|
||||||
|
/* myUserHandle= */ PERSONAL_USER_HANDLE,
|
||||||
|
/* tab= */ PERSONAL,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
|
||||||
|
new TestCase(
|
||||||
|
/* extraCallingUser= */ WORK_USER_HANDLE,
|
||||||
|
/* hasCrossProfileIntents= */ true,
|
||||||
|
/* myUserHandle= */ WORK_USER_HANDLE,
|
||||||
|
/* tab= */ WORK,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* extraCallingUser= */ WORK_USER_HANDLE,
|
||||||
|
/* hasCrossProfileIntents= */ false,
|
||||||
|
/* myUserHandle= */ WORK_USER_HANDLE,
|
||||||
|
/* tab= */ WORK,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* extraCallingUser= */ WORK_USER_HANDLE,
|
||||||
|
/* hasCrossProfileIntents= */ true,
|
||||||
|
/* myUserHandle= */ PERSONAL_USER_HANDLE,
|
||||||
|
/* tab= */ WORK,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* extraCallingUser= */ WORK_USER_HANDLE,
|
||||||
|
/* hasCrossProfileIntents= */ false,
|
||||||
|
/* myUserHandle= */ PERSONAL_USER_HANDLE,
|
||||||
|
/* tab= */ WORK,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* extraCallingUser= */ WORK_USER_HANDLE,
|
||||||
|
/* hasCrossProfileIntents= */ true,
|
||||||
|
/* myUserHandle= */ WORK_USER_HANDLE,
|
||||||
|
/* tab= */ PERSONAL,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* extraCallingUser= */ WORK_USER_HANDLE,
|
||||||
|
/* hasCrossProfileIntents= */ false,
|
||||||
|
/* myUserHandle= */ WORK_USER_HANDLE,
|
||||||
|
/* tab= */ PERSONAL,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* extraCallingUser= */ WORK_USER_HANDLE,
|
||||||
|
/* hasCrossProfileIntents= */ true,
|
||||||
|
/* myUserHandle= */ PERSONAL_USER_HANDLE,
|
||||||
|
/* tab= */ PERSONAL,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
),
|
||||||
|
new TestCase(
|
||||||
|
/* extraCallingUser= */ WORK_USER_HANDLE,
|
||||||
|
/* hasCrossProfileIntents= */ false,
|
||||||
|
/* myUserHandle= */ PERSONAL_USER_HANDLE,
|
||||||
|
/* tab= */ PERSONAL,
|
||||||
|
/* expectedBlocker= */ NO_BLOCKER
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile(
|
||||||
|
int numberOfResults, int userId) {
|
||||||
|
List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
|
||||||
|
for (int i = 0; i < numberOfResults; i++) {
|
||||||
|
infoList.add(
|
||||||
|
ResolverDataProvider.createResolvedComponentInfoWithOtherId(i, userId));
|
||||||
|
}
|
||||||
|
return infoList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) {
|
||||||
|
List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
|
||||||
|
for (int i = 0; i < numberOfResults; i++) {
|
||||||
|
infoList.add(ResolverDataProvider.createResolvedComponentInfo(i));
|
||||||
|
}
|
||||||
|
return infoList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUpPersonalAndWorkComponentInfos() {
|
||||||
|
// enable the work tab feature flag
|
||||||
|
ResolverActivity.ENABLE_TABBED_VIEW = true;
|
||||||
|
markWorkProfileUserAvailable();
|
||||||
|
int workProfileTargets = 4;
|
||||||
|
List<ResolvedComponentInfo> personalResolvedComponentInfos =
|
||||||
|
createResolvedComponentsForTestWithOtherProfile(3,
|
||||||
|
/* userId */ WORK_USER_HANDLE.getIdentifier());
|
||||||
|
List<ResolvedComponentInfo> workResolvedComponentInfos =
|
||||||
|
createResolvedComponentsForTest(workProfileTargets);
|
||||||
|
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupResolverControllers(
|
||||||
|
List<ResolvedComponentInfo> personalResolvedComponentInfos,
|
||||||
|
List<ResolvedComponentInfo> workResolvedComponentInfos) {
|
||||||
|
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
|
||||||
|
Mockito.anyBoolean(),
|
||||||
|
Mockito.anyBoolean(),
|
||||||
|
Mockito.isA(List.class)))
|
||||||
|
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
|
||||||
|
when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
|
||||||
|
Mockito.anyBoolean(),
|
||||||
|
Mockito.anyBoolean(),
|
||||||
|
Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
|
||||||
|
when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
|
||||||
|
Mockito.anyBoolean(),
|
||||||
|
Mockito.anyBoolean(),
|
||||||
|
Mockito.isA(List.class),
|
||||||
|
eq(UserHandle.SYSTEM)))
|
||||||
|
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void waitForIdle() {
|
||||||
|
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markWorkProfileUserAvailable() {
|
||||||
|
ResolverWrapperActivity.sOverrides.workProfileUserHandle = WORK_USER_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertCantAccessWorkAppsBlockerDisplayed() {
|
||||||
|
onView(withText(R.string.resolver_cross_profile_blocked))
|
||||||
|
.check(matches(isDisplayed()));
|
||||||
|
onView(withText(R.string.resolver_cant_access_work_apps_explanation))
|
||||||
|
.check(matches(isDisplayed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertCantAccessPersonalAppsBlockerDisplayed() {
|
||||||
|
onView(withText(R.string.resolver_cross_profile_blocked))
|
||||||
|
.check(matches(isDisplayed()));
|
||||||
|
onView(withText(R.string.resolver_cant_access_personal_apps_explanation))
|
||||||
|
.check(matches(isDisplayed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertNoBlockerDisplayed() {
|
||||||
|
try {
|
||||||
|
onView(withText(R.string.resolver_cross_profile_blocked))
|
||||||
|
.check(matches(not(isDisplayed())));
|
||||||
|
} catch (NoMatchingViewException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void switchToTab(Tab tab) {
|
||||||
|
final int stringId = tab == Tab.WORK ? R.string.resolver_work_tab
|
||||||
|
: R.string.resolver_personal_tab;
|
||||||
|
|
||||||
|
onView(withText(stringId)).perform(click());
|
||||||
|
waitForIdle();
|
||||||
|
|
||||||
|
onView(withId(R.id.contentPanel))
|
||||||
|
.perform(swipeUp());
|
||||||
|
waitForIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Intent createSendImageIntent() {
|
||||||
|
Intent sendIntent = new Intent();
|
||||||
|
sendIntent.setAction(Intent.ACTION_SEND);
|
||||||
|
sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
|
||||||
|
sendIntent.setType("image/jpeg");
|
||||||
|
return sendIntent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void launchActivity(UserHandle callingUser) {
|
||||||
|
Intent sendIntent = createSendImageIntent();
|
||||||
|
sendIntent.setType("TestType");
|
||||||
|
|
||||||
|
if (callingUser != null) {
|
||||||
|
sendIntent.putExtra(ResolverActivity.EXTRA_CALLING_USER, callingUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
mActivityRule.launchActivity(sendIntent);
|
||||||
|
waitForIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TestCase {
|
||||||
|
@Nullable
|
||||||
|
private final UserHandle mExtraCallingUser;
|
||||||
|
private final boolean mHasCrossProfileIntents;
|
||||||
|
private final UserHandle mMyUserHandle;
|
||||||
|
private final Tab mTab;
|
||||||
|
private final ExpectedBlocker mExpectedBlocker;
|
||||||
|
|
||||||
|
public enum ExpectedBlocker {
|
||||||
|
NO_BLOCKER,
|
||||||
|
PERSONAL_PROFILE_BLOCKER,
|
||||||
|
WORK_PROFILE_BLOCKER
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Tab {
|
||||||
|
WORK,
|
||||||
|
PERSONAL
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestCase(@Nullable UserHandle extraCallingUser, boolean hasCrossProfileIntents,
|
||||||
|
UserHandle myUserHandle, Tab tab, ExpectedBlocker expectedBlocker) {
|
||||||
|
mExtraCallingUser = extraCallingUser;
|
||||||
|
mHasCrossProfileIntents = hasCrossProfileIntents;
|
||||||
|
mMyUserHandle = myUserHandle;
|
||||||
|
mTab = tab;
|
||||||
|
mExpectedBlocker = expectedBlocker;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public UserHandle getExtraCallingUser() {
|
||||||
|
return mExtraCallingUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasCrossProfileIntents() {
|
||||||
|
return mHasCrossProfileIntents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserHandle getMyUserHandle() {
|
||||||
|
return mMyUserHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tab getTab() {
|
||||||
|
return mTab;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExpectedBlocker getExpectedBlocker() {
|
||||||
|
return mExpectedBlocker;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder result = new StringBuilder("test");
|
||||||
|
|
||||||
|
if (mTab == WORK) {
|
||||||
|
result.append("WorkTab_");
|
||||||
|
} else {
|
||||||
|
result.append("PersonalTab_");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mExtraCallingUser != null
|
||||||
|
&& !mExtraCallingUser.equals(PERSONAL_USER_HANDLE)) {
|
||||||
|
result.append("callingUserIsNonPersonal_");
|
||||||
|
} else {
|
||||||
|
result.append("callingUserIsPersonal_");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mHasCrossProfileIntents) {
|
||||||
|
result.append("hasCrossProfileIntents_");
|
||||||
|
} else {
|
||||||
|
result.append("doesNotHaveCrossProfileIntents_");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mMyUserHandle.equals(PERSONAL_USER_HANDLE)) {
|
||||||
|
result.append("myUserIsPersonal_");
|
||||||
|
} else {
|
||||||
|
result.append("myUserIsWork_");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mExpectedBlocker == ExpectedBlocker.NO_BLOCKER) {
|
||||||
|
result.append("thenNoBlocker");
|
||||||
|
} else if (mExpectedBlocker == ExpectedBlocker.PERSONAL_PROFILE_BLOCKER) {
|
||||||
|
result.append("thenBlockerOnPersonalProfile");
|
||||||
|
} else if (mExpectedBlocker == WORK_PROFILE_BLOCKER) {
|
||||||
|
result.append("thenBlockerOnWorkProfile");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.internal.app;
|
package com.android.internal.app;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@@ -27,6 +29,9 @@ import android.content.pm.ResolveInfo;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
|
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.CrossProfileIntentsChecker;
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.MyUserIdProvider;
|
||||||
|
import com.android.internal.app.AbstractMultiProfilePagerAdapter.QuietModeManager;
|
||||||
import com.android.internal.app.chooser.TargetInfo;
|
import com.android.internal.app.chooser.TargetInfo;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -52,12 +57,27 @@ public class ResolverWrapperActivity extends ResolverActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AbstractMultiProfilePagerAdapter createMultiProfilePagerAdapter(
|
protected MyUserIdProvider createMyUserIdProvider() {
|
||||||
Intent[] initialIntents, List<ResolveInfo> rList, boolean filterLastUsed) {
|
if (sOverrides.mMyUserIdProvider != null) {
|
||||||
AbstractMultiProfilePagerAdapter multiProfilePagerAdapter =
|
return sOverrides.mMyUserIdProvider;
|
||||||
super.createMultiProfilePagerAdapter(initialIntents, rList, filterLastUsed);
|
}
|
||||||
multiProfilePagerAdapter.setInjector(sOverrides.multiPagerAdapterInjector);
|
return super.createMyUserIdProvider();
|
||||||
return multiProfilePagerAdapter;
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CrossProfileIntentsChecker createCrossProfileIntentsChecker() {
|
||||||
|
if (sOverrides.mCrossProfileIntentsChecker != null) {
|
||||||
|
return sOverrides.mCrossProfileIntentsChecker;
|
||||||
|
}
|
||||||
|
return super.createCrossProfileIntentsChecker();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected QuietModeManager createQuietModeManager() {
|
||||||
|
if (sOverrides.mQuietModeManager != null) {
|
||||||
|
return sOverrides.mQuietModeManager;
|
||||||
|
}
|
||||||
|
return super.createQuietModeManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
ResolverWrapperAdapter getAdapter() {
|
ResolverWrapperAdapter getAdapter() {
|
||||||
@@ -137,9 +157,12 @@ public class ResolverWrapperActivity extends ResolverActivity {
|
|||||||
public ResolverListController workResolverListController;
|
public ResolverListController workResolverListController;
|
||||||
public Boolean isVoiceInteraction;
|
public Boolean isVoiceInteraction;
|
||||||
public UserHandle workProfileUserHandle;
|
public UserHandle workProfileUserHandle;
|
||||||
|
public Integer myUserId;
|
||||||
public boolean hasCrossProfileIntents;
|
public boolean hasCrossProfileIntents;
|
||||||
public boolean isQuietModeEnabled;
|
public boolean isQuietModeEnabled;
|
||||||
public AbstractMultiProfilePagerAdapter.Injector multiPagerAdapterInjector;
|
public QuietModeManager mQuietModeManager;
|
||||||
|
public MyUserIdProvider mMyUserIdProvider;
|
||||||
|
public CrossProfileIntentsChecker mCrossProfileIntentsChecker;
|
||||||
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
onSafelyStartCallback = null;
|
onSafelyStartCallback = null;
|
||||||
@@ -148,15 +171,11 @@ public class ResolverWrapperActivity extends ResolverActivity {
|
|||||||
resolverListController = mock(ResolverListController.class);
|
resolverListController = mock(ResolverListController.class);
|
||||||
workResolverListController = mock(ResolverListController.class);
|
workResolverListController = mock(ResolverListController.class);
|
||||||
workProfileUserHandle = null;
|
workProfileUserHandle = null;
|
||||||
|
myUserId = null;
|
||||||
hasCrossProfileIntents = true;
|
hasCrossProfileIntents = true;
|
||||||
isQuietModeEnabled = false;
|
isQuietModeEnabled = false;
|
||||||
multiPagerAdapterInjector = new AbstractMultiProfilePagerAdapter.Injector() {
|
|
||||||
@Override
|
|
||||||
public boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId,
|
|
||||||
int targetUserId) {
|
|
||||||
return hasCrossProfileIntents;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
mQuietModeManager = new QuietModeManager() {
|
||||||
@Override
|
@Override
|
||||||
public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
|
public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
|
||||||
return isQuietModeEnabled;
|
return isQuietModeEnabled;
|
||||||
@@ -167,7 +186,27 @@ public class ResolverWrapperActivity extends ResolverActivity {
|
|||||||
UserHandle workProfileUserHandle) {
|
UserHandle workProfileUserHandle) {
|
||||||
isQuietModeEnabled = enabled;
|
isQuietModeEnabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markWorkProfileEnabledBroadcastReceived() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isWaitingToEnableWorkProfile() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mMyUserIdProvider = new MyUserIdProvider() {
|
||||||
|
@Override
|
||||||
|
public int getMyUserId() {
|
||||||
|
return myUserId != null ? myUserId : UserHandle.myUserId();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mCrossProfileIntentsChecker = mock(CrossProfileIntentsChecker.class);
|
||||||
|
when(mCrossProfileIntentsChecker.hasCrossProfileIntents(any(), anyInt(), anyInt()))
|
||||||
|
.thenAnswer(invocation -> hasCrossProfileIntents);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user