Loading packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt +96 −56 Original line number Diff line number Diff line Loading @@ -15,39 +15,36 @@ */ package com.android.systemui.shared.regionsampling import android.app.WallpaperColors import android.app.WallpaperManager import android.graphics.Color import android.graphics.Point import android.graphics.Rect import android.graphics.RectF import android.view.View import androidx.annotation.VisibleForTesting import com.android.systemui.shared.navigationbar.RegionSamplingHelper import com.android.systemui.shared.navigationbar.RegionSamplingHelper.SamplingCallback import java.io.PrintWriter import java.util.concurrent.Executor /** Class for instance of RegionSamplingHelper */ open class RegionSampler( sampledView: View?, open class RegionSampler @JvmOverloads constructor( val sampledView: View?, mainExecutor: Executor?, bgExecutor: Executor?, regionSamplingEnabled: Boolean, updateFun: UpdateColorCallback ) { val bgExecutor: Executor?, val regionSamplingEnabled: Boolean, 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 @VisibleForTesting open fun createRegionSamplingHelper( sampledView: View, callback: SamplingCallback, mainExecutor: Executor?, bgExecutor: Executor? ): RegionSamplingHelper { return RegionSamplingHelper(sampledView, callback, mainExecutor, bgExecutor) } private val displaySize = Point() /** * Sets the colors to be used for Dark and Light Foreground. Loading @@ -73,7 +70,7 @@ open class RegionSampler( } } private fun convertToClockDarkness(isRegionDark: Boolean): RegionDarkness { private fun getRegionDarkness(isRegionDark: Boolean): RegionDarkness { return if (isRegionDark) { RegionDarkness.DARK } else { Loading @@ -87,12 +84,32 @@ open class RegionSampler( /** Start region sampler */ fun startRegionSampler() { regionSampler?.start(samplingBounds) if (!regionSamplingEnabled || sampledView == null) { return } val sampledRegion = calculateSampledRegion(sampledView) val regions = ArrayList<RectF>() val sampledRegionWithOffset = convertBounds(sampledRegion) regions.add(sampledRegionWithOffset) wallpaperManager?.removeOnColorsChangedListener(this) wallpaperManager?.addOnColorsChangedListener(this, regions) // TODO(b/265969235): conditionally set FLAG_LOCK or FLAG_SYSTEM once HS smartspace // implemented bgExecutor?.execute( Runnable { val initialSampling = wallpaperManager?.getWallpaperColors(WallpaperManager.FLAG_LOCK) onColorsChanged(sampledRegionWithOffset, initialSampling) } ) } /** Stop region sampler */ fun stopRegionSampler() { regionSampler?.stop() wallpaperManager?.removeOnColorsChangedListener(this) } /** Dump region sampler */ Loading @@ -100,43 +117,66 @@ open class RegionSampler( regionSampler?.dump(pw) } init { if (regionSamplingEnabled && sampledView != null) { regionSampler = createRegionSamplingHelper( sampledView, object : SamplingCallback { override fun onRegionDarknessChanged(isRegionDark: Boolean) { regionDarkness = convertToClockDarkness(isRegionDark) updateFun() } fun calculateSampledRegion(sampledView: View): RectF { val screenLocation = tmpScreenLocation /** * The method getLocationOnScreen is used to obtain the view coordinates * relative to its left and top edges on the device screen. Directly * accessing the X and Y coordinates of the view returns the location * relative to its parent view instead. * The method getLocationOnScreen is used to obtain the view coordinates relative to its * left and top edges on the device screen. Directly accessing the X and Y coordinates of * the view returns the location relative to its parent view instead. */ override fun getSampledRegion(sampledView: View): Rect { val screenLocation = tmpScreenLocation sampledView.getLocationOnScreen(screenLocation) val left = screenLocation[0] val top = screenLocation[1] samplingBounds.left = left samplingBounds.top = top samplingBounds.right = left + sampledView.width samplingBounds.bottom = top + sampledView.height return samplingBounds return RectF(samplingBounds) } override fun isSamplingEnabled(): Boolean { return regionSamplingEnabled /** * Convert the bounds of the region we want to sample from to fractional offsets because * WallpaperManager requires the bounds to be between [0,1]. The wallpaper is treated as one * continuous image, so if there are multiple screens, then each screen falls into a fractional * range. For instance, 4 screens have the ranges [0, 0.25], [0,25, 0.5], [0.5, 0.75], [0.75, * 1]. */ fun convertBounds(originalBounds: RectF): RectF { // TODO(b/265969235): GRAB # PAGES + CURRENT WALLPAPER PAGE # FROM LAUNCHER // TODO(b/265968912): remove hard-coded value once LS wallpaper supported val wallpaperPageNum = 0 val numScreens = 1 val screenWidth = displaySize.x // TODO: investigate small difference between this and the height reported in go/web-hv val screenHeight = displaySize.y val newBounds = RectF() // horizontal newBounds.left = ((originalBounds.left / screenWidth) + wallpaperPageNum) / numScreens newBounds.right = ((originalBounds.right / screenWidth) + wallpaperPageNum) / numScreens // vertical newBounds.top = originalBounds.top / screenHeight newBounds.bottom = originalBounds.bottom / screenHeight return newBounds } }, mainExecutor, bgExecutor ) init { sampledView?.context?.display?.getSize(displaySize) } regionSampler?.setWindowVisible(true) override fun onColorsChanged(area: RectF?, colors: WallpaperColors?) { // update text color when wallpaper color changes regionDarkness = getRegionDarkness( (colors?.colorHints?.and(WallpaperColors.HINT_SUPPORTS_DARK_TEXT)) != WallpaperColors.HINT_SUPPORTS_DARK_TEXT ) updateForegroundColor() } } Loading packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +88 −49 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.content.res.Resources import android.text.format.DateFormat import android.util.TypedValue import android.view.View import android.widget.FrameLayout import androidx.annotation.VisibleForTesting import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle Loading @@ -47,18 +48,17 @@ import com.android.systemui.shared.regionsampling.RegionSampler import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback import com.android.systemui.statusbar.policy.ConfigurationController import java.io.PrintWriter 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.collect import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch import java.io.PrintWriter 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 Loading Loading @@ -89,7 +89,11 @@ open class ClockEventController @Inject constructor( value.largeClock.logBuffer = largeLogBuffer value.initialize(resources, dozeAmount, 0f) updateRegionSamplers(value) if (regionSamplingEnabled) { clock?.smallClock?.view?.addOnLayoutChangeListener(mLayoutChangedListener) clock?.largeClock?.view?.addOnLayoutChangeListener(mLayoutChangedListener) } updateFontSizes() } } Loading @@ -104,47 +108,87 @@ open class ClockEventController @Inject constructor( private var disposableHandle: DisposableHandle? = null private val regionSamplingEnabled = featureFlags.isEnabled(REGION_SAMPLING) private fun updateColors() { private val mLayoutChangedListener = object : View.OnLayoutChangeListener { private var currentSmallClockView: View? = null private var currentLargeClockView: View? = null private var currentSmallClockLocation = IntArray(2) private var currentLargeClockLocation = IntArray(2) override fun onLayoutChange( view: View?, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int ) { 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 } if (regionSamplingEnabled && smallRegionSampler != null && largeRegionSampler != null) { // SMALL CLOCK if (parent.id == R.id.lockscreen_clock_view) { // view bounds have changed due to clock size changing (i.e. different character widths) // AND/OR the view has been translated when transitioning between small and large clock if (view != currentSmallClockView || !view.locationOnScreen.contentEquals(currentSmallClockLocation)) { currentSmallClockView = view currentSmallClockLocation = view.locationOnScreen updateRegionSampler(view) } } // LARGE CLOCK else if (parent.id == R.id.lockscreen_clock_view_large) { if (view != currentLargeClockView || !view.locationOnScreen.contentEquals(currentLargeClockLocation)) { currentLargeClockView = view currentLargeClockLocation = view.locationOnScreen updateRegionSampler(view) } } } } private fun updateColors() { val wallpaperManager = WallpaperManager.getInstance(context) if (!wallpaperManager.lockScreenWallpaperExists()) { smallClockIsDark = smallRegionSampler!!.currentRegionDarkness().isDark largeClockIsDark = largeRegionSampler!!.currentRegionDarkness().isDark if (regionSamplingEnabled && !wallpaperManager.lockScreenWallpaperExists()) { if (regionSampler != null) { if (regionSampler?.sampledView == clock?.smallClock?.view) { smallClockIsDark = regionSampler!!.currentRegionDarkness().isDark clock?.smallClock?.events?.onRegionDarknessChanged(smallClockIsDark) return } else if (regionSampler?.sampledView == clock?.largeClock?.view) { largeClockIsDark = regionSampler!!.currentRegionDarkness().isDark clock?.largeClock?.events?.onRegionDarknessChanged(largeClockIsDark) return } } else { } } val isLightTheme = TypedValue() context.theme.resolveAttribute(android.R.attr.isLightTheme, isLightTheme, true) smallClockIsDark = isLightTheme.data == 0 largeClockIsDark = isLightTheme.data == 0 } clock?.smallClock?.events?.onRegionDarknessChanged(smallClockIsDark) clock?.largeClock?.events?.onRegionDarknessChanged(largeClockIsDark) } private fun updateRegionSamplers(currentClock: ClockController?) { smallRegionSampler?.stopRegionSampler() largeRegionSampler?.stopRegionSampler() smallRegionSampler = createRegionSampler( currentClock?.smallClock?.view, mainExecutor, bgExecutor, regionSamplingEnabled, ::updateColors ) largeRegionSampler = createRegionSampler( currentClock?.largeClock?.view, private fun updateRegionSampler(sampledRegion: View) { regionSampler?.stopRegionSampler() regionSampler = createRegionSampler( sampledRegion, mainExecutor, bgExecutor, regionSamplingEnabled, ::updateColors ) smallRegionSampler!!.startRegionSampler() largeRegionSampler!!.startRegionSampler() )?.apply { startRegionSampler() } updateColors() } Loading @@ -155,7 +199,7 @@ open class ClockEventController @Inject constructor( bgExecutor: Executor?, regionSamplingEnabled: Boolean, updateColors: () -> Unit ): RegionSampler { ): RegionSampler? { return RegionSampler( sampledView, mainExecutor, Loading @@ -164,8 +208,7 @@ open class ClockEventController @Inject constructor( updateColors) } var smallRegionSampler: RegionSampler? = null var largeRegionSampler: RegionSampler? = null var regionSampler: RegionSampler? = null private var smallClockIsDark = true private var largeClockIsDark = true Loading Loading @@ -232,8 +275,6 @@ open class ClockEventController @Inject constructor( configurationController.addCallback(configListener) batteryController.addCallback(batteryCallback) keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback) smallRegionSampler?.startRegionSampler() largeRegionSampler?.startRegionSampler() disposableHandle = parent.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { listenForDozing(this) Loading @@ -258,8 +299,7 @@ open class ClockEventController @Inject constructor( configurationController.removeCallback(configListener) batteryController.removeCallback(batteryCallback) keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback) smallRegionSampler?.stopRegionSampler() largeRegionSampler?.stopRegionSampler() regionSampler?.stopRegionSampler() } private fun updateFontSizes() { Loading @@ -275,8 +315,7 @@ open class ClockEventController @Inject constructor( fun dump(pw: PrintWriter) { pw.println(this) clock?.dump(pw) smallRegionSampler?.dump(pw) largeRegionSampler?.dump(pw) regionSampler?.dump(pw) } @VisibleForTesting Loading packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt +21 −21 Original line number Diff line number Diff line Loading @@ -100,7 +100,7 @@ constructor( private val regionSamplingEnabled = featureFlags.isEnabled(Flags.REGION_SAMPLING) private var isContentUpdatedOnce = false private var showNotifications = false private var showSensitiveContentForCurrentUser = false private var showSensitiveContentForManagedUser = false Loading @@ -114,19 +114,6 @@ constructor( override fun onViewAttachedToWindow(v: View) { smartspaceViews.add(v as SmartspaceView) if (regionSamplingEnabled) { var regionSampler = RegionSampler( v, uiExecutor, bgExecutor, regionSamplingEnabled, updateFun ) initializeTextColors(regionSampler) regionSampler.startRegionSampler() regionSamplers.put(v, regionSampler) } connectSession() updateTextColorFromWallpaper() Loading @@ -136,12 +123,6 @@ constructor( override fun onViewDetachedFromWindow(v: View) { smartspaceViews.remove(v as SmartspaceView) if (regionSamplingEnabled) { var regionSampler = regionSamplers.getValue(v) regionSampler.stopRegionSampler() regionSamplers.remove(v) } if (smartspaceViews.isEmpty()) { disconnect() } Loading @@ -152,6 +133,24 @@ constructor( execution.assertIsMainThread() val filteredTargets = targets.filter(::filterSmartspaceTarget) plugin?.onTargetsAvailable(filteredTargets) if (!isContentUpdatedOnce) { for (v in smartspaceViews) { if (regionSamplingEnabled) { var regionSampler = RegionSampler( v as View, uiExecutor, bgExecutor, regionSamplingEnabled, updateFun ) initializeTextColors(regionSampler) regionSamplers[v] = regionSampler regionSampler.startRegionSampler() } updateTextColorFromWallpaper() } isContentUpdatedOnce = true } } private val userTrackerCallback = object : UserTracker.Callback { Loading Loading @@ -398,7 +397,8 @@ constructor( private fun updateTextColorFromWallpaper() { val wallpaperManager = WallpaperManager.getInstance(context) if (!regionSamplingEnabled || wallpaperManager.lockScreenWallpaperExists()) { if (!regionSamplingEnabled || wallpaperManager.lockScreenWallpaperExists() || regionSamplers.isEmpty()) { val wallpaperTextColor = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor) smartspaceViews.forEach { it.setPrimaryTextColor(wallpaperTextColor) } Loading packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt +3 −6 Original line number Diff line number Diff line Loading @@ -51,7 +51,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyFloat import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock Loading Loading @@ -140,8 +139,9 @@ class ClockEventControllerTest : SysuiTestCase() { @Test fun themeChanged_verifyClockPaletteUpdated() = runBlocking(IMMEDIATE) { verify(smallClockEvents).onRegionDarknessChanged(anyBoolean()) verify(largeClockEvents).onRegionDarknessChanged(anyBoolean()) // TODO(b/266103601): delete this test and add more coverage for updateColors() // verify(smallClockEvents).onRegionDarknessChanged(anyBoolean()) // verify(largeClockEvents).onRegionDarknessChanged(anyBoolean()) val captor = argumentCaptor<ConfigurationController.ConfigurationListener>() verify(configurationController).addCallback(capture(captor)) Loading @@ -152,9 +152,6 @@ class ClockEventControllerTest : SysuiTestCase() { @Test fun fontChanged_verifyFontSizeUpdated() = runBlocking(IMMEDIATE) { verify(smallClockEvents).onRegionDarknessChanged(anyBoolean()) verify(largeClockEvents).onRegionDarknessChanged(anyBoolean()) val captor = argumentCaptor<ConfigurationController.ConfigurationListener>() verify(configurationController).addCallback(capture(captor)) captor.value.onDensityOrFontScaleChanged() Loading packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt +3 −55 Original line number Diff line number Diff line package com.android.systemui.shared.regionsampling import android.graphics.Rect import android.app.WallpaperManager import android.testing.AndroidTestingRunner import android.view.View import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.shared.navigationbar.RegionSamplingHelper import java.io.PrintWriter import java.util.concurrent.Executor import org.junit.Assert import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit Loading @@ -28,9 +24,8 @@ class RegionSamplerTest : SysuiTestCase() { @Mock private lateinit var sampledView: View @Mock private lateinit var mainExecutor: Executor @Mock private lateinit var bgExecutor: Executor @Mock private lateinit var regionSampler: RegionSamplingHelper @Mock private lateinit var pw: PrintWriter @Mock private lateinit var callback: RegionSamplingHelper.SamplingCallback @Mock private lateinit var wallpaperManager: WallpaperManager private lateinit var mRegionSampler: RegionSampler private var updateFun: UpdateColorCallback = {} Loading @@ -38,65 +33,18 @@ class RegionSamplerTest : SysuiTestCase() { @Before fun setUp() { whenever(sampledView.isAttachedToWindow).thenReturn(true) whenever(regionSampler.callback).thenReturn(this@RegionSamplerTest.callback) mRegionSampler = object : RegionSampler(sampledView, mainExecutor, bgExecutor, true, updateFun) { override fun createRegionSamplingHelper( sampledView: View, callback: RegionSamplingHelper.SamplingCallback, mainExecutor: Executor?, bgExecutor: Executor? ): RegionSamplingHelper { return this@RegionSamplerTest.regionSampler } } RegionSampler(sampledView, mainExecutor, bgExecutor, true, updateFun, wallpaperManager) } @Test fun testStartRegionSampler() { mRegionSampler.startRegionSampler() verify(regionSampler).start(Rect(0, 0, 0, 0)) } @Test fun testStopRegionSampler() { mRegionSampler.stopRegionSampler() verify(regionSampler).stop() } @Test fun testDump() { mRegionSampler.dump(pw) verify(regionSampler).dump(pw) } @Test fun testUpdateColorCallback() { regionSampler.callback.onRegionDarknessChanged(false) verify(regionSampler.callback).onRegionDarknessChanged(false) clearInvocations(regionSampler.callback) regionSampler.callback.onRegionDarknessChanged(true) verify(regionSampler.callback).onRegionDarknessChanged(true) } @Test fun testFlagFalse() { mRegionSampler = object : RegionSampler(sampledView, mainExecutor, bgExecutor, false, updateFun) { override fun createRegionSamplingHelper( sampledView: View, callback: RegionSamplingHelper.SamplingCallback, mainExecutor: Executor?, bgExecutor: Executor? ): RegionSamplingHelper { return this@RegionSamplerTest.regionSampler } } Assert.assertEquals(mRegionSampler.regionSampler, null) } } Loading
packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt +96 −56 Original line number Diff line number Diff line Loading @@ -15,39 +15,36 @@ */ package com.android.systemui.shared.regionsampling import android.app.WallpaperColors import android.app.WallpaperManager import android.graphics.Color import android.graphics.Point import android.graphics.Rect import android.graphics.RectF import android.view.View import androidx.annotation.VisibleForTesting import com.android.systemui.shared.navigationbar.RegionSamplingHelper import com.android.systemui.shared.navigationbar.RegionSamplingHelper.SamplingCallback import java.io.PrintWriter import java.util.concurrent.Executor /** Class for instance of RegionSamplingHelper */ open class RegionSampler( sampledView: View?, open class RegionSampler @JvmOverloads constructor( val sampledView: View?, mainExecutor: Executor?, bgExecutor: Executor?, regionSamplingEnabled: Boolean, updateFun: UpdateColorCallback ) { val bgExecutor: Executor?, val regionSamplingEnabled: Boolean, 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 @VisibleForTesting open fun createRegionSamplingHelper( sampledView: View, callback: SamplingCallback, mainExecutor: Executor?, bgExecutor: Executor? ): RegionSamplingHelper { return RegionSamplingHelper(sampledView, callback, mainExecutor, bgExecutor) } private val displaySize = Point() /** * Sets the colors to be used for Dark and Light Foreground. Loading @@ -73,7 +70,7 @@ open class RegionSampler( } } private fun convertToClockDarkness(isRegionDark: Boolean): RegionDarkness { private fun getRegionDarkness(isRegionDark: Boolean): RegionDarkness { return if (isRegionDark) { RegionDarkness.DARK } else { Loading @@ -87,12 +84,32 @@ open class RegionSampler( /** Start region sampler */ fun startRegionSampler() { regionSampler?.start(samplingBounds) if (!regionSamplingEnabled || sampledView == null) { return } val sampledRegion = calculateSampledRegion(sampledView) val regions = ArrayList<RectF>() val sampledRegionWithOffset = convertBounds(sampledRegion) regions.add(sampledRegionWithOffset) wallpaperManager?.removeOnColorsChangedListener(this) wallpaperManager?.addOnColorsChangedListener(this, regions) // TODO(b/265969235): conditionally set FLAG_LOCK or FLAG_SYSTEM once HS smartspace // implemented bgExecutor?.execute( Runnable { val initialSampling = wallpaperManager?.getWallpaperColors(WallpaperManager.FLAG_LOCK) onColorsChanged(sampledRegionWithOffset, initialSampling) } ) } /** Stop region sampler */ fun stopRegionSampler() { regionSampler?.stop() wallpaperManager?.removeOnColorsChangedListener(this) } /** Dump region sampler */ Loading @@ -100,43 +117,66 @@ open class RegionSampler( regionSampler?.dump(pw) } init { if (regionSamplingEnabled && sampledView != null) { regionSampler = createRegionSamplingHelper( sampledView, object : SamplingCallback { override fun onRegionDarknessChanged(isRegionDark: Boolean) { regionDarkness = convertToClockDarkness(isRegionDark) updateFun() } fun calculateSampledRegion(sampledView: View): RectF { val screenLocation = tmpScreenLocation /** * The method getLocationOnScreen is used to obtain the view coordinates * relative to its left and top edges on the device screen. Directly * accessing the X and Y coordinates of the view returns the location * relative to its parent view instead. * The method getLocationOnScreen is used to obtain the view coordinates relative to its * left and top edges on the device screen. Directly accessing the X and Y coordinates of * the view returns the location relative to its parent view instead. */ override fun getSampledRegion(sampledView: View): Rect { val screenLocation = tmpScreenLocation sampledView.getLocationOnScreen(screenLocation) val left = screenLocation[0] val top = screenLocation[1] samplingBounds.left = left samplingBounds.top = top samplingBounds.right = left + sampledView.width samplingBounds.bottom = top + sampledView.height return samplingBounds return RectF(samplingBounds) } override fun isSamplingEnabled(): Boolean { return regionSamplingEnabled /** * Convert the bounds of the region we want to sample from to fractional offsets because * WallpaperManager requires the bounds to be between [0,1]. The wallpaper is treated as one * continuous image, so if there are multiple screens, then each screen falls into a fractional * range. For instance, 4 screens have the ranges [0, 0.25], [0,25, 0.5], [0.5, 0.75], [0.75, * 1]. */ fun convertBounds(originalBounds: RectF): RectF { // TODO(b/265969235): GRAB # PAGES + CURRENT WALLPAPER PAGE # FROM LAUNCHER // TODO(b/265968912): remove hard-coded value once LS wallpaper supported val wallpaperPageNum = 0 val numScreens = 1 val screenWidth = displaySize.x // TODO: investigate small difference between this and the height reported in go/web-hv val screenHeight = displaySize.y val newBounds = RectF() // horizontal newBounds.left = ((originalBounds.left / screenWidth) + wallpaperPageNum) / numScreens newBounds.right = ((originalBounds.right / screenWidth) + wallpaperPageNum) / numScreens // vertical newBounds.top = originalBounds.top / screenHeight newBounds.bottom = originalBounds.bottom / screenHeight return newBounds } }, mainExecutor, bgExecutor ) init { sampledView?.context?.display?.getSize(displaySize) } regionSampler?.setWindowVisible(true) override fun onColorsChanged(area: RectF?, colors: WallpaperColors?) { // update text color when wallpaper color changes regionDarkness = getRegionDarkness( (colors?.colorHints?.and(WallpaperColors.HINT_SUPPORTS_DARK_TEXT)) != WallpaperColors.HINT_SUPPORTS_DARK_TEXT ) updateForegroundColor() } } Loading
packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +88 −49 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.content.res.Resources import android.text.format.DateFormat import android.util.TypedValue import android.view.View import android.widget.FrameLayout import androidx.annotation.VisibleForTesting import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle Loading @@ -47,18 +48,17 @@ import com.android.systemui.shared.regionsampling.RegionSampler import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback import com.android.systemui.statusbar.policy.ConfigurationController import java.io.PrintWriter 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.collect import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch import java.io.PrintWriter 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 Loading Loading @@ -89,7 +89,11 @@ open class ClockEventController @Inject constructor( value.largeClock.logBuffer = largeLogBuffer value.initialize(resources, dozeAmount, 0f) updateRegionSamplers(value) if (regionSamplingEnabled) { clock?.smallClock?.view?.addOnLayoutChangeListener(mLayoutChangedListener) clock?.largeClock?.view?.addOnLayoutChangeListener(mLayoutChangedListener) } updateFontSizes() } } Loading @@ -104,47 +108,87 @@ open class ClockEventController @Inject constructor( private var disposableHandle: DisposableHandle? = null private val regionSamplingEnabled = featureFlags.isEnabled(REGION_SAMPLING) private fun updateColors() { private val mLayoutChangedListener = object : View.OnLayoutChangeListener { private var currentSmallClockView: View? = null private var currentLargeClockView: View? = null private var currentSmallClockLocation = IntArray(2) private var currentLargeClockLocation = IntArray(2) override fun onLayoutChange( view: View?, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int ) { 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 } if (regionSamplingEnabled && smallRegionSampler != null && largeRegionSampler != null) { // SMALL CLOCK if (parent.id == R.id.lockscreen_clock_view) { // view bounds have changed due to clock size changing (i.e. different character widths) // AND/OR the view has been translated when transitioning between small and large clock if (view != currentSmallClockView || !view.locationOnScreen.contentEquals(currentSmallClockLocation)) { currentSmallClockView = view currentSmallClockLocation = view.locationOnScreen updateRegionSampler(view) } } // LARGE CLOCK else if (parent.id == R.id.lockscreen_clock_view_large) { if (view != currentLargeClockView || !view.locationOnScreen.contentEquals(currentLargeClockLocation)) { currentLargeClockView = view currentLargeClockLocation = view.locationOnScreen updateRegionSampler(view) } } } } private fun updateColors() { val wallpaperManager = WallpaperManager.getInstance(context) if (!wallpaperManager.lockScreenWallpaperExists()) { smallClockIsDark = smallRegionSampler!!.currentRegionDarkness().isDark largeClockIsDark = largeRegionSampler!!.currentRegionDarkness().isDark if (regionSamplingEnabled && !wallpaperManager.lockScreenWallpaperExists()) { if (regionSampler != null) { if (regionSampler?.sampledView == clock?.smallClock?.view) { smallClockIsDark = regionSampler!!.currentRegionDarkness().isDark clock?.smallClock?.events?.onRegionDarknessChanged(smallClockIsDark) return } else if (regionSampler?.sampledView == clock?.largeClock?.view) { largeClockIsDark = regionSampler!!.currentRegionDarkness().isDark clock?.largeClock?.events?.onRegionDarknessChanged(largeClockIsDark) return } } else { } } val isLightTheme = TypedValue() context.theme.resolveAttribute(android.R.attr.isLightTheme, isLightTheme, true) smallClockIsDark = isLightTheme.data == 0 largeClockIsDark = isLightTheme.data == 0 } clock?.smallClock?.events?.onRegionDarknessChanged(smallClockIsDark) clock?.largeClock?.events?.onRegionDarknessChanged(largeClockIsDark) } private fun updateRegionSamplers(currentClock: ClockController?) { smallRegionSampler?.stopRegionSampler() largeRegionSampler?.stopRegionSampler() smallRegionSampler = createRegionSampler( currentClock?.smallClock?.view, mainExecutor, bgExecutor, regionSamplingEnabled, ::updateColors ) largeRegionSampler = createRegionSampler( currentClock?.largeClock?.view, private fun updateRegionSampler(sampledRegion: View) { regionSampler?.stopRegionSampler() regionSampler = createRegionSampler( sampledRegion, mainExecutor, bgExecutor, regionSamplingEnabled, ::updateColors ) smallRegionSampler!!.startRegionSampler() largeRegionSampler!!.startRegionSampler() )?.apply { startRegionSampler() } updateColors() } Loading @@ -155,7 +199,7 @@ open class ClockEventController @Inject constructor( bgExecutor: Executor?, regionSamplingEnabled: Boolean, updateColors: () -> Unit ): RegionSampler { ): RegionSampler? { return RegionSampler( sampledView, mainExecutor, Loading @@ -164,8 +208,7 @@ open class ClockEventController @Inject constructor( updateColors) } var smallRegionSampler: RegionSampler? = null var largeRegionSampler: RegionSampler? = null var regionSampler: RegionSampler? = null private var smallClockIsDark = true private var largeClockIsDark = true Loading Loading @@ -232,8 +275,6 @@ open class ClockEventController @Inject constructor( configurationController.addCallback(configListener) batteryController.addCallback(batteryCallback) keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback) smallRegionSampler?.startRegionSampler() largeRegionSampler?.startRegionSampler() disposableHandle = parent.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { listenForDozing(this) Loading @@ -258,8 +299,7 @@ open class ClockEventController @Inject constructor( configurationController.removeCallback(configListener) batteryController.removeCallback(batteryCallback) keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback) smallRegionSampler?.stopRegionSampler() largeRegionSampler?.stopRegionSampler() regionSampler?.stopRegionSampler() } private fun updateFontSizes() { Loading @@ -275,8 +315,7 @@ open class ClockEventController @Inject constructor( fun dump(pw: PrintWriter) { pw.println(this) clock?.dump(pw) smallRegionSampler?.dump(pw) largeRegionSampler?.dump(pw) regionSampler?.dump(pw) } @VisibleForTesting Loading
packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt +21 −21 Original line number Diff line number Diff line Loading @@ -100,7 +100,7 @@ constructor( private val regionSamplingEnabled = featureFlags.isEnabled(Flags.REGION_SAMPLING) private var isContentUpdatedOnce = false private var showNotifications = false private var showSensitiveContentForCurrentUser = false private var showSensitiveContentForManagedUser = false Loading @@ -114,19 +114,6 @@ constructor( override fun onViewAttachedToWindow(v: View) { smartspaceViews.add(v as SmartspaceView) if (regionSamplingEnabled) { var regionSampler = RegionSampler( v, uiExecutor, bgExecutor, regionSamplingEnabled, updateFun ) initializeTextColors(regionSampler) regionSampler.startRegionSampler() regionSamplers.put(v, regionSampler) } connectSession() updateTextColorFromWallpaper() Loading @@ -136,12 +123,6 @@ constructor( override fun onViewDetachedFromWindow(v: View) { smartspaceViews.remove(v as SmartspaceView) if (regionSamplingEnabled) { var regionSampler = regionSamplers.getValue(v) regionSampler.stopRegionSampler() regionSamplers.remove(v) } if (smartspaceViews.isEmpty()) { disconnect() } Loading @@ -152,6 +133,24 @@ constructor( execution.assertIsMainThread() val filteredTargets = targets.filter(::filterSmartspaceTarget) plugin?.onTargetsAvailable(filteredTargets) if (!isContentUpdatedOnce) { for (v in smartspaceViews) { if (regionSamplingEnabled) { var regionSampler = RegionSampler( v as View, uiExecutor, bgExecutor, regionSamplingEnabled, updateFun ) initializeTextColors(regionSampler) regionSamplers[v] = regionSampler regionSampler.startRegionSampler() } updateTextColorFromWallpaper() } isContentUpdatedOnce = true } } private val userTrackerCallback = object : UserTracker.Callback { Loading Loading @@ -398,7 +397,8 @@ constructor( private fun updateTextColorFromWallpaper() { val wallpaperManager = WallpaperManager.getInstance(context) if (!regionSamplingEnabled || wallpaperManager.lockScreenWallpaperExists()) { if (!regionSamplingEnabled || wallpaperManager.lockScreenWallpaperExists() || regionSamplers.isEmpty()) { val wallpaperTextColor = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor) smartspaceViews.forEach { it.setPrimaryTextColor(wallpaperTextColor) } Loading
packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt +3 −6 Original line number Diff line number Diff line Loading @@ -51,7 +51,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyFloat import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock Loading Loading @@ -140,8 +139,9 @@ class ClockEventControllerTest : SysuiTestCase() { @Test fun themeChanged_verifyClockPaletteUpdated() = runBlocking(IMMEDIATE) { verify(smallClockEvents).onRegionDarknessChanged(anyBoolean()) verify(largeClockEvents).onRegionDarknessChanged(anyBoolean()) // TODO(b/266103601): delete this test and add more coverage for updateColors() // verify(smallClockEvents).onRegionDarknessChanged(anyBoolean()) // verify(largeClockEvents).onRegionDarknessChanged(anyBoolean()) val captor = argumentCaptor<ConfigurationController.ConfigurationListener>() verify(configurationController).addCallback(capture(captor)) Loading @@ -152,9 +152,6 @@ class ClockEventControllerTest : SysuiTestCase() { @Test fun fontChanged_verifyFontSizeUpdated() = runBlocking(IMMEDIATE) { verify(smallClockEvents).onRegionDarknessChanged(anyBoolean()) verify(largeClockEvents).onRegionDarknessChanged(anyBoolean()) val captor = argumentCaptor<ConfigurationController.ConfigurationListener>() verify(configurationController).addCallback(capture(captor)) captor.value.onDensityOrFontScaleChanged() Loading
packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt +3 −55 Original line number Diff line number Diff line package com.android.systemui.shared.regionsampling import android.graphics.Rect import android.app.WallpaperManager import android.testing.AndroidTestingRunner import android.view.View import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.shared.navigationbar.RegionSamplingHelper import java.io.PrintWriter import java.util.concurrent.Executor import org.junit.Assert import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit Loading @@ -28,9 +24,8 @@ class RegionSamplerTest : SysuiTestCase() { @Mock private lateinit var sampledView: View @Mock private lateinit var mainExecutor: Executor @Mock private lateinit var bgExecutor: Executor @Mock private lateinit var regionSampler: RegionSamplingHelper @Mock private lateinit var pw: PrintWriter @Mock private lateinit var callback: RegionSamplingHelper.SamplingCallback @Mock private lateinit var wallpaperManager: WallpaperManager private lateinit var mRegionSampler: RegionSampler private var updateFun: UpdateColorCallback = {} Loading @@ -38,65 +33,18 @@ class RegionSamplerTest : SysuiTestCase() { @Before fun setUp() { whenever(sampledView.isAttachedToWindow).thenReturn(true) whenever(regionSampler.callback).thenReturn(this@RegionSamplerTest.callback) mRegionSampler = object : RegionSampler(sampledView, mainExecutor, bgExecutor, true, updateFun) { override fun createRegionSamplingHelper( sampledView: View, callback: RegionSamplingHelper.SamplingCallback, mainExecutor: Executor?, bgExecutor: Executor? ): RegionSamplingHelper { return this@RegionSamplerTest.regionSampler } } RegionSampler(sampledView, mainExecutor, bgExecutor, true, updateFun, wallpaperManager) } @Test fun testStartRegionSampler() { mRegionSampler.startRegionSampler() verify(regionSampler).start(Rect(0, 0, 0, 0)) } @Test fun testStopRegionSampler() { mRegionSampler.stopRegionSampler() verify(regionSampler).stop() } @Test fun testDump() { mRegionSampler.dump(pw) verify(regionSampler).dump(pw) } @Test fun testUpdateColorCallback() { regionSampler.callback.onRegionDarknessChanged(false) verify(regionSampler.callback).onRegionDarknessChanged(false) clearInvocations(regionSampler.callback) regionSampler.callback.onRegionDarknessChanged(true) verify(regionSampler.callback).onRegionDarknessChanged(true) } @Test fun testFlagFalse() { mRegionSampler = object : RegionSampler(sampledView, mainExecutor, bgExecutor, false, updateFun) { override fun createRegionSamplingHelper( sampledView: View, callback: RegionSamplingHelper.SamplingCallback, mainExecutor: Executor?, bgExecutor: Executor? ): RegionSamplingHelper { return this@RegionSamplerTest.regionSampler } } Assert.assertEquals(mRegionSampler.regionSampler, null) } }