1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.systemui.statusbar.policy; 16 17 import static junit.framework.Assert.assertEquals; 18 import static junit.framework.Assert.assertNotNull; 19 20 import android.app.ActivityManager; 21 import android.app.PendingIntent; 22 import android.app.RemoteInput; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.content.pm.ShortcutManager; 26 import android.os.Handler; 27 import android.os.Process; 28 import android.os.UserHandle; 29 import android.testing.AndroidTestingRunner; 30 import android.testing.TestableLooper; 31 import android.view.View; 32 import android.view.inputmethod.EditorInfo; 33 import android.view.inputmethod.InputConnection; 34 import android.widget.EditText; 35 import android.widget.ImageButton; 36 37 import androidx.test.filters.SmallTest; 38 39 import com.android.systemui.Dependency; 40 import com.android.systemui.R; 41 import com.android.systemui.SysuiTestCase; 42 import com.android.systemui.statusbar.NotificationTestHelper; 43 import com.android.systemui.statusbar.RemoteInputController; 44 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; 45 import com.android.systemui.statusbar.phone.LightBarController; 46 import com.android.systemui.util.Assert; 47 48 import org.junit.After; 49 import org.junit.Before; 50 import org.junit.Test; 51 import org.junit.runner.RunWith; 52 import org.mockito.Mock; 53 import org.mockito.MockitoAnnotations; 54 55 @RunWith(AndroidTestingRunner.class) 56 @TestableLooper.RunWithLooper 57 @SmallTest 58 public class RemoteInputViewTest extends SysuiTestCase { 59 60 private static final String TEST_RESULT_KEY = "test_result_key"; 61 private static final String TEST_REPLY = "hello"; 62 private static final String TEST_ACTION = "com.android.REMOTE_INPUT_VIEW_ACTION"; 63 64 private static final String DUMMY_MESSAGE_APP_PKG = 65 "com.android.sysuitest.dummynotificationsender"; 66 private static final int DUMMY_MESSAGE_APP_ID = Process.LAST_APPLICATION_UID - 1; 67 68 @Mock private RemoteInputController mController; 69 @Mock private ShortcutManager mShortcutManager; 70 @Mock private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler; 71 @Mock private LightBarController mLightBarController; 72 private BlockingQueueIntentReceiver mReceiver; 73 private RemoteInputView mView; 74 75 @Before setUp()76 public void setUp() throws Exception { 77 Assert.sMainLooper = TestableLooper.get(this).getLooper(); 78 MockitoAnnotations.initMocks(this); 79 80 mDependency.injectTestDependency(RemoteInputQuickSettingsDisabler.class, 81 mRemoteInputQuickSettingsDisabler); 82 mDependency.injectTestDependency(LightBarController.class, 83 mLightBarController); 84 85 mReceiver = new BlockingQueueIntentReceiver(); 86 mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION), null, 87 Handler.createAsync(Dependency.get(Dependency.BG_LOOPER))); 88 89 // Avoid SecurityException RemoteInputView#sendRemoteInput(). 90 mContext.addMockSystemService(ShortcutManager.class, mShortcutManager); 91 } 92 93 @After tearDown()94 public void tearDown() { 95 mContext.unregisterReceiver(mReceiver); 96 } 97 setTestPendingIntent(RemoteInputView view)98 private void setTestPendingIntent(RemoteInputView view) { 99 PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, 100 new Intent(TEST_ACTION), 0); 101 RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).build(); 102 103 view.setPendingIntent(pendingIntent); 104 view.setRemoteInput(new RemoteInput[]{input}, input, null /* editedSuggestionInfo */); 105 } 106 107 @Test testSendRemoteInput_intentContainsResultsAndSource()108 public void testSendRemoteInput_intentContainsResultsAndSource() throws Exception { 109 ExpandableNotificationRow row = new NotificationTestHelper(mContext).createRow(); 110 RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController); 111 112 setTestPendingIntent(view); 113 114 view.focus(); 115 116 EditText editText = view.findViewById(R.id.remote_input_text); 117 editText.setText(TEST_REPLY); 118 ImageButton sendButton = view.findViewById(R.id.remote_input_send); 119 sendButton.performClick(); 120 121 Intent resultIntent = mReceiver.waitForIntent(); 122 assertEquals(TEST_REPLY, 123 RemoteInput.getResultsFromIntent(resultIntent).get(TEST_RESULT_KEY)); 124 assertEquals(RemoteInput.SOURCE_FREE_FORM_INPUT, 125 RemoteInput.getResultsSource(resultIntent)); 126 } 127 getTargetInputMethodUser(UserHandle fromUser, UserHandle toUser)128 private UserHandle getTargetInputMethodUser(UserHandle fromUser, UserHandle toUser) 129 throws Exception { 130 ExpandableNotificationRow row = new NotificationTestHelper(mContext).createRow( 131 DUMMY_MESSAGE_APP_PKG, 132 UserHandle.getUid(fromUser.getIdentifier(), DUMMY_MESSAGE_APP_ID), 133 toUser); 134 RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController); 135 136 setTestPendingIntent(view); 137 138 view.focus(); 139 140 EditText editText = view.findViewById(R.id.remote_input_text); 141 EditorInfo editorInfo = new EditorInfo(); 142 editorInfo.packageName = DUMMY_MESSAGE_APP_PKG; 143 editorInfo.fieldId = editText.getId(); 144 InputConnection ic = editText.onCreateInputConnection(editorInfo); 145 assertNotNull(ic); 146 return editorInfo.targetInputMethodUser; 147 } 148 149 @Test testEditorInfoTargetInputMethodUserForCallingUser()150 public void testEditorInfoTargetInputMethodUserForCallingUser() throws Exception { 151 UserHandle callingUser = Process.myUserHandle(); 152 assertEquals(callingUser, getTargetInputMethodUser(callingUser, callingUser)); 153 } 154 155 @Test testEditorInfoTargetInputMethodUserForDifferentUser()156 public void testEditorInfoTargetInputMethodUserForDifferentUser() throws Exception { 157 UserHandle differentUser = UserHandle.of(UserHandle.getCallingUserId() + 1); 158 assertEquals(differentUser, getTargetInputMethodUser(differentUser, differentUser)); 159 } 160 161 @Test testEditorInfoTargetInputMethodUserForAllUser()162 public void testEditorInfoTargetInputMethodUserForAllUser() throws Exception { 163 // For the special pseudo user UserHandle.ALL, EditorInfo#targetInputMethodUser must be 164 // resolved as the current user. 165 UserHandle callingUser = Process.myUserHandle(); 166 assertEquals(UserHandle.of(ActivityManager.getCurrentUser()), 167 getTargetInputMethodUser(callingUser, UserHandle.ALL)); 168 } 169 170 @Test testNoCrashWithoutVisibilityListener()171 public void testNoCrashWithoutVisibilityListener() throws Exception { 172 ExpandableNotificationRow row = new NotificationTestHelper(mContext).createRow(); 173 RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController); 174 175 view.setOnVisibilityChangedListener(null); 176 view.setVisibility(View.INVISIBLE); 177 view.setVisibility(View.VISIBLE); 178 } 179 } 180