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