1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.wm.shell.common;
18 
19 import static android.view.Display.DEFAULT_DISPLAY;
20 import static android.view.InsetsSource.ID_IME;
21 import static android.view.Surface.ROTATION_0;
22 import static android.view.WindowInsets.Type.ime;
23 
24 import static org.junit.Assert.assertFalse;
25 import static org.junit.Assert.assertNotNull;
26 import static org.junit.Assert.assertNull;
27 import static org.junit.Assert.assertTrue;
28 import static org.mockito.ArgumentMatchers.any;
29 import static org.mockito.ArgumentMatchers.anyInt;
30 import static org.mockito.ArgumentMatchers.eq;
31 import static org.mockito.Mockito.mock;
32 import static org.mockito.Mockito.spy;
33 import static org.mockito.Mockito.times;
34 import static org.mockito.Mockito.verify;
35 import static org.mockito.Mockito.verifyZeroInteractions;
36 
37 import android.graphics.Insets;
38 import android.graphics.Point;
39 import android.os.Looper;
40 import android.platform.test.annotations.RequiresFlagsDisabled;
41 import android.platform.test.annotations.RequiresFlagsEnabled;
42 import android.platform.test.flag.junit.CheckFlagsRule;
43 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
44 import android.view.IWindowManager;
45 import android.view.InsetsSource;
46 import android.view.InsetsSourceControl;
47 import android.view.InsetsState;
48 import android.view.SurfaceControl;
49 import android.view.inputmethod.ImeTracker;
50 
51 import androidx.test.filters.SmallTest;
52 
53 import com.android.wm.shell.ShellTestCase;
54 import com.android.wm.shell.shared.TransactionPool;
55 import com.android.wm.shell.sysui.ShellInit;
56 
57 import org.junit.Before;
58 import org.junit.Rule;
59 import org.junit.Test;
60 import org.mockito.Mock;
61 import org.mockito.MockitoAnnotations;
62 
63 import java.util.concurrent.Executor;
64 
65 /**
66  * Tests for the display IME controller.
67  *
68  * <p> Build/Install/Run:
69  *  atest WMShellUnitTests:DisplayImeControllerTest
70  */
71 @SmallTest
72 public class DisplayImeControllerTest extends ShellTestCase {
73     @Rule
74     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
75 
76     @Mock
77     private SurfaceControl.Transaction mT;
78     @Mock
79     private ShellInit mShellInit;
80     @Mock
81     private IWindowManager mWm;
82     private DisplayImeController mDisplayImeController;
83     private DisplayImeController.PerDisplay mPerDisplay;
84     private Executor mExecutor;
85 
86     @Before
setUp()87     public void setUp() throws Exception {
88         MockitoAnnotations.initMocks(this);
89         mExecutor = spy(Runnable::run);
90         mDisplayImeController = new DisplayImeController(mWm, mShellInit, null, null,
91                 new TransactionPool() {
92             @Override
93             public SurfaceControl.Transaction acquire() {
94                 return mT;
95             }
96 
97             @Override
98             public void release(SurfaceControl.Transaction t) {
99             }
100         }, mExecutor) {
101             @Override
102             void removeImeSurface(int displayId) {
103             }
104         };
105         mPerDisplay = mDisplayImeController.new PerDisplay(DEFAULT_DISPLAY, ROTATION_0);
106     }
107 
108     @Test
instantiateController_addInitCallback()109     public void instantiateController_addInitCallback() {
110         verify(mShellInit, times(1)).addInitCallback(any(), any());
111     }
112 
113     @Test
insetsControlChanged_schedulesNoWorkOnExecutor()114     public void insetsControlChanged_schedulesNoWorkOnExecutor() {
115         Looper.prepare();
116         mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl());
117         verifyZeroInteractions(mExecutor);
118     }
119 
120     @Test
insetsChanged_schedulesNoWorkOnExecutor()121     public void insetsChanged_schedulesNoWorkOnExecutor() {
122         Looper.prepare();
123         mPerDisplay.insetsChanged(insetsStateWithIme(false));
124         verifyZeroInteractions(mExecutor);
125     }
126 
127     @Test
showInsets_schedulesNoWorkOnExecutor()128     public void showInsets_schedulesNoWorkOnExecutor() {
129         mPerDisplay.showInsets(ime(), true /* fromIme */, ImeTracker.Token.empty());
130         verifyZeroInteractions(mExecutor);
131     }
132 
133     @Test
hideInsets_schedulesNoWorkOnExecutor()134     public void hideInsets_schedulesNoWorkOnExecutor() {
135         mPerDisplay.hideInsets(ime(), true /* fromIme */, ImeTracker.Token.empty());
136         verifyZeroInteractions(mExecutor);
137     }
138 
139     // With the refactor, the control's isInitiallyVisible is used to apply to the IME, therefore
140     // this test is obsolete
141     @Test
142     @RequiresFlagsDisabled(android.view.inputmethod.Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
reappliesVisibilityToChangedLeash()143     public void reappliesVisibilityToChangedLeash() {
144         verifyZeroInteractions(mT);
145         mPerDisplay.mImeShowing = false;
146 
147         mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl());
148 
149         assertFalse(mPerDisplay.mImeShowing);
150         verify(mT).hide(any());
151 
152         mPerDisplay.mImeShowing = true;
153         mPerDisplay.insetsControlChanged(insetsStateWithIme(true), insetsSourceControl());
154 
155         assertTrue(mPerDisplay.mImeShowing);
156         verify(mT).show(any());
157     }
158 
159     @Test
insetsControlChanged_updateImeSourceControl()160     public void insetsControlChanged_updateImeSourceControl() {
161         Looper.prepare();
162         mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl());
163         assertNotNull(mPerDisplay.mImeSourceControl);
164 
165         mPerDisplay.insetsControlChanged(new InsetsState(), new InsetsSourceControl[]{});
166         assertNull(mPerDisplay.mImeSourceControl);
167     }
168 
169     @Test
170     @RequiresFlagsEnabled(android.view.inputmethod.Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
setImeInputTargetRequestedVisibility_invokeOnImeRequested()171     public void setImeInputTargetRequestedVisibility_invokeOnImeRequested() {
172         var mockPp = mock(DisplayImeController.ImePositionProcessor.class);
173         mDisplayImeController.addPositionProcessor(mockPp);
174 
175         mPerDisplay.setImeInputTargetRequestedVisibility(true, null /* statsToken */);
176         verify(mockPp).onImeRequested(anyInt(), eq(true));
177 
178         mPerDisplay.setImeInputTargetRequestedVisibility(false, null /* statsToken */);
179         verify(mockPp).onImeRequested(anyInt(), eq(false));
180     }
181 
insetsSourceControl()182     private InsetsSourceControl[] insetsSourceControl() {
183         return new InsetsSourceControl[]{
184                 new InsetsSourceControl(
185                         ID_IME, ime(), mock(SurfaceControl.class), false, new Point(0, 0),
186                         Insets.NONE)
187         };
188     }
189 
insetsStateWithIme(boolean visible)190     private InsetsState insetsStateWithIme(boolean visible) {
191         InsetsState state = new InsetsState();
192         state.addSource(new InsetsSource(ID_IME, ime()));
193         state.setSourceVisible(ID_IME, visible);
194         return state;
195     }
196 
197 }
198