diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java index ab8c19ea88619..78e85180f3d24 100644 --- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java +++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java @@ -15,15 +15,25 @@ */ package com.android.internal.app; +import android.annotation.DrawableRes; import android.annotation.IntDef; import android.annotation.Nullable; +import android.annotation.StringRes; +import android.app.AppGlobals; +import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; +import android.content.pm.IPackageManager; import android.content.pm.ResolveInfo; import android.os.UserHandle; import android.os.UserManager; import android.view.View; import android.view.ViewGroup; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.PagerAdapter; import com.android.internal.widget.ViewPager; @@ -213,26 +223,115 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { abstract @Nullable ViewGroup getInactiveAdapterView(); - boolean rebuildActiveTab(boolean post) { - return rebuildTab(getActiveListAdapter(), post); + /** + * Rebuilds the tab that is currently visible to the user. + *
Returns {@code true} if rebuild has completed. + */ + boolean rebuildActiveTab(boolean doPostProcessing) { + return rebuildTab(getActiveListAdapter(), doPostProcessing); } - boolean rebuildInactiveTab(boolean post) { + /** + * Rebuilds the tab that is not currently visible to the user, if such one exists. + *
Returns {@code true} if rebuild has completed.
+ */
+ boolean rebuildInactiveTab(boolean doPostProcessing) {
if (getItemCount() == 1) {
return false;
}
- return rebuildTab(getInactiveListAdapter(), post);
+ return rebuildTab(getInactiveListAdapter(), doPostProcessing);
+ }
+
+ private int userHandleToPageIndex(UserHandle userHandle) {
+ if (userHandle == getPersonalListAdapter().mResolverListController.getUserHandle()) {
+ return PROFILE_PERSONAL;
+ } else {
+ return PROFILE_WORK;
+ }
}
private boolean rebuildTab(ResolverListAdapter activeListAdapter, boolean doPostProcessing) {
UserHandle listUserHandle = activeListAdapter.getUserHandle();
- if (UserHandle.myUserId() != listUserHandle.getIdentifier() &&
- !hasAppsInOtherProfile(activeListAdapter)) {
- // TODO(arangelov): Show empty state UX here
+ UserManager userManager = mContext.getSystemService(UserManager.class);
+ if (listUserHandle == mWorkProfileUserHandle
+ && userManager.isQuietModeEnabled(mWorkProfileUserHandle)) {
+ showEmptyState(activeListAdapter,
+ R.drawable.ic_work_apps_off,
+ R.string.resolver_turn_on_work_apps,
+ R.string.resolver_turn_on_work_apps_explanation,
+ (View.OnClickListener) v ->
+ userManager.requestQuietModeEnabled(false, mWorkProfileUserHandle));
return false;
- } else {
- return activeListAdapter.rebuildList(doPostProcessing);
}
+ if (UserHandle.myUserId() != listUserHandle.getIdentifier()) {
+ if (!hasCrossProfileIntents(activeListAdapter.getIntents(),
+ UserHandle.myUserId(), listUserHandle.getIdentifier())) {
+ if (listUserHandle == mPersonalProfileUserHandle) {
+ showEmptyState(activeListAdapter,
+ R.drawable.ic_sharing_disabled,
+ R.string.resolver_cant_share_with_personal_apps,
+ R.string.resolver_cant_share_cross_profile_explanation);
+ } else {
+ showEmptyState(activeListAdapter,
+ R.drawable.ic_sharing_disabled,
+ R.string.resolver_cant_share_with_work_apps,
+ R.string.resolver_cant_share_cross_profile_explanation);
+ }
+ return false;
+ }
+ }
+ return activeListAdapter.rebuildList(doPostProcessing);
+ }
+
+ void showEmptyState(ResolverListAdapter listAdapter) {
+ UserHandle listUserHandle = listAdapter.getUserHandle();
+ if (UserHandle.myUserId() == listUserHandle.getIdentifier()
+ || !hasAppsInOtherProfile(listAdapter)) {
+ showEmptyState(listAdapter,
+ R.drawable.ic_no_apps,
+ R.string.resolver_no_apps_available,
+ R.string.resolver_no_apps_available_explanation);
+ }
+ }
+
+ private void showEmptyState(ResolverListAdapter activeListAdapter,
+ @DrawableRes int iconRes, @StringRes int titleRes, @StringRes int subtitleRes) {
+ showEmptyState(activeListAdapter, iconRes, titleRes, subtitleRes, /* buttonOnClick */ null);
+ }
+
+ private void showEmptyState(ResolverListAdapter activeListAdapter,
+ @DrawableRes int iconRes, @StringRes int titleRes, @StringRes int subtitleRes,
+ View.OnClickListener buttonOnClick) {
+ ProfileDescriptor descriptor = getItem(
+ userHandleToPageIndex(activeListAdapter.getUserHandle()));
+ descriptor.rootView.findViewById(R.id.resolver_list).setVisibility(View.GONE);
+ View emptyStateView = descriptor.rootView.findViewById(R.id.resolver_empty_state);
+ emptyStateView.setVisibility(View.VISIBLE);
+
+ ImageView icon = emptyStateView.findViewById(R.id.resolver_empty_state_icon);
+ icon.setImageResource(iconRes);
+
+ TextView title = emptyStateView.findViewById(R.id.resolver_empty_state_title);
+ title.setText(titleRes);
+
+ TextView subtitle = emptyStateView.findViewById(R.id.resolver_empty_state_subtitle);
+ subtitle.setText(subtitleRes);
+
+ Button button = emptyStateView.findViewById(R.id.resolver_empty_state_button);
+ button.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE);
+ button.setOnClickListener(buttonOnClick);
+ }
+
+ private boolean hasCrossProfileIntents(List Not to be confused with the sticky content preview which is above the
+ * personal and work tabs.
+ */
+ public void hideContentPreview() {
+ mHideContentPreview = true;
+ mLayoutRequested = true;
+ notifyDataSetChanged();
+ }
+
public boolean consumeLayoutRequest() {
boolean oldValue = mLayoutRequested;
mLayoutRequested = false;
@@ -2773,7 +2806,8 @@ public class ChooserActivity extends ResolverActivity implements
public int getRowCount() {
return (int) (
- getProfileRowCount()
+ getContentPreviewRowCount()
+ + getProfileRowCount()
+ getServiceTargetRowCount()
+ getCallerAndRankedTargetRowCount()
+ getAzLabelRowCount()
@@ -2783,7 +2817,33 @@ public class ChooserActivity extends ResolverActivity implements
);
}
+ /**
+ * Returns either {@code 0} or {@code 1} depending on whether we want to show the list item
+ * content preview. Not to be confused with the sticky content preview which is above the
+ * personal and work tabs.
+ */
+ public int getContentPreviewRowCount() {
+ // For the tabbed case we show the sticky content preview above the tabs,
+ // please refer to shouldShowStickyContentPreview
+ if (hasWorkProfile() && ENABLE_TABBED_VIEW) {
+ return 0;
+ }
+ if (!isSendAction(getTargetIntent())) {
+ return 0;
+ }
+
+ if (mHideContentPreview || mChooserListAdapter == null
+ || mChooserListAdapter.getCount() == 0) {
+ return 0;
+ }
+
+ return 1;
+ }
+
public int getProfileRowCount() {
+ if (hasWorkProfile() && ENABLE_TABBED_VIEW) {
+ return 0;
+ }
return mChooserListAdapter.getOtherProfile() == null ? 0 : 1;
}
@@ -2815,7 +2875,8 @@ public class ChooserActivity extends ResolverActivity implements
@Override
public int getItemCount() {
return (int) (
- getProfileRowCount()
+ getContentPreviewRowCount()
+ + getProfileRowCount()
+ getServiceTargetRowCount()
+ getCallerAndRankedTargetRowCount()
+ getAzLabelRowCount()
@@ -2827,6 +2888,8 @@ public class ChooserActivity extends ResolverActivity implements
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
+ case VIEW_TYPE_CONTENT_PREVIEW:
+ return new ItemViewHolder(createContentPreviewView(parent), false);
case VIEW_TYPE_PROFILE:
return new ItemViewHolder(createProfileView(parent), false);
case VIEW_TYPE_AZ_LABEL:
@@ -2866,7 +2929,10 @@ public class ChooserActivity extends ResolverActivity implements
public int getItemViewType(int position) {
int count;
- int countSum = (count = getProfileRowCount());
+ int countSum = (count = getContentPreviewRowCount());
+ if (count > 0 && position < countSum) return VIEW_TYPE_CONTENT_PREVIEW;
+
+ countSum += (count = getProfileRowCount());
if (count > 0 && position < countSum) return VIEW_TYPE_PROFILE;
countSum += (count = getServiceTargetRowCount());
@@ -3082,7 +3148,7 @@ public class ChooserActivity extends ResolverActivity implements
}
int getListPosition(int position) {
- position -= getProfileRowCount();
+ position -= getContentPreviewRowCount() + getProfileRowCount();
final int serviceCount = mChooserListAdapter.getServiceTargetCount();
final int serviceRows = (int) Math.ceil((float) serviceCount
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 9488e4f3b297e..7a0afa2036f69 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -26,6 +26,7 @@ import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.admin.DevicePolicyManager;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ContentResolver;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.IPackageManager;
@@ -108,7 +109,8 @@ public class IntentForwarderActivity extends Activity {
}
final int callingUserId = getUserId();
- final Intent newIntent = canForward(intentReceived, targetUserId);
+ final Intent newIntent = canForward(intentReceived, getUserId(), targetUserId,
+ mInjector.getIPackageManager(), getContentResolver());
if (newIntent != null) {
if (Intent.ACTION_CHOOSER.equals(newIntent.getAction())) {
Intent innerIntent = newIntent.getParcelableExtra(Intent.EXTRA_INTENT);
@@ -191,7 +193,8 @@ public class IntentForwarderActivity extends Activity {
* Check whether the intent can be forwarded to target user. Return the intent used for
* forwarding if it can be forwarded, {@code null} otherwise.
*/
- Intent canForward(Intent incomingIntent, int targetUserId) {
+ static Intent canForward(Intent incomingIntent, int sourceUserId, int targetUserId,
+ IPackageManager packageManager, ContentResolver contentResolver) {
Intent forwardIntent = new Intent(incomingIntent);
forwardIntent.addFlags(
Intent.FLAG_ACTIVITY_FORWARD_RESULT | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
@@ -220,11 +223,11 @@ public class IntentForwarderActivity extends Activity {
if (forwardIntent.getSelector() != null) {
intentToCheck = forwardIntent.getSelector();
}
- String resolvedType = intentToCheck.resolveTypeIfNeeded(getContentResolver());
+ String resolvedType = intentToCheck.resolveTypeIfNeeded(contentResolver);
sanitizeIntent(intentToCheck);
try {
- if (mInjector.getIPackageManager().
- canForwardTo(intentToCheck, resolvedType, getUserId(), targetUserId)) {
+ if (packageManager.canForwardTo(
+ intentToCheck, resolvedType, sourceUserId, targetUserId)) {
return forwardIntent;
}
} catch (RemoteException e) {
@@ -267,7 +270,7 @@ public class IntentForwarderActivity extends Activity {
/**
* Sanitize the intent in place.
*/
- private void sanitizeIntent(Intent intent) {
+ private static void sanitizeIntent(Intent intent) {
// Apps should not be allowed to target a specific package/ component in the target user.
intent.setPackage(null);
intent.setComponent(null);
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index a934de3289896..022573c5203a6 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -387,6 +387,11 @@ public class ResolverActivity extends Activity implements
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
rdl.setOnApplyWindowInsetsListener(this::onApplyWindowInsets);
+ rdl.setMaxCollapsedHeight(hasWorkProfile() && ENABLE_TABBED_VIEW
+ ? getResources().getDimensionPixelSize(
+ R.dimen.resolver_empty_state_height_with_tabs)
+ : getResources().getDimensionPixelSize(R.dimen.resolver_empty_state_height));
+
mResolverDrawerLayout = rdl;
}
@@ -548,13 +553,6 @@ public class ResolverActivity extends Activity implements
applyFooterView(mSystemWindowInsets.bottom);
}
- View emptyView = findViewById(R.id.empty);
- if (emptyView != null) {
- emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom
- + getResources().getDimensionPixelSize(
- R.dimen.chooser_edge_margin_normal) * 2);
- }
-
return insets.consumeSystemWindowInsets();
}
@@ -941,10 +939,13 @@ public class ResolverActivity extends Activity implements
}
@Override // ResolverListCommunicator
- public void onPostListReady(ResolverListAdapter listAdapter, boolean doPostProcessing) {
+ public final void onPostListReady(ResolverListAdapter listAdapter, boolean doPostProcessing) {
if (isAutolaunching() || maybeAutolaunchActivity()) {
return;
}
+ if (shouldShowEmptyState(listAdapter)) {
+ mMultiProfilePagerAdapter.showEmptyState(listAdapter);
+ }
if (doPostProcessing) {
if (mMultiProfilePagerAdapter.getCurrentUserHandle().getIdentifier()
== UserHandle.myUserId()) {
@@ -1497,15 +1498,17 @@ public class ResolverActivity extends Activity implements
}
private void setupViewVisibilities() {
- int count = mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredCount();
- boolean shouldShowEmptyState = count == 0
- && mMultiProfilePagerAdapter.getActiveListAdapter().getPlaceholderCount() == 0;
- //TODO(arangelov): Handle empty state
- if (!shouldShowEmptyState) {
- addUseDifferentAppLabelIfNecessary(mMultiProfilePagerAdapter.getActiveListAdapter());
+ ResolverListAdapter activeListAdapter = mMultiProfilePagerAdapter.getActiveListAdapter();
+ if (!shouldShowEmptyState(activeListAdapter)) {
+ addUseDifferentAppLabelIfNecessary(activeListAdapter);
}
}
+ private boolean shouldShowEmptyState(ResolverListAdapter listAdapter) {
+ int count = listAdapter.getUnfilteredCount();
+ return count == 0 && listAdapter.getPlaceholderCount() == 0;
+ }
+
/**
* Add a label to signify that the user can pick a different app.
* @param adapter The adapter used to provide data to item views.
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index 2321da14cebe1..ea8409058264d 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -607,6 +607,10 @@ public class ResolverListAdapter extends BaseAdapter {
mIntents, userHandle);
}
+ protected List The main purpose is for OEMs to customize the rendering of the
lockscreen, setting this to true should come with customized drawables. -->