From 27ecbde0997310a210e973acd41b0dae0ea14260 Mon Sep 17 00:00:00 2001 From: Steve Elliott Date: Mon, 14 Mar 2022 17:24:25 -0400 Subject: [PATCH] [RemoteInputView] move data fields to Controller These fields were used to track some non-View-related data for RemoteInputs. Test: manual Bug: 193539698 Change-Id: I220060d0ab9e77bdba6f8aa9956b27cc0b1183d8 --- .../NotificationRemoteInputManager.java | 5 +- .../row/NotificationContentView.java | 25 +++-- .../statusbar/policy/RemoteInputView.java | 89 ++++++--------- .../policy/RemoteInputViewController.kt | 101 ++++++++++++++---- .../statusbar/policy/RemoteInputViewTest.java | 12 +-- 5 files changed, 137 insertions(+), 95 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index 94a6d3e99842f..66c1d87fb812d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -507,12 +507,11 @@ public class NotificationRemoteInputManager implements Dumpable { Math.max(cx + cy, cx + (h - cy)), Math.max((w - cx) + cy, (w - cx) + (h - cy))); - riv.setRevealParameters(cx, cy, r); - riv.setPendingIntent(pendingIntent); + riv.getController().setRevealParams(new RemoteInputView.RevealParams(cx, cy, r)); riv.getController().setPendingIntent(pendingIntent); - riv.setRemoteInput(inputs, input, editedSuggestionInfo); riv.getController().setRemoteInput(input); riv.getController().setRemoteInputs(inputs); + riv.getController().setEditedSuggestionInfo(editedSuggestionInfo); riv.focusAnimated(); if (userMessageContent != null) { riv.setEditTextContent(userMessageContent); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 9cb5dc5d4aa4a..adb4ce68c0315 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -413,7 +413,10 @@ public class NotificationContentView extends FrameLayout implements Notification if (mExpandedRemoteInput != null) { mExpandedRemoteInput.onNotificationUpdateOrReset(); if (mExpandedRemoteInput.isActive()) { - mPreviousExpandedRemoteInputIntent = mExpandedRemoteInput.getPendingIntent(); + if (mExpandedRemoteInputController != null) { + mPreviousExpandedRemoteInputIntent = + mExpandedRemoteInputController.getPendingIntent(); + } mCachedExpandedRemoteInput = mExpandedRemoteInput; mCachedExpandedRemoteInputViewController = mExpandedRemoteInputController; mExpandedRemoteInput.dispatchStartTemporaryDetach(); @@ -460,7 +463,10 @@ public class NotificationContentView extends FrameLayout implements Notification if (mHeadsUpRemoteInput != null) { mHeadsUpRemoteInput.onNotificationUpdateOrReset(); if (mHeadsUpRemoteInput.isActive()) { - mPreviousHeadsUpRemoteInputIntent = mHeadsUpRemoteInput.getPendingIntent(); + if (mHeadsUpRemoteInputController != null) { + mPreviousHeadsUpRemoteInputIntent = + mHeadsUpRemoteInputController.getPendingIntent(); + } mCachedHeadsUpRemoteInput = mHeadsUpRemoteInput; mCachedHeadsUpRemoteInputViewController = mHeadsUpRemoteInputController; mHeadsUpRemoteInput.dispatchStartTemporaryDetach(); @@ -961,14 +967,16 @@ public class NotificationContentView extends FrameLayout implements Notification private void transferRemoteInputFocus(int visibleType) { if (visibleType == VISIBLE_TYPE_HEADSUP - && mHeadsUpRemoteInput != null - && (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive())) { - mHeadsUpRemoteInput.stealFocusFrom(mExpandedRemoteInput); + && mHeadsUpRemoteInputController != null + && mExpandedRemoteInputController != null + && mExpandedRemoteInputController.isActive()) { + mHeadsUpRemoteInputController.stealFocusFrom(mExpandedRemoteInputController); } if (visibleType == VISIBLE_TYPE_EXPANDED - && mExpandedRemoteInput != null - && (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive())) { - mExpandedRemoteInput.stealFocusFrom(mHeadsUpRemoteInput); + && mExpandedRemoteInputController != null + && mHeadsUpRemoteInputController != null + && mHeadsUpRemoteInputController.isActive()) { + mExpandedRemoteInputController.stealFocusFrom(mHeadsUpRemoteInputController); } } @@ -1313,7 +1321,6 @@ public class NotificationContentView extends FrameLayout implements Notification // If we find a matching action in the new notification, focus, otherwise close. Notification.Action[] actions = entry.getSbn().getNotification().actions; if (existingPendingIntent != null) { - result.mView.setPendingIntent(existingPendingIntent); result.mController.setPendingIntent(existingPendingIntent); } if (result.mController.updatePendingIntentFromActions(actions)) { 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 48949f92413db..4d6d05f2b6288 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -22,8 +22,6 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.app.ActivityManager; import android.app.Notification; -import android.app.PendingIntent; -import android.app.RemoteInput; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.ColorStateList; @@ -75,7 +73,6 @@ import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.collection.NotificationEntry.EditedSuggestionInfo; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.LightBarController; @@ -111,21 +108,15 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private ProgressBar mProgressBar; private ImageView mDelete; private ImageView mDeleteBg; - // TODO(b/193539698): remove reveal param fields, turn them into parameters where needed - private int mRevealCx; - private int mRevealCy; - private int mRevealR; private boolean mColorized; private int mTint; private boolean mResetting; + @Nullable private RevealParams mRevealParams; // TODO(b/193539698): move these to a Controller private RemoteInputController mController; private final UiEventLogger mUiEventLogger; private NotificationEntry mEntry; - private PendingIntent mPendingIntent; - private RemoteInput mRemoteInput; - private RemoteInput[] mRemoteInputs; private boolean mRemoved; private NotificationViewWrapper mWrapper; @@ -397,9 +388,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene // During removal, we get reattached and lose focus. Not hiding in that // case to prevent flicker. if (!mRemoved) { - if (animate && mRevealR > 0) { - Animator reveal = ViewAnimationUtils.createCircularReveal( - this, mRevealCx, mRevealCy, mRevealR, 0); + if (animate && mRevealParams != null && mRevealParams.radius > 0) { + Animator reveal = mRevealParams.createCircularHideAnimator(this); reveal.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); reveal.setDuration(StackStateAnimator.ANIMATION_DURATION_CLOSE_REMOTE_INPUT); reveal.addListener(new AnimatorListenerAdapter() { @@ -454,30 +444,12 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mController.removeSpinning(mEntry.getKey(), mToken); } - public void setPendingIntent(PendingIntent pendingIntent) { - mPendingIntent = pendingIntent; + public void setHintText(CharSequence hintText) { + mEditText.setHint(hintText); } - /** - * Sets the remote input for this view. - * - * @param remoteInputs The remote inputs that need to be sent to the app. - * @param remoteInput The remote input that needs to be activated. - * @param editedSuggestionInfo The smart reply that should be inserted in the remote input, or - * {@code null} if the user is not editing a smart reply. - */ - public void setRemoteInput(RemoteInput[] remoteInputs, RemoteInput remoteInput, - @Nullable EditedSuggestionInfo editedSuggestionInfo) { - mRemoteInputs = remoteInputs; - mRemoteInput = remoteInput; - mEditText.setHint(mRemoteInput.getLabel()); - mEditText.setSupportedMimeTypes(remoteInput.getAllowedDataTypes()); - - mEntry.editedSuggestionInfo = editedSuggestionInfo; - if (editedSuggestionInfo != null) { - mEntry.remoteInputText = editedSuggestionInfo.originalText; - mEntry.remoteInputAttachment = null; - } + public void setSupportedMimeTypes(Collection mimeTypes) { + mEditText.setSupportedMimeTypes(mimeTypes); } /** Populates the text field of the remote input with the given content. */ @@ -486,9 +458,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } public void focusAnimated() { - if (getVisibility() != VISIBLE) { - Animator animator = ViewAnimationUtils.createCircularReveal( - this, mRevealCx, mRevealCy, 0, mRevealR); + if (getVisibility() != VISIBLE && mRevealParams != null) { + Animator animator = mRevealParams.createCircularRevealAnimator(this); animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); animator.start(); @@ -587,30 +558,12 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene return mEditText.isFocused() && mEditText.isEnabled(); } - // TODO(b/193539698): move this to the controller - public void stealFocusFrom(RemoteInputView other) { - other.close(); - setPendingIntent(other.mPendingIntent); - setRemoteInput(other.mRemoteInputs, other.mRemoteInput, mEntry.editedSuggestionInfo); - setRevealParameters(other.mRevealCx, other.mRevealCy, other.mRevealR); - getController().setPendingIntent(other.mPendingIntent); - getController().setRemoteInput(other.mRemoteInput); - getController().setRemoteInputs(other.mRemoteInputs); - focus(); - } - - public PendingIntent getPendingIntent() { - return mPendingIntent; - } - public void setRemoved() { mRemoved = true; } - public void setRevealParameters(int cx, int cy, int r) { - mRevealCx = cx; - mRevealCy = cy; - mRevealR = r; + public void setRevealParameters(@Nullable RevealParams revealParams) { + mRevealParams = revealParams; } @Override @@ -938,4 +891,24 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } } + + public static class RevealParams { + final int centerX; + final int centerY; + final int radius; + + public RevealParams(int centerX, int centerY, int radius) { + this.centerX = centerX; + this.centerY = centerY; + this.radius = radius; + } + + Animator createCircularRevealAnimator(View view) { + return ViewAnimationUtils.createCircularReveal(view, centerX, centerY, radius, 0); + } + + Animator createCircularHideAnimator(View view) { + return ViewAnimationUtils.createCircularReveal(view, centerX, centerY, 0, radius); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt index ef0a5b4235c7c..bd878750dfe16 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt @@ -33,7 +33,9 @@ import com.android.systemui.R import com.android.systemui.statusbar.NotificationRemoteInputManager import com.android.systemui.statusbar.RemoteInputController import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.NotificationEntry.EditedSuggestionInfo import com.android.systemui.statusbar.policy.RemoteInputView.NotificationRemoteInputEvent +import com.android.systemui.statusbar.policy.RemoteInputView.RevealParams import com.android.systemui.statusbar.policy.dagger.RemoteInputViewScope import javax.inject.Inject @@ -41,6 +43,8 @@ interface RemoteInputViewController { fun bind() fun unbind() + val isActive: Boolean + /** * A [NotificationRemoteInputManager.BouncerChecker] that will be used to determine if the * device needs to be unlocked before sending the RemoteInput. @@ -55,6 +59,14 @@ interface RemoteInputViewController { /** Other [RemoteInput]s from the notification associated with this Controller. */ var remoteInputs: Array? + var revealParams: RevealParams? + + /** + * Sets the smart reply that should be inserted in the remote input, or `null` if the user is + * not editing a smart reply. + */ + fun setEditedSuggestionInfo(info: EditedSuggestionInfo?) + /** * Tries to find an action in {@param actions} that matches the current pending intent * of this view and updates its state to that of the found action @@ -68,6 +80,19 @@ interface RemoteInputViewController { /** Unregisters a listener previously registered via [addOnSendRemoteInputListener] */ fun removeOnSendRemoteInputListener(listener: OnSendRemoteInputListener) + + fun close() + + fun focus() + + fun stealFocusFrom(other: RemoteInputViewController) { + other.close() + remoteInput = other.remoteInput + remoteInputs = other.remoteInputs + revealParams = other.revealParams + pendingIntent = other.pendingIntent + focus() + } } /** Listener for send events */ @@ -100,15 +125,41 @@ class RemoteInputViewControllerImpl @Inject constructor( private var isBound = false - override var pendingIntent: PendingIntent? = null override var bouncerChecker: NotificationRemoteInputManager.BouncerChecker? = null + override var remoteInput: RemoteInput? = null + set(value) { + field = value + value?.takeIf { isBound }?.let { + view.setHintText(it.label) + view.setSupportedMimeTypes(it.allowedDataTypes) + } + } + + override var pendingIntent: PendingIntent? = null override var remoteInputs: Array? = null + override var revealParams: RevealParams? = null + set(value) { + field = value + if (isBound) { + view.setRevealParameters(value) + } + } + + override val isActive: Boolean get() = view.isActive + override fun bind() { if (isBound) return isBound = true + // TODO: refreshUI method? + remoteInput?.let { + view.setHintText(it.label) + view.setSupportedMimeTypes(it.allowedDataTypes) + } + view.setRevealParameters(revealParams) + view.addOnEditTextFocusChangedListener(onFocusChangeListener) view.addOnSendRemoteInputListener(onSendRemoteInputListener) } @@ -121,6 +172,14 @@ class RemoteInputViewControllerImpl @Inject constructor( view.removeOnSendRemoteInputListener(onSendRemoteInputListener) } + override fun setEditedSuggestionInfo(info: EditedSuggestionInfo?) { + entry.editedSuggestionInfo = info + if (info != null) { + entry.remoteInputText = info.originalText + entry.remoteInputAttachment = null + } + } + override fun updatePendingIntentFromActions(actions: Array?): Boolean { actions ?: return false val current: Intent = pendingIntent?.intent ?: return false @@ -132,8 +191,7 @@ class RemoteInputViewControllerImpl @Inject constructor( pendingIntent = actionIntent remoteInput = input remoteInputs = inputs - view.pendingIntent = actionIntent - view.setRemoteInput(inputs, input, null /* editedSuggestionInfo */) + setEditedSuggestionInfo(null) return true } return false @@ -148,6 +206,14 @@ class RemoteInputViewControllerImpl @Inject constructor( onSendListeners.remove(listener) } + override fun close() { + view.close() + } + + override fun focus() { + view.focus() + } + private val onFocusChangeListener = View.OnFocusChangeListener { _, hasFocus -> remoteInputQuickSettingsDisabler.setRemoteInputActive(hasFocus) } @@ -217,11 +283,12 @@ class RemoteInputViewControllerImpl @Inject constructor( * @return returns intent with granted URI permissions that should be used immediately */ private fun prepareRemoteInput(remoteInput: RemoteInput): Intent = - if (entry.remoteInputAttachment == null) prepareRemoteInputFromText(remoteInput) - else prepareRemoteInputFromData( - remoteInput, - entry.remoteInputMimeType, - entry.remoteInputUri) + if (entry.remoteInputAttachment == null) + prepareRemoteInputFromText(remoteInput) + else prepareRemoteInputFromData( + remoteInput, + entry.remoteInputMimeType, + entry.remoteInputUri) private fun prepareRemoteInputFromText(remoteInput: RemoteInput): Intent { val results = Bundle() @@ -232,11 +299,10 @@ class RemoteInputViewControllerImpl @Inject constructor( view.clearAttachment() entry.remoteInputUri = null entry.remoteInputMimeType = null - if (entry.editedSuggestionInfo == null) { - RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_FREE_FORM_INPUT) - } else { - RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_CHOICE) - } + val resultSource = entry.editedSuggestionInfo + ?.let { RemoteInput.SOURCE_FREE_FORM_INPUT } + ?: RemoteInput.SOURCE_CHOICE + RemoteInput.setResultsSource(fillInIntent, resultSource) return fillInIntent } @@ -266,11 +332,10 @@ class RemoteInputViewControllerImpl @Inject constructor( entry.remoteInputText = fullText // mirror prepareRemoteInputFromText for text input - if (entry.editedSuggestionInfo == null) { - RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_FREE_FORM_INPUT) - } else if (entry.remoteInputAttachment == null) { - RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_CHOICE) - } + val resultSource = entry.editedSuggestionInfo + ?.let { RemoteInput.SOURCE_FREE_FORM_INPUT } + ?: RemoteInput.SOURCE_CHOICE + RemoteInput.setResultsSource(fillInIntent, resultSource) return fillInIntent } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java index 20a3fdaa99a51..3a0a7c9d80fc5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java @@ -114,15 +114,13 @@ public class RemoteInputViewTest extends SysuiTestCase { mContext.unregisterReceiver(mReceiver); } - private void setTestPendingIntent(RemoteInputView view, RemoteInputViewController controller) { + private void setTestPendingIntent(RemoteInputViewController controller) { PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(TEST_ACTION), PendingIntent.FLAG_MUTABLE); RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).build(); RemoteInput[] inputs = {input}; - view.setPendingIntent(pendingIntent); controller.setPendingIntent(pendingIntent); - view.setRemoteInput(inputs, input, null /* editedSuggestionInfo */); controller.setRemoteInput(input); controller.setRemoteInputs(inputs); } @@ -137,7 +135,7 @@ public class RemoteInputViewTest extends SysuiTestCase { RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController); RemoteInputViewController controller = bindController(view, row.getEntry()); - setTestPendingIntent(view, controller); + setTestPendingIntent(controller); view.focus(); @@ -177,7 +175,7 @@ public class RemoteInputViewTest extends SysuiTestCase { RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController); RemoteInputViewController controller = bindController(view, row.getEntry()); - setTestPendingIntent(view, controller); + setTestPendingIntent(controller); view.focus(); @@ -235,7 +233,7 @@ public class RemoteInputViewTest extends SysuiTestCase { RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController); RemoteInputViewController controller = bindController(view, row.getEntry()); - setTestPendingIntent(view, controller); + setTestPendingIntent(controller); // Open view, send a reply view.focus(); @@ -265,7 +263,7 @@ public class RemoteInputViewTest extends SysuiTestCase { RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController); RemoteInputViewController controller = bindController(view, row.getEntry()); - setTestPendingIntent(view, controller); + setTestPendingIntent(controller); // Open view, attach an image view.focus();