diff options
| author | 2022-10-18 15:57:41 -0700 | |
|---|---|---|
| committer | 2022-10-21 10:05:48 -0700 | |
| commit | fc192c8a9971e6359ab2e68dbea8e2a44efe05e1 (patch) | |
| tree | ebd5a9d1ec9ea0fc2c7aa66aa9d103d9ce9a3ad7 | |
| parent | 5be30d7d29c2e9241d6a43d5ca2533aa827443db (diff) | |
Migrate RemoteInputView to Predictive Back.
Bug: 254342469
Test: atest RemoteInputViewTest
Change-Id: I9a3fbe648f810924415ca419b629e3f34fcae2d6
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java | 73 | ||||
| -rw-r--r-- | packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java | 70 |
2 files changed, 141 insertions, 2 deletions
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 da6d455fbb97..dd400b3fc0ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -47,6 +47,7 @@ import android.view.OnReceiveContentListener; import android.view.View; import android.view.ViewAnimationUtils; import android.view.ViewGroup; +import android.view.ViewRootImpl; import android.view.WindowInsets; import android.view.WindowInsetsAnimation; import android.view.WindowInsetsController; @@ -61,6 +62,8 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; +import android.window.OnBackInvokedCallback; +import android.window.OnBackInvokedDispatcher; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -88,6 +91,7 @@ import java.util.function.Consumer; */ public class RemoteInputView extends LinearLayout implements View.OnClickListener { + private static final boolean DEBUG = false; private static final String TAG = "RemoteInput"; // A marker object that let's us easily find views of this class. @@ -124,6 +128,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene // TODO(b/193539698): remove this; views shouldn't have access to their controller, and places // that need the controller shouldn't have access to the view private RemoteInputViewController mViewController; + private ViewRootImpl mTestableViewRootImpl; /** * Enum for logged notification remote input UiEvents. @@ -430,10 +435,20 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } } + @VisibleForTesting + protected void setViewRootImpl(ViewRootImpl viewRoot) { + mTestableViewRootImpl = viewRoot; + } + + @VisibleForTesting + protected void setEditTextReferenceToSelf() { + mEditText.mRemoteInputView = this; + } + @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - mEditText.mRemoteInputView = this; + setEditTextReferenceToSelf(); mEditText.setOnEditorActionListener(mEditorActionHandler); mEditText.addTextChangedListener(mTextWatcher); if (mEntry.getRow().isChangingPosition()) { @@ -457,7 +472,50 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } @Override + public ViewRootImpl getViewRootImpl() { + if (mTestableViewRootImpl != null) { + return mTestableViewRootImpl; + } + return super.getViewRootImpl(); + } + + private void registerBackCallback() { + ViewRootImpl viewRoot = getViewRootImpl(); + if (viewRoot == null) { + if (DEBUG) { + Log.d(TAG, "ViewRoot was null, NOT registering Predictive Back callback"); + } + return; + } + if (DEBUG) { + Log.d(TAG, "registering Predictive Back callback"); + } + viewRoot.getOnBackInvokedDispatcher().registerOnBackInvokedCallback( + OnBackInvokedDispatcher.PRIORITY_OVERLAY, mEditText.mOnBackInvokedCallback); + } + + private void unregisterBackCallback() { + ViewRootImpl viewRoot = getViewRootImpl(); + if (viewRoot == null) { + if (DEBUG) { + Log.d(TAG, "ViewRoot was null, NOT unregistering Predictive Back callback"); + } + return; + } + if (DEBUG) { + Log.d(TAG, "unregistering Predictive Back callback"); + } + viewRoot.getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback( + mEditText.mOnBackInvokedCallback); + } + + @Override public void onVisibilityAggregated(boolean isVisible) { + if (isVisible) { + registerBackCallback(); + } else { + unregisterBackCallback(); + } super.onVisibilityAggregated(isVisible); mEditText.setEnabled(isVisible && !mSending); } @@ -822,10 +880,21 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene return super.onKeyDown(keyCode, event); } + private final OnBackInvokedCallback mOnBackInvokedCallback = () -> { + if (DEBUG) { + Log.d(TAG, "Predictive Back Callback dispatched"); + } + respondToKeycodeBack(); + }; + + private void respondToKeycodeBack() { + defocusIfNeeded(true /* animate */); + } + @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { - defocusIfNeeded(true /* animate */); + respondToKeycodeBack(); return true; } return super.onKeyUp(keyCode, event); 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 6ace4044b3f7..915e999c2646 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 @@ -23,8 +23,12 @@ import static junit.framework.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.app.PendingIntent; @@ -43,10 +47,14 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.ContentInfo; import android.view.View; +import android.view.ViewRootImpl; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.widget.EditText; import android.widget.ImageButton; +import android.window.OnBackInvokedCallback; +import android.window.OnBackInvokedDispatcher; +import android.window.WindowOnBackInvokedDispatcher; import androidx.annotation.NonNull; import androidx.test.filters.SmallTest; @@ -67,6 +75,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -229,6 +238,67 @@ public class RemoteInputViewTest extends SysuiTestCase { } @Test + public void testPredictiveBack_registerAndUnregister() throws Exception { + NotificationTestHelper helper = new NotificationTestHelper( + mContext, + mDependency, + TestableLooper.get(this)); + ExpandableNotificationRow row = helper.createRow(); + RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController); + + ViewRootImpl viewRoot = mock(ViewRootImpl.class); + WindowOnBackInvokedDispatcher backInvokedDispatcher = mock( + WindowOnBackInvokedDispatcher.class); + ArgumentCaptor<OnBackInvokedCallback> onBackInvokedCallbackCaptor = ArgumentCaptor.forClass( + OnBackInvokedCallback.class); + when(viewRoot.getOnBackInvokedDispatcher()).thenReturn(backInvokedDispatcher); + view.setViewRootImpl(viewRoot); + + /* verify that predictive back callback registered when RemoteInputView becomes visible */ + view.onVisibilityAggregated(true); + verify(backInvokedDispatcher).registerOnBackInvokedCallback( + eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY), + onBackInvokedCallbackCaptor.capture()); + + /* verify that same callback unregistered when RemoteInputView becomes invisible */ + view.onVisibilityAggregated(false); + verify(backInvokedDispatcher).unregisterOnBackInvokedCallback( + eq(onBackInvokedCallbackCaptor.getValue())); + } + + @Test + public void testUiPredictiveBack_openAndDispatchCallback() throws Exception { + NotificationTestHelper helper = new NotificationTestHelper( + mContext, + mDependency, + TestableLooper.get(this)); + ExpandableNotificationRow row = helper.createRow(); + RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController); + ViewRootImpl viewRoot = mock(ViewRootImpl.class); + WindowOnBackInvokedDispatcher backInvokedDispatcher = mock( + WindowOnBackInvokedDispatcher.class); + ArgumentCaptor<OnBackInvokedCallback> onBackInvokedCallbackCaptor = ArgumentCaptor.forClass( + OnBackInvokedCallback.class); + when(viewRoot.getOnBackInvokedDispatcher()).thenReturn(backInvokedDispatcher); + view.setViewRootImpl(viewRoot); + view.onVisibilityAggregated(true); + view.setEditTextReferenceToSelf(); + + /* capture the callback during registration */ + verify(backInvokedDispatcher).registerOnBackInvokedCallback( + eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY), + onBackInvokedCallbackCaptor.capture()); + + view.focus(); + + /* invoke the captured callback */ + onBackInvokedCallbackCaptor.getValue().onBackInvoked(); + + /* verify that the RemoteInputView goes away */ + assertEquals(view.getVisibility(), View.GONE); + } + + @Test public void testUiEventLogging_openAndSend() throws Exception { NotificationTestHelper helper = new NotificationTestHelper( mContext, |