1/*
2 * Copyright (C) 2016 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 android.support.wear.widget;
18
19import static android.support.test.espresso.Espresso.onView;
20import static android.support.test.espresso.matcher.ViewMatchers.withId;
21import static android.support.wear.widget.util.MoreViewAssertions.approximateBottom;
22import static android.support.wear.widget.util.MoreViewAssertions.approximateTop;
23import static android.support.wear.widget.util.MoreViewAssertions.bottom;
24import static android.support.wear.widget.util.MoreViewAssertions.left;
25import static android.support.wear.widget.util.MoreViewAssertions.right;
26import static android.support.wear.widget.util.MoreViewAssertions.screenBottom;
27import static android.support.wear.widget.util.MoreViewAssertions.screenLeft;
28import static android.support.wear.widget.util.MoreViewAssertions.screenRight;
29import static android.support.wear.widget.util.MoreViewAssertions.screenTop;
30import static android.support.wear.widget.util.MoreViewAssertions.top;
31
32import static org.hamcrest.Matchers.closeTo;
33import static org.hamcrest.Matchers.equalTo;
34import static org.hamcrest.Matchers.is;
35
36import android.content.Intent;
37import android.support.test.InstrumentationRegistry;
38import android.support.test.filters.MediumTest;
39import android.support.test.rule.ActivityTestRule;
40import android.support.test.runner.AndroidJUnit4;
41import android.support.wear.test.R;
42import android.support.wear.widget.util.WakeLockRule;
43import android.util.DisplayMetrics;
44import android.view.View;
45
46import org.junit.Rule;
47import org.junit.Test;
48import org.junit.runner.RunWith;
49
50import java.util.HashMap;
51import java.util.Map;
52
53@MediumTest
54@RunWith(AndroidJUnit4.class)
55public class BoxInsetLayoutTest {
56    private static final float FACTOR = 0.146467f; //(1 - sqrt(2)/2)/2
57
58    @Rule
59    public final WakeLockRule mWakeLock = new WakeLockRule();
60
61    @Rule
62    public final ActivityTestRule<LayoutTestActivity> mActivityRule = new ActivityTestRule<>(
63            LayoutTestActivity.class, true, false);
64
65    @Test
66    public void testCase1() throws Throwable {
67        mActivityRule.launchActivity(new Intent().putExtra(LayoutTestActivity
68                .EXTRA_LAYOUT_RESOURCE_ID, R.layout.box_inset_layout_testcase_1));
69        DisplayMetrics dm = InstrumentationRegistry.getTargetContext().getResources()
70                .getDisplayMetrics();
71        int boxInset = (int) (FACTOR * Math.min(dm.widthPixels, dm.heightPixels));
72
73        int desiredPadding = 0;
74        if (mActivityRule.getActivity().getResources().getConfiguration().isScreenRound()) {
75            desiredPadding = boxInset;
76        }
77
78        ViewFetchingRunnable customRunnable = new ViewFetchingRunnable(){
79            @Override
80            public void run() {
81                View box = mActivityRule.getActivity().findViewById(R.id.box);
82                mIdViewMap.put(R.id.box, box);
83            }
84        };
85        mActivityRule.runOnUiThread(customRunnable);
86
87        View box = customRunnable.mIdViewMap.get(R.id.box);
88        // proxy for window location
89        View boxParent = (View) box.getParent();
90        int parentLeft = boxParent.getLeft();
91        int parentTop = boxParent.getTop();
92        int parentRight = boxParent.getLeft() + boxParent.getWidth();
93        int parentBottom = boxParent.getTop() + boxParent.getHeight();
94
95        // Child 1 is match_parent width and height
96        // layout_box=right|bottom
97        // Padding of boxInset should be added to the right and bottom sides only
98        onView(withId(R.id.child1))
99                .check(screenLeft(equalTo(parentLeft)))
100                .check(screenTop(equalTo(parentTop)))
101                .check(screenRight(equalTo(parentRight - desiredPadding)))
102                .check(screenBottom(equalTo(parentBottom - desiredPadding)));
103
104        // Content 1 is is width and height match_parent
105        // The bottom and right sides should be inset by boxInset pixels due to padding
106        // on the parent view
107        onView(withId(R.id.content1))
108                .check(screenLeft(equalTo(parentLeft)))
109                .check(screenTop(equalTo(parentTop)))
110                .check(screenRight(equalTo(parentRight - desiredPadding)))
111                .check(screenBottom(equalTo(parentBottom - desiredPadding)));
112    }
113
114    @Test
115    public void testCase2() throws Throwable {
116        mActivityRule.launchActivity(
117                new Intent().putExtra(LayoutTestActivity.EXTRA_LAYOUT_RESOURCE_ID,
118                        R.layout.box_inset_layout_testcase_2));
119        DisplayMetrics dm =
120                InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics();
121        int boxInset = (int) (FACTOR * Math.min(dm.widthPixels, dm.heightPixels));
122
123        int desiredPadding = 0;
124        if (mActivityRule.getActivity().getResources().getConfiguration().isScreenRound()) {
125            desiredPadding = boxInset;
126        }
127
128        ViewFetchingRunnable customRunnable = new ViewFetchingRunnable(){
129            @Override
130            public void run() {
131                View box = mActivityRule.getActivity().findViewById(R.id.box);
132                View child1 = mActivityRule.getActivity().findViewById(R.id.child1);
133                View child2 = mActivityRule.getActivity().findViewById(R.id.child2);
134                View child3 = mActivityRule.getActivity().findViewById(R.id.child3);
135                View child4 = mActivityRule.getActivity().findViewById(R.id.child4);
136                mIdViewMap.put(R.id.box, box);
137                mIdViewMap.put(R.id.child1, child1);
138                mIdViewMap.put(R.id.child2, child2);
139                mIdViewMap.put(R.id.child3, child3);
140                mIdViewMap.put(R.id.child4, child4);
141
142            }
143        };
144        mActivityRule.runOnUiThread(customRunnable);
145
146        View box = customRunnable.mIdViewMap.get(R.id.box);
147        View child1 = customRunnable.mIdViewMap.get(R.id.child1);
148        View child2 = customRunnable.mIdViewMap.get(R.id.child2);
149        View child3 = customRunnable.mIdViewMap.get(R.id.child3);
150        View child4 = customRunnable.mIdViewMap.get(R.id.child4);
151
152        // proxy for window location
153        View boxParent = (View) box.getParent();
154        int parentLeft = boxParent.getLeft();
155        int parentTop = boxParent.getTop();
156        int parentRight = boxParent.getLeft() + boxParent.getWidth();
157        int parentBottom = boxParent.getTop() + boxParent.getHeight();
158        int parentWidth = boxParent.getWidth();
159        int parentHeight = boxParent.getHeight();
160
161        // Child 1 is width match_parent, height=60dp, gravity top
162        // layout_box=all means it should have padding added to left, top and right
163        onView(withId(R.id.child1))
164                .check(screenLeft(is(equalTo(parentLeft + desiredPadding))))
165                .check(screenTop(is(equalTo(parentTop + desiredPadding))))
166                .check(screenRight(is(equalTo(parentRight - desiredPadding))))
167                .check(screenBottom(is(equalTo(parentTop + desiredPadding + child1.getHeight()))));
168
169        // Content 1 is width and height match_parent
170        // the left top and right edges should be inset by boxInset pixels, due to
171        // padding in the parent
172        onView(withId(R.id.content1))
173                .check(screenLeft(equalTo(parentLeft + desiredPadding)))
174                .check(screenTop(equalTo(parentTop + desiredPadding)))
175                .check(screenRight(equalTo(parentRight - desiredPadding)));
176
177        // Child 2 is width match_parent, height=60dp, gravity bottom
178        // layout_box=all means it should have padding added to left, bottom and right
179        onView(withId(R.id.child2))
180                .check(screenLeft(is(equalTo(parentLeft + desiredPadding))))
181                .check(screenTop(is(equalTo(parentBottom - desiredPadding - child2.getHeight()))))
182                .check(screenRight(is(equalTo(parentRight - desiredPadding))))
183                .check(screenBottom(is(equalTo(parentBottom - desiredPadding))));
184
185        // Content 2 is width and height match_parent
186        // the left bottom and right edges should be inset by boxInset pixels, due to
187        // padding in the parent
188        onView(withId(R.id.content2))
189                .check(screenLeft(equalTo(parentLeft + desiredPadding)))
190                .check(screenRight(equalTo(parentRight - desiredPadding)))
191                .check(screenBottom(equalTo(parentBottom - desiredPadding)));
192
193        // Child 3 is width wrap_content, height=20dp, gravity left|center_vertical.
194        // layout_box=all means it should have padding added to left
195        // marginLeft be ignored due to gravity and layout_box=all (screenLeft=0)
196        onView(withId(R.id.child3))
197                .check(screenLeft(is(equalTo(parentLeft + desiredPadding))))
198                .check(approximateTop(is(closeTo((parentHeight / 2 - child3.getHeight() / 2), 1))))
199                .check(screenRight(is(equalTo(parentLeft + desiredPadding + child3.getWidth()))))
200                .check(approximateBottom(is(
201                        closeTo((parentHeight / 2 + child3.getHeight() / 2), 1))));
202
203        // Content 3 width and height match_parent
204        // the left edge should be offset from the screen edge by boxInset pixels, due to left on
205        // the parent
206        onView(withId(R.id.content3)).check(screenLeft(equalTo(desiredPadding)));
207
208        // Child 4 is width wrap_content, height=20dp, gravity right|center_vertical.
209        // layout_box=all means it should have padding added to right
210        // it should have marginRight ignored due to gravity and layout_box=all (screenRight=max)
211        onView(withId(R.id.child4))
212                .check(screenLeft(is(parentWidth - desiredPadding - child4.getWidth())))
213                .check(approximateTop(is(closeTo((parentHeight / 2 - child3.getHeight() / 2), 1))))
214                .check(screenRight(is(equalTo(parentWidth - desiredPadding))))
215                .check(approximateBottom(is(
216                        closeTo((parentHeight / 2 + child4.getHeight() / 2), 1))));
217
218        // Content 4 width and height wrap_content
219        // the right edge should be offset from the screen edge by boxInset pixels, due to
220        // right on the parent
221        onView(withId(R.id.content4)).check(screenRight(equalTo(parentWidth - desiredPadding)));
222    }
223
224    @Test
225    public void testCase3() throws Throwable {
226        mActivityRule.launchActivity(
227                new Intent().putExtra(LayoutTestActivity.EXTRA_LAYOUT_RESOURCE_ID,
228                        R.layout.box_inset_layout_testcase_3));
229        DisplayMetrics dm =
230                InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics();
231        int boxInset = (int) (FACTOR * Math.min(dm.widthPixels, dm.heightPixels));
232
233        int desiredPadding = 0;
234        if (mActivityRule.getActivity().getResources().getConfiguration().isScreenRound()) {
235            desiredPadding = boxInset;
236        }
237
238        ViewFetchingRunnable customRunnable = new ViewFetchingRunnable(){
239            @Override
240            public void run() {
241                View box = mActivityRule.getActivity().findViewById(R.id.box);
242                View child1 = mActivityRule.getActivity().findViewById(R.id.child1);
243                View child2 = mActivityRule.getActivity().findViewById(R.id.child2);
244                View child3 = mActivityRule.getActivity().findViewById(R.id.child3);
245                View child4 = mActivityRule.getActivity().findViewById(R.id.child4);
246                mIdViewMap.put(R.id.box, box);
247                mIdViewMap.put(R.id.child1, child1);
248                mIdViewMap.put(R.id.child2, child2);
249                mIdViewMap.put(R.id.child3, child3);
250                mIdViewMap.put(R.id.child4, child4);
251            }
252        };
253        mActivityRule.runOnUiThread(customRunnable);
254
255        View box = customRunnable.mIdViewMap.get(R.id.box);
256        View child1 = customRunnable.mIdViewMap.get(R.id.child1);
257        View child2 = customRunnable.mIdViewMap.get(R.id.child2);
258        View child3 = customRunnable.mIdViewMap.get(R.id.child3);
259        View child4 = customRunnable.mIdViewMap.get(R.id.child4);
260        // proxy for window location
261        View boxParent = (View) box.getParent();
262        int parentLeft = boxParent.getLeft();
263        int parentTop = boxParent.getTop();
264        int parentBottom = boxParent.getTop() + boxParent.getHeight();
265        int parentWidth = boxParent.getWidth();
266
267        // Child 1 is width and height wrap_content
268        // gravity is top|left, position should be 0,0 on screen
269        onView(withId(R.id.child1))
270                .check(screenLeft(is(equalTo(parentLeft + desiredPadding))))
271                .check(screenTop(is(equalTo(parentTop + desiredPadding))))
272                .check(screenRight(is(equalTo(parentLeft + desiredPadding + child1.getWidth()))))
273                .check(screenBottom(is(equalTo(parentTop + desiredPadding + child1.getHeight()))));
274
275        // Content 1 is width and height wrap_content
276        // the left and top edges should be offset from the screen edges by boxInset pixels
277        onView(withId(R.id.content1))
278                .check(screenLeft(equalTo(parentLeft + desiredPadding)))
279                .check(screenTop(equalTo(parentTop + desiredPadding)));
280
281        // Child 2 is width and height wrap_content
282        // gravity is top|right, position should be 0,max on screen
283        onView(withId(R.id.child2))
284                .check(screenLeft(is(equalTo(parentWidth - desiredPadding - child2.getWidth()))))
285                .check(screenTop(is(equalTo(parentTop + desiredPadding))))
286                .check(screenRight(is(equalTo(parentWidth - desiredPadding))))
287                .check(screenBottom(is(equalTo(parentTop + desiredPadding + child2.getHeight()))));
288
289        // Content 2 is width and height wrap_content
290        // the top and right edges should be offset from the screen edges by boxInset pixels
291        onView(withId(R.id.content2))
292                .check(screenTop(equalTo(parentTop + desiredPadding)))
293                .check(screenRight(equalTo(parentWidth - desiredPadding)));
294
295        // Child 3 is width and height wrap_content
296        // gravity is bottom|right, position should be max,max on screen
297        onView(withId(R.id.child3))
298                .check(screenLeft(is(equalTo(parentWidth - desiredPadding - child3.getWidth()))))
299                .check(screenTop(is(
300                        equalTo(parentBottom - desiredPadding - child3.getHeight()))))
301                .check(screenRight(is(equalTo(parentWidth - desiredPadding))))
302                .check(screenBottom(is(equalTo(parentBottom - desiredPadding))));
303
304        // Content 3 is width and height wrap_content
305        // the right and bottom edges should be offset from the screen edges by boxInset pixels
306        onView(withId(R.id.content3))
307                .check(screenBottom(equalTo(parentBottom - desiredPadding)))
308                .check(screenRight(equalTo(parentWidth - desiredPadding)));
309
310        // Child 4 is width and height wrap_content
311        // gravity is bottom|left, position should be max,0 on screen
312        onView(withId(R.id.child4))
313                .check(screenLeft(is(equalTo(parentLeft + desiredPadding))))
314                .check(screenTop(is(equalTo(parentBottom - desiredPadding - child4.getHeight()))))
315                .check(screenRight(is(equalTo(parentLeft + desiredPadding + child4.getWidth()))))
316                .check(screenBottom(is(equalTo(parentBottom - desiredPadding))));
317
318        // Content 3 is width and height wrap_content
319        // the bottom and left edges should be offset from the screen edges by boxInset pixels
320        onView(withId(R.id.content4)).check(
321                screenBottom(equalTo(parentBottom - desiredPadding)))
322                .check(screenLeft(equalTo(parentLeft + desiredPadding)));
323    }
324
325    @Test
326    public void testCase4() throws Throwable {
327        mActivityRule.launchActivity(new Intent().putExtra(LayoutTestActivity
328                .EXTRA_LAYOUT_RESOURCE_ID, R.layout.box_inset_layout_testcase_4));
329        DisplayMetrics dm = InstrumentationRegistry.getTargetContext().getResources()
330                .getDisplayMetrics();
331        int boxInset = (int) (FACTOR * Math.min(dm.widthPixels, dm.heightPixels));
332
333        int desiredPadding = 0;
334        if (mActivityRule.getActivity().getResources().getConfiguration().isScreenRound()) {
335            desiredPadding = boxInset;
336        }
337
338        ViewFetchingRunnable customRunnable = new ViewFetchingRunnable(){
339            @Override
340            public void run() {
341                View container = mActivityRule.getActivity().findViewById(R.id.container);
342                View child1 = mActivityRule.getActivity().findViewById(R.id.child1);
343                mIdViewMap.put(R.id.container, container);
344                mIdViewMap.put(R.id.child1, child1);
345
346            }
347        };
348        mActivityRule.runOnUiThread(customRunnable);
349
350        View container = customRunnable.mIdViewMap.get(R.id.container);
351        View child1 = customRunnable.mIdViewMap.get(R.id.child1);
352        // Child 1 is match_parent width and wrap_content height
353        // layout_box=right|left
354        // Padding of boxInset should be added to the right and bottom sides only
355        onView(withId(R.id.child1)).check(left(equalTo(desiredPadding))).check(
356                top(equalTo(container.getTop()))).check(
357                right(equalTo(dm.widthPixels - desiredPadding))).check(
358                bottom(equalTo(container.getTop() + child1.getHeight())));
359    }
360
361    private abstract class ViewFetchingRunnable implements Runnable {
362        Map<Integer, View> mIdViewMap = new HashMap<>();
363    }
364}
365