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:
@@ -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.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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 =
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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++) {
|
||||||
|
|||||||
Reference in New Issue
Block a user