Merge "Show other profile tab if 0 apps in current profile and >1 in the other." into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
2dda126b01
@@ -300,30 +300,26 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
|
||||
}
|
||||
|
||||
private boolean rebuildTab(ResolverListAdapter activeListAdapter, boolean doPostProcessing) {
|
||||
UserHandle listUserHandle = activeListAdapter.getUserHandle();
|
||||
|
||||
if (UserHandle.myUserId() != listUserHandle.getIdentifier()) {
|
||||
if (!mInjector.hasCrossProfileIntents(activeListAdapter.getIntents(),
|
||||
UserHandle.myUserId(), listUserHandle.getIdentifier())) {
|
||||
if (listUserHandle.equals(mPersonalProfileUserHandle)) {
|
||||
DevicePolicyEventLogger.createEvent(
|
||||
DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL)
|
||||
.setStrings(getMetricsCategory())
|
||||
.write();
|
||||
showNoWorkToPersonalIntentsEmptyState(activeListAdapter);
|
||||
} else {
|
||||
DevicePolicyEventLogger.createEvent(
|
||||
DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK)
|
||||
.setStrings(getMetricsCategory())
|
||||
.write();
|
||||
showNoPersonalToWorkIntentsEmptyState(activeListAdapter);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (shouldShowNoCrossProfileIntentsEmptyState(activeListAdapter)) {
|
||||
activeListAdapter.postListReadyRunnable(doPostProcessing);
|
||||
return false;
|
||||
}
|
||||
return activeListAdapter.rebuildList(doPostProcessing);
|
||||
}
|
||||
|
||||
private boolean shouldShowNoCrossProfileIntentsEmptyState(
|
||||
ResolverListAdapter activeListAdapter) {
|
||||
UserHandle listUserHandle = activeListAdapter.getUserHandle();
|
||||
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);
|
||||
|
||||
@@ -353,12 +349,35 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
|
||||
* anyway.
|
||||
*/
|
||||
void showEmptyResolverListEmptyState(ResolverListAdapter listAdapter) {
|
||||
if (maybeShowNoCrossProfileIntentsEmptyState(listAdapter)) {
|
||||
return;
|
||||
}
|
||||
if (maybeShowWorkProfileOffEmptyState(listAdapter)) {
|
||||
return;
|
||||
}
|
||||
maybeShowNoAppsAvailableEmptyState(listAdapter);
|
||||
}
|
||||
|
||||
private boolean maybeShowNoCrossProfileIntentsEmptyState(ResolverListAdapter listAdapter) {
|
||||
if (!shouldShowNoCrossProfileIntentsEmptyState(listAdapter)) {
|
||||
return false;
|
||||
}
|
||||
if (listAdapter.getUserHandle().equals(mPersonalProfileUserHandle)) {
|
||||
DevicePolicyEventLogger.createEvent(
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -179,6 +179,7 @@ public class ResolverActivity extends Activity implements
|
||||
public static final String EXTRA_IS_AUDIO_CAPTURE_DEVICE = "is_audio_capture_device";
|
||||
|
||||
private BroadcastReceiver mWorkProfileStateReceiver;
|
||||
private boolean mIsHeaderCreated;
|
||||
|
||||
/**
|
||||
* Get the string resource to be used as a label for the link to the resolver activity for an
|
||||
@@ -479,13 +480,42 @@ public class ResolverActivity extends Activity implements
|
||||
== workProfileUserHandle.getIdentifier()),
|
||||
mUseLayoutForBrowsables,
|
||||
/* userHandle */ workProfileUserHandle);
|
||||
// In the edge case when we have 0 apps in the current profile and >1 apps in the other,
|
||||
// the intent resolver is started in the other profile. Since this is the only case when
|
||||
// this happens, we check for it here and set the current profile's tab.
|
||||
int selectedProfile = getCurrentProfile();
|
||||
UserHandle intentUser = UserHandle.of(getLaunchingUserId());
|
||||
if (!getUser().equals(intentUser)) {
|
||||
if (getPersonalProfileUserHandle().equals(intentUser)) {
|
||||
selectedProfile = PROFILE_PERSONAL;
|
||||
} else if (getWorkProfileUserHandle().equals(intentUser)) {
|
||||
selectedProfile = PROFILE_WORK;
|
||||
}
|
||||
}
|
||||
return new ResolverMultiProfilePagerAdapter(
|
||||
/* context */ this,
|
||||
personalAdapter,
|
||||
workAdapter,
|
||||
/* defaultProfile */ getCurrentProfile(),
|
||||
selectedProfile,
|
||||
getPersonalProfileUserHandle(),
|
||||
getWorkProfileUserHandle());
|
||||
getWorkProfileUserHandle(),
|
||||
/* shouldShowNoCrossProfileIntentsEmptyState= */ getUser().equals(intentUser));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user id of the user that the starting intent originated from.
|
||||
* <p>This is not necessarily equal to {@link #getUserId()} or {@link UserHandle#myUserId()},
|
||||
* as there are edge cases when the intent resolver is launched in the other profile.
|
||||
* For example, when we have 0 resolved apps in current profile and multiple resolved apps
|
||||
* in the other profile, opening a link from the current profile launches the intent resolver
|
||||
* in the other one. b/148536209 for more info.
|
||||
*/
|
||||
private int getLaunchingUserId() {
|
||||
int contentUserHint = getIntent().getContentUserHint();
|
||||
if (contentUserHint == UserHandle.USER_CURRENT) {
|
||||
return UserHandle.myUserId();
|
||||
}
|
||||
return contentUserHint;
|
||||
}
|
||||
|
||||
protected @Profile int getCurrentProfile() {
|
||||
@@ -856,7 +886,7 @@ public class ResolverActivity extends Activity implements
|
||||
|
||||
private void setAlwaysButtonEnabled(boolean hasValidSelection, int checkedPos,
|
||||
boolean filtered) {
|
||||
if (mMultiProfilePagerAdapter.getCurrentUserHandle() != getUser()) {
|
||||
if (!mMultiProfilePagerAdapter.getCurrentUserHandle().equals(getUser())) {
|
||||
// Never allow the inactive profile to always open an app.
|
||||
mAlwaysButton.setEnabled(false);
|
||||
return;
|
||||
@@ -995,10 +1025,7 @@ public class ResolverActivity extends Activity implements
|
||||
mMultiProfilePagerAdapter.showListView(listAdapter);
|
||||
}
|
||||
if (doPostProcessing) {
|
||||
if (mMultiProfilePagerAdapter.getCurrentUserHandle().getIdentifier()
|
||||
== UserHandle.myUserId()) {
|
||||
setHeader();
|
||||
}
|
||||
maybeCreateHeader(listAdapter);
|
||||
resetButtonBar();
|
||||
onListRebuilt(listAdapter);
|
||||
}
|
||||
@@ -1679,10 +1706,15 @@ public class ResolverActivity extends Activity implements
|
||||
|
||||
/**
|
||||
* Configure the area above the app selection list (title, content preview, etc).
|
||||
* <p>The header is created once when first launching the activity and whenever a package is
|
||||
* installed or uninstalled.
|
||||
*/
|
||||
public void setHeader() {
|
||||
if (mMultiProfilePagerAdapter.getActiveListAdapter().getCount() == 0
|
||||
&& mMultiProfilePagerAdapter.getActiveListAdapter().getPlaceholderCount() == 0) {
|
||||
private void maybeCreateHeader(ResolverListAdapter listAdapter) {
|
||||
if (mIsHeaderCreated) {
|
||||
return;
|
||||
}
|
||||
if (!shouldShowTabs()
|
||||
&& listAdapter.getCount() == 0 && listAdapter.getPlaceholderCount() == 0) {
|
||||
final TextView titleView = findViewById(R.id.title);
|
||||
if (titleView != null) {
|
||||
titleView.setVisibility(View.GONE);
|
||||
@@ -1703,8 +1735,9 @@ public class ResolverActivity extends Activity implements
|
||||
|
||||
final ImageView iconView = findViewById(R.id.icon);
|
||||
if (iconView != null) {
|
||||
mMultiProfilePagerAdapter.getActiveListAdapter().loadFilteredItemIconTaskAsync(iconView);
|
||||
listAdapter.loadFilteredItemIconTaskAsync(iconView);
|
||||
}
|
||||
mIsHeaderCreated = true;
|
||||
}
|
||||
|
||||
protected void resetButtonBar() {
|
||||
@@ -1804,6 +1837,7 @@ public class ResolverActivity extends Activity implements
|
||||
// turning on.
|
||||
return;
|
||||
}
|
||||
mIsHeaderCreated = false;
|
||||
boolean listRebuilt = mMultiProfilePagerAdapter.rebuildActiveTab(true);
|
||||
if (listRebuilt) {
|
||||
ResolverListAdapter activeListAdapter =
|
||||
|
||||
@@ -35,6 +35,7 @@ import com.android.internal.widget.PagerAdapter;
|
||||
public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerAdapter {
|
||||
|
||||
private final ResolverProfileDescriptor[] mItems;
|
||||
private final boolean mShouldShowNoCrossProfileIntentsEmptyState;
|
||||
|
||||
ResolverMultiProfilePagerAdapter(Context context,
|
||||
ResolverListAdapter adapter,
|
||||
@@ -44,6 +45,7 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
|
||||
mItems = new ResolverProfileDescriptor[] {
|
||||
createProfileDescriptor(adapter)
|
||||
};
|
||||
mShouldShowNoCrossProfileIntentsEmptyState = true;
|
||||
}
|
||||
|
||||
ResolverMultiProfilePagerAdapter(Context context,
|
||||
@@ -51,13 +53,15 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
|
||||
ResolverListAdapter workAdapter,
|
||||
@Profile int defaultProfile,
|
||||
UserHandle personalProfileUserHandle,
|
||||
UserHandle workProfileUserHandle) {
|
||||
UserHandle workProfileUserHandle,
|
||||
boolean shouldShowNoCrossProfileIntentsEmptyState) {
|
||||
super(context, /* currentPage */ defaultProfile, personalProfileUserHandle,
|
||||
workProfileUserHandle);
|
||||
mItems = new ResolverProfileDescriptor[] {
|
||||
createProfileDescriptor(personalAdapter),
|
||||
createProfileDescriptor(workAdapter)
|
||||
};
|
||||
mShouldShowNoCrossProfileIntentsEmptyState = shouldShowNoCrossProfileIntentsEmptyState;
|
||||
}
|
||||
|
||||
private ResolverProfileDescriptor createProfileDescriptor(
|
||||
@@ -162,6 +166,11 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
|
||||
return ResolverActivity.METRICS_CATEGORY_RESOLVER;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean allowShowNoCrossProfileIntentsEmptyState() {
|
||||
return mShouldShowNoCrossProfileIntentsEmptyState;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void showWorkProfileOffEmptyState(ResolverListAdapter activeListAdapter,
|
||||
View.OnClickListener listener) {
|
||||
|
||||
@@ -38,13 +38,16 @@ import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.testng.Assert.assertFalse;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.espresso.Espresso;
|
||||
@@ -543,6 +546,51 @@ public class ResolverActivityTest {
|
||||
assertThat(activity.getWorkListAdapter().getCount(), is(4));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkTab_headerIsVisibleInPersonalTab() {
|
||||
// enable the work tab feature flag
|
||||
ResolverActivity.ENABLE_TABBED_VIEW = true;
|
||||
markWorkProfileUserAvailable();
|
||||
List<ResolvedComponentInfo> personalResolvedComponentInfos =
|
||||
createResolvedComponentsForTestWithOtherProfile(1);
|
||||
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
|
||||
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
|
||||
Intent sendIntent = createOpenWebsiteIntent();
|
||||
|
||||
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
|
||||
waitForIdle();
|
||||
TextView headerText = activity.findViewById(R.id.title);
|
||||
String initialText = headerText.getText().toString();
|
||||
assertFalse(initialText.isEmpty(), "Header text is empty.");
|
||||
assertThat(headerText.getVisibility(), is(View.VISIBLE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkTab_switchTabs_headerStaysSame() {
|
||||
// enable the work tab feature flag
|
||||
ResolverActivity.ENABLE_TABBED_VIEW = true;
|
||||
markWorkProfileUserAvailable();
|
||||
List<ResolvedComponentInfo> personalResolvedComponentInfos =
|
||||
createResolvedComponentsForTestWithOtherProfile(1);
|
||||
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
|
||||
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
|
||||
Intent sendIntent = createOpenWebsiteIntent();
|
||||
|
||||
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
|
||||
waitForIdle();
|
||||
TextView headerText = activity.findViewById(R.id.title);
|
||||
String initialText = headerText.getText().toString();
|
||||
onView(withText(R.string.resolver_work_tab))
|
||||
.perform(click());
|
||||
|
||||
waitForIdle();
|
||||
String currentText = headerText.getText().toString();
|
||||
assertThat(headerText.getVisibility(), is(View.VISIBLE));
|
||||
assertThat(String.format("Header text is not the same when switching tabs, personal profile"
|
||||
+ " header was %s but work profile header is %s", initialText, currentText),
|
||||
TextUtils.equals(initialText, currentText));
|
||||
}
|
||||
|
||||
@Ignore // b/148156663
|
||||
@Test
|
||||
public void testWorkTab_noPersonalApps_canStartWorkApps()
|
||||
@@ -761,6 +809,13 @@ public class ResolverActivityTest {
|
||||
return sendIntent;
|
||||
}
|
||||
|
||||
private Intent createOpenWebsiteIntent() {
|
||||
Intent sendIntent = new Intent();
|
||||
sendIntent.setAction(Intent.ACTION_VIEW);
|
||||
sendIntent.setData(Uri.parse("https://google.com"));
|
||||
return sendIntent;
|
||||
}
|
||||
|
||||
private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) {
|
||||
List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
|
||||
for (int i = 0; i < numberOfResults; i++) {
|
||||
|
||||
Reference in New Issue
Block a user