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 static org.junit.Assert.assertEquals; 20 21import android.os.Build; 22import android.os.SystemClock; 23import android.support.design.test.R; 24import android.support.test.InstrumentationRegistry; 25import android.support.test.filters.SdkSuppress; 26import android.test.suitebuilder.annotation.MediumTest; 27import android.widget.ImageView; 28 29import org.junit.Test; 30 31@MediumTest 32public class AppBarWithCollapsingToolbarTest extends AppBarLayoutBaseTest { 33 @Test 34 public void testPinnedToolbar() { 35 configureContent(R.layout.design_appbar_toolbar_collapse_pin, 36 R.string.design_appbar_collapsing_toolbar_pin); 37 38 CollapsingToolbarLayout.LayoutParams toolbarLp = 39 (CollapsingToolbarLayout.LayoutParams) mToolbar.getLayoutParams(); 40 assertEquals(CollapsingToolbarLayout.LayoutParams.COLLAPSE_MODE_PIN, 41 toolbarLp.getCollapseMode()); 42 43 final int[] appbarOnScreenXY = new int[2]; 44 final int[] coordinatorLayoutOnScreenXY = new int[2]; 45 mAppBar.getLocationOnScreen(appbarOnScreenXY); 46 mCoordinatorLayout.getLocationOnScreen(coordinatorLayoutOnScreenXY); 47 48 final int originalAppbarTop = appbarOnScreenXY[1]; 49 final int originalAppbarBottom = appbarOnScreenXY[1] + mAppBar.getHeight(); 50 final int centerX = appbarOnScreenXY[0] + mAppBar.getWidth() / 2; 51 52 final int toolbarHeight = mToolbar.getHeight(); 53 final int appbarHeight = mAppBar.getHeight(); 54 final int longSwipeAmount = 3 * appbarHeight / 2; 55 final int reallyLongSwipeAmount = 2 * appbarHeight; 56 final int shortSwipeAmount = toolbarHeight; 57 58 assertAppBarElevation(0f); 59 60 // Perform a swipe-up gesture across the horizontal center of the screen. 61 performVerticalSwipeUpGesture( 62 R.id.coordinator_layout, 63 centerX, 64 originalAppbarBottom + longSwipeAmount / 2, 65 longSwipeAmount); 66 67 mAppBar.getLocationOnScreen(appbarOnScreenXY); 68 // At this point the app bar should be visually snapped below the system status bar. 69 // Allow for off-by-a-pixel margin of error. 70 assertEquals(originalAppbarTop + toolbarHeight + mAppBar.getTopInset(), 71 appbarOnScreenXY[1] + appbarHeight, 1); 72 73 // Perform another swipe-up gesture 74 performVerticalSwipeUpGesture( 75 R.id.coordinator_layout, 76 centerX, 77 appbarOnScreenXY[1] + appbarHeight + 5, 78 shortSwipeAmount); 79 80 mAppBar.getLocationOnScreen(appbarOnScreenXY); 81 // At this point the app bar should still be visually snapped below the system status bar 82 // as it is in the pinned mode. Allow for off-by-a-pixel margin of error. 83 assertEquals(originalAppbarTop + toolbarHeight + mAppBar.getTopInset(), 84 appbarOnScreenXY[1] + appbarHeight, 1); 85 assertAppBarElevation(mDefaultElevationValue); 86 87 // Perform a short swipe-down gesture across the horizontal center of the screen. 88 // Note that the swipe down is a bit longer than the swipe up to check that the app bar 89 // is not starting to expand too early. 90 performVerticalSwipeDownGesture( 91 R.id.coordinator_layout, 92 centerX, 93 originalAppbarBottom - shortSwipeAmount, 94 3 * shortSwipeAmount / 2); 95 96 mAppBar.getLocationOnScreen(appbarOnScreenXY); 97 // At this point the app bar should still be visually snapped below the system status bar 98 // as it is in the pinned mode and we haven't fully swiped down the content below the 99 // app bar. Allow for off-by-a-pixel margin of error. 100 assertEquals(originalAppbarTop + toolbarHeight + mAppBar.getTopInset(), 101 appbarOnScreenXY[1] + appbarHeight, 1); 102 assertAppBarElevation(mDefaultElevationValue); 103 104 // Perform another swipe-down gesture across the horizontal center of the screen. 105 performVerticalSwipeDownGesture( 106 R.id.coordinator_layout, 107 centerX, 108 originalAppbarBottom, 109 reallyLongSwipeAmount); 110 111 mAppBar.getLocationOnScreen(appbarOnScreenXY); 112 // At this point the app bar should be in its original position. 113 // Allow for off-by-a-pixel margin of error. 114 assertEquals(originalAppbarTop, appbarOnScreenXY[1], 1); 115 assertEquals(originalAppbarBottom, appbarOnScreenXY[1] + appbarHeight, 1); 116 assertAppBarElevation(0f); 117 118 // Perform yet another swipe-down gesture across the horizontal center of the screen. 119 performVerticalSwipeDownGesture( 120 R.id.coordinator_layout, 121 centerX, 122 originalAppbarBottom, 123 longSwipeAmount); 124 125 mAppBar.getLocationOnScreen(appbarOnScreenXY); 126 // At this point the app bar should still be in its original position. 127 // Allow for off-by-a-pixel margin of error. 128 assertEquals(originalAppbarTop, appbarOnScreenXY[1], 1); 129 assertEquals(originalAppbarBottom, appbarOnScreenXY[1] + appbarHeight, 1); 130 assertAppBarElevation(0f); 131 } 132 133 @Test 134 public void testScrollingToolbar() { 135 configureContent(R.layout.design_appbar_toolbar_collapse_scroll, 136 R.string.design_appbar_collapsing_toolbar_scroll); 137 138 CollapsingToolbarLayout.LayoutParams toolbarLp = 139 (CollapsingToolbarLayout.LayoutParams) mToolbar.getLayoutParams(); 140 assertEquals(CollapsingToolbarLayout.LayoutParams.COLLAPSE_MODE_PIN, 141 toolbarLp.getCollapseMode()); 142 143 final int[] appbarOnScreenXY = new int[2]; 144 final int[] coordinatorLayoutOnScreenXY = new int[2]; 145 mAppBar.getLocationOnScreen(appbarOnScreenXY); 146 mCoordinatorLayout.getLocationOnScreen(coordinatorLayoutOnScreenXY); 147 148 final int topInset = mAppBar.getTopInset(); 149 150 final int originalAppbarTop = appbarOnScreenXY[1]; 151 final int originalAppbarBottom = appbarOnScreenXY[1] + mAppBar.getHeight(); 152 final int centerX = appbarOnScreenXY[0] + mAppBar.getWidth() / 2; 153 154 final int toolbarHeight = mToolbar.getHeight(); 155 final int appbarHeight = mAppBar.getHeight(); 156 final int longSwipeAmount = 3 * appbarHeight / 2; 157 final int reallyLongSwipeAmount = 2 * appbarHeight; 158 final int shortSwipeAmount = toolbarHeight; 159 160 assertAppBarElevation(0f); 161 162 // Perform a swipe-up gesture across the horizontal center of the screen, starting from 163 // just below the AppBarLayout 164 performVerticalSwipeUpGesture( 165 R.id.coordinator_layout, 166 centerX, 167 originalAppbarBottom + 20, 168 longSwipeAmount); 169 170 mAppBar.getLocationOnScreen(appbarOnScreenXY); 171 // At this point the app bar should not be visually "present" on the screen, with its bottom 172 // edge aligned with the bottom of system status bar. If we're running on a device which 173 // supports a translucent status bar, we need to take the status bar height into account. 174 // Allow for off-by-a-pixel margin of error. 175 assertEquals(originalAppbarTop, appbarOnScreenXY[1] + appbarHeight - topInset, 1); 176 assertAppBarElevation(0f); 177 178 // Perform another swipe-up gesture 179 performVerticalSwipeUpGesture( 180 R.id.coordinator_layout, 181 centerX, 182 originalAppbarBottom, 183 shortSwipeAmount); 184 185 mAppBar.getLocationOnScreen(appbarOnScreenXY); 186 // At this point the app bar should still be off the screen. Allow for off-by-a-pixel 187 // margin of error. 188 assertEquals(originalAppbarTop, appbarOnScreenXY[1] + appbarHeight - topInset, 1); 189 assertAppBarElevation(0f); 190 191 // Perform a short swipe-down gesture across the horizontal center of the screen. 192 // Note that the swipe down is a bit longer than the swipe up to fully bring down 193 // the scrolled-away toolbar 194 performVerticalSwipeDownGesture( 195 R.id.coordinator_layout, 196 centerX, 197 originalAppbarBottom, 198 3 * shortSwipeAmount / 2); 199 200 mAppBar.getLocationOnScreen(appbarOnScreenXY); 201 // At this point the app bar should be visually snapped below the system status bar as it 202 // in scrolling mode and we've swiped down, but not fully. Allow for off-by-a-pixel 203 // margin of error. 204 assertEquals(originalAppbarTop + toolbarHeight + topInset, 205 appbarOnScreenXY[1] + appbarHeight, 1); 206 assertAppBarElevation(mDefaultElevationValue); 207 208 // Perform another swipe-down gesture across the horizontal center of the screen. 209 performVerticalSwipeDownGesture( 210 R.id.coordinator_layout, 211 centerX, 212 originalAppbarBottom, 213 reallyLongSwipeAmount); 214 215 mAppBar.getLocationOnScreen(appbarOnScreenXY); 216 // At this point the app bar should be in its original position. 217 // Allow for off-by-a-pixel margin of error. 218 assertEquals(originalAppbarTop, appbarOnScreenXY[1]); 219 assertEquals(originalAppbarBottom, appbarOnScreenXY[1] + appbarHeight); 220 assertAppBarElevation(0f); 221 222 // Perform yet another swipe-down gesture across the horizontal center of the screen. 223 performVerticalSwipeDownGesture( 224 R.id.coordinator_layout, 225 centerX, 226 originalAppbarBottom, 227 longSwipeAmount); 228 229 mAppBar.getLocationOnScreen(appbarOnScreenXY); 230 // At this point the app bar should still be in its original position. 231 // Allow for off-by-a-pixel margin of error. 232 assertEquals(originalAppbarTop, appbarOnScreenXY[1], 1); 233 assertEquals(originalAppbarBottom, appbarOnScreenXY[1] + appbarHeight, 1); 234 assertAppBarElevation(0f); 235 } 236 237 @Test 238 public void testPinnedToolbarAndAnchoredFab() throws Throwable { 239 configureContent(R.layout.design_appbar_toolbar_collapse_pin_with_fab, 240 R.string.design_appbar_collapsing_toolbar_pin_fab); 241 242 CollapsingToolbarLayout.LayoutParams toolbarLp = 243 (CollapsingToolbarLayout.LayoutParams) mToolbar.getLayoutParams(); 244 assertEquals(CollapsingToolbarLayout.LayoutParams.COLLAPSE_MODE_PIN, 245 toolbarLp.getCollapseMode()); 246 247 final FloatingActionButton fab = 248 (FloatingActionButton) mCoordinatorLayout.findViewById(R.id.fab); 249 250 final int[] appbarOnScreenXY = new int[2]; 251 final int[] coordinatorLayoutOnScreenXY = new int[2]; 252 mAppBar.getLocationOnScreen(appbarOnScreenXY); 253 mCoordinatorLayout.getLocationOnScreen(coordinatorLayoutOnScreenXY); 254 255 final int originalAppbarBottom = appbarOnScreenXY[1] + mAppBar.getHeight(); 256 final int centerX = appbarOnScreenXY[0] + mAppBar.getWidth() / 2; 257 258 final int appbarHeight = mAppBar.getHeight(); 259 final int longSwipeAmount = 3 * appbarHeight / 2; 260 261 // Perform a swipe-up gesture across the horizontal center of the screen. 262 performVerticalSwipeUpGesture( 263 R.id.coordinator_layout, 264 centerX, 265 originalAppbarBottom + longSwipeAmount / 2, 266 longSwipeAmount); 267 268 // Since we the visibility change listener path is only exposed via direct calls to 269 // FloatingActionButton.show and not the internal path that FAB's behavior is using, 270 // this test needs to be tied to the internal implementation details of running animation 271 // that scales the FAB to 0/0 scales and interpolates its alpha to 0. Since that animation 272 // starts running partway through our swipe gesture and may complete a bit later then 273 // the swipe gesture, sleep for a bit to catch the "final" state of the FAB. 274 SystemClock.sleep(200); 275 276 // At this point the FAB should be scaled to 0/0 and set at alpha 0. Since the relevant 277 // getter methods are only available on v11+, wrap the asserts with build version check. 278 if (Build.VERSION.SDK_INT >= 11) { 279 assertEquals(0.0f, fab.getScaleX(), 0.0f); 280 assertEquals(0.0f, fab.getScaleY(), 0.0f); 281 assertEquals(0.0f, fab.getAlpha(), 0.0f); 282 } 283 284 // Perform a swipe-down gesture across the horizontal center of the screen. 285 performVerticalSwipeDownGesture( 286 R.id.coordinator_layout, 287 centerX, 288 originalAppbarBottom, 289 longSwipeAmount); 290 291 // Same as for swipe-up gesture - sleep for a bit to catch the "final" visible state of 292 // the FAB. 293 SystemClock.sleep(200); 294 295 // At this point the FAB should be scaled back to its original size and be at full opacity. 296 if (Build.VERSION.SDK_INT >= 11) { 297 assertEquals(1.0f, fab.getScaleX(), 0.0f); 298 assertEquals(1.0f, fab.getScaleY(), 0.0f); 299 assertEquals(1.0f, fab.getAlpha(), 0.0f); 300 } 301 } 302 303 @Test 304 public void testPinnedToolbarAndParallaxImage() { 305 configureContent(R.layout.design_appbar_toolbar_collapse_with_image, 306 R.string.design_appbar_collapsing_toolbar_with_image); 307 308 final ImageView parallaxImageView = 309 (ImageView) mCoordinatorLayout.findViewById(R.id.app_bar_image); 310 311 // We have not set any padding on the ImageView, so ensure that none is set via 312 // window insets handling 313 assertEquals(0, parallaxImageView.getPaddingLeft()); 314 assertEquals(0, parallaxImageView.getPaddingTop()); 315 assertEquals(0, parallaxImageView.getPaddingRight()); 316 assertEquals(0, parallaxImageView.getPaddingBottom()); 317 318 CollapsingToolbarLayout.LayoutParams parallaxImageViewLp = 319 (CollapsingToolbarLayout.LayoutParams) parallaxImageView.getLayoutParams(); 320 assertEquals(CollapsingToolbarLayout.LayoutParams.COLLAPSE_MODE_PARALLAX, 321 parallaxImageViewLp.getCollapseMode()); 322 323 final float parallaxMultiplier = parallaxImageViewLp.getParallaxMultiplier(); 324 325 final int[] appbarOnScreenXY = new int[2]; 326 final int[] parallaxImageOnScreenXY = new int[2]; 327 final int appbarHeight = mAppBar.getHeight(); 328 final int toolbarHeight = mToolbar.getHeight(); 329 final int parallaxImageHeight = parallaxImageView.getHeight(); 330 331 mAppBar.getLocationOnScreen(appbarOnScreenXY); 332 parallaxImageView.getLocationOnScreen(parallaxImageOnScreenXY); 333 334 final int originalAppbarTop = appbarOnScreenXY[1]; 335 final int originalAppbarBottom = appbarOnScreenXY[1] + mAppBar.getHeight(); 336 final int originalParallaxImageTop = parallaxImageOnScreenXY[1]; 337 final int centerX = appbarOnScreenXY[0] + mAppBar.getWidth() / 2; 338 339 // Test that at the beginning our image is top-aligned with the app bar 340 assertEquals(appbarOnScreenXY[1], parallaxImageOnScreenXY[1]); 341 342 // Swipe up by the toolbar's height 343 performVerticalSwipeUpGesture( 344 R.id.coordinator_layout, 345 centerX, 346 originalAppbarBottom, 347 toolbarHeight); 348 349 // Test that the top edge of the image (in the screen coordinates) has "moved" by half 350 // the amount that the top edge of the app bar (in the screen coordinates) has. 351 mAppBar.getLocationOnScreen(appbarOnScreenXY); 352 parallaxImageView.getLocationOnScreen(parallaxImageOnScreenXY); 353 assertEquals(parallaxMultiplier * (appbarOnScreenXY[1] - originalAppbarTop), 354 parallaxImageOnScreenXY[1] - originalParallaxImageTop, 1); 355 356 // Swipe up by another toolbar's height 357 performVerticalSwipeUpGesture( 358 R.id.coordinator_layout, 359 centerX, 360 originalAppbarBottom, 361 toolbarHeight); 362 363 // Test that the top edge of the image (in the screen coordinates) has "moved" by half 364 // the amount that the top edge of the app bar (in the screen coordinates) has. 365 mAppBar.getLocationOnScreen(appbarOnScreenXY); 366 parallaxImageView.getLocationOnScreen(parallaxImageOnScreenXY); 367 assertEquals(parallaxMultiplier * (appbarOnScreenXY[1] - originalAppbarTop), 368 parallaxImageOnScreenXY[1] - originalParallaxImageTop, 1); 369 370 // Swipe down by a different value (150% of the toolbar's height) to test parallax going the 371 // other way 372 performVerticalSwipeDownGesture( 373 R.id.coordinator_layout, 374 centerX, 375 originalAppbarBottom, 376 3 * toolbarHeight / 2); 377 378 // Test that the top edge of the image (in the screen coordinates) has "moved" by half 379 // the amount that the top edge of the app bar (in the screen coordinates) has. 380 mAppBar.getLocationOnScreen(appbarOnScreenXY); 381 parallaxImageView.getLocationOnScreen(parallaxImageOnScreenXY); 382 assertEquals(parallaxMultiplier * (appbarOnScreenXY[1] - originalAppbarTop), 383 parallaxImageOnScreenXY[1] - originalParallaxImageTop, 1); 384 } 385 386 @Test 387 public void testAddViewWithDefaultLayoutParams() { 388 configureContent(R.layout.design_appbar_toolbar_collapse_pin, 389 R.string.design_appbar_collapsing_toolbar_pin); 390 391 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 392 @Override 393 public void run() { 394 ImageView view = new ImageView(mCollapsingToolbar.getContext()); 395 mCollapsingToolbar.addView(view); 396 } 397 }); 398 399 } 400 401 /** 402 * This test only runs on API 11+ since FrameLayout (which CollapsingToolbarLayout 403 * inherits from) has an issue with measuring children with margins when run on earlier 404 * versions of the platform. 405 */ 406 @Test 407 @SdkSuppress(minSdkVersion = 11) 408 public void testPinnedToolbarWithMargins() { 409 configureContent(R.layout.design_appbar_toolbar_collapse_pin_margins, 410 R.string.design_appbar_collapsing_toolbar_pin_margins); 411 412 CollapsingToolbarLayout.LayoutParams toolbarLp = 413 (CollapsingToolbarLayout.LayoutParams) mToolbar.getLayoutParams(); 414 assertEquals(CollapsingToolbarLayout.LayoutParams.COLLAPSE_MODE_PIN, 415 toolbarLp.getCollapseMode()); 416 417 final int[] appbarOnScreenXY = new int[2]; 418 final int[] toolbarOnScreenXY = new int[2]; 419 mAppBar.getLocationOnScreen(appbarOnScreenXY); 420 mToolbar.getLocationOnScreen(toolbarOnScreenXY); 421 422 final int originalAppbarTop = appbarOnScreenXY[1]; 423 final int originalAppbarBottom = originalAppbarTop + mAppBar.getHeight(); 424 final int centerX = appbarOnScreenXY[0] + mAppBar.getWidth() / 2; 425 426 final int toolbarHeight = mToolbar.getHeight(); 427 final int toolbarVerticalMargins = toolbarLp.topMargin + toolbarLp.bottomMargin; 428 final int appbarHeight = mAppBar.getHeight(); 429 430 // Perform a swipe-up gesture across the horizontal center of the screen. 431 int swipeAmount = appbarHeight - toolbarHeight - toolbarVerticalMargins; 432 performVerticalSwipeUpGesture( 433 R.id.coordinator_layout, 434 centerX, 435 originalAppbarBottom + (3 * swipeAmount / 2), 436 swipeAmount); 437 438 mAppBar.getLocationOnScreen(appbarOnScreenXY); 439 mToolbar.getLocationOnScreen(toolbarOnScreenXY); 440 // At this point the toolbar should be visually pinned to the bottom of the appbar layout, 441 // observing it's margins and top inset 442 // The toolbar should still be visually pinned to the bottom of the appbar layout 443 assertEquals(originalAppbarTop + mAppBar.getTopInset(), 444 toolbarOnScreenXY[1] - toolbarLp.topMargin, 1); 445 446 // Swipe up again, this time just 50% of the margin size 447 swipeAmount = toolbarVerticalMargins / 2; 448 performVerticalSwipeUpGesture( 449 R.id.coordinator_layout, 450 centerX, 451 originalAppbarBottom + (3 * swipeAmount / 2), 452 swipeAmount); 453 454 mAppBar.getLocationOnScreen(appbarOnScreenXY); 455 mToolbar.getLocationOnScreen(toolbarOnScreenXY); 456 457 // The toolbar should still be visually pinned to the bottom of the appbar layout 458 assertEquals(appbarOnScreenXY[1] + appbarHeight, 459 toolbarOnScreenXY[1] + toolbarHeight + toolbarLp.bottomMargin, 1); 460 } 461} 462