Loading packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +2 −3 Original line number Diff line number Diff line Loading @@ -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); Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +16 −9 Original line number Diff line number Diff line Loading @@ -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(); Loading Loading @@ -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(); Loading Loading @@ -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); } } Loading Loading @@ -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)) { Loading packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +31 −58 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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() { Loading Loading @@ -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. */ Loading @@ -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(); Loading Loading @@ -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 Loading Loading @@ -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); } } } packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt +83 −18 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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. Loading @@ -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 Loading @@ -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 */ Loading Loading @@ -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) } Loading @@ -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 Loading @@ -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 Loading @@ -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) } Loading Loading @@ -217,7 +283,8 @@ 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) if (entry.remoteInputAttachment == null) prepareRemoteInputFromText(remoteInput) else prepareRemoteInputFromData( remoteInput, entry.remoteInputMimeType, Loading @@ -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 } Loading Loading @@ -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 } } packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java +5 −7 Original line number Diff line number Diff line Loading @@ -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); } Loading @@ -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(); Loading Loading @@ -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(); Loading Loading @@ -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(); Loading Loading @@ -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(); Loading Loading
packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +2 −3 Original line number Diff line number Diff line Loading @@ -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); Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +16 −9 Original line number Diff line number Diff line Loading @@ -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(); Loading Loading @@ -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(); Loading Loading @@ -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); } } Loading Loading @@ -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)) { Loading
packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +31 −58 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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() { Loading Loading @@ -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. */ Loading @@ -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(); Loading Loading @@ -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 Loading Loading @@ -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); } } }
packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt +83 −18 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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. Loading @@ -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 Loading @@ -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 */ Loading Loading @@ -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) } Loading @@ -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 Loading @@ -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 Loading @@ -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) } Loading Loading @@ -217,7 +283,8 @@ 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) if (entry.remoteInputAttachment == null) prepareRemoteInputFromText(remoteInput) else prepareRemoteInputFromData( remoteInput, entry.remoteInputMimeType, Loading @@ -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 } Loading Loading @@ -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 } }
packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java +5 −7 Original line number Diff line number Diff line Loading @@ -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); } Loading @@ -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(); Loading Loading @@ -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(); Loading Loading @@ -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(); Loading Loading @@ -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(); Loading