diff options
12 files changed, 348 insertions, 314 deletions
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt index b9d66432b5f2..2bfa7d9bbd7b 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt @@ -33,6 +33,8 @@ import com.android.internal.annotations.VisibleForTesting import com.android.systemui.animation.GlyphCallback import com.android.systemui.animation.TextAnimator import com.android.systemui.customization.R +import com.android.systemui.log.core.LogcatOnlyMessageBuffer +import com.android.systemui.log.core.LogLevel import com.android.systemui.log.core.Logger import com.android.systemui.log.core.MessageBuffer import java.io.PrintWriter @@ -51,12 +53,13 @@ class AnimatableClockView @JvmOverloads constructor( defStyleAttr: Int = 0, defStyleRes: Int = 0 ) : TextView(context, attrs, defStyleAttr, defStyleRes) { - var messageBuffer: MessageBuffer? = null - set(value) { - logger = if (value != null) Logger(value, TAG) else null - } - - private var logger: Logger? = null + // To protect us from issues from this being null while the TextView constructor is running, we + // implement the get method and ensure a value is returned before initialization is complete. + private var logger = DEFAULT_LOGGER + get() = field ?: DEFAULT_LOGGER + var messageBuffer: MessageBuffer + get() = logger.buffer + set(value) { logger = Logger(value, TAG) } private val time = Calendar.getInstance() @@ -133,8 +136,8 @@ class AnimatableClockView @JvmOverloads constructor( } override fun onAttachedToWindow() { + logger.d("onAttachedToWindow") super.onAttachedToWindow() - logger?.d("onAttachedToWindow") refreshFormat() } @@ -150,13 +153,13 @@ class AnimatableClockView @JvmOverloads constructor( time.timeInMillis = timeOverrideInMillis ?: System.currentTimeMillis() contentDescription = DateFormat.format(descFormat, time) val formattedText = DateFormat.format(format, time) - logger?.d({ "refreshTime: new formattedText=$str1" }) { str1 = formattedText?.toString() } + logger.d({ "refreshTime: new formattedText=$str1" }) { str1 = formattedText?.toString() } // Setting text actually triggers a layout pass (because the text view is set to // wrap_content width and TextView always relayouts for this). Avoid needless // relayout if the text didn't actually change. if (!TextUtils.equals(text, formattedText)) { text = formattedText - logger?.d({ "refreshTime: done setting new time text to: $str1" }) { + logger.d({ "refreshTime: done setting new time text to: $str1" }) { str1 = formattedText?.toString() } // Because the TextLayout may mutate under the hood as a result of the new text, we @@ -165,21 +168,22 @@ class AnimatableClockView @JvmOverloads constructor( // without being notified TextInterpolator being notified. if (layout != null) { textAnimator?.updateLayout(layout) - logger?.d("refreshTime: done updating textAnimator layout") + logger.d("refreshTime: done updating textAnimator layout") } requestLayout() - logger?.d("refreshTime: after requestLayout") + logger.d("refreshTime: after requestLayout") } } fun onTimeZoneChanged(timeZone: TimeZone?) { + logger.d({ "onTimeZoneChanged($str1)" }) { str1 = timeZone?.toString() } time.timeZone = timeZone refreshFormat() - logger?.d({ "onTimeZoneChanged newTimeZone=$str1" }) { str1 = timeZone?.toString() } } @SuppressLint("DrawAllocation") override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + logger.d("onMeasure") super.onMeasure(widthMeasureSpec, heightMeasureSpec) val animator = textAnimator if (animator == null) { @@ -189,10 +193,10 @@ class AnimatableClockView @JvmOverloads constructor( } else { animator.updateLayout(layout) } - logger?.d("onMeasure") } override fun onDraw(canvas: Canvas) { + logger.d({ "onDraw($str1)"}) { str1 = text.toString() } // Use textAnimator to render text if animation is enabled. // Otherwise default to using standard draw functions. if (isAnimationEnabled) { @@ -201,22 +205,23 @@ class AnimatableClockView @JvmOverloads constructor( } else { super.onDraw(canvas) } - logger?.d("onDraw") } override fun invalidate() { + @Suppress("UNNECESSARY_SAFE_CALL") + // logger won't be initialized when called by TextView's constructor + logger.d("invalidate") super.invalidate() - logger?.d("invalidate") } override fun onTextChanged( - text: CharSequence, - start: Int, - lengthBefore: Int, - lengthAfter: Int + text: CharSequence, + start: Int, + lengthBefore: Int, + lengthAfter: Int ) { + logger.d({ "onTextChanged($str1)" }) { str1 = text.toString() } super.onTextChanged(text, start, lengthBefore, lengthAfter) - logger?.d({ "onTextChanged text=$str1" }) { str1 = text.toString() } } fun setLineSpacingScale(scale: Float) { @@ -230,7 +235,7 @@ class AnimatableClockView @JvmOverloads constructor( } fun animateColorChange() { - logger?.d("animateColorChange") + logger.d("animateColorChange") setTextStyle( weight = lockScreenWeight, textSize = -1f, @@ -252,7 +257,7 @@ class AnimatableClockView @JvmOverloads constructor( } fun animateAppearOnLockscreen() { - logger?.d("animateAppearOnLockscreen") + logger.d("animateAppearOnLockscreen") setTextStyle( weight = dozingWeight, textSize = -1f, @@ -278,7 +283,7 @@ class AnimatableClockView @JvmOverloads constructor( if (isAnimationEnabled && textAnimator == null) { return } - logger?.d("animateFoldAppear") + logger.d("animateFoldAppear") setTextStyle( weight = lockScreenWeightInternal, textSize = -1f, @@ -305,7 +310,7 @@ class AnimatableClockView @JvmOverloads constructor( // Skip charge animation if dozing animation is already playing. return } - logger?.d("animateCharge") + logger.d("animateCharge") val startAnimPhase2 = Runnable { setTextStyle( weight = if (isDozing()) dozingWeight else lockScreenWeight, @@ -329,7 +334,7 @@ class AnimatableClockView @JvmOverloads constructor( } fun animateDoze(isDozing: Boolean, animate: Boolean) { - logger?.d("animateDoze") + logger.d("animateDoze") setTextStyle( weight = if (isDozing) dozingWeight else lockScreenWeight, textSize = -1f, @@ -448,7 +453,7 @@ class AnimatableClockView @JvmOverloads constructor( isSingleLineInternal && !use24HourFormat -> Patterns.sClockView12 else -> DOUBLE_LINE_FORMAT_12_HOUR } - logger?.d({ "refreshFormat format=$str1" }) { str1 = format?.toString() } + logger.d({ "refreshFormat($str1)" }) { str1 = format?.toString() } descFormat = if (use24HourFormat) Patterns.sClockView24 else Patterns.sClockView12 refreshTime() @@ -552,6 +557,8 @@ class AnimatableClockView @JvmOverloads constructor( companion object { private val TAG = AnimatableClockView::class.simpleName!! + private val DEFAULT_LOGGER = Logger(LogcatOnlyMessageBuffer(LogLevel.WARNING), TAG) + const val ANIMATION_DURATION_FOLD_TO_AOD: Int = 600 private const val DOUBLE_LINE_FORMAT_12_HOUR = "hh\nmm" private const val DOUBLE_LINE_FORMAT_24_HOUR = "HH\nmm" diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt index cdd074d872c0..41bde5298c66 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt @@ -21,20 +21,16 @@ import android.graphics.drawable.Drawable import android.net.Uri import android.os.UserHandle import android.provider.Settings -import android.util.Log import androidx.annotation.OpenForTesting -import com.android.systemui.log.LogMessageImpl import com.android.systemui.log.core.LogLevel -import com.android.systemui.log.core.LogMessage +import com.android.systemui.log.core.LogcatOnlyMessageBuffer import com.android.systemui.log.core.Logger -import com.android.systemui.log.core.MessageBuffer -import com.android.systemui.log.core.MessageInitializer -import com.android.systemui.log.core.MessagePrinter import com.android.systemui.plugins.PluginLifecycleManager import com.android.systemui.plugins.PluginListener import com.android.systemui.plugins.PluginManager import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.plugins.clocks.ClockId +import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockMetadata import com.android.systemui.plugins.clocks.ClockProvider import com.android.systemui.plugins.clocks.ClockProviderPlugin @@ -77,32 +73,6 @@ private fun <TKey : Any, TVal : Any> ConcurrentHashMap<TKey, TVal>.concurrentGet return result ?: value } -private val TMP_MESSAGE: LogMessage by lazy { LogMessageImpl.Factory.create() } - -private inline fun Logger?.tryLog( - tag: String, - level: LogLevel, - messageInitializer: MessageInitializer, - noinline messagePrinter: MessagePrinter, - ex: Throwable? = null, -) { - if (this != null) { - // Wrap messagePrinter to convert it from crossinline to noinline - this.log(level, messagePrinter, ex, messageInitializer) - } else { - messageInitializer(TMP_MESSAGE) - val msg = messagePrinter(TMP_MESSAGE) - when (level) { - LogLevel.VERBOSE -> Log.v(tag, msg, ex) - LogLevel.DEBUG -> Log.d(tag, msg, ex) - LogLevel.INFO -> Log.i(tag, msg, ex) - LogLevel.WARNING -> Log.w(tag, msg, ex) - LogLevel.ERROR -> Log.e(tag, msg, ex) - LogLevel.WTF -> Log.wtf(tag, msg, ex) - } - } -} - /** ClockRegistry aggregates providers and plugins */ open class ClockRegistry( val context: Context, @@ -114,12 +84,15 @@ open class ClockRegistry( val handleAllUsers: Boolean, defaultClockProvider: ClockProvider, val fallbackClockId: ClockId = DEFAULT_CLOCK_ID, - messageBuffer: MessageBuffer? = null, + val clockBuffers: ClockMessageBuffers? = null, val keepAllLoaded: Boolean, subTag: String, var isTransitClockEnabled: Boolean = false, ) { private val TAG = "${ClockRegistry::class.simpleName} ($subTag)" + private val logger: Logger = + Logger(clockBuffers?.infraMessageBuffer ?: LogcatOnlyMessageBuffer(LogLevel.DEBUG), TAG) + interface ClockChangeListener { // Called when the active clock changes fun onCurrentClockChanged() {} @@ -128,7 +101,6 @@ open class ClockRegistry( fun onAvailableClocksChanged() {} } - private val logger: Logger? = if (messageBuffer != null) Logger(messageBuffer, TAG) else null private val availableClocks = ConcurrentHashMap<ClockId, ClockInfo>() private val clockChangeListeners = mutableListOf<ClockChangeListener>() private val settingObserver = @@ -157,21 +129,15 @@ open class ClockRegistry( val knownClocks = KNOWN_PLUGINS.get(manager.getPackage()) if (knownClocks == null) { - logger.tryLog( - TAG, - LogLevel.WARNING, - { str1 = manager.getPackage() }, - { "Loading unrecognized clock package: $str1" } - ) + logger.w({ "Loading unrecognized clock package: $str1" }) { + str1 = manager.getPackage() + } return true } - logger.tryLog( - TAG, - LogLevel.INFO, - { str1 = manager.getPackage() }, - { "Skipping initial load of known clock package package: $str1" } - ) + logger.i({ "Skipping initial load of known clock package package: $str1" }) { + str1 = manager.getPackage() + } var isCurrentClock = false var isClockListChanged = false @@ -185,19 +151,14 @@ open class ClockRegistry( } if (manager != info.manager) { - logger.tryLog( - TAG, - LogLevel.ERROR, - { - str1 = id - str2 = info.manager.toString() - str3 = manager.toString() - }, - { - "Clock Id conflict on attach: " + - "$str1 is double registered by $str2 and $str3" - } - ) + logger.e({ + "Clock Id conflict on attach: " + + "$str1 is double registered by $str2 and $str3" + }) { + str1 = id + str2 = info.manager.toString() + str3 = manager.toString() + } continue } @@ -219,6 +180,8 @@ open class ClockRegistry( pluginContext: Context, manager: PluginLifecycleManager<ClockProviderPlugin> ) { + plugin.initialize(clockBuffers) + var isClockListChanged = false for (clock in plugin.getClocks()) { val id = clock.clockId @@ -233,19 +196,14 @@ open class ClockRegistry( } if (manager != info.manager) { - logger.tryLog( - TAG, - LogLevel.ERROR, - { - str1 = id - str2 = info.manager.toString() - str3 = manager.toString() - }, - { - "Clock Id conflict on load: " + - "$str1 is double registered by $str2 and $str3" - } - ) + logger.e({ + "Clock Id conflict on load: " + + "$str1 is double registered by $str2 and $str3" + }) { + str1 = id + str2 = info.manager.toString() + str3 = manager.toString() + } manager.unloadPlugin() continue } @@ -268,19 +226,14 @@ open class ClockRegistry( val id = clock.clockId val info = availableClocks[id] if (info?.manager != manager) { - logger.tryLog( - TAG, - LogLevel.ERROR, - { - str1 = id - str2 = info?.manager.toString() - str3 = manager.toString() - }, - { - "Clock Id conflict on unload: " + - "$str1 is double registered by $str2 and $str3" - } - ) + logger.e({ + "Clock Id conflict on unload: " + + "$str1 is double registered by $str2 and $str3" + }) { + str1 = id + str2 = info?.manager.toString() + str3 = manager.toString() + } continue } info.provider = null @@ -350,7 +303,7 @@ open class ClockRegistry( ClockSettings.deserialize(json) } catch (ex: Exception) { - logger.tryLog(TAG, LogLevel.ERROR, {}, { "Failed to parse clock settings" }, ex) + logger.e("Failed to parse clock settings", ex) null } settings = result @@ -379,7 +332,7 @@ open class ClockRegistry( ) } } catch (ex: Exception) { - logger.tryLog(TAG, LogLevel.ERROR, {}, { "Failed to set clock settings" }, ex) + logger.e("Failed to set clock settings", ex) } settings = value } @@ -451,7 +404,8 @@ open class ClockRegistry( } init { - // Register default clock designs + // Initialize & register default clock designs + defaultClockProvider.initialize(clockBuffers) for (clock in defaultClockProvider.getClocks()) { availableClocks[clock.clockId] = ClockInfo(clock, defaultClockProvider, null) } @@ -514,12 +468,7 @@ open class ClockRegistry( fun verifyLoadedProviders() { val shouldSchedule = isQueued.compareAndSet(false, true) if (!shouldSchedule) { - logger.tryLog( - TAG, - LogLevel.VERBOSE, - {}, - { "verifyLoadedProviders: shouldSchedule=false" } - ) + logger.v("verifyLoadedProviders: shouldSchedule=false") return } @@ -528,12 +477,7 @@ open class ClockRegistry( synchronized(availableClocks) { isQueued.set(false) if (keepAllLoaded) { - logger.tryLog( - TAG, - LogLevel.INFO, - {}, - { "verifyLoadedProviders: keepAllLoaded=true" } - ) + logger.i("verifyLoadedProviders: keepAllLoaded=true") // Enforce that all plugins are loaded if requested for ((_, info) in availableClocks) { info.manager?.loadPlugin() @@ -543,12 +487,7 @@ open class ClockRegistry( val currentClock = availableClocks[currentClockId] if (currentClock == null) { - logger.tryLog( - TAG, - LogLevel.INFO, - {}, - { "verifyLoadedProviders: currentClock=null" } - ) + logger.i("verifyLoadedProviders: currentClock=null") // Current Clock missing, load no plugins and use default for ((_, info) in availableClocks) { info.manager?.unloadPlugin() @@ -556,12 +495,7 @@ open class ClockRegistry( return@launch } - logger.tryLog( - TAG, - LogLevel.INFO, - {}, - { "verifyLoadedProviders: load currentClock" } - ) + logger.i("verifyLoadedProviders: load currentClock") val currentManager = currentClock.manager currentManager?.loadPlugin() @@ -577,30 +511,26 @@ open class ClockRegistry( private fun onConnected(info: ClockInfo) { val isCurrent = currentClockId == info.metadata.clockId - logger.tryLog( - TAG, + logger.log( if (isCurrent) LogLevel.INFO else LogLevel.DEBUG, - { - str1 = info.metadata.clockId - str2 = info.manager.toString() - bool1 = isCurrent - }, { "Connected $str1 @$str2" + if (bool1) " (Current Clock)" else "" } - ) + ) { + str1 = info.metadata.clockId + str2 = info.manager.toString() + bool1 = isCurrent + } } private fun onLoaded(info: ClockInfo) { val isCurrent = currentClockId == info.metadata.clockId - logger.tryLog( - TAG, + logger.log( if (isCurrent) LogLevel.INFO else LogLevel.DEBUG, - { - str1 = info.metadata.clockId - str2 = info.manager.toString() - bool1 = isCurrent - }, { "Loaded $str1 @$str2" + if (bool1) " (Current Clock)" else "" } - ) + ) { + str1 = info.metadata.clockId + str2 = info.manager.toString() + bool1 = isCurrent + } if (isCurrent) { triggerOnCurrentClockChanged() @@ -609,16 +539,14 @@ open class ClockRegistry( private fun onUnloaded(info: ClockInfo) { val isCurrent = currentClockId == info.metadata.clockId - logger.tryLog( - TAG, + logger.log( if (isCurrent) LogLevel.WARNING else LogLevel.DEBUG, - { - str1 = info.metadata.clockId - str2 = info.manager.toString() - bool1 = isCurrent - }, { "Unloaded $str1 @$str2" + if (bool1) " (Current Clock)" else "" } - ) + ) { + str1 = info.metadata.clockId + str2 = info.manager.toString() + bool1 = isCurrent + } if (isCurrent) { triggerOnCurrentClockChanged() @@ -627,16 +555,14 @@ open class ClockRegistry( private fun onDisconnected(info: ClockInfo) { val isCurrent = currentClockId == info.metadata.clockId - logger.tryLog( - TAG, + logger.log( if (isCurrent) LogLevel.INFO else LogLevel.DEBUG, - { - str1 = info.metadata.clockId - str2 = info.manager.toString() - bool1 = isCurrent - }, { "Disconnected $str1 @$str2" + if (bool1) " (Current Clock)" else "" } - ) + ) { + str1 = info.metadata.clockId + str2 = info.manager.toString() + bool1 = isCurrent + } } fun getClocks(): List<ClockMetadata> { @@ -676,23 +602,13 @@ open class ClockRegistry( if (isEnabled && clockId.isNotEmpty()) { val clock = createClock(clockId) if (clock != null) { - logger.tryLog(TAG, LogLevel.INFO, { str1 = clockId }, { "Rendering clock $str1" }) + logger.i({ "Rendering clock $str1" }) { str1 = clockId } return clock } else if (availableClocks.containsKey(clockId)) { - logger.tryLog( - TAG, - LogLevel.WARNING, - { str1 = clockId }, - { "Clock $str1 not loaded; using default" } - ) + logger.w({ "Clock $str1 not loaded; using default" }) { str1 = clockId } verifyLoadedProviders() } else { - logger.tryLog( - TAG, - LogLevel.ERROR, - { str1 = clockId }, - { "Clock $str1 not found; using default" } - ) + logger.e({ "Clock $str1 not found; using default" }) { str1 = clockId } } } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt index 01c03b1f25f6..99d321695d04 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt @@ -33,6 +33,7 @@ import com.android.systemui.plugins.clocks.ClockEvents import com.android.systemui.plugins.clocks.ClockFaceConfig import com.android.systemui.plugins.clocks.ClockFaceController import com.android.systemui.plugins.clocks.ClockFaceEvents +import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockSettings import com.android.systemui.plugins.clocks.DefaultClockFaceLayout import com.android.systemui.plugins.clocks.WeatherData @@ -41,8 +42,6 @@ import java.io.PrintWriter import java.util.Locale import java.util.TimeZone -private val TAG = DefaultClockController::class.simpleName - /** * Controls the default clock visuals. * @@ -56,6 +55,7 @@ class DefaultClockController( private val settings: ClockSettings?, private val hasStepClockAnimation: Boolean = false, private val migratedClocks: Boolean = false, + messageBuffers: ClockMessageBuffers? = null, ) : ClockController { override val smallClock: DefaultClockFaceController override val largeClock: LargeClockFaceController @@ -83,13 +83,15 @@ class DefaultClockController( DefaultClockFaceController( layoutInflater.inflate(R.layout.clock_default_small, parent, false) as AnimatableClockView, - settings?.seedColor + settings?.seedColor, + messageBuffers?.smallClockMessageBuffer ) largeClock = LargeClockFaceController( layoutInflater.inflate(R.layout.clock_default_large, parent, false) as AnimatableClockView, - settings?.seedColor + settings?.seedColor, + messageBuffers?.largeClockMessageBuffer ) clocks = listOf(smallClock.view, largeClock.view) @@ -110,6 +112,7 @@ class DefaultClockController( open inner class DefaultClockFaceController( override val view: AnimatableClockView, var seedColor: Int?, + messageBuffer: MessageBuffer?, ) : ClockFaceController { // MAGENTA is a placeholder, and will be assigned correctly in initialize @@ -120,12 +123,6 @@ class DefaultClockController( override val config = ClockFaceConfig() override val layout = DefaultClockFaceLayout(view) - override var messageBuffer: MessageBuffer? - get() = view.messageBuffer - set(value) { - view.messageBuffer = value - } - override var animations: DefaultClockAnimations = DefaultClockAnimations(view, 0f, 0f) internal set @@ -134,6 +131,7 @@ class DefaultClockController( currentColor = seedColor!! } view.setColors(DOZE_COLOR, currentColor) + messageBuffer?.let { view.messageBuffer = it } } override val events = @@ -188,7 +186,8 @@ class DefaultClockController( inner class LargeClockFaceController( view: AnimatableClockView, seedColor: Int?, - ) : DefaultClockFaceController(view, seedColor) { + messageBuffer: MessageBuffer?, + ) : DefaultClockFaceController(view, seedColor, messageBuffer) { override val layout = DefaultClockFaceLayout(view) override val config = ClockFaceConfig(hasCustomPositionUpdatedAnimation = hasStepClockAnimation) diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt index a219be53bd1a..20f87a059656 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt @@ -20,6 +20,7 @@ import android.view.LayoutInflater import com.android.systemui.customization.R import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.plugins.clocks.ClockId +import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockMetadata import com.android.systemui.plugins.clocks.ClockProvider import com.android.systemui.plugins.clocks.ClockSettings @@ -35,6 +36,12 @@ class DefaultClockProvider( val hasStepClockAnimation: Boolean = false, val migratedClocks: Boolean = false ) : ClockProvider { + private var messageBuffers: ClockMessageBuffers? = null + + override fun initialize(buffers: ClockMessageBuffers?) { + messageBuffers = buffers + } + override fun getClocks(): List<ClockMetadata> = listOf(ClockMetadata(DEFAULT_CLOCK_ID)) override fun createClock(settings: ClockSettings): ClockController { @@ -49,6 +56,7 @@ class DefaultClockProvider( settings, hasStepClockAnimation, migratedClocks, + messageBuffers, ) } diff --git a/packages/SystemUI/log/src/com/android/systemui/log/core/LogcatOnlyMessageBuffer.kt b/packages/SystemUI/log/src/com/android/systemui/log/core/LogcatOnlyMessageBuffer.kt new file mode 100644 index 000000000000..006b521ad867 --- /dev/null +++ b/packages/SystemUI/log/src/com/android/systemui/log/core/LogcatOnlyMessageBuffer.kt @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2023 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.systemui.log.core + +import android.util.Log +import com.android.systemui.log.LogMessageImpl + +/** + * A simple implementation of [MessageBuffer] that forwards messages to [android.util.Log] + * immediately. This defeats the intention behind [LogBuffer] and should only be used when + * [LogBuffer]s are unavailable in a certain context. + */ +class LogcatOnlyMessageBuffer( + val targetLogLevel: LogLevel, +) : MessageBuffer { + private val singleMessage = LogMessageImpl.Factory.create() + private var isObtained: Boolean = false + + @Synchronized + override fun obtain( + tag: String, + level: LogLevel, + messagePrinter: MessagePrinter, + exception: Throwable?, + ): LogMessage { + if (isObtained) { + throw UnsupportedOperationException( + "Message has already been obtained. Call order is incorrect." + ) + } + + singleMessage.reset(tag, level, System.currentTimeMillis(), messagePrinter, exception) + isObtained = true + return singleMessage + } + + @Synchronized + override fun commit(message: LogMessage) { + if (singleMessage != message) { + throw IllegalArgumentException("Message argument is not the expected message.") + } + if (!isObtained) { + throw UnsupportedOperationException( + "Message has not been obtained. Call order is incorrect." + ) + } + + if (message.level >= targetLogLevel) { + val strMessage = message.messagePrinter(message) + when (message.level) { + LogLevel.VERBOSE -> Log.v(message.tag, strMessage, message.exception) + LogLevel.DEBUG -> Log.d(message.tag, strMessage, message.exception) + LogLevel.INFO -> Log.i(message.tag, strMessage, message.exception) + LogLevel.WARNING -> Log.w(message.tag, strMessage, message.exception) + LogLevel.ERROR -> Log.e(message.tag, strMessage, message.exception) + LogLevel.WTF -> Log.wtf(message.tag, strMessage, message.exception) + } + } + + isObtained = false + } +} diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt index 1c5f221f2efb..4436be7cd7d7 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt @@ -41,16 +41,13 @@ interface ClockProviderPlugin : Plugin, ClockProvider { /** Interface for building clocks and providing information about those clocks */ interface ClockProvider { + /** Initializes the clock provider with debug log buffers */ + fun initialize(buffers: ClockMessageBuffers?) + /** Returns metadata for all clocks this provider knows about */ fun getClocks(): List<ClockMetadata> /** Initializes and returns the target clock design */ - @Deprecated("Use overload with ClockSettings") - fun createClock(id: ClockId): ClockController { - return createClock(ClockSettings(id, null)) - } - - /** Initializes and returns the target clock design */ fun createClock(settings: ClockSettings): ClockController /** A static thumbnail for rendering in some examples */ @@ -98,11 +95,20 @@ interface ClockFaceController { /** Triggers for various animations */ val animations: ClockAnimations - - /** Some clocks may log debug information */ - var messageBuffer: MessageBuffer? } +/** For clocks that want to report debug information */ +data class ClockMessageBuffers( + /** Message buffer for general infra */ + val infraMessageBuffer: MessageBuffer, + + /** Message buffer for small clock renering */ + val smallClockMessageBuffer: MessageBuffer, + + /** Message buffer for large clock rendering */ + val largeClockMessageBuffer: MessageBuffer, +) + /** Specifies layout information for the */ interface ClockFaceLayout { /** All clock views to add to the root constraint layout before applying constraints. */ diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt index 76abad8ae863..bcc20448297d 100644 --- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt @@ -45,12 +45,11 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.lifecycle.repeatWhenAttached -import com.android.systemui.log.LogBuffer +import com.android.systemui.log.core.Logger import com.android.systemui.log.core.LogLevel.DEBUG -import com.android.systemui.log.dagger.KeyguardLargeClockLog -import com.android.systemui.log.dagger.KeyguardSmallClockLog import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.plugins.clocks.ClockFaceController +import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockTickRate import com.android.systemui.plugins.clocks.AlarmData import com.android.systemui.plugins.clocks.WeatherData @@ -91,117 +90,120 @@ constructor( private val context: Context, @Main private val mainExecutor: DelayableExecutor, @Background private val bgExecutor: Executor, - @KeyguardSmallClockLog private val smallLogBuffer: LogBuffer?, - @KeyguardLargeClockLog private val largeLogBuffer: LogBuffer?, + private val clockBuffers: ClockMessageBuffers, private val featureFlags: FeatureFlags, private val zenModeController: ZenModeController, ) { + var loggers = listOf( + clockBuffers.infraMessageBuffer, + clockBuffers.smallClockMessageBuffer, + clockBuffers.largeClockMessageBuffer + ).map { Logger(it, TAG) } + var clock: ClockController? = null + get() = field set(value) { - smallClockOnAttachStateChangeListener?.let { - field?.smallClock?.view?.removeOnAttachStateChangeListener(it) - smallClockFrame?.viewTreeObserver - ?.removeOnGlobalLayoutListener(onGlobalLayoutListener) - } - largeClockOnAttachStateChangeListener?.let { - field?.largeClock?.view?.removeOnAttachStateChangeListener(it) - } - + disconnectClock(field) field = value - if (value != null) { - smallLogBuffer?.log(TAG, DEBUG, {}, { "New Clock" }) - value.smallClock.messageBuffer = smallLogBuffer - largeLogBuffer?.log(TAG, DEBUG, {}, { "New Clock" }) - value.largeClock.messageBuffer = largeLogBuffer - - value.initialize(resources, dozeAmount, 0f) - - if (!regionSamplingEnabled) { - updateColors() - } else { - clock?.let { - smallRegionSampler = createRegionSampler( - it.smallClock.view, - mainExecutor, - bgExecutor, - regionSamplingEnabled, - isLockscreen = true, - ::updateColors - )?.apply { startRegionSampler() } - - largeRegionSampler = createRegionSampler( - it.largeClock.view, - mainExecutor, - bgExecutor, - regionSamplingEnabled, - isLockscreen = true, - ::updateColors - )?.apply { startRegionSampler() } - - updateColors() - } - } - updateFontSizes() - updateTimeListeners() - weatherData?.let { - if (WeatherData.DEBUG) { - Log.i(TAG, "Pushing cached weather data to new clock: $it") - } - value.events.onWeatherDataChanged(it) - } - zenData?.let { - value.events.onZenDataChanged(it) - } - alarmData?.let { - value.events.onAlarmDataChanged(it) - } + connectClock(value) + } - smallClockOnAttachStateChangeListener = - object : OnAttachStateChangeListener { - var pastVisibility: Int? = null - override fun onViewAttachedToWindow(view: View) { - value.events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) - // Match the asing for view.parent's layout classes. - smallClockFrame = view.parent as ViewGroup - smallClockFrame?.let { frame -> - pastVisibility = frame.visibility - onGlobalLayoutListener = OnGlobalLayoutListener { - val currentVisibility = frame.visibility - if (pastVisibility != currentVisibility) { - pastVisibility = currentVisibility - // when small clock is visible, - // recalculate bounds and sample - if (currentVisibility == View.VISIBLE) { - smallRegionSampler?.stopRegionSampler() - smallRegionSampler?.startRegionSampler() - } - } - } - frame.viewTreeObserver - .addOnGlobalLayoutListener(onGlobalLayoutListener) - } - } + private fun disconnectClock(clock: ClockController?) { + if (clock == null) { return; } + smallClockOnAttachStateChangeListener?.let { + clock.smallClock.view.removeOnAttachStateChangeListener(it) + smallClockFrame?.viewTreeObserver + ?.removeOnGlobalLayoutListener(onGlobalLayoutListener) + } + largeClockOnAttachStateChangeListener?.let { + clock.largeClock.view.removeOnAttachStateChangeListener(it) + } + } - override fun onViewDetachedFromWindow(p0: View) { - smallClockFrame?.viewTreeObserver - ?.removeOnGlobalLayoutListener(onGlobalLayoutListener) - } - } - value.smallClock.view - .addOnAttachStateChangeListener(smallClockOnAttachStateChangeListener) + private fun connectClock(clock: ClockController?) { + if (clock == null) { return; } + val clockStr = clock.toString() + loggers.forEach { it.d({ "New Clock: $str1" }) { str1 = clockStr } } - largeClockOnAttachStateChangeListener = - object : OnAttachStateChangeListener { - override fun onViewAttachedToWindow(p0: View) { - value.events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) - } - override fun onViewDetachedFromWindow(p0: View) { + clock.initialize(resources, dozeAmount, 0f) + + if (!regionSamplingEnabled) { + updateColors() + } else { + smallRegionSampler = createRegionSampler( + clock.smallClock.view, + mainExecutor, + bgExecutor, + regionSamplingEnabled, + isLockscreen = true, + ::updateColors + ).apply { startRegionSampler() } + + largeRegionSampler = createRegionSampler( + clock.largeClock.view, + mainExecutor, + bgExecutor, + regionSamplingEnabled, + isLockscreen = true, + ::updateColors + ).apply { startRegionSampler() } + + updateColors() + } + updateFontSizes() + updateTimeListeners() + + weatherData?.let { + if (WeatherData.DEBUG) { + Log.i(TAG, "Pushing cached weather data to new clock: $it") + } + clock.events.onWeatherDataChanged(it) + } + zenData?.let { + clock.events.onZenDataChanged(it) + } + alarmData?.let { + clock.events.onAlarmDataChanged(it) + } + + smallClockOnAttachStateChangeListener = object : OnAttachStateChangeListener { + var pastVisibility: Int? = null + override fun onViewAttachedToWindow(view: View) { + clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) + // Match the asing for view.parent's layout classes. + smallClockFrame = (view.parent as ViewGroup)?.also { frame -> + pastVisibility = frame.visibility + onGlobalLayoutListener = OnGlobalLayoutListener { + val currentVisibility = frame.visibility + if (pastVisibility != currentVisibility) { + pastVisibility = currentVisibility + // when small clock is visible, + // recalculate bounds and sample + if (currentVisibility == View.VISIBLE) { + smallRegionSampler?.stopRegionSampler() + smallRegionSampler?.startRegionSampler() + } } + } + frame.viewTreeObserver.addOnGlobalLayoutListener(onGlobalLayoutListener) } - value.largeClock.view - .addOnAttachStateChangeListener(largeClockOnAttachStateChangeListener) + } + + override fun onViewDetachedFromWindow(p0: View) { + smallClockFrame?.viewTreeObserver + ?.removeOnGlobalLayoutListener(onGlobalLayoutListener) } } + clock.smallClock.view.addOnAttachStateChangeListener(smallClockOnAttachStateChangeListener) + + largeClockOnAttachStateChangeListener = object : OnAttachStateChangeListener { + override fun onViewAttachedToWindow(p0: View) { + clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) + } + override fun onViewDetachedFromWindow(p0: View) {} + } + clock.largeClock.view.addOnAttachStateChangeListener(largeClockOnAttachStateChangeListener) + } @VisibleForTesting var smallClockOnAttachStateChangeListener: OnAttachStateChangeListener? = null @@ -247,6 +249,7 @@ constructor( largeClock.events.onRegionDarknessChanged(isRegionDark) } } + protected open fun createRegionSampler( sampledView: View, mainExecutor: Executor?, @@ -254,7 +257,7 @@ constructor( regionSamplingEnabled: Boolean, isLockscreen: Boolean, updateColors: () -> Unit - ): RegionSampler? { + ): RegionSampler { return RegionSampler( sampledView, mainExecutor, diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java index 661ce2ce60ba..878a5d88f164 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java @@ -28,9 +28,8 @@ import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; -import com.android.systemui.log.LogBuffer; -import com.android.systemui.log.dagger.KeyguardClockLog; import com.android.systemui.plugins.PluginManager; +import com.android.systemui.plugins.clocks.ClockMessageBuffers; import com.android.systemui.res.R; import com.android.systemui.shared.clocks.ClockRegistry; import com.android.systemui.shared.clocks.DefaultClockProvider; @@ -56,7 +55,7 @@ public abstract class ClockRegistryModule { FeatureFlags featureFlags, @Main Resources resources, LayoutInflater layoutInflater, - @KeyguardClockLog LogBuffer logBuffer) { + ClockMessageBuffers clockBuffers) { ClockRegistry registry = new ClockRegistry( context, pluginManager, @@ -72,7 +71,7 @@ public abstract class ClockRegistryModule { featureFlags.isEnabled(Flags.STEP_CLOCK_ANIMATION), migrateClocksToBlueprint()), context.getString(R.string.lockscreen_clock_id_fallback), - logBuffer, + clockBuffers, /* keepAllLoaded = */ false, /* subTag = */ "System", /* isTransitClockEnabled = */ featureFlags.isEnabled(Flags.TRANSIT_CLOCK)); diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index d8bb3e65392f..0d5ba641b599 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -26,6 +26,7 @@ import com.android.systemui.log.echo.LogcatEchoTrackerDebug; import com.android.systemui.log.echo.LogcatEchoTrackerProd; import com.android.systemui.log.table.TableLogBuffer; import com.android.systemui.log.table.TableLogBufferFactory; +import com.android.systemui.plugins.clocks.ClockMessageBuffers; import com.android.systemui.qs.QSFragmentLegacy; import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository; import com.android.systemui.qs.pipeline.shared.TileSpec; @@ -417,6 +418,18 @@ public class LogModule { } /** + * Provides a {@link ClockMessageBuffers} which contains the keyguard clock message buffers. + */ + @Provides + public static ClockMessageBuffers provideKeyguardClockMessageBuffers( + @KeyguardClockLog LogBuffer infraClockLog, + @KeyguardSmallClockLog LogBuffer smallClockLog, + @KeyguardLargeClockLog LogBuffer largeClockLog + ) { + return new ClockMessageBuffers(infraClockLog, smallClockLog, largeClockLog); + } + + /** * Provides a {@link LogBuffer} for use by {@link com.android.keyguard.KeyguardUpdateMonitor}. */ @Provides diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt index 6f58bc2fcc9f..e6637e60b146 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt @@ -30,13 +30,15 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep -import com.android.systemui.log.LogBuffer +import com.android.systemui.log.core.LogLevel +import com.android.systemui.log.core.LogcatOnlyMessageBuffer import com.android.systemui.plugins.clocks.ClockAnimations import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.plugins.clocks.ClockEvents import com.android.systemui.plugins.clocks.ClockFaceConfig import com.android.systemui.plugins.clocks.ClockFaceController import com.android.systemui.plugins.clocks.ClockFaceEvents +import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockTickRate import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.ConfigurationController @@ -94,9 +96,9 @@ class ClockEventControllerTest : SysuiTestCase() { @Mock private lateinit var largeClockEvents: ClockFaceEvents @Mock private lateinit var parentView: View private lateinit var repository: FakeKeyguardRepository - @Mock private lateinit var smallLogBuffer: LogBuffer - @Mock private lateinit var largeLogBuffer: LogBuffer @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor + private val messageBuffer = LogcatOnlyMessageBuffer(LogLevel.DEBUG) + private val clockBuffers = ClockMessageBuffers(messageBuffer, messageBuffer, messageBuffer) private lateinit var underTest: ClockEventController @Mock private lateinit var zenModeController: ZenModeController @@ -140,8 +142,7 @@ class ClockEventControllerTest : SysuiTestCase() { context, mainExecutor, bgExecutor, - smallLogBuffer, - largeLogBuffer, + clockBuffers, withDeps.featureFlags, zenModeController ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt index ee94cbbcfd79..ee27c5c9ba0d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt @@ -26,6 +26,7 @@ import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags.TRANSIT_CLOCK import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.plugins.clocks.ClockId +import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockMetadata import com.android.systemui.plugins.clocks.ClockProviderPlugin import com.android.systemui.plugins.clocks.ClockSettings @@ -128,6 +129,7 @@ class ClockRegistryTest : SysuiTestCase() { override fun createClock(settings: ClockSettings): ClockController = createCallbacks[settings.clockId!!]!!(settings.clockId!!) override fun getClockThumbnail(id: ClockId): Drawable? = thumbnailCallbacks[id]!!(id) + override fun initialize(buffers: ClockMessageBuffers?) { } fun addClock( id: ClockId, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt index fef262f50306..e0e8d1f51884 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt @@ -26,6 +26,7 @@ import android.widget.FrameLayout import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.customization.R +import com.android.systemui.plugins.clocks.ClockId import com.android.systemui.plugins.clocks.ClockSettings import com.android.systemui.shared.clocks.DefaultClockController.Companion.DOZE_COLOR import com.android.systemui.util.mockito.any @@ -49,6 +50,9 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit +private fun DefaultClockProvider.createClock(id: ClockId): DefaultClockController = + createClock(ClockSettings(id, null)) as DefaultClockController + @RunWith(AndroidTestingRunner::class) @SmallTest class DefaultClockProviderTest : SysuiTestCase() { |