Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d60e103d authored by Antoan Angelov's avatar Antoan Angelov Committed by Android (Google) Code Review
Browse files

Merge "Add empty state screens."

parents 80a75bdb 590fba38
Loading
Loading
Loading
Loading
+108 −9
Original line number Diff line number Diff line
@@ -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.
     * <p>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.
     * <p>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;
        }
        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<Intent> intents, int source, int target) {
        IPackageManager packageManager = AppGlobals.getPackageManager();
        ContentResolver contentResolver = mContext.getContentResolver();
        for (Intent intent : intents) {
            if (IntentForwarderActivity.canForward(intent, source, target, packageManager,
                    contentResolver) != null) {
                return true;
            }
        }
        return false;
    }

    private boolean hasAppsInOtherProfile(ResolverListAdapter adapter) {
+91 −25
Original line number Diff line number Diff line
@@ -373,7 +373,11 @@ public class ChooserActivity extends ResolverActivity implements
                Log.i(TAG, "Hiding image preview area. Timed out waiting for preview to load"
                        + " within " + mImageLoadTimeoutMillis + "ms.");
                collapseParentView();
                hideContentPreview();
                if (hasWorkProfile() && ENABLE_TABBED_VIEW) {
                    hideStickyContentPreview();
                } else if (mChooserMultiProfilePagerAdapter.getCurrentRootAdapter() != null) {
                    mChooserMultiProfilePagerAdapter.getCurrentRootAdapter().hideContentPreview();
                }
                mHideParentOnFail = false;
            }
        }
@@ -829,7 +833,12 @@ public class ChooserActivity extends ResolverActivity implements

    @Override
    protected boolean postRebuildList(boolean rebuildCompleted) {
        updateContentPreview();
        updateStickyContentPreview();
        if (shouldShowStickyContentPreview()
                || mChooserMultiProfilePagerAdapter
                        .getCurrentRootAdapter().getContentPreviewRowCount() != 0) {
            logActionShareWithPreview();
        }
        return postRebuildListInternal(rebuildCompleted);
    }

@@ -978,6 +987,8 @@ public class ChooserActivity extends ResolverActivity implements
        updateLayoutWidth(R.id.content_preview_text_layout, width, parent);
        updateLayoutWidth(R.id.content_preview_title_layout, width, parent);
        updateLayoutWidth(R.id.content_preview_file_layout, width, parent);
        findViewById(R.id.content_preview_container)
                .setVisibility(shouldShowStickyContentPreview() ? View.VISIBLE : View.GONE);
    }

    private void updateLayoutWidth(int layoutResourceId, int width, View parent) {
@@ -2413,14 +2424,14 @@ public class ChooserActivity extends ResolverActivity implements

                // still zero? then use a default height and leave, which
                // can happen when there are no targets to show
                if (rowsToShow == 0 && !shouldShowContentPreview()) {
                if (rowsToShow == 0 && !shouldShowStickyContentPreview()) {
                    offset += getResources().getDimensionPixelSize(
                            R.dimen.chooser_max_collapsed_height);
                    mResolverDrawerLayout.setCollapsibleHeightReserved(offset);
                    return;
                }

                if (shouldShowContentPreview()) {
                if (shouldShowStickyContentPreview()) {
                    offset += findViewById(R.id.content_preview_container).getHeight();
                }

@@ -2552,7 +2563,7 @@ public class ChooserActivity extends ResolverActivity implements
    }

    private void setupScrollListener() {
        if (mResolverDrawerLayout == null) {
        if (mResolverDrawerLayout == null || (hasWorkProfile() && ENABLE_TABBED_VIEW)) {
            return;
        }
        final View chooserHeader = mResolverDrawerLayout.findViewById(R.id.chooser_header);
@@ -2597,28 +2608,36 @@ public class ChooserActivity extends ResolverActivity implements
        return false;
    }

    private boolean shouldShowContentPreview() {
        return mMultiProfilePagerAdapter.getActiveListAdapter().getCount() > 0
                && isSendAction(getTargetIntent());
    }

    private void updateContentPreview() {
        if (shouldShowContentPreview()) {
            showContentPreview();
    /**
     * The sticky content preview is shown only when we have a tabbed view. It's shown above
     * the tabs so it is not part of the scrollable list. If we are not in tabbed view,
     * we instead show the content preview as a regular list item.
     */
    private boolean shouldShowStickyContentPreview() {
        return hasWorkProfile()
                && ENABLE_TABBED_VIEW
                && mMultiProfilePagerAdapter.getListAdapterForUserHandle(
                        UserHandle.of(UserHandle.myUserId())).getCount() > 0
                && isSendAction(getTargetIntent())
                && getResources().getBoolean(R.bool.sharesheet_show_content_preview);
    }

    private void updateStickyContentPreview() {
        if (shouldShowStickyContentPreview()) {
            showStickyContentPreview();
        } else {
            hideContentPreview();
            hideStickyContentPreview();
        }
    }

    private void showContentPreview() {
    private void showStickyContentPreview() {
        ViewGroup contentPreviewContainer = findViewById(R.id.content_preview_container);
        contentPreviewContainer.setVisibility(View.VISIBLE);
        ViewGroup contentPreviewView = createContentPreviewView(contentPreviewContainer);
        contentPreviewContainer.addView(contentPreviewView);
        logActionShareWithPreview();
    }

    private void hideContentPreview() {
    private void hideStickyContentPreview() {
        ViewGroup contentPreviewContainer = findViewById(R.id.content_preview_container);
        contentPreviewContainer.removeAllViews();
        contentPreviewContainer.setVisibility(View.GONE);
@@ -2634,6 +2653,7 @@ public class ChooserActivity extends ResolverActivity implements
    /**
     * Used to bind types of individual item including
     * {@link ChooserGridAdapter#VIEW_TYPE_NORMAL},
     * {@link ChooserGridAdapter#VIEW_TYPE_CONTENT_PREVIEW},
     * {@link ChooserGridAdapter#VIEW_TYPE_PROFILE},
     * and {@link ChooserGridAdapter#VIEW_TYPE_AZ_LABEL}.
     */
@@ -2695,16 +2715,18 @@ public class ChooserActivity extends ResolverActivity implements
        private int mChooserTargetWidth = 0;
        private boolean mShowAzLabelIfPoss;

        private boolean mHideContentPreview = false;
        private boolean mLayoutRequested = false;

        private int mFooterHeight = 0;

        private static final int VIEW_TYPE_DIRECT_SHARE = 0;
        private static final int VIEW_TYPE_NORMAL = 1;
        private static final int VIEW_TYPE_PROFILE = 2;
        private static final int VIEW_TYPE_AZ_LABEL = 3;
        private static final int VIEW_TYPE_CALLER_AND_RANK = 4;
        private static final int VIEW_TYPE_FOOTER = 5;
        private static final int VIEW_TYPE_CONTENT_PREVIEW = 2;
        private static final int VIEW_TYPE_PROFILE = 3;
        private static final int VIEW_TYPE_AZ_LABEL = 4;
        private static final int VIEW_TYPE_CALLER_AND_RANK = 5;
        private static final int VIEW_TYPE_FOOTER = 6;

        private static final int MAX_TARGETS_PER_ROW_PORTRAIT = 4;
        private static final int MAX_TARGETS_PER_ROW_LANDSCAPE = 8;
@@ -2765,6 +2787,17 @@ public class ChooserActivity extends ResolverActivity implements
            return maxTargets;
        }

        /**
         * Hides the list item content preview.
         * <p>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
+9 −6
Original line number Diff line number Diff line
@@ -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);
+17 −14
Original line number Diff line number Diff line
@@ -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,13 +1498,15 @@ 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;
    }

    /**
+4 −0
Original line number Diff line number Diff line
@@ -607,6 +607,10 @@ public class ResolverListAdapter extends BaseAdapter {
                mIntents, userHandle);
    }

    protected List<Intent> getIntents() {
        return mIntents;
    }

    /**
     * Necessary methods to communicate between {@link ResolverListAdapter}
     * and {@link ResolverActivity}.
Loading