From 73d2150d1572b878a4c23410b1bb8de72510a184 Mon Sep 17 00:00:00 2001 From: Jay Aliomer Date: Mon, 5 Apr 2021 19:22:28 -0400 Subject: [PATCH] Image inline reply Fixes: b/185377047 Test: manual Change-Id: I394f1485f95fe36b739e0d377c6a552f0621347b --- .../res/drawable/rounded_rect_background.xml | 22 +++ .../res/drawable/thumbnail_delete_btn_bg.xml | 23 +++ packages/SystemUI/res/layout/remote_input.xml | 89 +++++++--- packages/SystemUI/res/values/strings.xml | 3 +- .../notification/row/FooterView.java | 11 +- .../statusbar/policy/RemoteInputView.java | 160 ++++++++++++------ 6 files changed, 234 insertions(+), 74 deletions(-) create mode 100644 packages/SystemUI/res/drawable/rounded_rect_background.xml create mode 100644 packages/SystemUI/res/drawable/thumbnail_delete_btn_bg.xml diff --git a/packages/SystemUI/res/drawable/rounded_rect_background.xml b/packages/SystemUI/res/drawable/rounded_rect_background.xml new file mode 100644 index 0000000000000..83b0f7122169e --- /dev/null +++ b/packages/SystemUI/res/drawable/rounded_rect_background.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/thumbnail_delete_btn_bg.xml b/packages/SystemUI/res/drawable/thumbnail_delete_btn_bg.xml new file mode 100644 index 0000000000000..ea7d45d03b7f2 --- /dev/null +++ b/packages/SystemUI/res/drawable/thumbnail_delete_btn_bg.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml index 4fcce243f58fc..a909d0d4d3c24 100644 --- a/packages/SystemUI/res/layout/remote_input.xml +++ b/packages/SystemUI/res/layout/remote_input.xml @@ -22,28 +22,75 @@ android:id="@+id/remote_input" android:layout_height="match_parent" android:layout_width="match_parent"> + + + + + + - + + Num Lock Numpad %1$s - + + Remove attachment System diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java index 3bf0ddb1985b7..298d4f07a8df2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.row; import android.annotation.ColorInt; import android.content.Context; import android.content.res.Configuration; +import android.content.res.Resources; import android.util.AttributeSet; import android.view.View; @@ -93,14 +94,16 @@ public class FooterView extends StackScrollerDecorView { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); + int textColor = getResources().getColor(R.color.notif_pill_text); + Resources.Theme theme = getContext().getTheme(); mDismissButton.setBackground( - getResources().getDrawable(R.drawable.notif_footer_btn_background)); - mDismissButton.setTextColor(getResources().getColor(R.color.notif_pill_text)); + getResources().getDrawable(R.drawable.notif_footer_btn_background, theme)); + mDismissButton.setTextColor(textColor); mManageButton.setBackground( - getResources().getDrawable(R.drawable.notif_footer_btn_background)); - mManageButton.setTextColor(getResources().getColor(R.color.notif_pill_text)); + getResources().getDrawable(R.drawable.notif_footer_btn_background, theme)); mManageButton = findViewById(R.id.manage_text); mDismissButton.setText(R.string.clear_all_notifications_text); + mManageButton.setTextColor(textColor); mDismissButton.setContentDescription( mContext.getString(R.string.accessibility_clear_all)); showHistory(mShowHistory); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index f2a0e37868852..a3fd92f8e186c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -25,21 +25,18 @@ import android.app.ActivityManager; import android.app.Notification; import android.app.PendingIntent; import android.app.RemoteInput; -import android.content.ClipData; -import android.content.ClipDescription; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ShortcutManager; import android.content.res.ColorStateList; import android.content.res.TypedArray; +import android.graphics.BlendMode; import android.graphics.Color; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; -import android.graphics.drawable.LayerDrawable; import android.net.Uri; import android.os.Bundle; import android.os.ServiceManager; @@ -47,7 +44,9 @@ import android.os.SystemClock; import android.os.UserHandle; import android.text.Editable; import android.text.SpannedString; +import android.text.TextUtils; import android.text.TextWatcher; +import android.util.ArraySet; import android.util.AttributeSet; import android.util.Log; import android.util.Pair; @@ -68,6 +67,7 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.ImageButton; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; @@ -81,7 +81,6 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.ContrastColorUtil; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -89,6 +88,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry. import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.LightBarController; +import com.android.wm.shell.animation.Interpolators; import java.util.ArrayList; import java.util.Collection; @@ -114,6 +114,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private final List mEditTextFocusChangeListeners = new ArrayList<>(); private RemoteEditText mEditText; private ImageButton mSendButton; + private GradientDrawable mContentBackground; private ProgressBar mProgressBar; private PendingIntent mPendingIntent; private RemoteInput[] mRemoteInputs; @@ -130,6 +131,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private int mRevealCx; private int mRevealCy; private int mRevealR; + private ContentInfo mAttachment; private boolean mColorized; private int mTint; @@ -138,6 +140,9 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private NotificationViewWrapper mWrapper; private Consumer mOnVisibilityChangedListener; private NotificationRemoteInputManager.BouncerChecker mBouncerChecker; + private LinearLayout mContentView; + private ImageView mDelete; + private ImageView mDeleteBg; public RemoteInputView(Context context, AttributeSet attrs) { super(context, attrs); @@ -149,7 +154,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene ServiceManager.getService(Context.STATUS_BAR_SERVICE)); TypedArray ta = getContext().getTheme().obtainStyledAttributes(new int[]{ com.android.internal.R.attr.colorAccent, - com.android.internal.R.attr.colorBackgroundFloating, + com.android.internal.R.attr.colorSurface, }); mTint = ta.getColor(0, 0); ta.recycle(); @@ -165,14 +170,20 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mColorized = colorized; mTint = backgroundColor; final int editBgColor; + final int alternateBgColor; + final int alternateTextColor; final int accentColor; final int textColor; final int hintTextColor; + final int stroke = mContext.getResources().getDimensionPixelSize( + R.dimen.remote_input_view_text_stroke); if (colorized) { final boolean dark = !ContrastColorUtil.isColorLight(backgroundColor); final int foregroundColor = dark ? Color.WHITE : Color.BLACK; editBgColor = backgroundColor; accentColor = foregroundColor; + alternateBgColor = foregroundColor; + alternateTextColor = backgroundColor; textColor = foregroundColor; hintTextColor = ColorUtils.setAlphaComponent(foregroundColor, 0x99); } else { @@ -180,10 +191,14 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene hintTextColor = mContext.getColor(R.color.remote_input_hint); try (TypedArray ta = getContext().getTheme().obtainStyledAttributes(new int[]{ com.android.internal.R.attr.colorAccent, - com.android.internal.R.attr.colorBackgroundFloating, + com.android.internal.R.attr.colorSurface, + com.android.internal.R.attr.colorSurfaceVariant, + com.android.internal.R.attr.textColorPrimary })) { accentColor = ta.getColor(0, textColor); editBgColor = ta.getColor(1, backgroundColor); + alternateBgColor = ta.getColor(2, textColor); + alternateTextColor = ta.getColor(3, backgroundColor); } } mEditText.setAllColors(backgroundColor, editBgColor, @@ -195,11 +210,15 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene accentColor, accentColor & 0x4DFFFFFF // %30 opacity }); + mContentBackground.setColor(editBgColor); + mContentBackground.setStroke(stroke, accentTint); + mDelete.setImageTintList(ColorStateList.valueOf(alternateTextColor)); + mDeleteBg.setImageTintList(ColorStateList.valueOf(alternateBgColor)); mSendButton.setImageTintList(accentTint); mProgressBar.setProgressTintList(accentTint); mProgressBar.setIndeterminateTintList(accentTint); mProgressBar.setSecondaryProgressTintList(accentTint); - setBackgroundColor(backgroundColor); + setBackgroundColor(editBgColor); } @Override @@ -209,8 +228,16 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mProgressBar = findViewById(R.id.remote_input_progress); mSendButton = findViewById(R.id.remote_input_send); mSendButton.setOnClickListener(this); - - mEditText = (RemoteEditText) getChildAt(0); + mContentBackground = (GradientDrawable) + mContext.getDrawable(R.drawable.remote_input_view_text_bg).mutate(); + mDelete = findViewById(R.id.remote_input_delete); + mDeleteBg = findViewById(R.id.remote_input_delete_bg); + mDeleteBg.setImageTintBlendMode(BlendMode.SRC_IN); + mDelete.setImageTintBlendMode(BlendMode.SRC_IN); + mDelete.setOnClickListener(v -> setAttachment(null)); + mContentView = findViewById(R.id.remote_input_content); + mContentView.setBackground(mContentBackground); + mEditText = findViewById(R.id.remote_input_text); mEditText.setInnerFocusable(false); mEditText.setWindowInsetsAnimationCallback( new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) { @@ -220,7 +247,6 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene @NonNull List runningAnimations) { return insets; } - @Override public void onEnd(@NonNull WindowInsetsAnimation animation) { super.onEnd(animation); @@ -235,7 +261,43 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene }); } - protected Intent prepareRemoteInputFromText() { + private void setAttachment(ContentInfo item) { + if (mAttachment != null) { + // We need to release permissions when sending the attachment to the target + // app or if it is deleted by the user. When sending to the target app, we + // can safely release permissions as soon as the call to + // `mController.grantInlineReplyUriPermission` is made (ie, after the grant + // to the target app has been created). + mAttachment.releasePermissions(); + } + mAttachment = item; + View attachment = findViewById(R.id.remote_input_content_container); + ImageView iconView = findViewById(R.id.remote_input_attachment_image); + iconView.setImageDrawable(null); + if (item == null) { + attachment.setVisibility(GONE); + return; + } + iconView.setImageURI(item.getClip().getItemAt(0).getUri()); + if (iconView.getDrawable() == null) { + attachment.setVisibility(GONE); + } else { + attachment.setVisibility(VISIBLE); + } + } + + /** + * Reply intent + * @return returns intent with granted URI permissions that should be used immediately + */ + private Intent prepareRemoteInput() { + if (mAttachment == null) return prepareRemoteInputFromText(); + return prepareRemoteInputFromData( + mAttachment.getClip().getDescription().getMimeType(0), + mAttachment.getClip().getItemAt(0).getUri()); + } + + private Intent prepareRemoteInputFromText() { Bundle results = new Bundle(); results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString()); Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND); @@ -255,17 +317,40 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene return fillInIntent; } - protected Intent prepareRemoteInputFromData(String contentType, Uri data) { + private Intent prepareRemoteInputFromData(String contentType, Uri data) { HashMap results = new HashMap<>(); results.put(contentType, data); + // grant for the target app. mController.grantInlineReplyUriPermission(mEntry.getSbn(), data); Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND); RemoteInput.addDataResultToIntent(mRemoteInput, fillInIntent, results); - mEntry.remoteInputText = mContext.getString(R.string.remote_input_image_insertion_text); + Bundle bundle = new Bundle(); + bundle.putString(mRemoteInput.getResultKey(), mEditText.getText().toString()); + RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent, + bundle); + + CharSequence attachmentText = mAttachment.getClip().getDescription().getLabel(); + + CharSequence attachmentLabel = TextUtils.isEmpty(attachmentText) + ? mContext.getString(R.string.remote_input_image_insertion_text) + : attachmentText; + // add content description to reply text for context + CharSequence fullText = TextUtils.isEmpty(mEditText.getText()) + ? attachmentLabel + : "\"" + attachmentLabel + "\" " + mEditText.getText(); + + mEntry.remoteInputText = fullText; mEntry.remoteInputMimeType = contentType; mEntry.remoteInputUri = data; + // mirror prepareRemoteInputFromText for text input + if (mEntry.editedSuggestionInfo == null) { + RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_FREE_FORM_INPUT); + } else if (mAttachment == null) { + RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_CHOICE); + } + return fillInIntent; } @@ -303,6 +388,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_FAIL, mEntry.getSbn().getPackageName()); } + setAttachment(null); } public CharSequence getText() { @@ -327,7 +413,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene @Override public void onClick(View v) { if (v == mSendButton) { - sendRemoteInput(prepareRemoteInputFromText()); + sendRemoteInput(prepareRemoteInput()); } } @@ -682,8 +768,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene && event.getAction() == KeyEvent.ACTION_DOWN; if (isSoftImeEvent || isKeyboardEnterKey) { - if (mEditText.length() > 0) { - sendRemoteInput(prepareRemoteInputFromText()); + if (mEditText.length() > 0 || mAttachment != null) { + sendRemoteInput(prepareRemoteInput()); } // Consume action to prevent IME from closing. return true; @@ -716,21 +802,18 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private final OnReceiveContentListener mOnReceiveContentListener = this::onReceiveContent; private RemoteInputView mRemoteInputView; - private GradientDrawable mTextBackground; - private ColorDrawable mBackgroundColor; - private LayerDrawable mBackground; + private ColorDrawable mBackground; boolean mShowImeOnInputConnection; private LightBarController mLightBarController; private InputMethodManager mInputMethodManager; + private ArraySet mSupportedMimes = new ArraySet<>(); UserHandle mUser; public RemoteEditText(Context context, AttributeSet attrs) { super(context, attrs); mLightBarController = Dependency.get(LightBarController.class); - mTextBackground = (GradientDrawable) - context.getDrawable(R.drawable.remote_input_view_text_bg).mutate(); - mBackgroundColor = new ColorDrawable(); - mBackground = new LayerDrawable(new Drawable[] {mBackgroundColor, mTextBackground}); + + mBackground = new ColorDrawable(); } void setSupportedMimeTypes(@Nullable Collection mimeTypes) { @@ -741,6 +824,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene listener = mOnReceiveContentListener; } setOnReceiveContentListener(types, listener); + mSupportedMimes.clear(); + mSupportedMimes.addAll(mimeTypes); } private void hideIme() { @@ -899,36 +984,15 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene ContentInfo uriItems = split.first; ContentInfo remainingItems = split.second; if (uriItems != null) { - ClipData clip = uriItems.getClip(); - ClipDescription description = clip.getDescription(); - if (clip.getItemCount() > 1 - || description.getMimeTypeCount() < 1 - || remainingItems != null) { - // Direct-reply in notifications currently only supports only one uri item - // at a time and requires the MIME type to be set. - Log.w(TAG, "Invalid payload: " + payload); - return payload; - } - Uri contentUri = clip.getItemAt(0).getUri(); - String mimeType = description.getMimeType(0); - Intent dataIntent = - mRemoteInputView.prepareRemoteInputFromData(mimeType, contentUri); - // We can release the uri permissions granted to us as soon as we've created the - // grant for the target app in the call above. - payload.releasePermissions(); - mRemoteInputView.sendRemoteInput(dataIntent); + mRemoteInputView.setAttachment(uriItems); } return remainingItems; } protected void setAllColors(int backgroundColor, int editBackgroundColor, int accentColor, int textColor, int hintTextColor) { - setBackgroundColor(backgroundColor); - mBackgroundColor.setColor(backgroundColor); - mTextBackground.setColor(editBackgroundColor); - int stroke = getContext().getResources() - .getDimensionPixelSize(R.dimen.remote_input_view_text_stroke); - mTextBackground.setStroke(stroke, accentColor); + setBackgroundColor(editBackgroundColor); + mBackground.setColor(editBackgroundColor); setTextColor(textColor); setHintTextColor(hintTextColor); getTextCursorDrawable().setColorFilter(accentColor, PorterDuff.Mode.SRC_IN);