1 /*
2  * Copyright (C) 2018 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.server.wm;
18 
19 import static android.view.InsetsSource.ID_IME;
20 import static android.view.WindowInsets.Type.ime;
21 import static android.view.WindowInsets.Type.statusBars;
22 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
23 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
24 
25 import static org.junit.Assert.assertEquals;
26 import static org.junit.Assert.assertFalse;
27 import static org.junit.Assert.assertNotNull;
28 import static org.junit.Assert.assertNull;
29 import static org.junit.Assert.assertTrue;
30 
31 import android.graphics.Insets;
32 import android.graphics.Rect;
33 import android.platform.test.annotations.Presubmit;
34 import android.view.InsetsSource;
35 
36 import androidx.test.filters.SmallTest;
37 
38 import org.junit.Before;
39 import org.junit.Test;
40 import org.junit.runner.RunWith;
41 
42 @SmallTest
43 @Presubmit
44 @RunWith(WindowTestRunner.class)
45 public class InsetsSourceProviderTest extends WindowTestsBase {
46 
47     private InsetsSource mSource = new InsetsSource(
48             InsetsSource.createId(null, 0, statusBars()), statusBars());
49     private InsetsSourceProvider mProvider;
50     private InsetsSource mImeSource = new InsetsSource(ID_IME, ime());
51     private InsetsSourceProvider mImeProvider;
52 
53     @Before
setUp()54     public void setUp() throws Exception {
55         mSource.setVisible(true);
56         mProvider = new InsetsSourceProvider(mSource,
57                 mDisplayContent.getInsetsStateController(), mDisplayContent);
58         mImeProvider = new InsetsSourceProvider(mImeSource,
59                 mDisplayContent.getInsetsStateController(), mDisplayContent);
60     }
61 
62     @Test
testPostLayout()63     public void testPostLayout() {
64         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
65         statusBar.setBounds(0, 0, 500, 1000);
66         statusBar.getFrame().set(0, 0, 500, 100);
67         statusBar.mHasSurface = true;
68         mProvider.setWindowContainer(statusBar, null, null);
69         mProvider.updateSourceFrame(statusBar.getFrame());
70         mProvider.onPostLayout();
71         assertEquals(new Rect(0, 0, 500, 100), mProvider.getSource().getFrame());
72         assertEquals(Insets.of(0, 100, 0, 0), mProvider.getInsetsHint());
73 
74         // Change the bounds and call onPostLayout. Make sure the insets hint gets updated.
75         statusBar.setBounds(0, 10, 500, 1000);
76         mProvider.onPostLayout();
77         assertEquals(Insets.of(0, 90, 0, 0), mProvider.getInsetsHint());
78     }
79 
80     @Test
testPostLayout_invisible()81     public void testPostLayout_invisible() {
82         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
83         statusBar.setBounds(0, 0, 500, 1000);
84         statusBar.getFrame().set(0, 0, 500, 100);
85         mProvider.setWindowContainer(statusBar, null, null);
86         mProvider.updateSourceFrame(statusBar.getFrame());
87         mProvider.onPostLayout();
88         assertTrue(mProvider.getSource().getFrame().isEmpty());
89         assertEquals(Insets.NONE, mProvider.getInsetsHint());
90     }
91 
92     @Test
testPostLayout_frameProvider()93     public void testPostLayout_frameProvider() {
94         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
95         statusBar.getFrame().set(0, 0, 500, 100);
96         statusBar.mHasSurface = true;
97         mProvider.setWindowContainer(statusBar,
98                 (displayFrames, windowState, rect) -> {
99                     rect.set(10, 10, 20, 20);
100                     return 0;
101                 }, null);
102         mProvider.updateSourceFrame(statusBar.getFrame());
103         mProvider.onPostLayout();
104         assertEquals(new Rect(10, 10, 20, 20), mProvider.getSource().getFrame());
105     }
106 
107     @Test
testUpdateControlForTarget()108     public void testUpdateControlForTarget() {
109         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
110         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
111         statusBar.getFrame().set(0, 0, 500, 100);
112 
113         // We must not have control or control target before we have the insets source window.
114         mProvider.updateControlForTarget(target, true /* force */);
115         assertNull(mProvider.getControl(target));
116         assertNull(mProvider.getControlTarget());
117 
118         // We can have the control or the control target after we have the insets source window.
119         mProvider.setWindowContainer(statusBar, null, null);
120         mProvider.updateControlForTarget(target, false /* force */);
121         assertNotNull(mProvider.getControl(target));
122         assertNotNull(mProvider.getControlTarget());
123 
124         // We must not have control or control target while we are performing seamless rotation.
125         // And the control and the control target must not be updated during that.
126         mProvider.startSeamlessRotation();
127         assertNull(mProvider.getControl(target));
128         assertNull(mProvider.getControlTarget());
129         mProvider.updateControlForTarget(target, true /* force */);
130         assertNull(mProvider.getControl(target));
131         assertNull(mProvider.getControlTarget());
132 
133         // We can have the control and the control target after seamless rotation.
134         mProvider.finishSeamlessRotation();
135         mProvider.updateControlForTarget(target, false /* force */);
136         assertNotNull(mProvider.getControl(target));
137         assertNotNull(mProvider.getControlTarget());
138 
139         // We can clear the control and the control target.
140         mProvider.updateControlForTarget(null, false /* force */);
141         assertNull(mProvider.getControl(target));
142         assertNull(mProvider.getControlTarget());
143 
144         // We must not have control or control target if the insets source window doesn't have a
145         // surface.
146         statusBar.setSurfaceControl(null);
147         mProvider.updateControlForTarget(target, true /* force */);
148         assertNull(mProvider.getControl(target));
149         assertNull(mProvider.getControlTarget());
150     }
151 
152     @Test
testUpdateControlForFakeTarget()153     public void testUpdateControlForFakeTarget() {
154         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
155         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
156         statusBar.getFrame().set(0, 0, 500, 100);
157         mProvider.setWindowContainer(statusBar, null, null);
158         mProvider.updateFakeControlTarget(target);
159         assertNotNull(mProvider.getControl(target));
160         assertNull(mProvider.getControl(target).getLeash());
161         mProvider.updateFakeControlTarget(null);
162         assertNull(mProvider.getControl(target));
163     }
164 
165     @Test
testUpdateSourceFrame()166     public void testUpdateSourceFrame() {
167         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
168         mProvider.setWindowContainer(statusBar, null, null);
169         statusBar.setBounds(0, 0, 500, 1000);
170 
171         mProvider.setServerVisible(true);
172         statusBar.getFrame().set(0, 0, 500, 100);
173         mProvider.updateSourceFrame(statusBar.getFrame());
174         assertEquals(statusBar.getFrame(), mProvider.getSource().getFrame());
175         assertEquals(Insets.of(0, 100, 0, 0), mProvider.getInsetsHint());
176 
177         // Only change the source frame but not the visibility.
178         statusBar.getFrame().set(0, 0, 500, 90);
179         mProvider.updateSourceFrame(statusBar.getFrame());
180         assertEquals(statusBar.getFrame(), mProvider.getSource().getFrame());
181         assertEquals(Insets.of(0, 90, 0, 0), mProvider.getInsetsHint());
182 
183         mProvider.setServerVisible(false);
184         statusBar.getFrame().set(0, 0, 500, 80);
185         mProvider.updateSourceFrame(statusBar.getFrame());
186         assertTrue(mProvider.getSource().getFrame().isEmpty());
187         assertEquals(Insets.of(0, 90, 0, 0), mProvider.getInsetsHint());
188 
189         // Only change the visibility but not the frame.
190         mProvider.setServerVisible(true);
191         assertEquals(statusBar.getFrame(), mProvider.getSource().getFrame());
192         assertEquals(Insets.of(0, 80, 0, 0), mProvider.getInsetsHint());
193     }
194 
195     @Test
testUpdateSourceFrameForIme()196     public void testUpdateSourceFrameForIme() {
197         final WindowState inputMethod = createWindow(null, TYPE_INPUT_METHOD, "inputMethod");
198 
199         inputMethod.getFrame().set(new Rect(0, 400, 500, 500));
200 
201         mImeProvider.setWindowContainer(inputMethod, null, null);
202         mImeProvider.setServerVisible(false);
203         mImeSource.setVisible(true);
204         mImeProvider.updateSourceFrame(inputMethod.getFrame());
205         assertEquals(new Rect(0, 0, 0, 0), mImeSource.getFrame());
206         Insets insets = mImeSource.calculateInsets(new Rect(0, 0, 500, 500),
207                 false /* ignoreVisibility */);
208         assertEquals(Insets.of(0, 0, 0, 0), insets);
209 
210         mImeProvider.setServerVisible(true);
211         mImeSource.setVisible(true);
212         mImeProvider.updateSourceFrame(inputMethod.getFrame());
213         assertEquals(inputMethod.getFrame(), mImeSource.getFrame());
214         insets = mImeSource.calculateInsets(new Rect(0, 0, 500, 500),
215                 false /* ignoreVisibility */);
216         assertEquals(Insets.of(0, 0, 0, 100), insets);
217     }
218 
219     @Test
testSetRequestedVisibleTypes()220     public void testSetRequestedVisibleTypes() {
221         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
222         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
223         statusBar.getFrame().set(0, 0, 500, 100);
224         mProvider.setWindowContainer(statusBar, null, null);
225         mProvider.updateControlForTarget(target, false /* force */);
226         target.setRequestedVisibleTypes(0, statusBars());
227         mProvider.updateClientVisibility(target);
228         assertFalse(mSource.isVisible());
229     }
230 
231     @Test
testSetRequestedVisibleTypes_noControl()232     public void testSetRequestedVisibleTypes_noControl() {
233         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
234         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
235         statusBar.getFrame().set(0, 0, 500, 100);
236         mProvider.setWindowContainer(statusBar, null, null);
237         target.setRequestedVisibleTypes(0, statusBars());
238         mProvider.updateClientVisibility(target);
239         assertTrue(mSource.isVisible());
240     }
241 
242     @Test
testInsetGeometries()243     public void testInsetGeometries() {
244         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
245         statusBar.getFrame().set(0, 0, 500, 100);
246         statusBar.mHasSurface = true;
247         mProvider.setWindowContainer(statusBar, null, null);
248         mProvider.updateSourceFrame(statusBar.getFrame());
249         mProvider.onPostLayout();
250         assertEquals(new Rect(0, 0, 500, 100), mProvider.getSource().getFrame());
251         // Still apply top insets if window overlaps even if it's top doesn't exactly match
252         // the inset-window's top.
253         assertEquals(Insets.of(0, 100, 0, 0),
254                 mProvider.getSource().calculateInsets(new Rect(0, -100, 500, 400),
255                         false /* ignoreVisibility */));
256 
257         // Don't apply left insets if window is left-of inset-window but still overlaps
258         statusBar.getFrame().set(100, 0, 0, 0);
259         assertEquals(Insets.of(0, 0, 0, 0),
260                 mProvider.getSource().calculateInsets(new Rect(-100, 0, 400, 500),
261                         false /* ignoreVisibility */));
262     }
263 }
264