1 /* 2 * Copyright (C) 2021 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 android.server.wm; 18 19 import static android.server.wm.ComponentNameUtils.getWindowName; 20 import static android.view.Display.DEFAULT_DISPLAY; 21 import static android.view.WindowInsets.Type.systemBars; 22 23 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 24 25 import static org.junit.Assert.assertTrue; 26 import static org.junit.Assume.assumeTrue; 27 import static org.mockito.Mockito.spy; 28 import static org.mockito.Mockito.timeout; 29 30 import android.app.Activity; 31 import android.content.ComponentName; 32 import android.graphics.Bitmap; 33 import android.graphics.Color; 34 import android.graphics.Rect; 35 import android.os.Bundle; 36 import android.platform.test.annotations.Presubmit; 37 import android.provider.Settings; 38 import android.server.wm.cts.R; 39 import android.server.wm.settings.SettingsSession; 40 import android.view.View; 41 import android.view.WindowManager; 42 43 import androidx.test.rule.ActivityTestRule; 44 import androidx.test.uiautomator.UiDevice; 45 46 import com.android.compatibility.common.util.ApiTest; 47 import com.android.compatibility.common.util.ColorUtils; 48 import com.android.compatibility.common.util.PollingCheck; 49 50 import org.junit.Before; 51 import org.junit.Rule; 52 import org.junit.Test; 53 import org.junit.rules.RuleChain; 54 import org.junit.rules.TestRule; 55 import org.mockito.Mockito; 56 57 import java.util.function.Consumer; 58 59 @Presubmit 60 public class BlurTests extends WindowManagerTestBase { 61 private static final int BACKGROUND_BLUR_PX = 80; 62 private static final int BLUR_BEHIND_PX = 40; 63 private static final int NO_BLUR_BACKGROUND_COLOR = 0xFF550055; 64 private static final int BROADCAST_WAIT_TIMEOUT = 300; 65 66 private Rect mBackgroundActivityBounds; 67 68 private final DumpOnFailure mDumpOnFailure = new DumpOnFailure(); 69 70 private final TestRule mEnableBlurRule = SettingsSession.overrideForTest( 71 Settings.Global.getUriFor(Settings.Global.DISABLE_WINDOW_BLURS), 72 Settings.Global::getInt, 73 Settings.Global::putInt, 74 0); 75 76 private final ActivityTestRule<BackgroundActivity> mBackgroundActivity = 77 new ActivityTestRule<>(BackgroundActivity.class); 78 79 @Rule 80 public final TestRule methodRules = RuleChain.outerRule(mDumpOnFailure) 81 .around(mEnableBlurRule) 82 .around(mBackgroundActivity); 83 84 @Before setUp()85 public void setUp() { 86 assumeTrue(supportsBlur()); 87 ComponentName cn = mBackgroundActivity.getActivity().getComponentName(); 88 waitAndAssertResumedActivity(cn, cn + " must be resumed"); 89 mBackgroundActivity.getActivity().waitAndAssertWindowFocusState(true); 90 91 // Use the background activity's bounds when taking the device screenshot. 92 // This is needed for multi-screen devices (foldables) where 93 // the launched activity covers just one screen 94 WindowManagerState.WindowState windowState = mWmState.getWindowState(cn); 95 WindowManagerState.Activity act = mWmState.getActivity(cn); 96 mBackgroundActivityBounds = act.getBounds(); 97 insetGivenFrame(windowState, WindowManagerState.InsetsSource::isCaptionBar, 98 mBackgroundActivityBounds); 99 100 // Wait for the first frame *after* the splash screen is removed to take screenshots. 101 // We don't currently have a definite event / callback for this. 102 mWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY); 103 waitForActivityIdle(mBackgroundActivity.getActivity()); 104 105 // Basic checks common to all tests 106 verifyOnlyBackgroundImageVisible(); 107 assertTrue(mContext.getSystemService(WindowManager.class).isCrossWindowBlurEnabled()); 108 } 109 110 @Test 111 @ApiTest(apis = {"android.view.Window#setBackgroundBlurRadius(int)"}) testBackgroundBlurSimple()112 public void testBackgroundBlurSimple() { 113 final BlurActivity blurActivity = startTestActivity(BlurActivity.class); 114 getInstrumentation().runOnMainSync(() -> { 115 blurActivity.setBackgroundBlurRadius(BACKGROUND_BLUR_PX); 116 }); 117 118 waitForActivityIdle(blurActivity); 119 120 final Rect windowFrame = getFloatingWindowFrame(blurActivity); 121 assertBackgroundBlur(takeScreenshotForBounds(mBackgroundActivityBounds), windowFrame); 122 } 123 124 @Test 125 @ApiTest(apis = {"android.view.WindowManager.LayoutParams#setBlurBehindRadius", 126 "android.R.styleable#Window_windowBlurBehindEnabled"}) testBlurBehindSimple()127 public void testBlurBehindSimple() throws Exception { 128 final BlurActivity blurActivity = startTestActivity(BlurActivity.class); 129 getInstrumentation().runOnMainSync(() -> { 130 blurActivity.setBlurBehindRadius(BLUR_BEHIND_PX); 131 }); 132 waitForActivityIdle(blurActivity); 133 final Rect windowFrame = getFloatingWindowFrame(blurActivity); 134 135 Bitmap screenshot = takeScreenshotForBounds(mBackgroundActivityBounds); 136 assertBlurBehind(screenshot, windowFrame); 137 assertNoBackgroundBlur(screenshot, windowFrame); 138 139 getInstrumentation().runOnMainSync(() -> { 140 blurActivity.setBlurBehindRadius(0); 141 }); 142 waitForActivityIdle(blurActivity); 143 144 screenshot = takeScreenshotForBounds(mBackgroundActivityBounds); 145 assertNoBlurBehind(screenshot, windowFrame); 146 assertNoBackgroundBlur(screenshot, windowFrame); 147 } 148 149 @Test 150 @ApiTest(apis = {"android.view.Window#setBackgroundBlurRadius"}) testNoBackgroundBlurWhenBlurDisabled()151 public void testNoBackgroundBlurWhenBlurDisabled() { 152 setAndAssertForceBlurDisabled(true); 153 final BlurActivity blurActivity = startTestActivity(BlurActivity.class); 154 getInstrumentation().runOnMainSync(() -> { 155 blurActivity.setBackgroundBlurRadius(BACKGROUND_BLUR_PX); 156 blurActivity.setBackgroundColor(Color.TRANSPARENT); 157 }); 158 waitForActivityIdle(blurActivity); 159 160 verifyOnlyBackgroundImageVisible(); 161 162 setAndAssertForceBlurDisabled(false, blurActivity.mBlurEnabledListener); 163 waitForActivityIdle(blurActivity); 164 165 final Rect windowFrame = getFloatingWindowFrame(blurActivity); 166 assertBackgroundBlur(takeScreenshotForBounds(mBackgroundActivityBounds), windowFrame); 167 } 168 169 @Test 170 @ApiTest(apis = {"android.view.Window#setBackgroundBlurRadius"}) testNoBackgroundBlurForNonTranslucentWindow()171 public void testNoBackgroundBlurForNonTranslucentWindow() { 172 final BlurActivity blurActivity = startTestActivity(BadBlurActivity.class); 173 getInstrumentation().runOnMainSync(() -> { 174 blurActivity.setBackgroundBlurRadius(BACKGROUND_BLUR_PX); 175 blurActivity.setBackgroundColor(Color.TRANSPARENT); 176 }); 177 waitForActivityIdle(blurActivity); 178 179 verifyOnlyBackgroundImageVisible(); 180 } 181 182 @Test 183 @ApiTest(apis = {"android.view.WindowManager.LayoutParams#setBlurBehindRadius", 184 "android.R.styleable#Window_windowBlurBehindEnabled"}) testNoBlurBehindWhenBlurDisabled()185 public void testNoBlurBehindWhenBlurDisabled() { 186 setAndAssertForceBlurDisabled(true); 187 final BlurActivity blurActivity = startTestActivity(BlurActivity.class); 188 getInstrumentation().runOnMainSync(() -> { 189 blurActivity.setBlurBehindRadius(BLUR_BEHIND_PX); 190 blurActivity.setBackgroundColor(Color.TRANSPARENT); 191 }); 192 waitForActivityIdle(blurActivity); 193 194 verifyOnlyBackgroundImageVisible(); 195 196 setAndAssertForceBlurDisabled(false, blurActivity.mBlurEnabledListener); 197 waitForActivityIdle(blurActivity); 198 199 final Rect windowFrame = getFloatingWindowFrame(blurActivity); 200 final Bitmap screenshot = takeScreenshotForBounds(mBackgroundActivityBounds); 201 assertBlurBehind(screenshot, windowFrame); 202 assertNoBackgroundBlur(screenshot, windowFrame); 203 } 204 205 @Test 206 @ApiTest(apis = {"android.view.WindowManager.LayoutParams#setBlurBehindRadius", 207 "android.R.styleable#Window_windowBlurBehindEnabled"}) testNoBlurBehindWhenFlagNotSet()208 public void testNoBlurBehindWhenFlagNotSet() { 209 final BlurActivity blurActivity = startTestActivity(BadBlurActivity.class); 210 getInstrumentation().runOnMainSync(() -> { 211 blurActivity.setBlurBehindRadius(BLUR_BEHIND_PX); 212 blurActivity.setBackgroundColor(Color.TRANSPARENT); 213 }); 214 waitForActivityIdle(blurActivity); 215 216 verifyOnlyBackgroundImageVisible(); 217 } 218 219 @Test 220 @ApiTest(apis = {"android.view.Window#setBackgroundBlurRadius"}) testBackgroundBlurActivatesFallbackDynamically()221 public void testBackgroundBlurActivatesFallbackDynamically() { 222 final BlurActivity blurActivity = startTestActivity(BlurActivity.class); 223 getInstrumentation().runOnMainSync(() -> { 224 blurActivity.setBackgroundBlurRadius(BACKGROUND_BLUR_PX); 225 }); 226 waitForActivityIdle(blurActivity); 227 final Rect windowFrame = getFloatingWindowFrame(blurActivity); 228 229 Bitmap screenshot = takeScreenshotForBounds(mBackgroundActivityBounds); 230 assertBackgroundBlur(screenshot, windowFrame); 231 assertNoBlurBehind(screenshot, windowFrame); 232 233 setAndAssertForceBlurDisabled(true, blurActivity.mBlurEnabledListener); 234 waitForActivityIdle(blurActivity); 235 236 screenshot = takeScreenshotForBounds(mBackgroundActivityBounds); 237 assertNoBackgroundBlur(screenshot, windowFrame); 238 assertNoBlurBehind(screenshot, windowFrame); 239 240 setAndAssertForceBlurDisabled(false, blurActivity.mBlurEnabledListener); 241 waitForActivityIdle(blurActivity); 242 243 screenshot = takeScreenshotForBounds(mBackgroundActivityBounds); 244 assertBackgroundBlur(screenshot, windowFrame); 245 assertNoBlurBehind(screenshot, windowFrame); 246 } 247 248 @Test 249 @ApiTest(apis = {"android.view.WindowManager.LayoutParams#setBlurBehindRadius", 250 "android.R.styleable#Window_windowBlurBehindEnabled"}) testBlurBehindDisabledDynamically()251 public void testBlurBehindDisabledDynamically() { 252 final BlurActivity blurActivity = startTestActivity(BlurActivity.class); 253 getInstrumentation().runOnMainSync(() -> { 254 blurActivity.setBlurBehindRadius(BLUR_BEHIND_PX); 255 }); 256 waitForActivityIdle(blurActivity); 257 final Rect windowFrame = getFloatingWindowFrame(blurActivity); 258 259 Bitmap screenshot = takeScreenshotForBounds(mBackgroundActivityBounds); 260 assertBlurBehind(screenshot, windowFrame); 261 assertNoBackgroundBlur(screenshot, windowFrame); 262 263 getInstrumentation().runOnMainSync(() -> { 264 blurActivity.setBlurBehindRadius(0); 265 }); 266 waitForActivityIdle(blurActivity); 267 268 screenshot = takeScreenshotForBounds(mBackgroundActivityBounds); 269 assertNoBackgroundBlur(screenshot, windowFrame); 270 assertNoBlurBehind(screenshot, windowFrame); 271 272 getInstrumentation().runOnMainSync(() -> { 273 blurActivity.setBlurBehindRadius(BLUR_BEHIND_PX); 274 }); 275 waitForActivityIdle(blurActivity); 276 277 screenshot = takeScreenshotForBounds(mBackgroundActivityBounds); 278 assertBlurBehind(screenshot, windowFrame); 279 assertNoBackgroundBlur(screenshot, windowFrame); 280 } 281 282 @Test 283 @ApiTest(apis = {"android.view.WindowManager.LayoutParams#setBlurBehindRadius", 284 "android.R.styleable#Window_windowBlurBehindEnabled", 285 "android.view.Window#setBackgroundBlurRadius"}) testBlurBehindAndBackgroundBlur()286 public void testBlurBehindAndBackgroundBlur() { 287 final BlurActivity blurActivity = startTestActivity(BlurActivity.class); 288 getInstrumentation().runOnMainSync(() -> { 289 blurActivity.setBlurBehindRadius(BLUR_BEHIND_PX); 290 blurActivity.setBackgroundBlurRadius(BACKGROUND_BLUR_PX); 291 }); 292 waitForActivityIdle(blurActivity); 293 final Rect windowFrame = getFloatingWindowFrame(blurActivity); 294 295 Bitmap screenshot = takeScreenshotForBounds(mBackgroundActivityBounds); 296 assertBlurBehind(screenshot, windowFrame); 297 assertBackgroundBlurOverBlurBehind(screenshot, windowFrame); 298 299 getInstrumentation().runOnMainSync(() -> { 300 blurActivity.setBlurBehindRadius(0); 301 blurActivity.setBackgroundBlurRadius(0); 302 }); 303 waitForActivityIdle(blurActivity); 304 305 screenshot = takeScreenshotForBounds(mBackgroundActivityBounds); 306 assertNoBackgroundBlur(screenshot, windowFrame); 307 assertNoBlurBehind(screenshot, windowFrame); 308 309 getInstrumentation().runOnMainSync(() -> { 310 blurActivity.setBlurBehindRadius(BLUR_BEHIND_PX); 311 blurActivity.setBackgroundBlurRadius(BACKGROUND_BLUR_PX); 312 }); 313 waitForActivityIdle(blurActivity); 314 315 screenshot = takeScreenshotForBounds(mBackgroundActivityBounds); 316 assertBlurBehind(screenshot, windowFrame); 317 assertBackgroundBlurOverBlurBehind(screenshot, windowFrame); 318 } 319 320 @Test 321 @ApiTest(apis = {"android.R.styleable#Window_windowBackgroundBlurRadius", 322 "android.R.styleable#Window_windowBlurBehindRadius", 323 "android.R.styleable#Window_windowBlurBehindEnabled"}) testBlurBehindAndBackgroundBlurSetWithAttributes()324 public void testBlurBehindAndBackgroundBlurSetWithAttributes() { 325 final Activity blurAttrActivity = startTestActivity(BlurAttributesActivity.class); 326 final Rect windowFrame = getFloatingWindowFrame(blurAttrActivity); 327 final Bitmap screenshot = takeScreenshotForBounds(mBackgroundActivityBounds); 328 329 assertBlurBehind(screenshot, windowFrame); 330 assertBackgroundBlurOverBlurBehind(screenshot, windowFrame); 331 } 332 333 @Test 334 @ApiTest(apis = {"android.view.WindowManager.LayoutParams#setBlurBehindRadius", 335 "android.R.styleable#Window_windowBlurBehindEnabled", 336 "android.view.Window#setBackgroundBlurRadius"}) testAllBlurRemovedAndRestoredWhenToggleBlurDisabled()337 public void testAllBlurRemovedAndRestoredWhenToggleBlurDisabled() { 338 final BlurActivity blurActivity = startTestActivity(BlurActivity.class); 339 getInstrumentation().runOnMainSync(() -> { 340 blurActivity.setBlurBehindRadius(BLUR_BEHIND_PX); 341 blurActivity.setBackgroundBlurRadius(BACKGROUND_BLUR_PX); 342 }); 343 waitForActivityIdle(blurActivity); 344 final Rect windowFrame = getFloatingWindowFrame(blurActivity); 345 346 Bitmap screenshot = takeScreenshotForBounds(mBackgroundActivityBounds); 347 assertBlurBehind(screenshot, windowFrame); 348 assertBackgroundBlurOverBlurBehind(screenshot, windowFrame); 349 350 setAndAssertForceBlurDisabled(true, blurActivity.mBlurEnabledListener); 351 waitForActivityIdle(blurActivity); 352 353 screenshot = takeScreenshotForBounds(mBackgroundActivityBounds); 354 assertNoBackgroundBlur(screenshot, windowFrame); 355 assertNoBlurBehind(screenshot, windowFrame); 356 357 getInstrumentation().runOnMainSync(() -> { 358 blurActivity.setBackgroundColor(Color.TRANSPARENT); 359 }); 360 waitForActivityIdle(blurActivity); 361 verifyOnlyBackgroundImageVisible(); 362 363 setAndAssertForceBlurDisabled(false, blurActivity.mBlurEnabledListener); 364 waitForActivityIdle(blurActivity); 365 366 screenshot = takeScreenshotForBounds(mBackgroundActivityBounds); 367 assertBlurBehind(screenshot, windowFrame); 368 assertBackgroundBlurOverBlurBehind(screenshot, windowFrame); 369 } 370 371 @Test 372 @ApiTest(apis = {"android.view.WindowManager.LayoutParams#setBlurBehindRadius", 373 "android.R.styleable#Window_windowBlurBehindEnabled", 374 "android.view.Window#setBackgroundBlurRadius"}) testBlurDestroyedAfterActivityFinished()375 public void testBlurDestroyedAfterActivityFinished() { 376 final BlurActivity blurActivity = startTestActivity(BlurActivity.class); 377 getInstrumentation().runOnMainSync(() -> { 378 blurActivity.setBlurBehindRadius(BLUR_BEHIND_PX); 379 blurActivity.setBackgroundBlurRadius(BACKGROUND_BLUR_PX); 380 }); 381 waitForActivityIdle(blurActivity); 382 383 final Rect windowFrame = getFloatingWindowFrame(blurActivity); 384 Bitmap screenshot = takeScreenshotForBounds(mBackgroundActivityBounds); 385 386 assertBlurBehind(screenshot, windowFrame); 387 assertBackgroundBlurOverBlurBehind(screenshot, windowFrame); 388 389 blurActivity.finish(); 390 mWmState.waitAndAssertActivityRemoved(blurActivity.getComponentName()); 391 waitForActivityIdle(blurActivity); 392 393 verifyOnlyBackgroundImageVisible(); 394 } 395 396 @Test 397 @ApiTest(apis = {"android.view.WindowManager#isCrossWindowBlurEnabled"}) testIsCrossWindowBlurEnabledUpdatedCorrectly()398 public void testIsCrossWindowBlurEnabledUpdatedCorrectly() { 399 setAndAssertForceBlurDisabled(true); 400 setAndAssertForceBlurDisabled(false); 401 } 402 403 @Test 404 @ApiTest(apis = {"android.view.WindowManager#addCrossWindowBlurEnabledListener", 405 "android.view.WindowManager#removeCrossWindowBlurEnabledListener"}) testBlurListener()406 public void testBlurListener() { 407 final BlurActivity activity = startTestActivity(BlurActivity.class); 408 Mockito.verify(activity.mBlurEnabledListener).accept(true); 409 410 setAndAssertForceBlurDisabled(true, activity.mBlurEnabledListener); 411 setAndAssertForceBlurDisabled(false, activity.mBlurEnabledListener); 412 413 activity.finishAndRemoveTask(); 414 mWmState.waitAndAssertActivityRemoved(activity.getComponentName()); 415 416 Mockito.clearInvocations(activity.mBlurEnabledListener); 417 setAndAssertForceBlurDisabled(true); 418 Mockito.verifyNoMoreInteractions(activity.mBlurEnabledListener); 419 } 420 421 public static class BackgroundActivity extends FocusableActivity { 422 @Override onCreate(Bundle savedInstanceState)423 protected void onCreate(Bundle savedInstanceState) { 424 super.onCreate(savedInstanceState); 425 getSplashScreen().setOnExitAnimationListener(view -> view.remove()); 426 427 setContentView(new View(this)); 428 429 getWindow().setDecorFitsSystemWindows(false); 430 getWindow().getInsetsController().hide(systemBars()); 431 } 432 } 433 434 public static class BlurActivity extends FocusableActivity { 435 public final Consumer<Boolean> mBlurEnabledListener = spy(new BlurListener()); 436 437 private int mBackgroundBlurRadius = 0; 438 private int mBlurBehindRadius = 0; 439 440 @Override onCreate(Bundle savedInstanceState)441 protected void onCreate(Bundle savedInstanceState) { 442 super.onCreate(savedInstanceState); 443 setContentView(R.layout.blur_activity); 444 getWindow().setDecorFitsSystemWindows(false); 445 getWindow().getInsetsController().hide(systemBars()); 446 } 447 448 @Override onAttachedToWindow()449 public void onAttachedToWindow() { 450 super.onAttachedToWindow(); 451 getWindowManager().addCrossWindowBlurEnabledListener(getMainExecutor(), 452 mBlurEnabledListener); 453 } 454 455 @Override onDetachedFromWindow()456 public void onDetachedFromWindow() { 457 super.onDetachedFromWindow(); 458 getWindowManager().removeCrossWindowBlurEnabledListener(mBlurEnabledListener); 459 } 460 setBackgroundBlurRadius(int backgroundBlurRadius)461 void setBackgroundBlurRadius(int backgroundBlurRadius) { 462 mBackgroundBlurRadius = backgroundBlurRadius; 463 getWindow().setBackgroundBlurRadius(mBackgroundBlurRadius); 464 setBackgroundColor( 465 mBackgroundBlurRadius > 0 && getWindowManager().isCrossWindowBlurEnabled() 466 ? Color.TRANSPARENT : NO_BLUR_BACKGROUND_COLOR); 467 } 468 setBlurBehindRadius(int blurBehindRadius)469 void setBlurBehindRadius(int blurBehindRadius) { 470 mBlurBehindRadius = blurBehindRadius; 471 getWindow().getAttributes().setBlurBehindRadius(mBlurBehindRadius); 472 getWindow().setAttributes(getWindow().getAttributes()); 473 getWindowManager().updateViewLayout(getWindow().getDecorView(), 474 getWindow().getAttributes()); 475 } 476 setBackgroundColor(int color)477 void setBackgroundColor(int color) { 478 getWindow().getDecorView().setBackgroundColor(color); 479 getWindowManager().updateViewLayout(getWindow().getDecorView(), 480 getWindow().getAttributes()); 481 } 482 483 public class BlurListener implements Consumer<Boolean> { 484 @Override accept(Boolean enabled)485 public void accept(Boolean enabled) { 486 setBackgroundBlurRadius(mBackgroundBlurRadius); 487 setBlurBehindRadius(mBlurBehindRadius); 488 } 489 } 490 } 491 492 /** 493 * This activity is used to test 2 things: 494 * 1. Blur behind does not work if WindowManager.LayoutParams.FLAG_BLUR_BEHIND is not set, 495 * respectively if windowBlurBehindEnabled is not set. 496 * 2. Background blur does not work for opaque activities (where windowIsTranslucent is false) 497 * 498 * In the style of this activity windowBlurBehindEnabled is false and windowIsTranslucent is 499 * false. As a result, we expect that neither blur behind, nor background blur is rendered, 500 * even though they are requested with setBlurBehindRadius and setBackgroundBlurRadius. 501 */ 502 public static class BadBlurActivity extends BlurActivity { 503 } 504 505 public static class BlurAttributesActivity extends FocusableActivity { 506 @Override onCreate(Bundle savedInstanceState)507 protected void onCreate(Bundle savedInstanceState) { 508 super.onCreate(savedInstanceState); 509 setContentView(R.layout.blur_activity); 510 getWindow().setDecorFitsSystemWindows(false); 511 getWindow().getInsetsController().hide(systemBars()); 512 } 513 } 514 startTestActivity(Class<T> activityClass)515 private <T extends FocusableActivity> T startTestActivity(Class<T> activityClass) { 516 T activity = startActivity(activityClass); 517 ComponentName activityName = activity.getComponentName(); 518 waitAndAssertResumedActivity(activityName, activityName + " must be resumed"); 519 waitForActivityIdle(activity); 520 return activity; 521 } 522 getFloatingWindowFrame(Activity activity)523 private Rect getFloatingWindowFrame(Activity activity) { 524 mWmState.computeState(activity.getComponentName()); 525 String windowName = getWindowName(activity.getComponentName()); 526 Rect windowFrame = 527 new Rect(mWmState.getMatchingVisibleWindowState(windowName).get(0).getFrame()); 528 // Offset the frame of the BlurActivity to the coordinates of 529 // mBackgroundActivityBounds, because we only take the screenshot in that area. 530 windowFrame.offset(-mBackgroundActivityBounds.left, -mBackgroundActivityBounds.top); 531 return windowFrame; 532 } 533 waitForActivityIdle(Activity activity)534 private void waitForActivityIdle(Activity activity) { 535 // This helps with the test flakiness 536 getInstrumentation().runOnMainSync(() -> {}); 537 UiDevice.getInstance(getInstrumentation()).waitForIdle(); 538 getInstrumentation().getUiAutomation().syncInputTransactions(); 539 mWmState.computeState(activity.getComponentName()); 540 } 541 setAndAssertForceBlurDisabled(boolean disable)542 private void setAndAssertForceBlurDisabled(boolean disable) { 543 setAndAssertForceBlurDisabled(disable, null); 544 } 545 setAndAssertForceBlurDisabled(boolean disable, Consumer<Boolean> blurEnabledListener)546 private void setAndAssertForceBlurDisabled(boolean disable, 547 Consumer<Boolean> blurEnabledListener) { 548 if (blurEnabledListener != null) { 549 Mockito.clearInvocations(blurEnabledListener); 550 } 551 Settings.Global.putInt(mContext.getContentResolver(), 552 Settings.Global.DISABLE_WINDOW_BLURS, disable ? 1 : 0); 553 if (blurEnabledListener != null) { 554 Mockito.verify(blurEnabledListener, timeout(BROADCAST_WAIT_TIMEOUT)) 555 .accept(!disable); 556 } else { 557 PollingCheck.waitFor(BROADCAST_WAIT_TIMEOUT, () -> { 558 return disable != mContext.getSystemService(WindowManager.class) 559 .isCrossWindowBlurEnabled(); 560 }); 561 assertTrue(!disable == mContext.getSystemService(WindowManager.class) 562 .isCrossWindowBlurEnabled()); 563 } 564 } 565 verifyOnlyBackgroundImageVisible()566 private void verifyOnlyBackgroundImageVisible() { 567 final Bitmap screenshot = takeScreenshotForBounds(mBackgroundActivityBounds); 568 mDumpOnFailure.dumpOnFailure("verifyOnlyBackgroundImageVisible", screenshot); 569 final int height = screenshot.getHeight(); 570 final int width = screenshot.getWidth(); 571 572 final int blueWidth = width / 2; 573 574 final int[] row = new int[width]; 575 576 for (int y = 0; y < height; y++) { 577 screenshot.getPixels(row, 0, width, 0, y, row.length, 1); 578 for (int x = 0; x < width; x++) { 579 final int actual = row[x]; 580 final int expected = (x < blueWidth ? Color.BLUE : Color.RED); 581 582 if (actual != expected) { 583 ColorUtils.verifyColor("failed for pixel (x, y) = (" + x + ", " + y + ")", 584 expected, actual, 1); 585 } 586 } 587 } 588 } 589 assertBlurBehind(Bitmap screenshot, Rect windowFrame)590 private void assertBlurBehind(Bitmap screenshot, Rect windowFrame) { 591 mDumpOnFailure.dumpOnFailure("assertBlurBehind", screenshot); 592 assertBlur(screenshot, BLUR_BEHIND_PX, 0, windowFrame.top); 593 assertBlur(screenshot, BLUR_BEHIND_PX, windowFrame.bottom, screenshot.getHeight()); 594 } 595 assertBackgroundBlur(Bitmap screenshot, Rect windowFrame)596 private void assertBackgroundBlur(Bitmap screenshot, Rect windowFrame) { 597 mDumpOnFailure.dumpOnFailure("assertBackgroundBlur", screenshot); 598 assertBlur(screenshot, BACKGROUND_BLUR_PX, windowFrame.top, windowFrame.bottom); 599 } 600 assertBackgroundBlurOverBlurBehind(Bitmap screenshot, Rect windowFrame)601 private void assertBackgroundBlurOverBlurBehind(Bitmap screenshot, Rect windowFrame) { 602 mDumpOnFailure.dumpOnFailure("assertBackgroundBlurOverBlurBehind", screenshot); 603 assertBlur( 604 screenshot, 605 (int) Math.hypot(BACKGROUND_BLUR_PX, BLUR_BEHIND_PX), 606 windowFrame.top, 607 windowFrame.bottom); 608 } 609 assertNoBlurBehind(Bitmap screenshot, Rect windowFrame)610 private void assertNoBlurBehind(Bitmap screenshot, Rect windowFrame) { 611 mDumpOnFailure.dumpOnFailure("assertNoBlurBehind", screenshot); 612 613 // Batch fetch pixels from each row of the bitmap to speed up the test. 614 final int[] row = new int[screenshot.getWidth()]; 615 616 for (int y = 0; y < screenshot.getHeight(); y++) { 617 screenshot.getPixels(row, 0, screenshot.getWidth(), 0, y, row.length, 1); 618 for (int x = 0; x < screenshot.getWidth(); x++) { 619 if (!windowFrame.contains(x, y)) { 620 final int actual = row[x]; 621 final int expected = (x < screenshot.getWidth() / 2 ? Color.BLUE : Color.RED); 622 623 if (actual != expected) { 624 ColorUtils.verifyColor( 625 "failed for pixel (x, y) = (" + x + ", " + y + ")", 626 expected, actual, 1); 627 } 628 } 629 } 630 } 631 } 632 assertNoBackgroundBlur(Bitmap screenshot, Rect windowFrame)633 private void assertNoBackgroundBlur(Bitmap screenshot, Rect windowFrame) { 634 mDumpOnFailure.dumpOnFailure("assertNoBackgroundBlur", screenshot); 635 636 // Batch fetch pixels from each row of the bitmap to speed up the test. 637 final int[] row = new int[windowFrame.width()]; 638 639 for (int y = windowFrame.top; y < windowFrame.bottom; y++) { 640 screenshot.getPixels( 641 row, 0, screenshot.getWidth(), windowFrame.left, y, row.length, 1); 642 for (int x = windowFrame.left; x < windowFrame.right; x++) { 643 final int actual = row[x - windowFrame.left]; 644 final int expected = NO_BLUR_BACKGROUND_COLOR; 645 646 if (actual != expected) { 647 ColorUtils.verifyColor("failed for pixel (x, y) = (" + x + ", " + y + ")", 648 expected, actual, 1); 649 } 650 } 651 } 652 } 653 assertBlur(Bitmap screenshot, int blurRadius, int startHeight, int endHeight)654 private void assertBlur(Bitmap screenshot, int blurRadius, int startHeight, 655 int endHeight) { 656 final int width = screenshot.getWidth(); 657 658 // Adjust the test to check a smaller part of the blurred area in order to accept 659 // various blur algorithm approximations used in RenderEngine 660 final int stepSize = blurRadius / 4; 661 final int blurAreaStartX = width / 2 - blurRadius + stepSize; 662 final int blurAreaEndX = width / 2 + blurRadius; 663 664 // At 2 * radius there should be no visible blur effects. 665 final int unaffectedBluePixelX = width / 2 - blurRadius * 2 - 1; 666 final int unaffectedRedPixelX = width / 2 + blurRadius * 2 + 1; 667 668 for (int y = startHeight; y < endHeight; y++) { 669 Color previousColor = Color.valueOf(Color.BLUE); 670 for (int x = blurAreaStartX; x < blurAreaEndX; x += stepSize) { 671 Color currentColor = screenshot.getColor(x, y); 672 673 if (previousColor.blue() <= currentColor.blue()) { 674 assertTrue("assertBlur failed for blue for pixel (x, y) = (" 675 + x + ", " + y + ");" 676 + " previousColor blue: " + previousColor.blue() 677 + ", currentColor blue: " + currentColor.blue() 678 , previousColor.blue() > currentColor.blue()); 679 } 680 if (previousColor.red() >= currentColor.red()) { 681 assertTrue("assertBlur failed for red for pixel (x, y) = (" 682 + x + ", " + y + ");" 683 + " previousColor red: " + previousColor.red() 684 + ", currentColor red: " + currentColor.red(), 685 previousColor.red() < currentColor.red()); 686 } 687 previousColor = currentColor; 688 } 689 } 690 691 for (int y = startHeight; y < endHeight; y++) { 692 final int unaffectedBluePixel = screenshot.getPixel(unaffectedBluePixelX, y); 693 if (unaffectedBluePixel != Color.BLUE) { 694 ColorUtils.verifyColor( 695 "failed for pixel (x, y) = (" + unaffectedBluePixelX + ", " + y + ")", 696 Color.BLUE, unaffectedBluePixel, 1); 697 } 698 final int unaffectedRedPixel = screenshot.getPixel(unaffectedRedPixelX, y); 699 if (unaffectedRedPixel != Color.RED) { 700 ColorUtils.verifyColor( 701 "failed for pixel (x, y) = (" + unaffectedRedPixelX + ", " + y + ")", 702 Color.RED, unaffectedRedPixel, 1); 703 } 704 } 705 } 706 } 707