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.design.widget;
18
19import android.os.Build;
20import android.support.design.custom.TestFloatingBehavior;
21import android.support.design.test.R;
22import android.support.design.testutils.SnackbarUtils;
23import android.support.test.espresso.UiController;
24import android.support.test.espresso.ViewAction;
25import android.support.v7.widget.AppCompatTextView;
26import android.test.suitebuilder.annotation.MediumTest;
27import android.view.View;
28import android.view.ViewGroup;
29import org.hamcrest.Matcher;
30import org.junit.After;
31import org.junit.Test;
32
33import static android.support.design.widget.DesignViewActions.setVisibility;
34import static android.support.test.espresso.Espresso.onView;
35import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
36import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
37import static android.support.test.espresso.matcher.ViewMatchers.withId;
38import static org.junit.Assert.assertTrue;
39import static org.mockito.Mockito.mock;
40
41@MediumTest
42public class CoordinatorSnackbarWithFabTest extends BaseDynamicCoordinatorLayoutTest {
43    private static final String MESSAGE_TEXT = "Test Message";
44    private static final String ACTION_TEXT = "Action";
45
46    private Snackbar mSnackbar;
47
48    @After
49    public void teardown() {
50        // Dismiss the snackbar to get back to clean state for the next test
51        if (mSnackbar != null) {
52            SnackbarUtils.dismissSnackbarAndWaitUntilFullyDismissed(mSnackbar);
53        }
54    }
55
56    /**
57     * Returns the location of our snackbar on the screen.
58     */
59    private static int[] getSnackbarLocationOnScreen() {
60        final int[] location = new int[2];
61        onView(isAssignableFrom(Snackbar.SnackbarLayout.class)).perform(new ViewAction() {
62            @Override
63            public Matcher<View> getConstraints() {
64                return isEnabled();
65            }
66
67            @Override
68            public String getDescription() {
69                return "Snackbar matcher";
70            }
71
72            @Override
73            public void perform(UiController uiController, View view) {
74                view.getLocationOnScreen(location);
75            }
76        });
77        return location;
78    }
79
80    /**
81     * Helper method that verifies that the passed view is above the snackbar in the activity
82     * window.
83     */
84    private static void verifySnackbarViewStacking(View view, int extraBottomMargin) {
85        if (Build.VERSION.SDK_INT >= 11) {
86            // Get location of snackbar in window
87            final int[] snackbarOnScreenXY = getSnackbarLocationOnScreen();
88            // Get location of passed view in window
89            final int[] viewOnScreenXY = new int[2];
90            view.getLocationOnScreen(viewOnScreenXY);
91
92            // Compute the bottom visible edge of the view
93            int viewBottom = viewOnScreenXY[1] + view.getHeight() - extraBottomMargin;
94            int snackbarTop = snackbarOnScreenXY[1];
95            // and verify that our view is above the snackbar
96            assertTrue(viewBottom <= snackbarTop);
97        }
98    }
99
100    @Test
101    public void testBuiltInSliding() {
102        onView(withId(R.id.coordinator_stub)).perform(
103                inflateViewStub(R.layout.design_snackbar_with_fab));
104
105        // Create and show a snackbar
106        mSnackbar = Snackbar.make(mCoordinatorLayout, MESSAGE_TEXT, Snackbar.LENGTH_INDEFINITE)
107                .setAction(ACTION_TEXT, mock(View.OnClickListener.class));
108        SnackbarUtils.showSnackbarAndWaitUntilFullyShown(mSnackbar);
109
110        // Take into account bottom padding and bottom margin to account for how drop shadow is
111        // emulated on pre-Lollipop devices
112        final FloatingActionButton fab =
113                (FloatingActionButton) mCoordinatorLayout.findViewById(R.id.fab);
114        verifySnackbarViewStacking(fab, fab.getPaddingBottom()
115                - ((ViewGroup.MarginLayoutParams) fab.getLayoutParams()).bottomMargin);
116    }
117
118    @Test
119    public void testBuiltInSlidingFromHiddenFab() {
120        onView(withId(R.id.coordinator_stub)).perform(
121                inflateViewStub(R.layout.design_snackbar_with_fab));
122        onView(withId(R.id.fab)).perform(setVisibility(View.GONE));
123
124        // Create and show a snackbar
125        mSnackbar = Snackbar.make(mCoordinatorLayout, MESSAGE_TEXT, Snackbar.LENGTH_INDEFINITE)
126                .setAction(ACTION_TEXT, mock(View.OnClickListener.class));
127        SnackbarUtils.showSnackbarAndWaitUntilFullyShown(mSnackbar);
128
129        // Take into account bottom padding and bottom margin to account for how drop shadow is
130        // emulated on pre-Lollipop devices
131        onView(withId(R.id.fab)).perform(setVisibility(View.VISIBLE));
132        final FloatingActionButton fab =
133                (FloatingActionButton) mCoordinatorLayout.findViewById(R.id.fab);
134        verifySnackbarViewStacking(fab, fab.getPaddingBottom()
135                - ((ViewGroup.MarginLayoutParams) fab.getLayoutParams()).bottomMargin);
136    }
137
138    @Test
139    public void testBehaviorBasedSlidingFromLayoutAttribute() {
140        // Use a layout in which an AppCompatTextView child has Behavior object configured via
141        // layout_behavior XML attribute
142        onView(withId(R.id.coordinator_stub)).perform(
143                inflateViewStub(R.layout.design_snackbar_behavior_layout_attr));
144
145        // Create and show a snackbar
146        mSnackbar = Snackbar.make(mCoordinatorLayout, MESSAGE_TEXT, Snackbar.LENGTH_INDEFINITE)
147                .setAction(ACTION_TEXT, mock(View.OnClickListener.class));
148        SnackbarUtils.showSnackbarAndWaitUntilFullyShown(mSnackbar);
149
150        final AppCompatTextView textView =
151                (AppCompatTextView) mCoordinatorLayout.findViewById(R.id.text);
152        verifySnackbarViewStacking(textView, 0);
153    }
154
155    @Test
156    public void testBehaviorBasedSlidingFromClassAnnotation() {
157        // Use a layout in which a custom child view has Behavior object configured via
158        // annotation on the class that extends AppCompatTextView
159        onView(withId(R.id.coordinator_stub)).perform(
160                inflateViewStub(R.layout.design_snackbar_behavior_annotation));
161
162        // Create and show a snackbar
163        mSnackbar = Snackbar.make(mCoordinatorLayout, MESSAGE_TEXT, Snackbar.LENGTH_INDEFINITE)
164                .setAction(ACTION_TEXT, mock(View.OnClickListener.class));
165        SnackbarUtils.showSnackbarAndWaitUntilFullyShown(mSnackbar);
166
167        final AppCompatTextView textView =
168                (AppCompatTextView) mCoordinatorLayout.findViewById(R.id.text);
169        verifySnackbarViewStacking(textView, 0);
170    }
171
172    @Test
173    public void testBehaviorBasedSlidingFromRuntimeApiCall() {
174        // Use a layout in which an AppCompatTextView child doesn't have any configured Behavior
175        onView(withId(R.id.coordinator_stub)).perform(
176                inflateViewStub(R.layout.design_snackbar_behavior_runtime));
177
178        // and configure that Behavior at runtime by setting it on its LayoutParams
179        final AppCompatTextView textView =
180                (AppCompatTextView) mCoordinatorLayout.findViewById(R.id.text);
181        final CoordinatorLayout.LayoutParams textViewLp =
182                (CoordinatorLayout.LayoutParams) textView.getLayoutParams();
183        textViewLp.setBehavior(new TestFloatingBehavior());
184
185        // Create and show a snackbar
186        mSnackbar = Snackbar.make(mCoordinatorLayout, MESSAGE_TEXT, Snackbar.LENGTH_INDEFINITE)
187                .setAction(ACTION_TEXT, mock(View.OnClickListener.class));
188        SnackbarUtils.showSnackbarAndWaitUntilFullyShown(mSnackbar);
189
190        verifySnackbarViewStacking(textView, 0);
191    }
192}
193