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

Commit 512cbcab authored by Nicolò Mazzucato's avatar Nicolò Mazzucato Committed by Android (Google) Code Review
Browse files

Merge "Fix clocks layout on external displays" into main

parents 921f2de4 2534980b
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -45,6 +45,20 @@ open class DefaultClockFaceLayout(val view: View) : ClockFaceLayout {
        return constraints
    }

    override fun applyExternalDisplayPresentationConstraints(
        constraints: ConstraintSet,
    ): ConstraintSet {
        return constraints.apply {
            constrainWidth(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE, WRAP_CONTENT)
            constrainHeight(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE, WRAP_CONTENT)

            connect(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE, TOP, PARENT_ID, TOP)
            connect(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE, BOTTOM, PARENT_ID, BOTTOM)
            connect(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE, START, PARENT_ID, START)
            connect(ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE, END, PARENT_ID, END)
        }
    }

    override fun applyPreviewConstraints(
        clockPreviewConfig: ClockPreviewConfig,
        constraints: ConstraintSet,
+1 −1
Original line number Diff line number Diff line
@@ -54,7 +54,7 @@ import org.mockito.kotlin.whenever
class KeyguardDisplayManagerTest : SysuiTestCase() {
    @Mock private val navigationBarController = mock(NavigationBarController::class.java)
    @Mock
    private val presentationFactory = mock(ConnectedDisplayKeyguardPresentation.Factory::class.java)
    private val presentationFactory = mock(ConnectedDisplayKeyguardPresentationFactory::class.java)
    @Mock
    private val connectedDisplayKeyguardPresentation =
        mock(ConnectedDisplayKeyguardPresentation::class.java)
+4 −0
Original line number Diff line number Diff line
@@ -32,6 +32,10 @@ interface ClockFaceLayout {
    /** Custom constraints to apply to Lockscreen ConstraintLayout. */
    fun applyConstraints(constraints: ConstraintSet): ConstraintSet

    @ProtectedReturn("return constraints;")
    /** Custom constraints to apply to the external display presentation ConstraintLayout. */
    fun applyExternalDisplayPresentationConstraints(constraints: ConstraintSet): ConstraintSet

    @ProtectedReturn("return constraints;")
    /** Custom constraints to apply to preview ConstraintLayout. */
    fun applyPreviewConstraints(
+164 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.app.Presentation
import android.content.Context
import android.graphics.Color
import android.os.Bundle
import android.view.Display
import android.view.View
import android.view.WindowManager
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockFaceController
import com.android.systemui.plugins.clocks.ClockFaceLayout
import com.android.systemui.res.R
import com.android.systemui.shared.clocks.ClockRegistry
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject

/** Creates a keyguard presentation for a [Display]. */
fun interface ConnectedDisplayKeyguardPresentationFactory {
    fun create(display: Display): Presentation
}

/** [Presentation] shown in connected displays while on keyguard. */
class ConnectedDisplayConstraintLayoutKeyguardPresentation
@AssistedInject
constructor(
    @Assisted display: Display,
    context: Context,
    private val clockRegistry: ClockRegistry,
    private val clockEventController: ClockEventController,
) :
    Presentation(
        context,
        display,
        R.style.Theme_SystemUI_KeyguardPresentation,
        WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
    ) {

    private lateinit var constraintLayoutRootView: ConstraintLayout
    private lateinit var faceController: ClockFaceController

    private val clockChangedListener =
        object : ClockRegistry.ClockChangeListener {
            override fun onCurrentClockChanged() {
                setClock(constraintLayoutRootView, clockRegistry.createCurrentClock(context))
            }

            override fun onAvailableClocksChanged() {}
        }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        onCreateInternal()

        setShowWallpaperFlagOnWindow()
    }

    private fun onCreateInternal() {
        constraintLayoutRootView =
            ConstraintLayout(context).apply {
                layoutParams =
                    ConstraintLayout.LayoutParams(
                        ConstraintLayout.LayoutParams.MATCH_PARENT,
                        ConstraintLayout.LayoutParams.MATCH_PARENT,
                    )
                clipChildren = false
            }

        setContentView(constraintLayoutRootView)

        setFullscreen()

        setClock(constraintLayoutRootView, clockRegistry.createCurrentClock(context))
    }

    private fun setShowWallpaperFlagOnWindow() {
        val window = window ?: error("No window available")
        window.attributes =
            window.attributes.apply {
                flags = flags or WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER
            }
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        clockRegistry.registerClockChangeListener(clockChangedListener)
        clockEventController.registerListeners(constraintLayoutRootView)
        faceController.animations.enter()
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        clockEventController.unregisterListeners()
        clockRegistry.unregisterClockChangeListener(clockChangedListener)
    }

    override fun onDisplayChanged() {
        val window = window ?: error("no window available.")
        window.decorView.requestLayout()
    }

    private fun setClock(rootView: ConstraintLayout, clockController: ClockController) {
        clockEventController.clock = clockController
        clockEventController.setLargeClockOnSecondaryDisplay(true)
        faceController = clockController.largeClock
        faceController.events.onSecondaryDisplayChanged(true)

        rootView.removeAllViews()
        addClockLayoutConstraints(rootView, clockController.largeClock.layout)
    }

    private fun addClockLayoutConstraints(
        rootView: ConstraintLayout,
        clockLayout: ClockFaceLayout,
    ) {
        val cs = ConstraintSet().apply { clone(rootView) }
        clockLayout.views.forEach { view ->
            rootView.addView(view)
            cs.setVisibility(view.id, View.VISIBLE)
        }
        clockLayout.applyExternalDisplayPresentationConstraints(cs)
        cs.applyTo(rootView)
    }

    private fun setFullscreen() {
        window?.apply {
            // Logic to make the lock screen fullscreen
            decorView.systemUiVisibility =
                (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
                    View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
                    View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
            attributes.fitInsetsTypes = 0
            isNavigationBarContrastEnforced = false
            navigationBarColor = Color.TRANSPARENT
        } ?: error("No window available")
    }

    /** [ConnectedDisplayConstraintLayoutKeyguardPresentation] factory. */
    @AssistedFactory
    interface Factory {
        /** Creates a new [Presentation] for the given [display]. */
        fun create(display: Display): ConnectedDisplayConstraintLayoutKeyguardPresentation
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject

/** [Presentation] shown in connected displays while on keyguard. */
@Deprecated("Use ConnectedDisplayConstraintLayoutKeyguardPresentation instead.")
class ConnectedDisplayKeyguardPresentation
@AssistedInject
constructor(
Loading