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 android.view;
18 
19 import static org.hamcrest.Matchers.equalTo;
20 import static org.junit.Assert.assertThat;
21 
22 import android.content.Context;
23 import android.graphics.Rect;
24 import android.platform.test.annotations.Presubmit;
25 import android.support.test.InstrumentationRegistry;
26 import android.support.test.filters.SmallTest;
27 import android.support.test.runner.AndroidJUnit4;
28 
29 import org.junit.Before;
30 import org.junit.Test;
31 import org.junit.runner.RunWith;
32 
33 import java.lang.reflect.Field;
34 import java.lang.reflect.Method;
35 
36 @Presubmit
37 @SmallTest
38 @RunWith(AndroidJUnit4.class)
39 public class ViewRootImplTest {
40 
41     private Context mContext;
42     private ViewRootImplAccessor mViewRootImpl;
43 
44     @Before
setUp()45     public void setUp() throws Exception {
46         mContext = InstrumentationRegistry.getContext();
47 
48         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
49             mViewRootImpl = new ViewRootImplAccessor(
50                     new ViewRootImpl(mContext, mContext.getDisplay()));
51         });
52     }
53 
54     @Test
negativeInsets_areSetToZero()55     public void negativeInsets_areSetToZero() throws Exception {
56         mViewRootImpl.getAttachInfo().getContentInsets().set(-10, -20, -30 , -40);
57         mViewRootImpl.getAttachInfo().getStableInsets().set(-10, -20, -30 , -40);
58         final WindowInsets insets = mViewRootImpl.getWindowInsets(true /* forceConstruct */);
59 
60         assertThat(insets.getSystemWindowInsets(), equalTo(new Rect()));
61         assertThat(new Rect(insets.getStableInsetLeft(), insets.getStableInsetTop(),
62                 insets.getStableInsetRight(), insets.getStableInsetBottom()), equalTo(new Rect()));
63     }
64 
65     @Test
negativeInsets_areSetToZero_positiveAreLeftAsIs()66     public void negativeInsets_areSetToZero_positiveAreLeftAsIs() throws Exception {
67         mViewRootImpl.getAttachInfo().getContentInsets().set(-10, 20, -30 , 40);
68         mViewRootImpl.getAttachInfo().getStableInsets().set(10, -20, 30 , -40);
69         final WindowInsets insets = mViewRootImpl.getWindowInsets(true /* forceConstruct */);
70 
71         assertThat(insets.getSystemWindowInsets(), equalTo(new Rect(0, 20, 0, 40)));
72         assertThat(new Rect(insets.getStableInsetLeft(), insets.getStableInsetTop(),
73                 insets.getStableInsetRight(), insets.getStableInsetBottom()),
74                 equalTo(new Rect(10, 0, 30, 0)));
75     }
76 
77     @Test
positiveInsets_areLeftAsIs()78     public void positiveInsets_areLeftAsIs() throws Exception {
79         mViewRootImpl.getAttachInfo().getContentInsets().set(10, 20, 30 , 40);
80         mViewRootImpl.getAttachInfo().getStableInsets().set(10, 20, 30 , 40);
81         final WindowInsets insets = mViewRootImpl.getWindowInsets(true /* forceConstruct */);
82 
83         assertThat(insets.getSystemWindowInsets(), equalTo(new Rect(10, 20, 30, 40)));
84         assertThat(new Rect(insets.getStableInsetLeft(), insets.getStableInsetTop(),
85                 insets.getStableInsetRight(), insets.getStableInsetBottom()),
86                 equalTo(new Rect(10, 20, 30, 40)));
87     }
88 
89     private static class ViewRootImplAccessor {
90 
91         private final ViewRootImpl mViewRootImpl;
92 
ViewRootImplAccessor(ViewRootImpl viewRootImpl)93         ViewRootImplAccessor(ViewRootImpl viewRootImpl) {
94             mViewRootImpl = viewRootImpl;
95         }
96 
get()97         public ViewRootImpl get() {
98             return mViewRootImpl;
99         }
100 
getAttachInfo()101         AttachInfoAccessor getAttachInfo() throws Exception {
102             return new AttachInfoAccessor(
103                     getField(mViewRootImpl, ViewRootImpl.class.getDeclaredField("mAttachInfo")));
104         }
105 
getWindowInsets(boolean forceConstruct)106         WindowInsets getWindowInsets(boolean forceConstruct) throws Exception {
107             return (WindowInsets) invokeMethod(mViewRootImpl,
108                     ViewRootImpl.class.getDeclaredMethod("getWindowInsets", boolean.class),
109                     forceConstruct);
110         }
111 
112         class AttachInfoAccessor {
113 
114             private final Class<?> mClass;
115             private final Object mAttachInfo;
116 
AttachInfoAccessor(Object attachInfo)117             AttachInfoAccessor(Object attachInfo) throws Exception {
118                 mAttachInfo = attachInfo;
119                 mClass = ViewRootImpl.class.getClassLoader().loadClass(
120                         "android.view.View$AttachInfo");
121             }
122 
getContentInsets()123             Rect getContentInsets() throws Exception {
124                 return (Rect) getField(mAttachInfo, mClass.getDeclaredField("mContentInsets"));
125             }
126 
getStableInsets()127             Rect getStableInsets() throws Exception {
128                 return (Rect) getField(mAttachInfo, mClass.getDeclaredField("mStableInsets"));
129             }
130         }
131 
getField(Object o, Field field)132         private static Object getField(Object o, Field field) throws Exception {
133             field.setAccessible(true);
134             return field.get(o);
135         }
136 
invokeMethod(Object o, Method method, Object... args)137         private static Object invokeMethod(Object o, Method method, Object... args)
138                 throws Exception {
139             method.setAccessible(true);
140             return method.invoke(o, args);
141         }
142     }
143 }
144