diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java index b81e47303a9d7..b1e356d258ee1 100644 --- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java +++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java @@ -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. */ diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index dd3a6603f46a8..1bc982cdb42b5 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -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. + *
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). + *
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 =
diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
index b690a18f2d0ea..ad31d8b2e49a6 100644
--- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -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) {
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index eb39d58019d9e..07aa654cae1ec 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -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