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.internal.widget;
18 
19 import static android.view.View.VISIBLE;
20 
21 import static org.hamcrest.Matchers.is;
22 import static org.junit.Assert.assertThat;
23 import static org.mockito.ArgumentMatchers.any;
24 import static org.mockito.Mockito.atLeastOnce;
25 import static org.mockito.Mockito.mock;
26 import static org.mockito.Mockito.verify;
27 import static org.mockito.Mockito.verifyNoMoreInteractions;
28 
29 import static java.util.Arrays.asList;
30 import static java.util.Collections.emptyList;
31 
32 import android.graphics.Color;
33 import android.graphics.drawable.ColorDrawable;
34 import android.graphics.drawable.Drawable;
35 import android.view.ViewGroup;
36 import android.widget.FrameLayout;
37 
38 import androidx.test.InstrumentationRegistry;
39 
40 import org.junit.Before;
41 import org.junit.Test;
42 
43 import java.util.List;
44 
45 public class BackgroundFallbackTest {
46 
47     private static final int NAVBAR_BOTTOM = 0;
48     private static final int NAVBAR_LEFT = 1;
49     private static final int NAVBAR_RIGHT = 2;
50 
51     private static final int SCREEN_HEIGHT = 2000;
52     private static final int SCREEN_WIDTH = 1000;
53     private static final int STATUS_HEIGHT = 100;
54     private static final int NAV_SIZE = 200;
55 
56     private static final boolean INSET_CONTENT_VIEWS = true;
57     private static final boolean DONT_INSET_CONTENT_VIEWS = false;
58 
59     BackgroundFallback mFallback;
60     Drawable mDrawableMock;
61 
62     ViewGroup mStatusBarView;
63     ViewGroup mNavigationBarView;
64 
65     ViewGroup mDecorViewMock;
66     ViewGroup mContentRootMock;
67     ViewGroup mContentContainerMock;
68     ViewGroup mContentMock;
69 
70     int mLastTop = 0;
71 
72     @Before
setUp()73     public void setUp() throws Exception {
74         mFallback = new BackgroundFallback();
75         mDrawableMock = mock(Drawable.class);
76 
77         mFallback.setDrawable(mDrawableMock);
78 
79     }
80 
81     @Test
hasFallback_withDrawable_true()82     public void hasFallback_withDrawable_true() {
83         mFallback.setDrawable(mDrawableMock);
84         assertThat(mFallback.hasFallback(), is(true));
85     }
86 
87     @Test
hasFallback_withoutDrawable_false()88     public void hasFallback_withoutDrawable_false() {
89         mFallback.setDrawable(null);
90         assertThat(mFallback.hasFallback(), is(false));
91     }
92 
93     @Test
draw_portrait_noFallback()94     public void draw_portrait_noFallback() {
95         setUpViewHierarchy(INSET_CONTENT_VIEWS, NAVBAR_BOTTOM);
96 
97         mFallback.draw(mDecorViewMock, mContentRootMock, null /* canvas */, mContentContainerMock,
98                 mStatusBarView, mNavigationBarView);
99 
100         verifyNoMoreInteractions(mDrawableMock);
101     }
102 
103     @Test
draw_portrait_translucentBars_fallback()104     public void draw_portrait_translucentBars_fallback() {
105         setUpViewHierarchy(INSET_CONTENT_VIEWS, NAVBAR_BOTTOM);
106         setTranslucent(mStatusBarView);
107         setTranslucent(mNavigationBarView);
108 
109         mFallback.draw(mDecorViewMock, mContentRootMock, null /* canvas */, mContentContainerMock,
110                 mStatusBarView, mNavigationBarView);
111 
112         verifyFallbackTop(STATUS_HEIGHT);
113         verifyFallbackBottom(NAV_SIZE);
114         verifyNoMoreInteractions(mDrawableMock);
115     }
116 
117     @Test
draw_landscape_translucentBars_fallback()118     public void draw_landscape_translucentBars_fallback() {
119         setUpViewHierarchy(INSET_CONTENT_VIEWS, NAVBAR_RIGHT);
120         setTranslucent(mStatusBarView);
121         setTranslucent(mNavigationBarView);
122 
123         mFallback.draw(mDecorViewMock, mContentRootMock, null /* canvas */, mContentContainerMock,
124                 mStatusBarView, mNavigationBarView);
125 
126         verifyFallbackTop(STATUS_HEIGHT);
127         verifyFallbackRight(NAV_SIZE);
128         verifyNoMoreInteractions(mDrawableMock);
129     }
130 
131     @Test
draw_seascape_translucentBars_fallback()132     public void draw_seascape_translucentBars_fallback() {
133         setUpViewHierarchy(INSET_CONTENT_VIEWS, NAVBAR_LEFT);
134         setTranslucent(mStatusBarView);
135         setTranslucent(mNavigationBarView);
136 
137         mFallback.draw(mDecorViewMock, mContentRootMock, null /* canvas */, mContentContainerMock,
138                 mStatusBarView, mNavigationBarView);
139 
140         verifyFallbackTop(STATUS_HEIGHT);
141         verifyFallbackLeft(NAV_SIZE);
142         verifyNoMoreInteractions(mDrawableMock);
143     }
144 
145     @Test
draw_landscape_noFallback()146     public void draw_landscape_noFallback() {
147         setUpViewHierarchy(INSET_CONTENT_VIEWS, NAVBAR_RIGHT);
148 
149         mFallback.draw(mDecorViewMock, mContentRootMock, null /* canvas */, mContentContainerMock,
150                 mStatusBarView, mNavigationBarView);
151 
152         verifyNoMoreInteractions(mDrawableMock);
153     }
154 
155     @Test
draw_seascape_noFallback()156     public void draw_seascape_noFallback() {
157         setUpViewHierarchy(INSET_CONTENT_VIEWS, NAVBAR_LEFT);
158 
159         mFallback.draw(mDecorViewMock, mContentRootMock, null /* canvas */, mContentContainerMock,
160                 mStatusBarView, mNavigationBarView);
161 
162         verifyNoMoreInteractions(mDrawableMock);
163     }
164 
165     @Test
draw_seascape_translucentBars_noInsets_noFallback()166     public void draw_seascape_translucentBars_noInsets_noFallback() {
167         setUpViewHierarchy(DONT_INSET_CONTENT_VIEWS, NAVBAR_LEFT);
168         setTranslucent(mStatusBarView);
169         setTranslucent(mNavigationBarView);
170 
171         mFallback.draw(mDecorViewMock, mContentRootMock, null /* canvas */, mContentContainerMock,
172                 mStatusBarView, mNavigationBarView);
173 
174         verifyNoMoreInteractions(mDrawableMock);
175     }
176 
verifyFallbackTop(int size)177     private void verifyFallbackTop(int size) {
178         verify(mDrawableMock).setBounds(0, 0, SCREEN_WIDTH, size);
179         verify(mDrawableMock, atLeastOnce()).draw(any());
180         mLastTop = size;
181     }
182 
verifyFallbackLeft(int size)183     private void verifyFallbackLeft(int size) {
184         verify(mDrawableMock).setBounds(0, mLastTop, size, SCREEN_HEIGHT);
185         verify(mDrawableMock, atLeastOnce()).draw(any());
186     }
187 
verifyFallbackRight(int size)188     private void verifyFallbackRight(int size) {
189         verify(mDrawableMock).setBounds(SCREEN_WIDTH - size, mLastTop, SCREEN_WIDTH, SCREEN_HEIGHT);
190         verify(mDrawableMock, atLeastOnce()).draw(any());
191     }
192 
verifyFallbackBottom(int size)193     private void verifyFallbackBottom(int size) {
194         verify(mDrawableMock).setBounds(0, SCREEN_HEIGHT - size, SCREEN_WIDTH, SCREEN_HEIGHT);
195         verify(mDrawableMock, atLeastOnce()).draw(any());
196     }
197 
setUpViewHierarchy(boolean insetContentViews, int navBarPosition)198     private void setUpViewHierarchy(boolean insetContentViews, int navBarPosition) {
199         int insetLeft = 0;
200         int insetTop = 0;
201         int insetRight = 0;
202         int insetBottom = 0;
203 
204         mStatusBarView = mockView(0, 0, SCREEN_WIDTH, STATUS_HEIGHT,
205                 new ColorDrawable(Color.BLACK), VISIBLE, emptyList());
206         if (insetContentViews) {
207             insetTop = STATUS_HEIGHT;
208         }
209 
210         switch (navBarPosition) {
211             case NAVBAR_BOTTOM:
212                 mNavigationBarView = mockView(0, SCREEN_HEIGHT - NAV_SIZE, SCREEN_WIDTH,
213                         SCREEN_HEIGHT, new ColorDrawable(Color.BLACK), VISIBLE, emptyList());
214                 if (insetContentViews) {
215                     insetBottom = NAV_SIZE;
216                 }
217                 break;
218             case NAVBAR_LEFT:
219                 mNavigationBarView = mockView(0, 0, NAV_SIZE, SCREEN_HEIGHT,
220                         new ColorDrawable(Color.BLACK), VISIBLE, emptyList());
221                 if (insetContentViews) {
222                     insetLeft = NAV_SIZE;
223                 }
224                 break;
225             case NAVBAR_RIGHT:
226                 mNavigationBarView = mockView(SCREEN_WIDTH - NAV_SIZE, 0, SCREEN_WIDTH,
227                         SCREEN_HEIGHT, new ColorDrawable(Color.BLACK), VISIBLE, emptyList());
228                 if (insetContentViews) {
229                     insetRight = NAV_SIZE;
230                 }
231                 break;
232         }
233 
234         mContentMock = mockView(0, 0, SCREEN_WIDTH - insetLeft - insetRight,
235                 SCREEN_HEIGHT - insetTop - insetBottom, null, VISIBLE, emptyList());
236         mContentContainerMock = mockView(0, 0, SCREEN_WIDTH - insetLeft - insetRight,
237                 SCREEN_HEIGHT - insetTop - insetBottom, null, VISIBLE, asList(mContentMock));
238         mContentRootMock = mockView(insetLeft, insetTop, SCREEN_WIDTH - insetRight,
239                 SCREEN_HEIGHT - insetBottom, null, VISIBLE, asList(mContentContainerMock));
240 
241         mDecorViewMock = mockView(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, null, VISIBLE,
242                 asList(mContentRootMock, mStatusBarView, mNavigationBarView));
243     }
244 
setTranslucent(ViewGroup bar)245     private void setTranslucent(ViewGroup bar) {
246         bar.setBackground(new ColorDrawable(Color.TRANSPARENT));
247     }
248 
mockView(int left, int top, int right, int bottom, Drawable background, int visibility, List<ViewGroup> children)249     private ViewGroup mockView(int left, int top, int right, int bottom, Drawable background,
250             int visibility, List<ViewGroup> children) {
251         final ViewGroup v = new FrameLayout(InstrumentationRegistry.getTargetContext());
252 
253         v.layout(left, top, right, bottom);
254         v.setBackground(background);
255         v.setVisibility(visibility);
256 
257         for (ViewGroup c : children) {
258             v.addView(c);
259         }
260 
261         return v;
262     }
263 }