Merge "Hide sensitive clipboard content" into tm-dev

This commit is contained in:
Miranda Kephart
2022-05-05 19:10:28 +00:00
committed by Android (Google) Code Review
4 changed files with 92 additions and 29 deletions

View File

@@ -121,6 +121,30 @@
android:adjustViewBounds="true" android:adjustViewBounds="true"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"/>
<TextView
android:id="@+id/hidden_text_preview"
android:visibility="gone"
android:textFontWeight="500"
android:padding="8dp"
android:gravity="center"
android:textSize="14sp"
android:text="@string/clipboard_text_hidden"
android:textColor="?attr/overlayButtonTextColor"
android:background="?androidprv:attr/colorAccentSecondary"
android:layout_width="@dimen/clipboard_preview_size"
android:layout_height="@dimen/clipboard_preview_size"/>
<TextView
android:id="@+id/hidden_image_preview"
android:visibility="gone"
android:textFontWeight="500"
android:padding="8dp"
android:gravity="center"
android:textSize="14sp"
android:text="@string/clipboard_text_hidden"
android:textColor="#ffffff"
android:background="#000000"
android:layout_width="@dimen/clipboard_preview_size"
android:layout_height="@dimen/clipboard_preview_size"/>
</FrameLayout> </FrameLayout>
<FrameLayout <FrameLayout
android:id="@+id/dismiss_button" android:id="@+id/dismiss_button"

View File

@@ -2482,6 +2482,8 @@
<string name="clipboard_edit_image_description">Edit copied image</string> <string name="clipboard_edit_image_description">Edit copied image</string>
<!-- Label for button to send copied content to a nearby device [CHAR LIMIT=NONE] --> <!-- Label for button to send copied content to a nearby device [CHAR LIMIT=NONE] -->
<string name="clipboard_send_nearby_description">Send to nearby device</string> <string name="clipboard_send_nearby_description">Send to nearby device</string>
<!-- Text informing user that copied content is hidden [CHAR LIMIT=NONE] -->
<string name="clipboard_text_hidden">Tap to view</string>
<!-- Generic "add" string [CHAR LIMIT=NONE] --> <!-- Generic "add" string [CHAR LIMIT=NONE] -->
<string name="add">Add</string> <string name="add">Add</string>

View File

@@ -38,6 +38,7 @@ import android.annotation.MainThread;
import android.app.RemoteAction; import android.app.RemoteAction;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ClipData; import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
@@ -128,6 +129,8 @@ public class ClipboardOverlayController {
private final View mClipboardPreview; private final View mClipboardPreview;
private final ImageView mImagePreview; private final ImageView mImagePreview;
private final TextView mTextPreview; private final TextView mTextPreview;
private final TextView mHiddenTextPreview;
private final TextView mHiddenImagePreview;
private final View mPreviewBorder; private final View mPreviewBorder;
private final OverlayActionChip mEditChip; private final OverlayActionChip mEditChip;
private final OverlayActionChip mRemoteCopyChip; private final OverlayActionChip mRemoteCopyChip;
@@ -186,6 +189,8 @@ public class ClipboardOverlayController {
mClipboardPreview = requireNonNull(mView.findViewById(R.id.clipboard_preview)); mClipboardPreview = requireNonNull(mView.findViewById(R.id.clipboard_preview));
mImagePreview = requireNonNull(mView.findViewById(R.id.image_preview)); mImagePreview = requireNonNull(mView.findViewById(R.id.image_preview));
mTextPreview = requireNonNull(mView.findViewById(R.id.text_preview)); mTextPreview = requireNonNull(mView.findViewById(R.id.text_preview));
mHiddenTextPreview = requireNonNull(mView.findViewById(R.id.hidden_text_preview));
mHiddenImagePreview = requireNonNull(mView.findViewById(R.id.hidden_image_preview));
mPreviewBorder = requireNonNull(mView.findViewById(R.id.preview_border)); mPreviewBorder = requireNonNull(mView.findViewById(R.id.preview_border));
mEditChip = requireNonNull(mView.findViewById(R.id.edit_chip)); mEditChip = requireNonNull(mView.findViewById(R.id.edit_chip));
mRemoteCopyChip = requireNonNull(mView.findViewById(R.id.remote_copy_chip)); mRemoteCopyChip = requireNonNull(mView.findViewById(R.id.remote_copy_chip));
@@ -270,21 +275,32 @@ public class ClipboardOverlayController {
mExitAnimator.cancel(); mExitAnimator.cancel();
} }
reset(); reset();
boolean isSensitive = clipData != null && clipData.getDescription().getExtras() != null
&& clipData.getDescription().getExtras()
.getBoolean(ClipDescription.EXTRA_IS_SENSITIVE);
if (clipData == null || clipData.getItemCount() == 0) { if (clipData == null || clipData.getItemCount() == 0) {
showTextPreview(mContext.getResources().getString( showTextPreview(
R.string.clipboard_overlay_text_copied)); mContext.getResources().getString(R.string.clipboard_overlay_text_copied),
mTextPreview);
} else if (!TextUtils.isEmpty(clipData.getItemAt(0).getText())) { } else if (!TextUtils.isEmpty(clipData.getItemAt(0).getText())) {
ClipData.Item item = clipData.getItemAt(0); ClipData.Item item = clipData.getItemAt(0);
if (item.getTextLinks() != null) { if (item.getTextLinks() != null) {
AsyncTask.execute(() -> classifyText(clipData.getItemAt(0), clipSource)); AsyncTask.execute(() -> classifyText(clipData.getItemAt(0), clipSource));
} }
showEditableText(item.getText()); if (isSensitive) {
showEditableText(
mContext.getResources().getString(R.string.clipboard_text_hidden), true);
} else {
showEditableText(item.getText(), false);
}
} else if (clipData.getItemAt(0).getUri() != null) { } else if (clipData.getItemAt(0).getUri() != null) {
// How to handle non-image URIs? // How to handle non-image URIs?
showEditableImage(clipData.getItemAt(0).getUri()); showEditableImage(clipData.getItemAt(0).getUri(), isSensitive);
} else { } else {
showTextPreview( showTextPreview(
mContext.getResources().getString(R.string.clipboard_overlay_text_copied)); mContext.getResources().getString(R.string.clipboard_overlay_text_copied),
mTextPreview);
} }
Intent remoteCopyIntent = getRemoteCopyIntent(clipData); Intent remoteCopyIntent = getRemoteCopyIntent(clipData);
// Only show remote copy if it's available. // Only show remote copy if it's available.
@@ -406,15 +422,23 @@ public class ClipboardOverlayController {
animateOut(); animateOut();
} }
private void showTextPreview(CharSequence text) { private void showSinglePreview(View v) {
mTextPreview.setVisibility(View.VISIBLE); mTextPreview.setVisibility(View.GONE);
mImagePreview.setVisibility(View.GONE); mImagePreview.setVisibility(View.GONE);
mTextPreview.setText(text.subSequence(0, Math.min(500, text.length()))); mHiddenTextPreview.setVisibility(View.GONE);
mHiddenImagePreview.setVisibility(View.GONE);
v.setVisibility(View.VISIBLE);
}
private void showTextPreview(CharSequence text, TextView textView) {
showSinglePreview(textView);
textView.setText(text.subSequence(0, Math.min(500, text.length())));
mEditChip.setVisibility(View.GONE); mEditChip.setVisibility(View.GONE);
} }
private void showEditableText(CharSequence text) { private void showEditableText(CharSequence text, boolean hidden) {
showTextPreview(text); TextView textView = hidden ? mHiddenTextPreview : mTextPreview;
showTextPreview(text, textView);
mEditChip.setVisibility(View.VISIBLE); mEditChip.setVisibility(View.VISIBLE);
mActionContainerBackground.setVisibility(View.VISIBLE); mActionContainerBackground.setVisibility(View.VISIBLE);
mEditChip.setAlpha(1f); mEditChip.setAlpha(1f);
@@ -422,32 +446,36 @@ public class ClipboardOverlayController {
mContext.getString(R.string.clipboard_edit_text_description)); mContext.getString(R.string.clipboard_edit_text_description));
View.OnClickListener listener = v -> editText(); View.OnClickListener listener = v -> editText();
mEditChip.setOnClickListener(listener); mEditChip.setOnClickListener(listener);
mTextPreview.setOnClickListener(listener); textView.setOnClickListener(listener);
} }
private void showEditableImage(Uri uri) { private void showEditableImage(Uri uri, boolean isSensitive) {
ContentResolver resolver = mContext.getContentResolver();
try {
int size = mContext.getResources().getDimensionPixelSize(R.dimen.overlay_x_scale);
// The width of the view is capped, height maintains aspect ratio, so allow it to be
// taller if needed.
Bitmap thumbnail = resolver.loadThumbnail(uri, new Size(size, size * 4), null);
mImagePreview.setImageBitmap(thumbnail);
} catch (IOException e) {
Log.e(TAG, "Thumbnail loading failed", e);
showTextPreview(
mContext.getResources().getString(R.string.clipboard_overlay_text_copied));
return;
}
mTextPreview.setVisibility(View.GONE);
mImagePreview.setVisibility(View.VISIBLE);
mEditChip.setAlpha(1f); mEditChip.setAlpha(1f);
mActionContainerBackground.setVisibility(View.VISIBLE); mActionContainerBackground.setVisibility(View.VISIBLE);
View.OnClickListener listener = v -> editImage(uri); View.OnClickListener listener = v -> editImage(uri);
if (isSensitive) {
showSinglePreview(mHiddenImagePreview);
mHiddenImagePreview.setOnClickListener(listener);
} else {
showSinglePreview(mImagePreview);
ContentResolver resolver = mContext.getContentResolver();
try {
int size = mContext.getResources().getDimensionPixelSize(R.dimen.overlay_x_scale);
// The width of the view is capped, height maintains aspect ratio, so allow it to be
// taller if needed.
Bitmap thumbnail = resolver.loadThumbnail(uri, new Size(size, size * 4), null);
mImagePreview.setImageBitmap(thumbnail);
} catch (IOException e) {
Log.e(TAG, "Thumbnail loading failed", e);
showTextPreview(
mContext.getResources().getString(R.string.clipboard_overlay_text_copied),
mTextPreview);
}
mImagePreview.setOnClickListener(listener);
}
mEditChip.setOnClickListener(listener); mEditChip.setOnClickListener(listener);
mEditChip.setContentDescription( mEditChip.setContentDescription(
mContext.getString(R.string.clipboard_edit_image_description)); mContext.getString(R.string.clipboard_edit_image_description));
mImagePreview.setOnClickListener(listener);
} }
private Intent getRemoteCopyIntent(ClipData clipData) { private Intent getRemoteCopyIntent(ClipData clipData) {

View File

@@ -20,10 +20,12 @@ import static java.util.Objects.requireNonNull;
import android.app.Activity; import android.app.Activity;
import android.content.ClipData; import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Bundle; import android.os.Bundle;
import android.os.PersistableBundle;
import android.util.Log; import android.util.Log;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.EditText; import android.widget.EditText;
@@ -41,6 +43,7 @@ public class EditTextActivity extends Activity
private EditText mEditText; private EditText mEditText;
private ClipboardManager mClipboardManager; private ClipboardManager mClipboardManager;
private TextView mAttribution; private TextView mAttribution;
private boolean mSensitive;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@@ -72,6 +75,9 @@ public class EditTextActivity extends Activity
} }
mEditText.setText(clip.getItemAt(0).getText()); mEditText.setText(clip.getItemAt(0).getText());
mEditText.requestFocus(); mEditText.requestFocus();
mSensitive = clip.getDescription().getExtras() != null
&& clip.getDescription().getExtras()
.getBoolean(ClipDescription.EXTRA_IS_SENSITIVE);
mClipboardManager.addPrimaryClipChangedListener(this); mClipboardManager.addPrimaryClipChangedListener(this);
} }
@@ -88,6 +94,9 @@ public class EditTextActivity extends Activity
private void saveToClipboard() { private void saveToClipboard() {
ClipData clip = ClipData.newPlainText("text", mEditText.getText()); ClipData clip = ClipData.newPlainText("text", mEditText.getText());
PersistableBundle extras = new PersistableBundle();
extras.putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, mSensitive);
clip.getDescription().setExtras(extras);
mClipboardManager.setPrimaryClip(clip); mClipboardManager.setPrimaryClip(clip);
hideImeAndFinish(); hideImeAndFinish();
} }