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
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -1659,14 +1659,14 @@ public class WallpaperManager {
|
||||
* @hide
|
||||
*/
|
||||
public void addOnColorsChangedListener(@NonNull LocalWallpaperColorConsumer callback,
|
||||
List<RectF> regions) throws IllegalArgumentException {
|
||||
List<RectF> 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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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<RectF>()
|
||||
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<RectF>()
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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<WallpaperManager.LocalWallpaperColorConsumer>
|
||||
|
||||
@Captor private lateinit var layoutChangedListener: ArgumentCaptor<View.OnLayoutChangeListener>
|
||||
|
||||
@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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user