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

Commit bc9f5a51 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Show updated labels for apps and DS in Sharesheet"

parents 41fd7447 ba23268f
Loading
Loading
Loading
Loading
+50 −28
Original line number Diff line number Diff line
@@ -1594,7 +1594,7 @@ public class ChooserActivity extends ResolverActivity {
            if (info == null) return null;

            // Now fetch app icon and raster with no badging even in work profile
            Bitmap appIcon = (new ActivityInfoPresentationGetter(info)).getIconBitmap();
            Bitmap appIcon = makePresentationGetter(info).getIconBitmap();

            // Raster target drawable with appIcon as a badge
            SimpleIconFactory sif = SimpleIconFactory.obtain(ChooserActivity.this);
@@ -1865,8 +1865,9 @@ public class ChooserActivity extends ResolverActivity {
                        ri.noResourceId = true;
                        ri.icon = 0;
                    }
                    ResolveInfoPresentationGetter getter = makePresentationGetter(ri);
                    mCallerTargets.add(new DisplayResolveInfo(ii, ri,
                            ri.loadLabel(pm), null, ii));
                            getter.getLabel(), getter.getSubLabel(), ii));
                }
            }
        }
@@ -1878,12 +1879,6 @@ public class ChooserActivity extends ResolverActivity {
            }
        }

        @Override
        public boolean showsExtendedInfo(TargetInfo info) {
            // We have badges so we don't need this text shown.
            return false;
        }

        @Override
        public View onCreateView(ViewGroup parent) {
            return mInflater.inflate(
@@ -2301,8 +2296,10 @@ public class ChooserActivity extends ResolverActivity {
            final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
            int columnCount = holder.getColumnCount();

            final boolean isDirectShare = holder instanceof DirectShareViewHolder;

            for (int i = 0; i < columnCount; i++) {
                final View v = mChooserListAdapter.createView(holder.getRow(i));
                final View v = mChooserListAdapter.createView(holder.getRowByIndex(i));
                final int column = i;
                v.setOnClickListener(new OnClickListener() {
                    @Override
@@ -2321,27 +2318,31 @@ public class ChooserActivity extends ResolverActivity {
                });
                ViewGroup row = holder.addView(i, v);

                // Force Direct Share to be 2 lines and auto-wrap to second line via hoz scroll =
                // false. TextView#setHorizontallyScrolling must be reset after #setLines. Must be
                // done before measuring.
                if (isDirectShare) {
                    final ViewHolder vh = (ViewHolder) v.getTag();
                    vh.text.setLines(2);
                    vh.text.setHorizontallyScrolling(false);
                    vh.text2.setVisibility(View.GONE);
                }

                // Force height to be a given so we don't have visual disruption during scaling.
                LayoutParams lp = v.getLayoutParams();
                v.measure(spec, spec);
                if (lp == null) {
                    lp = new LayoutParams(LayoutParams.MATCH_PARENT, v.getMeasuredHeight());
                    row.setLayoutParams(lp);
                } else {
                    lp.height = v.getMeasuredHeight();
                }
                setViewHeight(v, v.getMeasuredHeight());
            }

            final ViewGroup viewGroup = holder.getViewGroup();

            // Pre-measure so we can scale later.
            // Pre-measure and fix height so we can scale later.
            holder.measure();
            LayoutParams lp = viewGroup.getLayoutParams();
            if (lp == null) {
                lp = new LayoutParams(LayoutParams.MATCH_PARENT, holder.getMeasuredRowHeight());
                viewGroup.setLayoutParams(lp);
            } else {
                lp.height = holder.getMeasuredRowHeight();
            setViewHeight(viewGroup, holder.getMeasuredRowHeight());

            if (isDirectShare) {
                DirectShareViewHolder dsvh = (DirectShareViewHolder) holder;
                setViewHeight(dsvh.getRow(0), holder.getMeasuredRowHeight());
                setViewHeight(dsvh.getRow(1), holder.getMeasuredRowHeight());
            }

            viewGroup.setTag(holder);
@@ -2349,6 +2350,16 @@ public class ChooserActivity extends ResolverActivity {
            return holder;
        }

        private void setViewHeight(View view, int heightPx) {
            LayoutParams lp = view.getLayoutParams();
            if (lp == null) {
                lp = new LayoutParams(LayoutParams.MATCH_PARENT, heightPx);
                view.setLayoutParams(lp);
            } else {
                lp.height = heightPx;
            }
        }

        RowViewHolder createViewHolder(int viewType, ViewGroup parent) {
            if (viewType == VIEW_TYPE_DIRECT_SHARE) {
                ViewGroup parentGroup = (ViewGroup) mLayoutInflater.inflate(
@@ -2386,7 +2397,7 @@ public class ChooserActivity extends ResolverActivity {

            if (startType != lastStartType || rowPosition == getContentPreviewRowCount()) {
                row.setBackground(mChooserRowLayer);
                setVertPadding(row, mChooserRowServiceSpacing, 0);
                setVertPadding(row, 0, 0);
            } else {
                row.setBackground(null);
                setVertPadding(row, 0, 0);
@@ -2483,7 +2494,9 @@ public class ChooserActivity extends ResolverActivity {

        abstract ViewGroup getViewGroup();

        abstract ViewGroup getRow(int index);
        abstract ViewGroup getRowByIndex(int index);

        abstract ViewGroup getRow(int rowNumber);

        abstract void setViewVisibility(int i, int visibility);

@@ -2532,10 +2545,15 @@ public class ChooserActivity extends ResolverActivity {
            return mRow;
        }

        public ViewGroup getRow(int index) {
        public ViewGroup getRowByIndex(int index) {
            return mRow;
        }

        public ViewGroup getRow(int rowNumber) {
            if (rowNumber == 0) return mRow;
            return null;
        }

        public ViewGroup addView(int index, View v) {
            mRow.addView(v);
            mCells[index] = v;
@@ -2574,7 +2592,7 @@ public class ChooserActivity extends ResolverActivity {
        }

        public ViewGroup addView(int index, View v) {
            ViewGroup row = getRow(index);
            ViewGroup row = getRowByIndex(index);
            row.addView(v);
            mCells[index] = v;

@@ -2589,10 +2607,14 @@ public class ChooserActivity extends ResolverActivity {
            return mParent;
        }

        public ViewGroup getRow(int index) {
        public ViewGroup getRowByIndex(int index) {
            return mRows.get(index / mCellCountPerRow);
        }

        public ViewGroup getRow(int rowNumber) {
            return mRows.get(rowNumber);
        }

        public void measure() {
            final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
            getRow(0).measure(spec, spec);
+76 −116
Original line number Diff line number Diff line
@@ -83,7 +83,6 @@ import com.android.internal.widget.ResolverDrawerLayout;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
@@ -499,33 +498,40 @@ public class ResolverActivity extends Activity {


    /**
     * Loads the icon for the provided ApplicationInfo. Defaults to using the application icon over
     * any IntentFilter or Activity icon to increase user understanding, with an exception for
     * applications that hold the right permission. Always attempts to use icon resources over
     * PackageManager loading mechanisms so badging can be done by iconloader.
     * Loads the icon and label for the provided ApplicationInfo. Defaults to using the application
     * icon and label over any IntentFilter or Activity icon to increase user understanding, with an
     * exception for applications that hold the right permission. Always attempts to use available
     * resources over PackageManager loading mechanisms so badging can be done by iconloader. Uses
     * Strings to strip creative formatting.
     */
    private abstract class TargetPresentationGetter {
        @Nullable abstract Drawable getIconSubstitute();
        @Nullable abstract String getAppSubLabel();
    private abstract static class TargetPresentationGetter {
        @Nullable abstract Drawable getIconSubstituteInternal();
        @Nullable abstract String getAppSubLabelInternal();

        private Context mCtx;
        protected PackageManager mPm;
        private final ApplicationInfo mAi;
        private final int mIconDpi;
        private final boolean mHasSubstitutePermission;

        TargetPresentationGetter(ApplicationInfo ai) {
        TargetPresentationGetter(Context ctx, int iconDpi, ApplicationInfo ai) {
            mCtx = ctx;
            mPm = ctx.getPackageManager();
            mAi = ai;
            mIconDpi = iconDpi;
            mHasSubstitutePermission = PackageManager.PERMISSION_GRANTED == mPm.checkPermission(
                    android.Manifest.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON,
                    mAi.packageName);
        }

        Drawable getIcon() {
            return new BitmapDrawable(getResources(), getIconBitmap());
        public Drawable getIcon() {
            return new BitmapDrawable(mCtx.getResources(), getIconBitmap());
        }

        Bitmap getIconBitmap() {
        public Bitmap getIconBitmap() {
            Drawable dr = null;
            if (mHasSubstitutePermission) {
                dr = getIconSubstitute();
                dr = getIconSubstituteInternal();
            }

            if (dr == null) {
@@ -542,18 +548,18 @@ public class ResolverActivity extends Activity {
                dr = mAi.loadIcon(mPm);
            }

            SimpleIconFactory sif = SimpleIconFactory.obtain(ResolverActivity.this);
            SimpleIconFactory sif = SimpleIconFactory.obtain(mCtx);
            Bitmap icon = sif.createUserBadgedIconBitmap(dr, Process.myUserHandle());
            sif.recycle();

            return icon;
        }

        String getLabel() {
        public String getLabel() {
            String label = null;
            // Apps with the substitute permission will always show the sublabel as their label
            if (mHasSubstitutePermission) {
                label = getAppSubLabel();
                label = getAppSubLabelInternal();
            }

            if (label == null) {
@@ -563,10 +569,14 @@ public class ResolverActivity extends Activity {
            return label;
        }

        String getSubLabel() {
        public String getSubLabel() {
            // Apps with the substitute permission will never have a sublabel
            if (mHasSubstitutePermission) return null;
            return getAppSubLabel();
            return getAppSubLabelInternal();
        }

        protected String loadLabelFromResource(Resources res, int resId) {
            return res.getString(resId);
        }

        @Nullable
@@ -576,17 +586,19 @@ public class ResolverActivity extends Activity {

    }

    protected class ResolveInfoPresentationGetter extends TargetPresentationGetter {

    /**
     * Loads the icon and label for the provided ResolveInfo.
     */
    @VisibleForTesting
    public static class ResolveInfoPresentationGetter extends TargetPresentationGetter {
        private final ResolveInfo mRi;

        ResolveInfoPresentationGetter(ResolveInfo ri) {
            super(ri.activityInfo.applicationInfo);
        public ResolveInfoPresentationGetter(Context ctx, int iconDpi, ResolveInfo ri) {
            super(ctx, iconDpi, ri.activityInfo.applicationInfo);
            mRi = ri;
        }

        @Override
        Drawable getIconSubstitute() {
        Drawable getIconSubstituteInternal() {
            Drawable dr = null;
            try {
                // Do not use ResolveInfo#getIconResource() as it defaults to the app
@@ -603,20 +615,31 @@ public class ResolverActivity extends Activity {
        }

        @Override
        String getAppSubLabel() {
        String getAppSubLabelInternal() {
            // Will default to app name if no intent filter or activity label set, make sure to
            // check if subLabel matches label before final display
            return (String) mRi.loadLabel(mPm);
        }
    }

    protected class ActivityInfoPresentationGetter extends TargetPresentationGetter {
    ResolveInfoPresentationGetter makePresentationGetter(ResolveInfo ri) {
        return new ResolveInfoPresentationGetter(this, mIconDpi, ri);
    }

    /**
     * Loads the icon and label for the provided ActivityInfo.
     */
    @VisibleForTesting
    public static class ActivityInfoPresentationGetter extends TargetPresentationGetter {
        private final ActivityInfo mActivityInfo;
        protected ActivityInfoPresentationGetter(ActivityInfo activityInfo) {
            super(activityInfo.applicationInfo);
        public ActivityInfoPresentationGetter(Context ctx, int iconDpi,
                ActivityInfo activityInfo) {
            super(ctx, iconDpi, activityInfo.applicationInfo);
            mActivityInfo = activityInfo;
        }

        @Override
        Drawable getIconSubstitute() {
        Drawable getIconSubstituteInternal() {
            Drawable dr = null;
            try {
                // Do not use ActivityInfo#getIconResource() as it defaults to the app
@@ -634,13 +657,19 @@ public class ResolverActivity extends Activity {
        }

        @Override
        String getAppSubLabel() {
        String getAppSubLabelInternal() {
            // Will default to app name if no activity label set, make sure to check if subLabel
            // matches label before final display
            return (String) mActivityInfo.loadLabel(mPm);
        }
    }

    protected ActivityInfoPresentationGetter makePresentationGetter(ActivityInfo ai) {
        return new ActivityInfoPresentationGetter(this, mIconDpi, ai);
    }

    Drawable loadIconForResolveInfo(ResolveInfo ri) {
        return (new ResolveInfoPresentationGetter(ri)).getIcon();
        return makePresentationGetter(ri).getIcon();
    }

    @Override
@@ -1713,34 +1742,13 @@ public class ResolverActivity extends Activity {
                    }
                }

                // Check for applications with same name and use application name or
                // package name if necessary
                ResolvedComponentInfo rci0 = sortedComponents.get(0);
                ResolveInfo r0 = rci0.getResolveInfoAt(0);
                int start = 0;
                CharSequence r0Label = r0.loadLabel(mPm);
                mHasExtendedInfo = false;
                for (int i = 1; i < N; i++) {
                    if (r0Label == null) {
                        r0Label = r0.activityInfo.packageName;
                for (ResolvedComponentInfo rci : sortedComponents) {
                    final ResolveInfo ri = rci.getResolveInfoAt(0);
                    if (ri != null) {
                        ResolveInfoPresentationGetter pg = makePresentationGetter(ri);
                        addResolveInfoWithAlternates(rci, pg.getSubLabel(), pg.getLabel());
                    }
                    ResolvedComponentInfo rci = sortedComponents.get(i);
                    ResolveInfo ri = rci.getResolveInfoAt(0);
                    CharSequence riLabel = ri.loadLabel(mPm);
                    if (riLabel == null) {
                        riLabel = ri.activityInfo.packageName;
                }
                    if (riLabel.equals(r0Label)) {
                        continue;
                    }
                    processGroup(sortedComponents, start, (i - 1), rci0, r0Label);
                    rci0 = rci;
                    r0 = ri;
                    r0Label = riLabel;
                    start = i;
                }
                // Process last group
                processGroup(sortedComponents, start, (N - 1), rci0, r0Label);
            }

            postListReadyRunnable();
@@ -1782,55 +1790,6 @@ public class ResolverActivity extends Activity {
            return mFilterLastUsed;
        }

        private void processGroup(List<ResolvedComponentInfo> rList, int start, int end,
                ResolvedComponentInfo ro, CharSequence roLabel) {
            // Process labels from start to i
            int num = end - start+1;
            if (num == 1) {
                // No duplicate labels. Use label for entry at start
                addResolveInfoWithAlternates(ro, null, roLabel);
            } else {
                mHasExtendedInfo = true;
                boolean usePkg = false;
                final ApplicationInfo ai = ro.getResolveInfoAt(0).activityInfo.applicationInfo;
                final CharSequence startApp = ai.loadLabel(mPm);
                if (startApp == null) {
                    usePkg = true;
                }
                if (!usePkg) {
                    // Use HashSet to track duplicates
                    HashSet<CharSequence> duplicates =
                        new HashSet<CharSequence>();
                    duplicates.add(startApp);
                    for (int j = start+1; j <= end ; j++) {
                        ResolveInfo jRi = rList.get(j).getResolveInfoAt(0);
                        CharSequence jApp = jRi.activityInfo.applicationInfo.loadLabel(mPm);
                        if ( (jApp == null) || (duplicates.contains(jApp))) {
                            usePkg = true;
                            break;
                        } else {
                            duplicates.add(jApp);
                        }
                    }
                    // Clear HashSet for later use
                    duplicates.clear();
                }
                for (int k = start; k <= end; k++) {
                    final ResolvedComponentInfo rci = rList.get(k);
                    final ResolveInfo add = rci.getResolveInfoAt(0);
                    final CharSequence extraInfo;
                    if (usePkg) {
                        // Use package name for all entries from start to end-1
                        extraInfo = add.activityInfo.packageName;
                    } else {
                        // Use application name for all entries from start to end-1
                        extraInfo = add.activityInfo.applicationInfo.loadLabel(mPm);
                    }
                    addResolveInfoWithAlternates(rci, extraInfo, roLabel);
                }
            }
        }

        private void addResolveInfoWithAlternates(ResolvedComponentInfo rci,
                CharSequence extraInfo, CharSequence roLabel) {
            final int count = rci.getCount();
@@ -1979,31 +1938,32 @@ public class ResolverActivity extends Activity {
                    com.android.internal.R.layout.resolve_list_item, parent, false);
        }

        public boolean showsExtendedInfo(TargetInfo info) {
            return !TextUtils.isEmpty(info.getExtendedInfo());
        }

        public final void bindView(int position, View view) {
            onBindView(view, getItem(position));
        }

        private void onBindView(View view, TargetInfo info) {
        protected void onBindView(View view, TargetInfo info) {
            final ViewHolder holder = (ViewHolder) view.getTag();
            if (info == null) {
                holder.icon.setImageDrawable(
                        getDrawable(R.drawable.resolver_icon_placeholder));
                return;
            }

            final CharSequence label = info.getDisplayLabel();
            if (!TextUtils.equals(holder.text.getText(), label)) {
                holder.text.setText(info.getDisplayLabel());
            }
            if (showsExtendedInfo(info)) {
                holder.text2.setVisibility(View.VISIBLE);
                holder.text2.setText(info.getExtendedInfo());
            } else {
                holder.text2.setVisibility(View.GONE);

            // Always show a subLabel for visual consistency across list items. Show an empty
            // subLabel if the subLabel is the same as the label
            CharSequence subLabel = info.getExtendedInfo();
            if (TextUtils.equals(label, subLabel)) subLabel = null;

            if (!TextUtils.equals(holder.text2.getText(), subLabel)) {
                holder.text2.setText(subLabel);
            }

            if (info instanceof DisplayResolveInfo
                    && !((DisplayResolveInfo) info).hasDisplayIcon()) {
                new LoadAdapterIconTask((DisplayResolveInfo) info).execute();
+16 −19
Original line number Diff line number Diff line
@@ -22,46 +22,43 @@
              android:layout_height="wrap_content"
              android:minHeight="100dp"
              android:gravity="center"
              android:paddingTop="8dp"
              android:paddingTop="24dp"
              android:paddingBottom="8dp"
              android:paddingLeft="2dp"
              android:paddingRight="2dp"
              android:focusable="true"
              android:background="?attr/selectableItemBackgroundBorderless">

    <ImageView android:id="@+id/icon"
               android:layout_width="@dimen/resolver_icon_size"
               android:layout_height="@dimen/resolver_icon_size"
               android:layout_marginLeft="3dp"
               android:layout_marginRight="3dp"
               android:layout_marginBottom="3dp"
               android:scaleType="fitCenter" />

    <!-- Activity name -->
    <!-- Size manually tuned to match specs -->
    <Space android:layout_width="1dp"
           android:layout_height="7dp"/>

    <!-- App name or Direct Share target name, DS set to 2 lines -->
    <TextView android:id="@android:id/text1"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_marginTop="4dp"
              android:layout_marginLeft="4dp"
              android:layout_marginRight="4dp"
              android:textAppearance="?attr/textAppearanceSmall"
              android:textColor="?attr/textColorPrimary"
              android:textSize="12sp"
              android:textSize="14sp"
              android:fontFamily="sans-serif-condensed"
              android:gravity="top|center_horizontal"
              android:minLines="2"
              android:maxLines="2"
              android:ellipsize="marquee" />
    <!-- Extended activity info to distinguish between duplicate activity names -->
              android:lines="1"
              android:ellipsize="end" />

    <!-- Activity name if set, gone for Direct Share targets -->
    <TextView android:id="@android:id/text2"
              android:textAppearance="?android:attr/textAppearanceSmall"
              android:textSize="12sp"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_marginLeft="4dp"
              android:layout_marginRight="4dp"
              android:minLines="2"
              android:maxLines="2"
              android:lines="1"
              android:gravity="top|center_horizontal"
              android:ellipsize="marquee"
              android:visibility="gone" />
              android:ellipsize="end"/>

</LinearLayout>
+49 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;

import static com.android.internal.app.ResolverDataProvider.createPackageManagerMockedInfo;
import static com.android.internal.app.ResolverWrapperActivity.sOverrides;

import static org.hamcrest.CoreMatchers.is;
@@ -32,6 +33,7 @@ import static org.mockito.Mockito.when;

import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.text.TextUtils;
import android.view.View;
import android.widget.RelativeLayout;

@@ -40,7 +42,10 @@ import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;

import com.android.internal.R;
import com.android.internal.app.ResolverActivity.ActivityInfoPresentationGetter;
import com.android.internal.app.ResolverActivity.ResolveInfoPresentationGetter;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
import com.android.internal.app.ResolverDataProvider.PackageManagerMockedInfo;
import com.android.internal.widget.ResolverDrawerLayout;

import org.junit.Before;
@@ -319,6 +324,50 @@ public class ResolverActivityTest {
        assertThat(chosen[0], is(toChoose));
    }

    @Test
    public void getActivityLabelAndSubLabel() throws Exception {
        ActivityInfoPresentationGetter pg;
        PackageManagerMockedInfo info;

        info = createPackageManagerMockedInfo(false);
        pg = new ActivityInfoPresentationGetter(
                info.ctx, 0, info.activityInfo);
        assertThat("Label should match app label", pg.getLabel().equals(
                info.setAppLabel));
        assertThat("Sublabel should match activity label if set",
                pg.getSubLabel().equals(info.setActivityLabel));

        info = createPackageManagerMockedInfo(true);
        pg = new ActivityInfoPresentationGetter(
                info.ctx, 0, info.activityInfo);
        assertThat("With override permission label should match activity label if set",
                pg.getLabel().equals(info.setActivityLabel));
        assertThat("With override permission sublabel should be empty",
                TextUtils.isEmpty(pg.getSubLabel()));
    }

    @Test
    public void getResolveInfoLabelAndSubLabel() throws Exception {
        ResolveInfoPresentationGetter pg;
        PackageManagerMockedInfo info;

        info = createPackageManagerMockedInfo(false);
        pg = new ResolveInfoPresentationGetter(
                info.ctx, 0, info.resolveInfo);
        assertThat("Label should match app label", pg.getLabel().equals(
                info.setAppLabel));
        assertThat("Sublabel should match resolve info label if set",
                pg.getSubLabel().equals(info.setResolveInfoLabel));

        info = createPackageManagerMockedInfo(true);
        pg = new ResolveInfoPresentationGetter(
                info.ctx, 0, info.resolveInfo);
        assertThat("With override permission label should match resolve info label if set",
                pg.getLabel().equals(info.setResolveInfoLabel));
        assertThat("With override permission sublabel should be empty",
                TextUtils.isEmpty(pg.getSubLabel()));
    }

    private Intent createSendImageIntent() {
        Intent sendIntent = new Intent();
        sendIntent.setAction(Intent.ACTION_SEND);
+86 −0

File changed.

Preview size limit exceeded, changes collapsed.