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.RemoteInputController;
43 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
44 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
45 import com.android.systemui.statusbar.phone.LightBarController;
46 
47 import org.junit.After;
48 import org.junit.Before;
49 import org.junit.Test;
50 import org.junit.runner.RunWith;
51 import org.mockito.Mock;
52 import org.mockito.MockitoAnnotations;
53 
54 @RunWith(AndroidTestingRunner.class)
55 @TestableLooper.RunWithLooper
56 @SmallTest
57 public class RemoteInputViewTest extends SysuiTestCase {
58 
59     private static final String TEST_RESULT_KEY = "test_result_key";
60     private static final String TEST_REPLY = "hello";
61     private static final String TEST_ACTION = "com.android.REMOTE_INPUT_VIEW_ACTION";
62 
63     private static final String DUMMY_MESSAGE_APP_PKG =
64             "com.android.sysuitest.dummynotificationsender";
65     private static final int DUMMY_MESSAGE_APP_ID = Process.LAST_APPLICATION_UID - 1;
66 
67     @Mock private RemoteInputController mController;
68     @Mock private ShortcutManager mShortcutManager;
69     @Mock private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
70     @Mock private LightBarController mLightBarController;
71     private BlockingQueueIntentReceiver mReceiver;
72     private RemoteInputView mView;
73 
74     @Before
setUp()75     public void setUp() throws Exception {
76         allowTestableLooperAsMainThread();
77         MockitoAnnotations.initMocks(this);
78 
79         mDependency.injectTestDependency(RemoteInputQuickSettingsDisabler.class,
80                 mRemoteInputQuickSettingsDisabler);
81         mDependency.injectTestDependency(LightBarController.class,
82                 mLightBarController);
83 
84         mReceiver = new BlockingQueueIntentReceiver();
85         mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION), null,
86                 Handler.createAsync(Dependency.get(Dependency.BG_LOOPER)));
87 
88         // Avoid SecurityException RemoteInputView#sendRemoteInput().
89         mContext.addMockSystemService(ShortcutManager.class, mShortcutManager);
90     }
91 
92     @After
tearDown()93     public void tearDown() {
94         mContext.unregisterReceiver(mReceiver);
95     }
96 
setTestPendingIntent(RemoteInputView view)97     private void setTestPendingIntent(RemoteInputView view) {
98         PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
99                 new Intent(TEST_ACTION), 0);
100         RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).build();
101 
102         view.setPendingIntent(pendingIntent);
103         view.setRemoteInput(new RemoteInput[]{input}, input, null /* editedSuggestionInfo */);
104     }
105 
106     @Test
testSendRemoteInput_intentContainsResultsAndSource()107     public void testSendRemoteInput_intentContainsResultsAndSource() throws Exception {
108         NotificationTestHelper helper = new NotificationTestHelper(
109                 mContext,
110                 mDependency,
111                 TestableLooper.get(this));
112         ExpandableNotificationRow row = helper.createRow();
113         RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController);
114 
115         setTestPendingIntent(view);
116 
117         view.focus();
118 
119         EditText editText = view.findViewById(R.id.remote_input_text);
120         editText.setText(TEST_REPLY);
121         ImageButton sendButton = view.findViewById(R.id.remote_input_send);
122         sendButton.performClick();
123 
124         Intent resultIntent = mReceiver.waitForIntent();
125         assertEquals(TEST_REPLY,
126                 RemoteInput.getResultsFromIntent(resultIntent).get(TEST_RESULT_KEY));
127         assertEquals(RemoteInput.SOURCE_FREE_FORM_INPUT,
128                 RemoteInput.getResultsSource(resultIntent));
129     }
130 
getTargetInputMethodUser(UserHandle fromUser, UserHandle toUser)131     private UserHandle getTargetInputMethodUser(UserHandle fromUser, UserHandle toUser)
132             throws Exception {
133         NotificationTestHelper helper = new NotificationTestHelper(
134                 mContext,
135                 mDependency,
136                 TestableLooper.get(this));
137         ExpandableNotificationRow row = helper.createRow(
138                 DUMMY_MESSAGE_APP_PKG,
139                 UserHandle.getUid(fromUser.getIdentifier(), DUMMY_MESSAGE_APP_ID),
140                 toUser);
141         RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController);
142 
143         setTestPendingIntent(view);
144 
145         view.focus();
146 
147         EditText editText = view.findViewById(R.id.remote_input_text);
148         EditorInfo editorInfo = new EditorInfo();
149         editorInfo.packageName = DUMMY_MESSAGE_APP_PKG;
150         editorInfo.fieldId = editText.getId();
151         InputConnection ic = editText.onCreateInputConnection(editorInfo);
152         assertNotNull(ic);
153         return editorInfo.targetInputMethodUser;
154     }
155 
156     @Test
testEditorInfoTargetInputMethodUserForCallingUser()157     public void testEditorInfoTargetInputMethodUserForCallingUser() throws Exception {
158         UserHandle callingUser = Process.myUserHandle();
159         assertEquals(callingUser, getTargetInputMethodUser(callingUser, callingUser));
160     }
161 
162     @Test
testEditorInfoTargetInputMethodUserForDifferentUser()163     public void testEditorInfoTargetInputMethodUserForDifferentUser() throws Exception {
164         UserHandle differentUser = UserHandle.of(UserHandle.getCallingUserId() + 1);
165         assertEquals(differentUser, getTargetInputMethodUser(differentUser, differentUser));
166     }
167 
168     @Test
testEditorInfoTargetInputMethodUserForAllUser()169     public void testEditorInfoTargetInputMethodUserForAllUser() throws Exception {
170         // For the special pseudo user UserHandle.ALL, EditorInfo#targetInputMethodUser must be
171         // resolved as the current user.
172         UserHandle callingUser = Process.myUserHandle();
173         assertEquals(UserHandle.of(ActivityManager.getCurrentUser()),
174                 getTargetInputMethodUser(callingUser, UserHandle.ALL));
175     }
176 
177     @Test
testNoCrashWithoutVisibilityListener()178     public void testNoCrashWithoutVisibilityListener() throws Exception {
179         NotificationTestHelper helper = new NotificationTestHelper(
180                 mContext,
181                 mDependency,
182                 TestableLooper.get(this));
183         ExpandableNotificationRow row = helper.createRow();
184         RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController);
185 
186         view.setOnVisibilityChangedListener(null);
187         view.setVisibility(View.INVISIBLE);
188         view.setVisibility(View.VISIBLE);
189     }
190 }
191