Merge "Show other profile tab if 0 apps in current profile and >1 in the other." into rvc-dev am: 2dda126b01 am: 8c3dca9ebb

Change-Id: Ie7c0d1e1655e982fe21f398012006fad2a7f3146
This commit is contained in:
TreeHugger Robot
2020-04-16 20:57:04 +00:00
committed by Automerger Merge Worker
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) { private boolean rebuildTab(ResolverListAdapter activeListAdapter, boolean doPostProcessing) {
UserHandle listUserHandle = activeListAdapter.getUserHandle(); if (shouldShowNoCrossProfileIntentsEmptyState(activeListAdapter)) {
activeListAdapter.postListReadyRunnable(doPostProcessing);
if (UserHandle.myUserId() != listUserHandle.getIdentifier()) { return false;
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;
}
} }
return activeListAdapter.rebuildList(doPostProcessing); 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( protected abstract void showWorkProfileOffEmptyState(
ResolverListAdapter activeListAdapter, View.OnClickListener listener); ResolverListAdapter activeListAdapter, View.OnClickListener listener);
@@ -353,12 +349,35 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
* anyway. * anyway.
*/ */
void showEmptyResolverListEmptyState(ResolverListAdapter listAdapter) { void showEmptyResolverListEmptyState(ResolverListAdapter listAdapter) {
if (maybeShowNoCrossProfileIntentsEmptyState(listAdapter)) {
return;
}
if (maybeShowWorkProfileOffEmptyState(listAdapter)) { if (maybeShowWorkProfileOffEmptyState(listAdapter)) {
return; return;
} }
maybeShowNoAppsAvailableEmptyState(listAdapter); 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. * 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"; public static final String EXTRA_IS_AUDIO_CAPTURE_DEVICE = "is_audio_capture_device";
private BroadcastReceiver mWorkProfileStateReceiver; 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 * 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()), == workProfileUserHandle.getIdentifier()),
mUseLayoutForBrowsables, mUseLayoutForBrowsables,
/* userHandle */ workProfileUserHandle); /* 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( return new ResolverMultiProfilePagerAdapter(
/* context */ this, /* context */ this,
personalAdapter, personalAdapter,
workAdapter, workAdapter,
/* defaultProfile */ getCurrentProfile(), selectedProfile,
getPersonalProfileUserHandle(), 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() { protected @Profile int getCurrentProfile() {
@@ -856,7 +886,7 @@ public class ResolverActivity extends Activity implements
private void setAlwaysButtonEnabled(boolean hasValidSelection, int checkedPos, private void setAlwaysButtonEnabled(boolean hasValidSelection, int checkedPos,
boolean filtered) { boolean filtered) {
if (mMultiProfilePagerAdapter.getCurrentUserHandle() != getUser()) { if (!mMultiProfilePagerAdapter.getCurrentUserHandle().equals(getUser())) {
// Never allow the inactive profile to always open an app. // Never allow the inactive profile to always open an app.
mAlwaysButton.setEnabled(false); mAlwaysButton.setEnabled(false);
return; return;
@@ -995,10 +1025,7 @@ public class ResolverActivity extends Activity implements
mMultiProfilePagerAdapter.showListView(listAdapter); mMultiProfilePagerAdapter.showListView(listAdapter);
} }
if (doPostProcessing) { if (doPostProcessing) {
if (mMultiProfilePagerAdapter.getCurrentUserHandle().getIdentifier() maybeCreateHeader(listAdapter);
== UserHandle.myUserId()) {
setHeader();
}
resetButtonBar(); resetButtonBar();
onListRebuilt(listAdapter); onListRebuilt(listAdapter);
} }
@@ -1679,10 +1706,15 @@ public class ResolverActivity extends Activity implements
/** /**
* Configure the area above the app selection list (title, content preview, etc). * 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() { private void maybeCreateHeader(ResolverListAdapter listAdapter) {
if (mMultiProfilePagerAdapter.getActiveListAdapter().getCount() == 0 if (mIsHeaderCreated) {
&& mMultiProfilePagerAdapter.getActiveListAdapter().getPlaceholderCount() == 0) { return;
}
if (!shouldShowTabs()
&& listAdapter.getCount() == 0 && listAdapter.getPlaceholderCount() == 0) {
final TextView titleView = findViewById(R.id.title); final TextView titleView = findViewById(R.id.title);
if (titleView != null) { if (titleView != null) {
titleView.setVisibility(View.GONE); titleView.setVisibility(View.GONE);
@@ -1703,8 +1735,9 @@ public class ResolverActivity extends Activity implements
final ImageView iconView = findViewById(R.id.icon); final ImageView iconView = findViewById(R.id.icon);
if (iconView != null) { if (iconView != null) {
mMultiProfilePagerAdapter.getActiveListAdapter().loadFilteredItemIconTaskAsync(iconView); listAdapter.loadFilteredItemIconTaskAsync(iconView);
} }
mIsHeaderCreated = true;
} }
protected void resetButtonBar() { protected void resetButtonBar() {
@@ -1804,6 +1837,7 @@ public class ResolverActivity extends Activity implements
// turning on. // turning on.
return; return;
} }
mIsHeaderCreated = false;
boolean listRebuilt = mMultiProfilePagerAdapter.rebuildActiveTab(true); boolean listRebuilt = mMultiProfilePagerAdapter.rebuildActiveTab(true);
if (listRebuilt) { if (listRebuilt) {
ResolverListAdapter activeListAdapter = ResolverListAdapter activeListAdapter =

View File

@@ -35,6 +35,7 @@ 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;
ResolverMultiProfilePagerAdapter(Context context, ResolverMultiProfilePagerAdapter(Context context,
ResolverListAdapter adapter, ResolverListAdapter adapter,
@@ -44,6 +45,7 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
mItems = new ResolverProfileDescriptor[] { mItems = new ResolverProfileDescriptor[] {
createProfileDescriptor(adapter) createProfileDescriptor(adapter)
}; };
mShouldShowNoCrossProfileIntentsEmptyState = true;
} }
ResolverMultiProfilePagerAdapter(Context context, ResolverMultiProfilePagerAdapter(Context context,
@@ -51,13 +53,15 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
ResolverListAdapter workAdapter, ResolverListAdapter workAdapter,
@Profile int defaultProfile, @Profile int defaultProfile,
UserHandle personalProfileUserHandle, UserHandle personalProfileUserHandle,
UserHandle workProfileUserHandle) { UserHandle workProfileUserHandle,
boolean shouldShowNoCrossProfileIntentsEmptyState) {
super(context, /* currentPage */ defaultProfile, personalProfileUserHandle, 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(
@@ -162,6 +166,11 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
return ResolverActivity.METRICS_CATEGORY_RESOLVER; return ResolverActivity.METRICS_CATEGORY_RESOLVER;
} }
@Override
boolean allowShowNoCrossProfileIntentsEmptyState() {
return mShouldShowNoCrossProfileIntentsEmptyState;
}
@Override @Override
protected void showWorkProfileOffEmptyState(ResolverListAdapter activeListAdapter, protected void showWorkProfileOffEmptyState(ResolverListAdapter activeListAdapter,
View.OnClickListener listener) { View.OnClickListener listener) {

View File

@@ -38,13 +38,16 @@ import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.testng.Assert.assertFalse;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.UserHandle; import android.os.UserHandle;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.View; import android.view.View;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.test.InstrumentationRegistry; import androidx.test.InstrumentationRegistry;
import androidx.test.espresso.Espresso; import androidx.test.espresso.Espresso;
@@ -543,6 +546,51 @@ public class ResolverActivityTest {
assertThat(activity.getWorkListAdapter().getCount(), is(4)); 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 @Ignore // b/148156663
@Test @Test
public void testWorkTab_noPersonalApps_canStartWorkApps() public void testWorkTab_noPersonalApps_canStartWorkApps()
@@ -761,6 +809,13 @@ public class ResolverActivityTest {
return sendIntent; 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) { private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) {
List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults); List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
for (int i = 0; i < numberOfResults; i++) { for (int i = 0; i < numberOfResults; i++) {