Merge "Show other profile tab if 0 apps in current profile and >1 in the other." into rvc-dev

This commit is contained in:
TreeHugger Robot
2020-04-16 20:03:45 +00:00
committed by Android (Google) Code Review
4 changed files with 149 additions and 32 deletions

View File

@@ -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.
*/

View File

@@ -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 =

View File

@@ -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) {

View File

@@ -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++) {