From aad58839271c0cb2ff60eaa363bdb6e2dba039b2 Mon Sep 17 00:00:00 2001 From: Florence Yang Date: Wed, 26 Apr 2023 00:44:08 +0000 Subject: [PATCH] LS Wallpaper Sampling for LS Smartspace Refactoring RegionSampler implementations (LS smartspace) to properly sample from lockscreen wallpaper since it is no longer an image view. This also addresses an edge case where Wallpaper Colors was incorrectly calculating the hint for applying dark text on an all-white wallpaper/region if it was very small. This change also introduces new unit tests for Region Sampler to ensure there is full test coverage for this feature. Flag: region_sampling Bug: 265968912 Test: manual, atest WallpaperColorsTest, atest RegionSamplerTest Change-Id: Ic46c7c86bf8bc28eb0cb01c202f785a181c83564 --- core/java/android/app/WallpaperColors.java | 2 +- core/java/android/app/WallpaperManager.java | 4 +- .../shared/regionsampling/RegionSampler.kt | 116 ++++++++--- .../android/keyguard/ClockEventController.kt | 61 +----- .../LockscreenSmartspaceController.kt | 27 ++- .../regionsampling/RegionSamplerTest.kt | 190 +++++++++++++++++- .../src/android/app/WallpaperColorsTest.java | 66 ++++-- 7 files changed, 348 insertions(+), 118 deletions(-) diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java index be1d8b8ad7d3f..b710644a308cf 100644 --- a/core/java/android/app/WallpaperColors.java +++ b/core/java/android/app/WallpaperColors.java @@ -588,7 +588,7 @@ public final class WallpaperColors implements Parcelable { int hints = 0; double meanLuminance = totalLuminance / pixels.length; - if (meanLuminance > BRIGHT_IMAGE_MEAN_LUMINANCE && darkPixels < maxDarkPixels) { + if (meanLuminance > BRIGHT_IMAGE_MEAN_LUMINANCE && darkPixels <= maxDarkPixels) { hints |= HINT_SUPPORTS_DARK_TEXT; } if (meanLuminance < DARK_THEME_MEAN_LUMINANCE) { diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 1603cd9e2c815..f8211af311596 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -1659,14 +1659,14 @@ public class WallpaperManager { * @hide */ public void addOnColorsChangedListener(@NonNull LocalWallpaperColorConsumer callback, - List regions) throws IllegalArgumentException { + List regions, int which) throws IllegalArgumentException { for (RectF region : regions) { if (!LOCAL_COLOR_BOUNDS.contains(region)) { throw new IllegalArgumentException("Regions must be within bounds " + LOCAL_COLOR_BOUNDS); } } - sGlobals.addOnColorsChangedListener(callback, regions, FLAG_SYSTEM, + sGlobals.addOnColorsChangedListener(callback, regions, which, mContext.getUserId(), mContext.getDisplayId()); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt index 482158e80d0fd..9a00447615049 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt @@ -21,9 +21,9 @@ import android.graphics.Color import android.graphics.Point import android.graphics.Rect import android.graphics.RectF +import android.util.Log import android.view.View import androidx.annotation.VisibleForTesting -import com.android.systemui.shared.navigationbar.RegionSamplingHelper import java.io.PrintWriter import java.util.concurrent.Executor @@ -31,20 +31,21 @@ import java.util.concurrent.Executor open class RegionSampler @JvmOverloads constructor( - val sampledView: View?, + val sampledView: View, mainExecutor: Executor?, val bgExecutor: Executor?, val regionSamplingEnabled: Boolean, + val isLockscreen: Boolean = false, + val wallpaperManager: WallpaperManager? = WallpaperManager.getInstance(sampledView.context), val updateForegroundColor: UpdateColorCallback, - val wallpaperManager: WallpaperManager? = WallpaperManager.getInstance(sampledView?.context) ) : WallpaperManager.LocalWallpaperColorConsumer { private var regionDarkness = RegionDarkness.DEFAULT private var samplingBounds = Rect() private val tmpScreenLocation = IntArray(2) - @VisibleForTesting var regionSampler: RegionSamplingHelper? = null private var lightForegroundColor = Color.WHITE private var darkForegroundColor = Color.BLACK - private val displaySize = Point() + @VisibleForTesting val displaySize = Point() + private var initialSampling: WallpaperColors? = null /** * Sets the colors to be used for Dark and Light Foreground. @@ -57,6 +58,36 @@ constructor( darkForegroundColor = darkColor } + private val layoutChangedListener = + object : View.OnLayoutChangeListener { + + override fun onLayoutChange( + view: View?, + left: Int, + top: Int, + right: Int, + bottom: Int, + oldLeft: Int, + oldTop: Int, + oldRight: Int, + oldBottom: Int + ) { + + // don't pass in negative bounds when region is in transition state + if (sampledView.locationOnScreen[0] < 0 || sampledView.locationOnScreen[1] < 0) { + return + } + + val currentViewRect = Rect(left, top, right, bottom) + val oldViewRect = Rect(oldLeft, oldTop, oldRight, oldBottom) + + if (currentViewRect != oldViewRect) { + stopRegionSampler() + startRegionSampler() + } + } + } + /** * Determines which foreground color to use based on region darkness. * @@ -84,40 +115,57 @@ constructor( /** Start region sampler */ fun startRegionSampler() { - if (!regionSamplingEnabled || sampledView == null) { + + if (!regionSamplingEnabled) { + if (DEBUG) Log.d(TAG, "startRegionSampler() | RegionSampling flag not enabled") return } - val sampledRegion = calculateSampledRegion(sampledView) - val regions = ArrayList() - val sampledRegionWithOffset = convertBounds(sampledRegion) + sampledView.addOnLayoutChangeListener(layoutChangedListener) + val screenLocationBounds = calculateScreenLocation(sampledView) + if (screenLocationBounds == null) { + if (DEBUG) Log.d(TAG, "startRegionSampler() | passed in null region") + return + } + if (screenLocationBounds.isEmpty) { + if (DEBUG) Log.d(TAG, "startRegionSampler() | passed in empty region") + return + } + + val sampledRegionWithOffset = convertBounds(screenLocationBounds) if ( sampledRegionWithOffset.left < 0.0 || sampledRegionWithOffset.right > 1.0 || sampledRegionWithOffset.top < 0.0 || sampledRegionWithOffset.bottom > 1.0 ) { - android.util.Log.e( - "RegionSampler", - "view out of bounds: $sampledRegion | " + - "screen width: ${displaySize.x}, screen height: ${displaySize.y}", - Exception() - ) + if (DEBUG) + Log.d( + TAG, + "startRegionSampler() | view out of bounds: $screenLocationBounds | " + + "screen width: ${displaySize.x}, screen height: ${displaySize.y}", + Exception() + ) return } + val regions = ArrayList() regions.add(sampledRegionWithOffset) - wallpaperManager?.removeOnColorsChangedListener(this) - wallpaperManager?.addOnColorsChangedListener(this, regions) + wallpaperManager?.addOnColorsChangedListener( + this, + regions, + if (isLockscreen) WallpaperManager.FLAG_LOCK else WallpaperManager.FLAG_SYSTEM + ) - // TODO(b/265969235): conditionally set FLAG_LOCK or FLAG_SYSTEM once HS smartspace - // implemented bgExecutor?.execute( Runnable { - val initialSampling = - wallpaperManager?.getWallpaperColors(WallpaperManager.FLAG_LOCK) + initialSampling = + wallpaperManager?.getWallpaperColors( + if (isLockscreen) WallpaperManager.FLAG_LOCK + else WallpaperManager.FLAG_SYSTEM + ) onColorsChanged(sampledRegionWithOffset, initialSampling) } ) @@ -126,6 +174,7 @@ constructor( /** Stop region sampler */ fun stopRegionSampler() { wallpaperManager?.removeOnColorsChangedListener(this) + sampledView.removeOnLayoutChangeListener(layoutChangedListener) } /** Dump region sampler */ @@ -138,22 +187,23 @@ constructor( pw.println("passed-in sampledView: $sampledView") pw.println("calculated samplingBounds: $samplingBounds") pw.println( - "sampledView width: ${sampledView?.width}, sampledView height: ${sampledView?.height}" + "sampledView width: ${sampledView.width}, sampledView height: ${sampledView.height}" ) pw.println("screen width: ${displaySize.x}, screen height: ${displaySize.y}") pw.println( - "sampledRegionWithOffset: ${convertBounds(calculateSampledRegion(sampledView!!))}" + "sampledRegionWithOffset: ${convertBounds( + calculateScreenLocation(sampledView) ?: RectF())}" ) - // TODO(b/265969235): mock initialSampling based on if component is on HS or LS wallpaper - // HS Smartspace - wallpaperManager?.getWallpaperColors(WallpaperManager.FLAG_SYSTEM) - // LS Smartspace, clock - wallpaperManager?.getWallpaperColors(WallpaperManager.FLAG_LOCK) pw.println( - "initialSampling for lockscreen: " + - "${wallpaperManager?.getWallpaperColors(WallpaperManager.FLAG_LOCK)}" + "initialSampling for ${if (isLockscreen) "lockscreen" else "homescreen" }" + + ": $initialSampling" ) } - fun calculateSampledRegion(sampledView: View): RectF { + fun calculateScreenLocation(sampledView: View): RectF? { + + if (!sampledView.isLaidOut) return null + val screenLocation = tmpScreenLocation /** * The method getLocationOnScreen is used to obtain the view coordinates relative to its @@ -181,7 +231,8 @@ constructor( */ fun convertBounds(originalBounds: RectF): RectF { - // TODO(b/265969235): GRAB # PAGES + CURRENT WALLPAPER PAGE # FROM LAUNCHER + // TODO(b/265969235): GRAB # PAGES + CURRENT WALLPAPER PAGE # FROM LAUNCHER (--> HS + // Smartspace always on 1st page) // TODO(b/265968912): remove hard-coded value once LS wallpaper supported val wallpaperPageNum = 0 val numScreens = 1 @@ -214,6 +265,11 @@ constructor( ) updateForegroundColor() } + + companion object { + private const val TAG = "RegionSampler" + private const val DEBUG = false + } } typealias UpdateColorCallback = () -> Unit diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt index 4bf7be6df5bea..05878f16d2eb5 100644 --- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt @@ -21,13 +21,11 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.content.res.Resources -import android.graphics.Rect import android.text.format.DateFormat import android.util.TypedValue import android.view.View import android.view.View.OnAttachStateChangeListener import android.view.ViewTreeObserver -import android.widget.FrameLayout import androidx.annotation.VisibleForTesting import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle @@ -55,16 +53,16 @@ import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.concurrency.DelayableExecutor -import java.util.Locale -import java.util.TimeZone -import java.util.concurrent.Executor -import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.Job import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch +import java.util.Locale +import java.util.TimeZone +import java.util.concurrent.Executor +import javax.inject.Inject /** * Controller for a Clock provided by the registry and used on the keyguard. Instantiated by @@ -98,12 +96,7 @@ constructor( value.initialize(resources, dozeAmount, 0f) - if (regionSamplingEnabled) { - clock?.run { - smallClock.view.addOnLayoutChangeListener(mLayoutChangedListener) - largeClock.view.addOnLayoutChangeListener(mLayoutChangedListener) - } - } else { + if (!regionSamplingEnabled) { updateColors() } updateFontSizes() @@ -139,44 +132,10 @@ constructor( private var disposableHandle: DisposableHandle? = null private val regionSamplingEnabled = featureFlags.isEnabled(REGION_SAMPLING) - private val mLayoutChangedListener = - object : View.OnLayoutChangeListener { - - override fun onLayoutChange( - view: View?, - left: Int, - top: Int, - right: Int, - bottom: Int, - oldLeft: Int, - oldTop: Int, - oldRight: Int, - oldBottom: Int - ) { - view?.removeOnLayoutChangeListener(this) - - val parent = (view?.parent) as FrameLayout - - // don't pass in negative bounds when clocks are in transition state - if (view.locationOnScreen[0] < 0 || view.locationOnScreen[1] < 0) { - return - } - - val currentViewRect = Rect(left, top, right, bottom) - val oldViewRect = Rect(oldLeft, oldTop, oldRight, oldBottom) - - if ( - currentViewRect.width() != oldViewRect.width() || - currentViewRect.height() != oldViewRect.height() - ) { - updateRegionSampler(view) - } - } - } private fun updateColors() { val wallpaperManager = WallpaperManager.getInstance(context) - if (regionSamplingEnabled && !wallpaperManager.lockScreenWallpaperExists()) { + if (regionSamplingEnabled) { regionSampler?.let { regionSampler -> clock?.let { clock -> if (regionSampler.sampledView == clock.smallClock.view) { @@ -211,6 +170,7 @@ constructor( mainExecutor, bgExecutor, regionSamplingEnabled, + isLockscreen = true, ::updateColors ) ?.apply { startRegionSampler() } @@ -219,10 +179,11 @@ constructor( } protected open fun createRegionSampler( - sampledView: View?, + sampledView: View, mainExecutor: Executor?, bgExecutor: Executor?, regionSamplingEnabled: Boolean, + isLockscreen: Boolean, updateColors: () -> Unit ): RegionSampler? { return RegionSampler( @@ -230,8 +191,8 @@ constructor( mainExecutor, bgExecutor, regionSamplingEnabled, - updateColors - ) + isLockscreen, + ) { updateColors() } } var regionSampler: RegionSampler? = null diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt index 950dbd9300b32..7cc917f3b0b86 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.lockscreen import android.app.PendingIntent -import android.app.WallpaperManager import android.app.smartspace.SmartspaceConfig import android.app.smartspace.SmartspaceManager import android.app.smartspace.SmartspaceSession @@ -56,7 +55,6 @@ import com.android.systemui.plugins.WeatherData import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.settings.UserTracker import com.android.systemui.shared.regionsampling.RegionSampler -import com.android.systemui.shared.regionsampling.UpdateColorCallback import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DATE_SMARTSPACE_DATA_PLUGIN import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.WEATHER_SMARTSPACE_DATA_PLUGIN import com.android.systemui.statusbar.phone.KeyguardBypassController @@ -120,7 +118,7 @@ constructor( private val regionSamplingEnabled = featureFlags.isEnabled(Flags.REGION_SAMPLING) - private var isContentUpdatedOnce = false + private var isRegionSamplersCreated = false private var showNotifications = false private var showSensitiveContentForCurrentUser = false private var showSensitiveContentForManagedUser = false @@ -128,7 +126,6 @@ constructor( // TODO(b/202758428): refactor so that we can test color updates via region samping, similar to // how we test color updates when theme changes (See testThemeChangeUpdatesTextColor). - private val updateFun: UpdateColorCallback = { updateTextColorFromRegionSampler() } // TODO: Move logic into SmartspaceView var stateChangeListener = object : View.OnAttachStateChangeListener { @@ -144,6 +141,9 @@ constructor( override fun onViewDetachedFromWindow(v: View) { smartspaceViews.remove(v as SmartspaceView) + regionSamplers[v]?.stopRegionSampler() + regionSamplers.remove(v as SmartspaceView) + if (smartspaceViews.isEmpty()) { disconnect() } @@ -170,7 +170,7 @@ constructor( val filteredTargets = targets.filter(::filterSmartspaceTarget) plugin?.onTargetsAvailable(filteredTargets) - if (!isContentUpdatedOnce) { + if (!isRegionSamplersCreated) { for (v in smartspaceViews) { if (regionSamplingEnabled) { var regionSampler = RegionSampler( @@ -178,15 +178,14 @@ constructor( uiExecutor, bgExecutor, regionSamplingEnabled, - updateFun - ) + isLockscreen = true, + ) { updateTextColorFromRegionSampler() } initializeTextColors(regionSampler) regionSamplers[v] = regionSampler regionSampler.startRegionSampler() } - updateTextColorFromWallpaper() } - isContentUpdatedOnce = true + isRegionSamplersCreated = true } } @@ -504,18 +503,16 @@ constructor( } private fun updateTextColorFromRegionSampler() { - smartspaceViews.forEach { - val textColor = regionSamplers.get(it)?.currentForegroundColor() + regionSamplers.forEach { (view, region) -> + val textColor = region.currentForegroundColor() if (textColor != null) { - it.setPrimaryTextColor(textColor) + view.setPrimaryTextColor(textColor) } } } private fun updateTextColorFromWallpaper() { - val wallpaperManager = WallpaperManager.getInstance(context) - if (!regionSamplingEnabled || wallpaperManager.lockScreenWallpaperExists() || - regionSamplers.isEmpty()) { + if (!regionSamplingEnabled || regionSamplers.isEmpty()) { val wallpaperTextColor = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor) smartspaceViews.forEach { it.setPrimaryTextColor(wallpaperTextColor) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt index ae1c8cbe2a656..1031621e2e7e7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt @@ -1,17 +1,30 @@ package com.android.systemui.shared.regionsampling +import android.app.WallpaperColors import android.app.WallpaperManager +import android.graphics.Color +import android.graphics.RectF import android.testing.AndroidTestingRunner import android.view.View import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.capture +import com.google.common.truth.Truth.assertThat import java.io.PrintWriter import java.util.concurrent.Executor +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.eq +import org.mockito.Captor import org.mockito.Mock +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.never +import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit @@ -26,25 +39,188 @@ class RegionSamplerTest : SysuiTestCase() { @Mock private lateinit var bgExecutor: Executor @Mock private lateinit var pw: PrintWriter @Mock private lateinit var wallpaperManager: WallpaperManager + @Mock private lateinit var updateForegroundColor: UpdateColorCallback - private lateinit var mRegionSampler: RegionSampler - private var updateFun: UpdateColorCallback = {} + private lateinit var regionSampler: RegionSampler // lockscreen + private lateinit var homescreenRegionSampler: RegionSampler + + @Captor + private lateinit var colorsChangedListener: + ArgumentCaptor + + @Captor private lateinit var layoutChangedListener: ArgumentCaptor @Before fun setUp() { whenever(sampledView.isAttachedToWindow).thenReturn(true) + whenever(sampledView.width).thenReturn(100) + whenever(sampledView.height).thenReturn(100) + whenever(sampledView.isLaidOut).thenReturn(true) + whenever(sampledView.locationOnScreen).thenReturn(intArrayOf(0, 0)) - mRegionSampler = - RegionSampler(sampledView, mainExecutor, bgExecutor, true, updateFun, wallpaperManager) + regionSampler = + RegionSampler( + sampledView, + mainExecutor, + bgExecutor, + regionSamplingEnabled = true, + isLockscreen = true, + wallpaperManager, + updateForegroundColor + ) + regionSampler.displaySize.set(1080, 2050) + + // TODO(b/265969235): test sampling on home screen via WallpaperManager.FLAG_SYSTEM + homescreenRegionSampler = + RegionSampler( + sampledView, + mainExecutor, + bgExecutor, + regionSamplingEnabled = true, + isLockscreen = false, + wallpaperManager, + updateForegroundColor + ) } @Test - fun testStartRegionSampler() { - mRegionSampler.startRegionSampler() + fun testCalculatedBounds_inRange() { + // test calculations return region within [0,1] + sampledView.setLeftTopRightBottom(100, 100, 200, 200) + var fractionalBounds = + regionSampler.calculateScreenLocation(sampledView)?.let { + regionSampler.convertBounds(it) + } + + assertTrue(fractionalBounds?.left!! >= 0.0f) + assertTrue(fractionalBounds.right <= 1.0f) + assertTrue(fractionalBounds.top >= 0.0f) + assertTrue(fractionalBounds.bottom <= 1.0f) + } + + @Test + fun testEmptyView_returnsEarly() { + sampledView.setLeftTopRightBottom(0, 0, 0, 0) + whenever(sampledView.width).thenReturn(0) + whenever(sampledView.height).thenReturn(0) + regionSampler.startRegionSampler() + // returns early so should never call this function + verify(wallpaperManager, never()) + .addOnColorsChangedListener( + any(WallpaperManager.LocalWallpaperColorConsumer::class.java), + any(), + any() + ) + } + + @Test + fun testLayoutChange_notifiesListener() { + regionSampler.startRegionSampler() + // don't count addOnColorsChangedListener() call made in startRegionSampler() + clearInvocations(wallpaperManager) + + verify(sampledView).addOnLayoutChangeListener(capture(layoutChangedListener)) + layoutChangedListener.value.onLayoutChange( + sampledView, + 300, + 300, + 400, + 400, + 100, + 100, + 200, + 200 + ) + verify(sampledView).removeOnLayoutChangeListener(layoutChangedListener.value) + verify(wallpaperManager) + .removeOnColorsChangedListener( + any(WallpaperManager.LocalWallpaperColorConsumer::class.java) + ) + verify(wallpaperManager) + .addOnColorsChangedListener( + any(WallpaperManager.LocalWallpaperColorConsumer::class.java), + any(), + any() + ) + } + + @Test + fun testColorsChanged_triggersCallback() { + regionSampler.startRegionSampler() + verify(wallpaperManager) + .addOnColorsChangedListener( + capture(colorsChangedListener), + any(), + eq(WallpaperManager.FLAG_LOCK) + ) + setWhiteWallpaper() + verify(updateForegroundColor).invoke() + } + + @Test + fun testRegionDarkness() { + regionSampler.startRegionSampler() + verify(wallpaperManager) + .addOnColorsChangedListener( + capture(colorsChangedListener), + any(), + eq(WallpaperManager.FLAG_LOCK) + ) + + // should detect dark region + setBlackWallpaper() + assertThat(regionSampler.currentRegionDarkness()).isEqualTo(RegionDarkness.DARK) + + // should detect light region + setWhiteWallpaper() + assertThat(regionSampler.currentRegionDarkness()).isEqualTo(RegionDarkness.LIGHT) + } + + @Test + fun testForegroundColor() { + regionSampler.setForegroundColors(Color.WHITE, Color.BLACK) + regionSampler.startRegionSampler() + verify(wallpaperManager) + .addOnColorsChangedListener( + capture(colorsChangedListener), + any(), + eq(WallpaperManager.FLAG_LOCK) + ) + + // dark background, light text + setBlackWallpaper() + assertThat(regionSampler.currentForegroundColor()).isEqualTo(Color.WHITE) + + // light background, dark text + setWhiteWallpaper() + assertThat(regionSampler.currentForegroundColor()).isEqualTo(Color.BLACK) + } + + private fun setBlackWallpaper() { + val wallpaperColors = + WallpaperColors(Color.valueOf(Color.BLACK), Color.valueOf(Color.BLACK), null) + colorsChangedListener.value.onColorsChanged( + RectF(100.0f, 100.0f, 200.0f, 200.0f), + wallpaperColors + ) + } + private fun setWhiteWallpaper() { + val wallpaperColors = + WallpaperColors( + Color.valueOf(Color.WHITE), + Color.valueOf(Color.WHITE), + null, + WallpaperColors.HINT_SUPPORTS_DARK_TEXT + ) + colorsChangedListener.value.onColorsChanged( + RectF(100.0f, 100.0f, 200.0f, 200.0f), + wallpaperColors + ) } @Test fun testDump() { - mRegionSampler.dump(pw) + regionSampler.dump(pw) + homescreenRegionSampler.dump(pw) } } diff --git a/tests/Internal/src/android/app/WallpaperColorsTest.java b/tests/Internal/src/android/app/WallpaperColorsTest.java index 9ffb236d3f59c..70660a0a117e6 100644 --- a/tests/Internal/src/android/app/WallpaperColorsTest.java +++ b/tests/Internal/src/android/app/WallpaperColorsTest.java @@ -48,10 +48,10 @@ public class WallpaperColorsTest { } /** - * Check that white supports dark text and black doesn't + * Check that white surface supports dark text */ @Test - public void colorHintsTest() { + public void whiteSurfaceColorHintsTest() { Bitmap image = Bitmap.createBitmap(30, 30, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(image); @@ -59,28 +59,68 @@ public class WallpaperColorsTest { int hints = WallpaperColors.fromBitmap(image).getColorHints(); boolean supportsDarkText = (hints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0; boolean supportsDarkTheme = (hints & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0; - boolean fromBitmap = (hints & WallpaperColors.HINT_FROM_BITMAP) != 0; Assert.assertTrue("White surface should support dark text.", supportsDarkText); Assert.assertFalse("White surface shouldn't support dark theme.", supportsDarkTheme); - Assert.assertTrue("From bitmap should be true if object was created " - + "using WallpaperColors#fromBitmap.", fromBitmap); - - canvas.drawColor(Color.BLACK); - hints = WallpaperColors.fromBitmap(image).getColorHints(); - supportsDarkText = (hints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0; - supportsDarkTheme = (hints & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0; - Assert.assertFalse("Black surface shouldn't support dark text.", supportsDarkText); - Assert.assertTrue("Black surface should support dark theme.", supportsDarkTheme); Paint paint = new Paint(); paint.setStyle(Paint.Style.FILL); paint.setColor(Color.BLACK); - canvas.drawColor(Color.WHITE); canvas.drawRect(0, 0, 8, 8, paint); supportsDarkText = (WallpaperColors.fromBitmap(image) .getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0; Assert.assertFalse("Light surface shouldn't support dark text " + "when it contains dark pixels.", supportsDarkText); + } + + /** + * Check that x-small white region supports dark text when max number of dark pixels = 0 + */ + @Test + public void xSmallWhiteSurfaceColorHintsTest() { + Bitmap xsmall_image = Bitmap.createBitmap(1, 5, Bitmap.Config.ARGB_8888); + Canvas xsmall_canvas = new Canvas(xsmall_image); + + xsmall_canvas.drawColor(Color.WHITE); + int hints = WallpaperColors.fromBitmap(xsmall_image).getColorHints(); + boolean supportsDarkText = (hints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0; + boolean supportsDarkTheme = (hints & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0; + Assert.assertTrue("X-small white surface should support dark text.", + supportsDarkText); + Assert.assertFalse("X-small white surface shouldn't support dark theme.", + supportsDarkTheme); + } + + /** + * Check that black surface doesn't support dark text + */ + @Test + public void blackSurfaceColorHintsTest() { + Bitmap image = Bitmap.createBitmap(30, 30, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(image); + + canvas.drawColor(Color.BLACK); + int hints = WallpaperColors.fromBitmap(image).getColorHints(); + boolean supportsDarkText = (hints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0; + boolean supportsDarkTheme = (hints & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0; + Assert.assertFalse("Black surface shouldn't support dark text.", supportsDarkText); + Assert.assertTrue("Black surface should support dark theme.", supportsDarkTheme); + } + + /** + * Check that bitmap hint properly indicates when object created via WallpaperColors#fromBitmap + * versus WallpaperColors() public constructor + */ + @Test + public void bitmapHintsTest() { + Bitmap image = Bitmap.createBitmap(30, 30, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(image); + + canvas.drawColor(Color.WHITE); + int hints = WallpaperColors.fromBitmap(image).getColorHints(); + + boolean fromBitmap = (hints & WallpaperColors.HINT_FROM_BITMAP) != 0; + Assert.assertTrue("From bitmap should be true if object was created " + + "using WallpaperColors#fromBitmap.", fromBitmap); WallpaperColors colors = new WallpaperColors(Color.valueOf(Color.GREEN), null, null); fromBitmap = (colors.getColorHints() & WallpaperColors.HINT_FROM_BITMAP) != 0;