[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
This commit is contained in:
Steve Elliott
2022-03-14 17:24:25 -04:00
parent 40ca086c40
commit 27ecbde099
5 changed files with 137 additions and 95 deletions

View File

@@ -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);

View File

@@ -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)) {

View File

@@ -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<String> 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);
}
}
}

View File

@@ -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<RemoteInput>?
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<RemoteInput>? = 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<Notification.Action>?): 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
}
}

View File

@@ -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();