Loading packages/SystemUI/Android.bp +2 −2 Original line number Diff line number Diff line Loading @@ -76,8 +76,8 @@ android_library { "androidx.dynamicanimation_dynamicanimation", "androidx-constraintlayout_constraintlayout", "androidx.exifinterface_exifinterface", "kotlinx-coroutines-android", "kotlinx-coroutines-core", "kotlinx_coroutines_android", "kotlinx_coroutines", "iconloader_base", "SystemUI-tags", "SystemUI-proto", Loading packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +108 −36 Original line number Diff line number Diff line Loading @@ -388,7 +388,28 @@ public class NotificationRemoteInputManager implements Dumpable { */ public boolean activateRemoteInput(View view, RemoteInput[] inputs, RemoteInput input, PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo) { return activateRemoteInput(view, inputs, input, pendingIntent, editedSuggestionInfo, null /* userMessageContent */, null /* authBypassCheck */); } /** * Activates a given {@link RemoteInput} * * @param view The view of the action button or suggestion chip that was tapped. * @param inputs The remote inputs that need to be sent to the app. * @param input The remote input that needs to be activated. * @param pendingIntent The pending intent to be sent to the app. * @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. * @param userMessageContent User-entered text with which to initialize the remote input view. * @param authBypassCheck Optional auth bypass check associated with this remote input * activation. If {@code null}, we never bypass. * @return Whether the {@link RemoteInput} was activated. */ public boolean activateRemoteInput(View view, RemoteInput[] inputs, RemoteInput input, PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo, @Nullable String userMessageContent, @Nullable AuthBypassPredicate authBypassCheck) { ViewParent p = view.getParent(); RemoteInputView riv = null; ExpandableNotificationRow row = null; Loading @@ -410,41 +431,10 @@ public class NotificationRemoteInputManager implements Dumpable { row.setUserExpanded(true); if (!mLockscreenUserManager.shouldAllowLockscreenRemoteInput()) { final int userId = pendingIntent.getCreatorUserHandle().getIdentifier(); final boolean isLockedManagedProfile = mUserManager.getUserInfo(userId).isManagedProfile() && mKeyguardManager.isDeviceLocked(userId); final boolean isParentUserLocked; if (isLockedManagedProfile) { final UserInfo profileParent = mUserManager.getProfileParent(userId); isParentUserLocked = (profileParent != null) && mKeyguardManager.isDeviceLocked(profileParent.id); } else { isParentUserLocked = false; } if (mLockscreenUserManager.isLockscreenPublicMode(userId) || mStatusBarStateController.getState() == StatusBarState.KEYGUARD) { // If the parent user is no longer locked, and the user to which the remote input // is destined is a locked, managed profile, then onLockedWorkRemoteInput should be // called to unlock it. if (isLockedManagedProfile && !isParentUserLocked) { mCallback.onLockedWorkRemoteInput(userId, row, view); } else { // Even if we don't have security we should go through this flow, otherwise // we won't go to the shade. mCallback.onLockedRemoteInput(row, view); } return true; } if (isLockedManagedProfile) { mCallback.onLockedWorkRemoteInput(userId, row, view); final boolean deferBouncer = authBypassCheck != null; if (!deferBouncer && showBouncerForRemoteInput(view, pendingIntent, row)) { return true; } } if (riv != null && !riv.isAttachedToWindow()) { // the remoteInput isn't attached to the window anymore :/ Let's focus on the expanded Loading @@ -461,7 +451,10 @@ public class NotificationRemoteInputManager implements Dumpable { && !row.getPrivateLayout().getExpandedChild().isShown()) { // The expanded layout is selected, but it's not shown yet, let's wait on it to // show before we do the animation. mCallback.onMakeExpandedVisibleForRemoteInput(row, view); mCallback.onMakeExpandedVisibleForRemoteInput(row, view, deferBouncer, () -> { activateRemoteInput(view, inputs, input, pendingIntent, editedSuggestionInfo, userMessageContent, authBypassCheck); }); return true; } Loading Loading @@ -491,9 +484,61 @@ public class NotificationRemoteInputManager implements Dumpable { riv.setPendingIntent(pendingIntent); riv.setRemoteInput(inputs, input, editedSuggestionInfo); riv.focusAnimated(); if (userMessageContent != null) { riv.setEditTextContent(userMessageContent); } if (deferBouncer) { final ExpandableNotificationRow finalRow = row; riv.setBouncerChecker(() -> !authBypassCheck.canSendRemoteInputWithoutBouncer() && showBouncerForRemoteInput(view, pendingIntent, finalRow)); } return true; } private boolean showBouncerForRemoteInput(View view, PendingIntent pendingIntent, ExpandableNotificationRow row) { if (mLockscreenUserManager.shouldAllowLockscreenRemoteInput()) { return false; } final int userId = pendingIntent.getCreatorUserHandle().getIdentifier(); final boolean isLockedManagedProfile = mUserManager.getUserInfo(userId).isManagedProfile() && mKeyguardManager.isDeviceLocked(userId); final boolean isParentUserLocked; if (isLockedManagedProfile) { final UserInfo profileParent = mUserManager.getProfileParent(userId); isParentUserLocked = (profileParent != null) && mKeyguardManager.isDeviceLocked(profileParent.id); } else { isParentUserLocked = false; } if ((mLockscreenUserManager.isLockscreenPublicMode(userId) || mStatusBarStateController.getState() == StatusBarState.KEYGUARD)) { // If the parent user is no longer locked, and the user to which the remote // input // is destined is a locked, managed profile, then onLockedWorkRemoteInput // should be // called to unlock it. if (isLockedManagedProfile && !isParentUserLocked) { mCallback.onLockedWorkRemoteInput(userId, row, view); } else { // Even if we don't have security we should go through this flow, otherwise // we won't go to the shade. mCallback.onLockedRemoteInput(row, view); } return true; } if (isLockedManagedProfile) { mCallback.onLockedWorkRemoteInput(userId, row, view); return true; } return false; } private RemoteInputView findRemoteInputView(View v) { if (v == null) { Loading Loading @@ -807,8 +852,11 @@ public class NotificationRemoteInputManager implements Dumpable { * * @param row * @param clickedView * @param deferBouncer * @param runnable */ void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row, View clickedView); void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row, View clickedView, boolean deferBouncer, Runnable runnable); /** * Return whether or not remote input should be handled for this view. Loading Loading @@ -845,4 +893,28 @@ public class NotificationRemoteInputManager implements Dumpable { */ boolean handleClick(); } /** * Predicate that is associated with a specific {@link #activateRemoteInput(View, RemoteInput[], * RemoteInput, PendingIntent, EditedSuggestionInfo, String, AuthBypassPredicate)} * invocation that determines whether or not the bouncer can be bypassed when sending the * RemoteInput. */ public interface AuthBypassPredicate { /** * Determines if the RemoteInput can be sent without the bouncer. Should be checked the * same frame that the RemoteInput is to be sent. */ boolean canSendRemoteInputWithoutBouncer(); } /** Shows the bouncer if necessary */ public interface BouncerChecker { /** * Shows the bouncer if necessary in order to send a RemoteInput. * * @return {@code true} if the bouncer was shown, {@code false} otherwise */ boolean showBouncerIfNecessary(); } } packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java +3 −3 Original line number Diff line number Diff line Loading @@ -180,8 +180,8 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, @Override public void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row, View clickedView) { if (mKeyguardStateController.isShowing()) { View clickedView, boolean deferBouncer, Runnable runnable) { if (!deferBouncer && mKeyguardStateController.isShowing()) { onLockedRemoteInput(row, clickedView); } else { if (row.isChildInGroup() && !row.areChildrenExpanded()) { Loading @@ -189,7 +189,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, mGroupExpansionManager.toggleGroupExpansion(row.getEntry()); } row.setUserExpanded(true); row.getPrivateLayout().setOnExpandedVisibleListener(clickedView::performClick); row.getPrivateLayout().setOnExpandedVisibleListener(runnable); } } Loading packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +61 −5 Original line number Diff line number Diff line Loading @@ -73,6 +73,7 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; 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; Loading @@ -80,6 +81,7 @@ import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewW import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.LightBarController; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; Loading @@ -99,6 +101,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private final SendButtonTextWatcher mTextWatcher; private final TextView.OnEditorActionListener mEditorActionHandler; private final NotificationRemoteInputManager mRemoteInputManager; private final List<OnFocusChangeListener> mEditTextFocusChangeListeners = new ArrayList<>(); private RemoteEditText mEditText; private ImageButton mSendButton; private ProgressBar mProgressBar; Loading @@ -121,12 +125,14 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private boolean mResetting; private NotificationViewWrapper mWrapper; private Consumer<Boolean> mOnVisibilityChangedListener; private NotificationRemoteInputManager.BouncerChecker mBouncerChecker; public RemoteInputView(Context context, AttributeSet attrs) { super(context, attrs); mTextWatcher = new SendButtonTextWatcher(); mEditorActionHandler = new EditorActionHandler(); mRemoteInputQuickSettingsDisabler = Dependency.get(RemoteInputQuickSettingsDisabler.class); mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class); mStatusBarManagerService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); } Loading Loading @@ -200,6 +206,11 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } private void sendRemoteInput(Intent intent) { if (mBouncerChecker != null && mBouncerChecker.showBouncerIfNecessary()) { mEditText.hideIme(); return; } mEditText.setEnabled(false); mSendButton.setVisibility(INVISIBLE); mProgressBar.setVisibility(VISIBLE); Loading Loading @@ -351,6 +362,11 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } } /** Populates the text field of the remote input with the given content. */ public void setEditTextContent(@Nullable CharSequence editTextContent) { mEditText.setText(editTextContent); } public void focusAnimated() { if (getVisibility() != VISIBLE) { Animator animator = ViewAnimationUtils.createCircularReveal( Loading Loading @@ -552,6 +568,37 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene return getVisibility() == VISIBLE && mController.isSpinning(mEntry.getKey(), mToken); } /** * Sets a {@link com.android.systemui.statusbar.NotificationRemoteInputManager.BouncerChecker} * that will be used to determine if the device needs to be unlocked before sending the * RemoteInput. */ public void setBouncerChecker( @Nullable NotificationRemoteInputManager.BouncerChecker bouncerChecker) { mBouncerChecker = bouncerChecker; } /** Registers a listener for focus-change events on the EditText */ public void addOnEditTextFocusChangedListener(View.OnFocusChangeListener listener) { mEditTextFocusChangeListeners.add(listener); } /** Removes a previously-added listener for focus-change events on the EditText */ public void removeOnEditTextFocusChangedListener(View.OnFocusChangeListener listener) { mEditTextFocusChangeListeners.remove(listener); } /** Determines if the EditText has focus. */ public boolean editTextHasFocus() { return mEditText != null && mEditText.hasFocus(); } private void onEditTextFocusChanged(RemoteEditText remoteEditText, boolean focused) { for (View.OnFocusChangeListener listener : mEditTextFocusChangeListeners) { listener.onFocusChange(remoteEditText, focused); } } /** Handler for button click on send action in IME. */ private class EditorActionHandler implements TextView.OnEditorActionListener { Loading Loading @@ -603,6 +650,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private RemoteInputView mRemoteInputView; boolean mShowImeOnInputConnection; private LightBarController mLightBarController; private InputMethodManager mInputMethodManager; UserHandle mUser; public RemoteEditText(Context context, AttributeSet attrs) { Loading @@ -621,6 +669,12 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene setOnReceiveContentListener(types, listener); } private void hideIme() { if (mInputMethodManager != null) { mInputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0); } } private void defocusIfNeeded(boolean animate) { if (mRemoteInputView != null && mRemoteInputView.mEntry.getRow().isChangingPosition() || isTemporarilyDetached()) { Loading Loading @@ -654,6 +708,9 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene @Override protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { super.onFocusChanged(focused, direction, previouslyFocusedRect); if (mRemoteInputView != null) { mRemoteInputView.onEditTextFocusChanged(this, focused); } if (!focused) { defocusIfNeeded(true /* animate */); } Loading Loading @@ -724,17 +781,16 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene if (mShowImeOnInputConnection && ic != null) { Context targetContext = userContext != null ? userContext : getContext(); final InputMethodManager imm = targetContext.getSystemService(InputMethodManager.class); if (imm != null) { mInputMethodManager = targetContext.getSystemService(InputMethodManager.class); if (mInputMethodManager != null) { // onCreateInputConnection is called by InputMethodManager in the middle of // setting up the connection to the IME; wait with requesting the IME until that // work has completed. post(new Runnable() { @Override public void run() { imm.viewClicked(RemoteEditText.this); imm.showSoftInput(RemoteEditText.this, 0); mInputMethodManager.viewClicked(RemoteEditText.this); mInputMethodManager.showSoftInput(RemoteEditText.this, 0); } }); } Loading Loading
packages/SystemUI/Android.bp +2 −2 Original line number Diff line number Diff line Loading @@ -76,8 +76,8 @@ android_library { "androidx.dynamicanimation_dynamicanimation", "androidx-constraintlayout_constraintlayout", "androidx.exifinterface_exifinterface", "kotlinx-coroutines-android", "kotlinx-coroutines-core", "kotlinx_coroutines_android", "kotlinx_coroutines", "iconloader_base", "SystemUI-tags", "SystemUI-proto", Loading
packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +108 −36 Original line number Diff line number Diff line Loading @@ -388,7 +388,28 @@ public class NotificationRemoteInputManager implements Dumpable { */ public boolean activateRemoteInput(View view, RemoteInput[] inputs, RemoteInput input, PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo) { return activateRemoteInput(view, inputs, input, pendingIntent, editedSuggestionInfo, null /* userMessageContent */, null /* authBypassCheck */); } /** * Activates a given {@link RemoteInput} * * @param view The view of the action button or suggestion chip that was tapped. * @param inputs The remote inputs that need to be sent to the app. * @param input The remote input that needs to be activated. * @param pendingIntent The pending intent to be sent to the app. * @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. * @param userMessageContent User-entered text with which to initialize the remote input view. * @param authBypassCheck Optional auth bypass check associated with this remote input * activation. If {@code null}, we never bypass. * @return Whether the {@link RemoteInput} was activated. */ public boolean activateRemoteInput(View view, RemoteInput[] inputs, RemoteInput input, PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo, @Nullable String userMessageContent, @Nullable AuthBypassPredicate authBypassCheck) { ViewParent p = view.getParent(); RemoteInputView riv = null; ExpandableNotificationRow row = null; Loading @@ -410,41 +431,10 @@ public class NotificationRemoteInputManager implements Dumpable { row.setUserExpanded(true); if (!mLockscreenUserManager.shouldAllowLockscreenRemoteInput()) { final int userId = pendingIntent.getCreatorUserHandle().getIdentifier(); final boolean isLockedManagedProfile = mUserManager.getUserInfo(userId).isManagedProfile() && mKeyguardManager.isDeviceLocked(userId); final boolean isParentUserLocked; if (isLockedManagedProfile) { final UserInfo profileParent = mUserManager.getProfileParent(userId); isParentUserLocked = (profileParent != null) && mKeyguardManager.isDeviceLocked(profileParent.id); } else { isParentUserLocked = false; } if (mLockscreenUserManager.isLockscreenPublicMode(userId) || mStatusBarStateController.getState() == StatusBarState.KEYGUARD) { // If the parent user is no longer locked, and the user to which the remote input // is destined is a locked, managed profile, then onLockedWorkRemoteInput should be // called to unlock it. if (isLockedManagedProfile && !isParentUserLocked) { mCallback.onLockedWorkRemoteInput(userId, row, view); } else { // Even if we don't have security we should go through this flow, otherwise // we won't go to the shade. mCallback.onLockedRemoteInput(row, view); } return true; } if (isLockedManagedProfile) { mCallback.onLockedWorkRemoteInput(userId, row, view); final boolean deferBouncer = authBypassCheck != null; if (!deferBouncer && showBouncerForRemoteInput(view, pendingIntent, row)) { return true; } } if (riv != null && !riv.isAttachedToWindow()) { // the remoteInput isn't attached to the window anymore :/ Let's focus on the expanded Loading @@ -461,7 +451,10 @@ public class NotificationRemoteInputManager implements Dumpable { && !row.getPrivateLayout().getExpandedChild().isShown()) { // The expanded layout is selected, but it's not shown yet, let's wait on it to // show before we do the animation. mCallback.onMakeExpandedVisibleForRemoteInput(row, view); mCallback.onMakeExpandedVisibleForRemoteInput(row, view, deferBouncer, () -> { activateRemoteInput(view, inputs, input, pendingIntent, editedSuggestionInfo, userMessageContent, authBypassCheck); }); return true; } Loading Loading @@ -491,9 +484,61 @@ public class NotificationRemoteInputManager implements Dumpable { riv.setPendingIntent(pendingIntent); riv.setRemoteInput(inputs, input, editedSuggestionInfo); riv.focusAnimated(); if (userMessageContent != null) { riv.setEditTextContent(userMessageContent); } if (deferBouncer) { final ExpandableNotificationRow finalRow = row; riv.setBouncerChecker(() -> !authBypassCheck.canSendRemoteInputWithoutBouncer() && showBouncerForRemoteInput(view, pendingIntent, finalRow)); } return true; } private boolean showBouncerForRemoteInput(View view, PendingIntent pendingIntent, ExpandableNotificationRow row) { if (mLockscreenUserManager.shouldAllowLockscreenRemoteInput()) { return false; } final int userId = pendingIntent.getCreatorUserHandle().getIdentifier(); final boolean isLockedManagedProfile = mUserManager.getUserInfo(userId).isManagedProfile() && mKeyguardManager.isDeviceLocked(userId); final boolean isParentUserLocked; if (isLockedManagedProfile) { final UserInfo profileParent = mUserManager.getProfileParent(userId); isParentUserLocked = (profileParent != null) && mKeyguardManager.isDeviceLocked(profileParent.id); } else { isParentUserLocked = false; } if ((mLockscreenUserManager.isLockscreenPublicMode(userId) || mStatusBarStateController.getState() == StatusBarState.KEYGUARD)) { // If the parent user is no longer locked, and the user to which the remote // input // is destined is a locked, managed profile, then onLockedWorkRemoteInput // should be // called to unlock it. if (isLockedManagedProfile && !isParentUserLocked) { mCallback.onLockedWorkRemoteInput(userId, row, view); } else { // Even if we don't have security we should go through this flow, otherwise // we won't go to the shade. mCallback.onLockedRemoteInput(row, view); } return true; } if (isLockedManagedProfile) { mCallback.onLockedWorkRemoteInput(userId, row, view); return true; } return false; } private RemoteInputView findRemoteInputView(View v) { if (v == null) { Loading Loading @@ -807,8 +852,11 @@ public class NotificationRemoteInputManager implements Dumpable { * * @param row * @param clickedView * @param deferBouncer * @param runnable */ void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row, View clickedView); void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row, View clickedView, boolean deferBouncer, Runnable runnable); /** * Return whether or not remote input should be handled for this view. Loading Loading @@ -845,4 +893,28 @@ public class NotificationRemoteInputManager implements Dumpable { */ boolean handleClick(); } /** * Predicate that is associated with a specific {@link #activateRemoteInput(View, RemoteInput[], * RemoteInput, PendingIntent, EditedSuggestionInfo, String, AuthBypassPredicate)} * invocation that determines whether or not the bouncer can be bypassed when sending the * RemoteInput. */ public interface AuthBypassPredicate { /** * Determines if the RemoteInput can be sent without the bouncer. Should be checked the * same frame that the RemoteInput is to be sent. */ boolean canSendRemoteInputWithoutBouncer(); } /** Shows the bouncer if necessary */ public interface BouncerChecker { /** * Shows the bouncer if necessary in order to send a RemoteInput. * * @return {@code true} if the bouncer was shown, {@code false} otherwise */ boolean showBouncerIfNecessary(); } }
packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java +3 −3 Original line number Diff line number Diff line Loading @@ -180,8 +180,8 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, @Override public void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row, View clickedView) { if (mKeyguardStateController.isShowing()) { View clickedView, boolean deferBouncer, Runnable runnable) { if (!deferBouncer && mKeyguardStateController.isShowing()) { onLockedRemoteInput(row, clickedView); } else { if (row.isChildInGroup() && !row.areChildrenExpanded()) { Loading @@ -189,7 +189,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, mGroupExpansionManager.toggleGroupExpansion(row.getEntry()); } row.setUserExpanded(true); row.getPrivateLayout().setOnExpandedVisibleListener(clickedView::performClick); row.getPrivateLayout().setOnExpandedVisibleListener(runnable); } } Loading
packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +61 −5 Original line number Diff line number Diff line Loading @@ -73,6 +73,7 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; 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; Loading @@ -80,6 +81,7 @@ import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewW import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.LightBarController; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; Loading @@ -99,6 +101,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private final SendButtonTextWatcher mTextWatcher; private final TextView.OnEditorActionListener mEditorActionHandler; private final NotificationRemoteInputManager mRemoteInputManager; private final List<OnFocusChangeListener> mEditTextFocusChangeListeners = new ArrayList<>(); private RemoteEditText mEditText; private ImageButton mSendButton; private ProgressBar mProgressBar; Loading @@ -121,12 +125,14 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private boolean mResetting; private NotificationViewWrapper mWrapper; private Consumer<Boolean> mOnVisibilityChangedListener; private NotificationRemoteInputManager.BouncerChecker mBouncerChecker; public RemoteInputView(Context context, AttributeSet attrs) { super(context, attrs); mTextWatcher = new SendButtonTextWatcher(); mEditorActionHandler = new EditorActionHandler(); mRemoteInputQuickSettingsDisabler = Dependency.get(RemoteInputQuickSettingsDisabler.class); mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class); mStatusBarManagerService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); } Loading Loading @@ -200,6 +206,11 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } private void sendRemoteInput(Intent intent) { if (mBouncerChecker != null && mBouncerChecker.showBouncerIfNecessary()) { mEditText.hideIme(); return; } mEditText.setEnabled(false); mSendButton.setVisibility(INVISIBLE); mProgressBar.setVisibility(VISIBLE); Loading Loading @@ -351,6 +362,11 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } } /** Populates the text field of the remote input with the given content. */ public void setEditTextContent(@Nullable CharSequence editTextContent) { mEditText.setText(editTextContent); } public void focusAnimated() { if (getVisibility() != VISIBLE) { Animator animator = ViewAnimationUtils.createCircularReveal( Loading Loading @@ -552,6 +568,37 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene return getVisibility() == VISIBLE && mController.isSpinning(mEntry.getKey(), mToken); } /** * Sets a {@link com.android.systemui.statusbar.NotificationRemoteInputManager.BouncerChecker} * that will be used to determine if the device needs to be unlocked before sending the * RemoteInput. */ public void setBouncerChecker( @Nullable NotificationRemoteInputManager.BouncerChecker bouncerChecker) { mBouncerChecker = bouncerChecker; } /** Registers a listener for focus-change events on the EditText */ public void addOnEditTextFocusChangedListener(View.OnFocusChangeListener listener) { mEditTextFocusChangeListeners.add(listener); } /** Removes a previously-added listener for focus-change events on the EditText */ public void removeOnEditTextFocusChangedListener(View.OnFocusChangeListener listener) { mEditTextFocusChangeListeners.remove(listener); } /** Determines if the EditText has focus. */ public boolean editTextHasFocus() { return mEditText != null && mEditText.hasFocus(); } private void onEditTextFocusChanged(RemoteEditText remoteEditText, boolean focused) { for (View.OnFocusChangeListener listener : mEditTextFocusChangeListeners) { listener.onFocusChange(remoteEditText, focused); } } /** Handler for button click on send action in IME. */ private class EditorActionHandler implements TextView.OnEditorActionListener { Loading Loading @@ -603,6 +650,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private RemoteInputView mRemoteInputView; boolean mShowImeOnInputConnection; private LightBarController mLightBarController; private InputMethodManager mInputMethodManager; UserHandle mUser; public RemoteEditText(Context context, AttributeSet attrs) { Loading @@ -621,6 +669,12 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene setOnReceiveContentListener(types, listener); } private void hideIme() { if (mInputMethodManager != null) { mInputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0); } } private void defocusIfNeeded(boolean animate) { if (mRemoteInputView != null && mRemoteInputView.mEntry.getRow().isChangingPosition() || isTemporarilyDetached()) { Loading Loading @@ -654,6 +708,9 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene @Override protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { super.onFocusChanged(focused, direction, previouslyFocusedRect); if (mRemoteInputView != null) { mRemoteInputView.onEditTextFocusChanged(this, focused); } if (!focused) { defocusIfNeeded(true /* animate */); } Loading Loading @@ -724,17 +781,16 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene if (mShowImeOnInputConnection && ic != null) { Context targetContext = userContext != null ? userContext : getContext(); final InputMethodManager imm = targetContext.getSystemService(InputMethodManager.class); if (imm != null) { mInputMethodManager = targetContext.getSystemService(InputMethodManager.class); if (mInputMethodManager != null) { // onCreateInputConnection is called by InputMethodManager in the middle of // setting up the connection to the IME; wait with requesting the IME until that // work has completed. post(new Runnable() { @Override public void run() { imm.viewClicked(RemoteEditText.this); imm.showSoftInput(RemoteEditText.this, 0); mInputMethodManager.viewClicked(RemoteEditText.this); mInputMethodManager.showSoftInput(RemoteEditText.this, 0); } }); } Loading