Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d743e110 authored by Hawkwood Glazier's avatar Hawkwood Glazier
Browse files

Priority ordering for keyguard ConstraintSet transitions

To prevent transitions from fighting with each other, we assign each a
priority and only execute the highest priority transition when several
are triggered on the same frame.

Due to the ordering of animator updates and layout calls, we use a
ViewTreeObserver to assign the animation values. This prevents single
frame hiccups when layouts are unexpectedly triggered during a
transition (such as those from Smartspace).

There are still some odd animation hiccups possible here, particularly
from simultaneous smartspace transitions. It mostly looks okay, but due
to the inconsisntent timing of when smartspace decides to hide, the
interaction between these transitions is somewhat unpredictictable. We
may need a way to update SmartspaceMoveTransition while it's running.

Bug: 322196219
Test: atest com.android.systemui.keyguard
Test: manual; spotify playing splitshade LS <-> AOD
Flag: ACONFIG com.android.systemui.migrate_clocks_to_blueprint DEVELOPMENT
Change-Id: I31968ed495cba85464f422defaf197267478bf3b
parent 6a4c96f6
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.util

/** Injectable helper providing thread assertions. */
class ThreadAssert() {
    fun isMainThread() = Assert.isMainThread()
    fun isNotMainThread() = Assert.isNotMainThread()
}
+1 −0
Original line number Diff line number Diff line
@@ -228,6 +228,7 @@
    <item type="id" name="ambient_indication_container" />
    <item type="id" name="status_view_media_container" />
    <item type="id" name="smart_space_barrier_bottom" />
    <item type="id" name="small_clock_guideline_top" />
    <item type="id" name="weather_clock_date_and_icons_barrier_bottom" />

    <!-- Privacy dialog -->
+2 −1
Original line number Diff line number Diff line
@@ -100,6 +100,7 @@ constructor(
    private val keyguardClockViewModel: KeyguardClockViewModel,
    private val lockscreenContentViewModel: LockscreenContentViewModel,
    private val lockscreenSceneBlueprintsLazy: Lazy<Set<LockscreenSceneBlueprint>>,
    private val keyguardBlueprintViewBinder: KeyguardBlueprintViewBinder,
) : CoreStartable {

    private var rootViewHandle: DisposableHandle? = null
@@ -143,7 +144,7 @@ constructor(
                cs.connect(composeView.id, BOTTOM, PARENT_ID, BOTTOM)
                keyguardRootView.addView(composeView)
            } else {
                KeyguardBlueprintViewBinder.bind(
                keyguardBlueprintViewBinder.bind(
                    keyguardRootView,
                    keyguardBlueprintViewModel,
                    keyguardClockViewModel,
+8 −0
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.ThreadAssert;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.settings.SystemSettings;
@@ -223,6 +224,13 @@ public interface KeyguardModule {
        return new KeyguardQuickAffordancesMetricsLoggerImpl();
    }

    /** */
    @Provides
    @SysUISingleton
    static ThreadAssert providesThreadAssert() {
        return new ThreadAssert();
    }

    /** Binds {@link KeyguardUpdateMonitor} as a {@link CoreStartable}. */
    @Binds
    @IntoMap
+33 −12
Original line number Diff line number Diff line
@@ -17,14 +17,16 @@

package com.android.systemui.keyguard.data.repository

import android.os.Handler
import android.util.Log
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint.Companion.DEFAULT
import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransitionType
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransitionType.DefaultTransition
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config
import com.android.systemui.util.ThreadAssert
import java.io.PrintWriter
import java.util.TreeMap
import javax.inject.Inject
@@ -49,16 +51,17 @@ class KeyguardBlueprintRepository
constructor(
    configurationRepository: ConfigurationRepository,
    blueprints: Set<@JvmSuppressWildcards KeyguardBlueprint>,
    @Main val handler: Handler,
    val assert: ThreadAssert,
) {
    // This is TreeMap so that we can order the blueprints and assign numerical values to the
    // blueprints in the adb tool.
    private val blueprintIdMap: TreeMap<String, KeyguardBlueprint> =
        TreeMap<String, KeyguardBlueprint>().apply { putAll(blueprints.associateBy { it.id }) }
    val blueprint: MutableStateFlow<KeyguardBlueprint> = MutableStateFlow(blueprintIdMap[DEFAULT]!!)
    val refreshBluePrint: MutableSharedFlow<Unit> = MutableSharedFlow(extraBufferCapacity = 1)
    val refreshBlueprintTransition: MutableSharedFlow<IntraBlueprintTransitionType> =
        MutableSharedFlow(extraBufferCapacity = 1)
    val refreshTransition = MutableSharedFlow<Config>(extraBufferCapacity = 1)
    val configurationChange: Flow<Unit> = configurationRepository.onAnyConfigurationChange
    private var targetTransitionConfig: Config? = null

    /**
     * Emits the blueprint value to the collectors.
@@ -105,14 +108,32 @@ constructor(
        blueprint?.let { this.blueprint.value = it }
    }

    /** Re-emits the last emitted blueprint value if possible. */
    fun refreshBlueprint() {
        refreshBlueprintWithTransition(DefaultTransition)
    /**
     * Re-emits the last emitted blueprint value if possible. This is delayed until next frame to
     * dedupe requests and determine the correct transition to execute.
     */
    fun refreshBlueprint(config: Config = Config.DEFAULT) {
        fun scheduleCallback() {
            // We use a handler here instead of a CoroutineDipsatcher because the one provided by
            // @Main CoroutineDispatcher is currently Dispatchers.Main.immediate, which doesn't
            // delay the callback, and instead runs it imemdiately.
            handler.post {
                assert.isMainThread()
                targetTransitionConfig?.let {
                    val success = refreshTransition.tryEmit(it)
                    if (!success) {
                        Log.e(TAG, "refreshBlueprint: Failed to emit blueprint refresh: $it")
                    }
                }
                targetTransitionConfig = null
            }
        }

    fun refreshBlueprintWithTransition(type: IntraBlueprintTransitionType = DefaultTransition) {
        refreshBluePrint.tryEmit(Unit)
        refreshBlueprintTransition.tryEmit(type)
        assert.isMainThread()
        if ((targetTransitionConfig?.type?.priority ?: Int.MIN_VALUE) < config.type.priority) {
            if (targetTransitionConfig == null) scheduleCallback()
            targetTransitionConfig = config
        }
    }

    /** Prints all available blueprints to the PrintWriter. */
Loading