blob: 0e1e0cb2adcf0783eab92c05d19268fc1d53dbd9 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.keyguard
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.res.Resources
import android.text.format.DateFormat
import android.util.TypedValue
import android.view.View
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.plugins.Clock
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shared.regionsampling.RegionSamplingInstance
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
/**
* Controller for a Clock provided by the registry and used on the keyguard. Instantiated by
* [KeyguardClockSwitchController]. Functionality is forked from [AnimatableClockController].
*/
open class ClockEventController @Inject constructor(
private val statusBarStateController: StatusBarStateController,
private val broadcastDispatcher: BroadcastDispatcher,
private val batteryController: BatteryController,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val configurationController: ConfigurationController,
@Main private val resources: Resources,
private val context: Context,
@Main private val mainExecutor: Executor,
@Background private val bgExecutor: Executor,
private val featureFlags: FeatureFlags
) {
var clock: Clock? = null
set(value) {
field = value
if (value != null) {
value.initialize(resources, dozeAmount, 0f)
updateRegionSamplers(value)
}
}
private var isDozing = false
private set
private var isCharging = false
private var dozeAmount = 0f
private var isKeyguardShowing = false
private val regionSamplingEnabled =
featureFlags.isEnabled(com.android.systemui.flags.Flags.REGION_SAMPLING)
private val updateFun = object : RegionSamplingInstance.UpdateColorCallback {
override fun updateColors() {
if (regionSamplingEnabled) {
smallClockIsDark = smallRegionSamplingInstance.currentRegionDarkness().isDark
largeClockIsDark = largeRegionSamplingInstance.currentRegionDarkness().isDark
} else {
val isLightTheme = TypedValue()
context.theme.resolveAttribute(android.R.attr.isLightTheme, isLightTheme, true)
smallClockIsDark = isLightTheme.data == 0
largeClockIsDark = isLightTheme.data == 0
}
clock?.events?.onColorPaletteChanged(resources, smallClockIsDark, largeClockIsDark)
}
}
fun updateRegionSamplers(currentClock: Clock?) {
smallRegionSamplingInstance = createRegionSampler(
currentClock?.smallClock,
mainExecutor,
bgExecutor,
regionSamplingEnabled,
updateFun
)
largeRegionSamplingInstance = createRegionSampler(
currentClock?.largeClock,
mainExecutor,
bgExecutor,
regionSamplingEnabled,
updateFun
)
smallRegionSamplingInstance.startRegionSampler()
largeRegionSamplingInstance.startRegionSampler()
updateFun.updateColors()
}
protected open fun createRegionSampler(
sampledView: View?,
mainExecutor: Executor?,
bgExecutor: Executor?,
regionSamplingEnabled: Boolean,
updateFun: RegionSamplingInstance.UpdateColorCallback
): RegionSamplingInstance {
return RegionSamplingInstance(
sampledView,
mainExecutor,
bgExecutor,
regionSamplingEnabled,
updateFun)
}
lateinit var smallRegionSamplingInstance: RegionSamplingInstance
lateinit var largeRegionSamplingInstance: RegionSamplingInstance
private var smallClockIsDark = true
private var largeClockIsDark = true
private val configListener = object : ConfigurationController.ConfigurationListener {
override fun onThemeChanged() {
updateFun.updateColors()
}
}
private val batteryCallback = object : BatteryStateChangeCallback {
override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) {
if (isKeyguardShowing && !isCharging && charging) {
clock?.animations?.charge()
}
isCharging = charging
}
}
private val localeBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
clock?.events?.onLocaleChanged(Locale.getDefault())
}
}
private val statusBarStateListener = object : StatusBarStateController.StateListener {
override fun onDozeAmountChanged(linear: Float, eased: Float) {
clock?.animations?.doze(linear)
isDozing = linear > dozeAmount
dozeAmount = linear
}
}
private val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() {
override fun onKeyguardVisibilityChanged(showing: Boolean) {
isKeyguardShowing = showing
if (!isKeyguardShowing) {
clock?.animations?.doze(if (isDozing) 1f else 0f)
}
}
override fun onTimeFormatChanged(timeFormat: String) {
clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
}
override fun onTimeZoneChanged(timeZone: TimeZone) {
clock?.events?.onTimeZoneChanged(timeZone)
}
override fun onUserSwitchComplete(userId: Int) {
clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
}
}
init {
isDozing = statusBarStateController.isDozing
}
fun registerListeners() {
dozeAmount = statusBarStateController.dozeAmount
isDozing = statusBarStateController.isDozing || dozeAmount != 0f
broadcastDispatcher.registerReceiver(
localeBroadcastReceiver,
IntentFilter(Intent.ACTION_LOCALE_CHANGED)
)
configurationController.addCallback(configListener)
batteryController.addCallback(batteryCallback)
keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
statusBarStateController.addCallback(statusBarStateListener)
smallRegionSamplingInstance.startRegionSampler()
largeRegionSamplingInstance.startRegionSampler()
}
fun unregisterListeners() {
broadcastDispatcher.unregisterReceiver(localeBroadcastReceiver)
configurationController.removeCallback(configListener)
batteryController.removeCallback(batteryCallback)
keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
statusBarStateController.removeCallback(statusBarStateListener)
smallRegionSamplingInstance.stopRegionSampler()
largeRegionSamplingInstance.stopRegionSampler()
}
/**
* Dump information for debugging
*/
fun dump(pw: PrintWriter) {
pw.println(this)
clock?.dump(pw)
smallRegionSamplingInstance.dump(pw)
largeRegionSamplingInstance.dump(pw)
}
companion object {
private val TAG = ClockEventController::class.simpleName
private const val FORMAT_NUMBER = 1234567890
}
}