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

Commit 26038402 authored by Matt Pietal's avatar Matt Pietal
Browse files

Sharesheet content preview - adding text support only

Adding initial support for a content preview section within
the ChooserActivity. File/Image support is pending.

Bug: 120419296
Test: 'atest ChooserActivityTest' as well as visual inspection
of sharing from multiple google apps like Chrome

Change-Id: Iee4746940fb8ddd4f0a54a0bf7ef485be2eab30d
parent f5e95eff
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -1942,7 +1942,6 @@ Lcom/android/internal/R$id;->title:I
Lcom/android/internal/R$id;->titleDivider:I
Lcom/android/internal/R$id;->titleDividerTop:I
Lcom/android/internal/R$id;->title_container:I
Lcom/android/internal/R$id;->title_icon:I
Lcom/android/internal/R$id;->title_template:I
Lcom/android/internal/R$id;->topPanel:I
Lcom/android/internal/R$id;->up:I
+4 −2
Original line number Diff line number Diff line
@@ -971,7 +971,8 @@ public class Intent implements Parcelable, Cloneable {
     *
     * @param target The Intent that the user will be selecting an activity
     * to perform.
     * @param title Optional title that will be displayed in the chooser.
     * @param title Optional title that will be displayed in the chooser,
     * only when the target action is not ACTION_SEND or ACTION_SEND_MULTIPLE.
     * @return Return a new Intent object that you can hand to
     * {@link Context#startActivity(Intent) Context.startActivity()} and
     * related methods.
@@ -998,7 +999,8 @@ public class Intent implements Parcelable, Cloneable {
     *
     * @param target The Intent that the user will be selecting an activity
     * to perform.
     * @param title Optional title that will be displayed in the chooser.
     * @param title Optional title that will be displayed in the chooser,
     * only when the target action is not ACTION_SEND or ACTION_SEND_MULTIPLE.
     * @param sender Optional IntentSender to be called when a choice is made.
     * @return Return a new Intent object that you can hand to
     * {@link Context#startActivity(Intent) Context.startActivity()} and
+181 −12
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.app.prediction.AppPredictor;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
import android.content.ClipData;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -41,9 +42,13 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.database.DataSetObserver;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Path;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
@@ -62,7 +67,9 @@ import android.service.chooser.ChooserTargetService;
import android.service.chooser.IChooserTargetResult;
import android.service.chooser.IChooserTargetService;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Size;
import android.util.Slog;
import android.view.LayoutInflater;
import android.view.View;
@@ -73,9 +80,11 @@ import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.Space;
import android.widget.TextView;

import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -85,6 +94,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.google.android.collect.Lists;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -262,11 +272,26 @@ public class ChooserActivity extends ResolverActivity {
        }

        mReplacementExtras = intent.getBundleExtra(Intent.EXTRA_REPLACEMENT_EXTRAS);
        CharSequence title = intent.getCharSequenceExtra(Intent.EXTRA_TITLE);

        // Do not allow the title to be changed when sharing content
        CharSequence title = null;
        if (target != null) {
            String targetAction = target.getAction();
            if (!(Intent.ACTION_SEND.equals(targetAction) || Intent.ACTION_SEND_MULTIPLE.equals(
                    targetAction))) {
                title = intent.getCharSequenceExtra(Intent.EXTRA_TITLE);
            } else {
                Log.w(TAG, "Ignoring intent's EXTRA_TITLE, deprecated in P. You may wish to set a"
                        + " preview title by using EXTRA_TITLE property of the wrapped"
                        + " EXTRA_INTENT.");
            }
        }

        int defaultTitleRes = 0;
        if (title == null) {
            defaultTitleRes = com.android.internal.R.string.chooseActivity;
        }

        Parcelable[] pa = intent.getParcelableArrayExtra(Intent.EXTRA_INITIAL_INTENTS);
        Intent[] initialIntents = null;
        if (pa != null) {
@@ -364,6 +389,68 @@ public class ChooserActivity extends ResolverActivity {
        }
    }

    /**
     * Override method to add content preview area, specific to the chooser activity.
     */
    @Override
    public void setHeader() {
        super.setHeader();

        Intent targetIntent = getTargetIntent();
        if (targetIntent == null) {
            return;
        }

        ViewGroup contentPreviewLayout = findViewById(R.id.content_preview);
        String action = targetIntent.getAction();
        if (!(Intent.ACTION_SEND.equals(action) || Intent.ACTION_SEND_MULTIPLE.equals(action))) {
            contentPreviewLayout.setVisibility(View.GONE);
            return;
        }

        showDefaultContentPreview(contentPreviewLayout, targetIntent);
    }

    private void showDefaultContentPreview(final ViewGroup parentLayout,
            final Intent targetIntent) {
        CharSequence sharingText = targetIntent.getCharSequenceExtra(Intent.EXTRA_TEXT);
        TextView previewTextView = findViewById(R.id.content_preview_text);
        if (sharingText == null) {
            previewTextView.setVisibility(View.GONE);
        } else {
            previewTextView.setText(sharingText);
        }

        String previewTitle = targetIntent.getStringExtra(Intent.EXTRA_TITLE);
        TextView previewTitleView = findViewById(R.id.content_preview_title);
        if (previewTitle == null) {
            previewTitleView.setVisibility(View.GONE);
        } else {
            previewTitleView.setText(previewTitle);
        }

        ClipData previewData = targetIntent.getClipData();
        Uri previewThumbnail = null;
        if (previewData != null) {
            if (previewData.getItemCount() > 0) {
                ClipData.Item previewDataItem = previewData.getItemAt(0);
                previewThumbnail = previewDataItem.getUri();
            }
        }

        ImageView previewThumbnailView = findViewById(R.id.content_preview_thumbnail);
        if (previewThumbnail == null) {
            previewThumbnailView.setVisibility(View.GONE);
        } else {
            Bitmap bmp = loadThumbnail(previewThumbnail, new Size(200, 200));
            if (bmp == null) {
                previewThumbnailView.setVisibility(View.GONE);
            } else {
                previewThumbnailView.setImageBitmap(bmp);
            }
        }
    }

    static SharedPreferences getPinnedSharedPrefs(Context context) {
        // The code below is because in the android:ui process, no one can hear you scream.
        // The package info in the context isn't initialized in the way it is for normal apps,
@@ -630,15 +717,19 @@ public class ChooserActivity extends ResolverActivity {
                }
            }
            if (targetsToQuery >= QUERY_TARGET_SERVICE_LIMIT) {
                if (DEBUG) Log.d(TAG, "queryTargets hit query target limit "
                if (DEBUG) {
                    Log.d(TAG, "queryTargets hit query target limit "
                            + QUERY_TARGET_SERVICE_LIMIT);
                }
                break;
            }
        }

        if (!mServiceConnections.isEmpty()) {
            if (DEBUG) Log.d(TAG, "queryTargets setting watchdog timer for "
            if (DEBUG) {
                Log.d(TAG, "queryTargets setting watchdog timer for "
                        + WATCHDOG_TIMEOUT_MILLIS + "ms");
            }
            mChooserHandler.sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT,
                    WATCHDOG_TIMEOUT_MILLIS);
        } else {
@@ -969,6 +1060,20 @@ public class ChooserActivity extends ResolverActivity {
                mLaunchedFromUid);
    }

    @VisibleForTesting
    protected Bitmap loadThumbnail(Uri uri, Size size) {
        if (uri == null || size == null) {
            return null;
        }

        try {
            return getContentResolver().loadThumbnail(uri, size, null);
        } catch (IOException | NullPointerException ex) {
            Log.w(TAG, "Error loading preview thumbnail for uri: " + uri.toString(), ex);
        }
        return null;
    }

    final class ChooserTargetInfo implements TargetInfo {
        private final DisplayResolveInfo mSourceInfo;
        private final ResolveInfo mBackupResolveInfo;
@@ -1391,8 +1496,10 @@ public class ChooserActivity extends ResolverActivity {
        }

        public void addServiceResults(DisplayResolveInfo origTarget, List<ChooserTarget> targets) {
            if (DEBUG) Log.d(TAG, "addServiceResults " + origTarget + ", " + targets.size()
            if (DEBUG) {
                Log.d(TAG, "addServiceResults " + origTarget + ", " + targets.size()
                        + " targets");
            }

            if (mTargetsNeedPruning && targets.size() > 0) {
                // First proper update since we got an onListRebuilt() with (transient) 0 items.
@@ -1493,7 +1600,8 @@ public class ChooserActivity extends ResolverActivity {
            return (int) (
                    getCallerTargetRowCount()
                            + getServiceTargetRowCount()
                    + Math.ceil((float) mChooserListAdapter.getStandardTargetCount() / mColumnCount)
                            + Math.ceil(
                            (float) mChooserListAdapter.getStandardTargetCount() / mColumnCount)
            );
        }

@@ -1882,4 +1990,65 @@ public class ChooserActivity extends ResolverActivity {
            mResolverDrawerLayout.setCollapsibleHeightReserved(offset);
        }
    }


    /**
     * Used internally to round image corners while obeying view padding.
     */
    public static class RoundedRectImageView extends ImageView {
        private int mRadius = 0;
        private Path mPath = new Path();

        public RoundedRectImageView(Context context) {
            super(context);
        }

        public RoundedRectImageView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }

        public RoundedRectImageView(Context context, AttributeSet attrs, int defStyleAttr) {
            this(context, attrs, defStyleAttr, 0);
        }

        public RoundedRectImageView(Context context, AttributeSet attrs, int defStyleAttr,
                int defStyleRes) {
            super(context, attrs, defStyleAttr, defStyleRes);
            mRadius = context.getResources().getDimensionPixelSize(R.dimen.chooser_corner_radius);
        }

        private void updatePath(int width, int height) {
            mPath.reset();

            int imageWidth = width - getPaddingLeft() - getPaddingRight();
            int imageHeight = height - getPaddingTop() - getPaddingBottom();
            mPath.addRoundRect(getPaddingLeft(), getPaddingTop(), imageWidth, imageHeight, mRadius,
                    mRadius, Path.Direction.CW);
        }

        /**
          * Sets the corner radius on all corners
          *
          * param radius 0 for no radius, &gt; 0 for a visible corner radius
          */
        public void setRadius(int radius) {
            mRadius = radius;
            updatePath(getWidth(), getHeight());
        }

        @Override
        protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
            super.onSizeChanged(width, height, oldWidth, oldHeight);
            updatePath(width, height);
        }

        @Override
        protected void onDraw(Canvas canvas) {
            if (mRadius != 0) {
                canvas.clipPath(mPath);
            }

            super.onDraw(canvas);
        }
    }
}
+5 −19
Original line number Diff line number Diff line
@@ -1044,7 +1044,10 @@ public class ResolverActivity extends Activity {
        }
    }

    public void setTitleAndIcon() {
    /**
     * Configure the area above the app selection list (title, content preview, etc).
     */
    public void setHeader() {
        if (mAdapter.getCount() == 0 && mAdapter.mPlaceholderCount == 0) {
            final TextView titleView = findViewById(R.id.title);
            if (titleView != null) {
@@ -1062,23 +1065,6 @@ public class ResolverActivity extends Activity {
                titleView.setText(title);
            }
            setTitle(title);

            // Try to initialize the title icon if we have a view for it and a title to match
            final ImageView titleIcon = findViewById(R.id.title_icon);
            if (titleIcon != null) {
                ApplicationInfo ai = null;
                try {
                    if (!TextUtils.isEmpty(mReferrerPackage)) {
                        ai = mPm.getApplicationInfo(mReferrerPackage, 0);
                    }
                } catch (NameNotFoundException e) {
                    Log.e(TAG, "Could not find referrer package " + mReferrerPackage);
                }

                if (ai != null) {
                    titleIcon.setImageDrawable(ai.loadIcon(mPm));
                }
            }
        }

        final ImageView iconView = findViewById(R.id.icon);
@@ -1692,7 +1678,7 @@ public class ResolverActivity extends Activity {
                mPostListReadyRunnable = new Runnable() {
                    @Override
                    public void run() {
                        setTitleAndIcon();
                        setHeader();
                        resetButtonBar();
                        onListRebuilt();
                        mPostListReadyRunnable = null;
+53 −14
Original line number Diff line number Diff line
@@ -29,8 +29,6 @@
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alwaysShow="true"
            android:elevation="8dp"
            android:paddingStart="16dp"
            android:background="?attr/colorBackgroundFloating" >
        <TextView android:id="@+id/profile_button"
                  android:layout_width="wrap_content"
@@ -46,25 +44,66 @@
                  android:layout_alignParentTop="true"
                  android:layout_alignParentRight="true"
                  android:singleLine="true"/>
        <ImageView android:id="@+id/title_icon"
                   android:layout_width="24dp"
                   android:layout_height="24dp"
                   android:layout_marginEnd="16dp"
                   android:visibility="gone"
                   android:scaleType="fitCenter"
                   android:layout_below="@id/profile_button"
                   android:layout_alignParentLeft="true"/>

        <TextView android:id="@+id/title"
                  android:layout_height="wrap_content"
                  android:layout_width="wrap_content"
                  android:textAppearance="?attr/textAppearanceMedium"
                  android:textSize="14sp"
                  android:gravity="start|center_vertical"
                  android:textSize="20sp"
                  android:gravity="center"
                  android:paddingEnd="?attr/dialogPreferredPadding"
                  android:paddingTop="12dp"
                  android:paddingBottom="12dp"
                  android:paddingBottom="6dp"
                  android:layout_below="@id/profile_button"
                  android:layout_toRightOf="@id/title_icon"/>
                  android:layout_centerHorizontal="true"/>
    </RelativeLayout>

    <RelativeLayout 
        android:id="@+id/content_preview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/colorBackgroundFloating">
      
      <view class="com.android.internal.app.ChooserActivity$RoundedRectImageView"
            android:id="@+id/content_preview_thumbnail"
            android:layout_alignParentTop="true"
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:layout_alignParentStart="true"
            android:gravity="center"
            android:adjustViewBounds="true"
            android:maxWidth="90dp"
            android:maxHeight="90dp"
            android:scaleType="fitCenter"
            android:padding="5dp"/>

      <TextView
          android:id="@+id/content_preview_title"
          android:layout_alignParentTop="true"
          android:layout_toEndOf="@id/content_preview_thumbnail"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:gravity="start|top"
          android:textAppearance="?attr/textAppearanceMedium"
          android:maxLines="2"
          android:ellipsize="end"
          android:paddingStart="15dp"
          android:paddingEnd="15dp"
          android:paddingTop="10dp" />

      <TextView
          android:id="@+id/content_preview_text"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:layout_below="@id/content_preview_title"
          android:layout_toEndOf="@id/content_preview_thumbnail"
          android:gravity="start|top"
          android:maxLines="2"
          android:ellipsize="end"
          android:paddingStart="15dp"
          android:paddingEnd="15dp"
          android:paddingTop="10dp"
          android:paddingBottom="5dp"/>
    </RelativeLayout>

    <ListView
Loading