1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 package com.android.systemui.plugins.clocks 15 16 import android.content.Context 17 import android.util.DisplayMetrics 18 import android.view.View 19 import androidx.constraintlayout.widget.ConstraintSet 20 import androidx.constraintlayout.widget.ConstraintSet.BOTTOM 21 import androidx.constraintlayout.widget.ConstraintSet.END 22 import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID 23 import androidx.constraintlayout.widget.ConstraintSet.START 24 import androidx.constraintlayout.widget.ConstraintSet.TOP 25 import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT 26 import com.android.internal.policy.SystemBarUtils 27 import com.android.systemui.plugins.annotations.GeneratedImport 28 import com.android.systemui.plugins.annotations.ProtectedInterface 29 import com.android.systemui.plugins.annotations.ProtectedReturn 30 31 /** Specifies layout information for the clock face */ 32 @ProtectedInterface 33 @GeneratedImport("java.util.ArrayList") 34 @GeneratedImport("android.view.View") 35 interface ClockFaceLayout { 36 @get:ProtectedReturn("return new ArrayList<View>();") 37 /** All clock views to add to the root constraint layout before applying constraints. */ 38 val views: List<View> 39 40 @ProtectedReturn("return constraints;") 41 /** Custom constraints to apply to Lockscreen ConstraintLayout. */ applyConstraintsnull42 fun applyConstraints(constraints: ConstraintSet): ConstraintSet 43 44 @ProtectedReturn("return constraints;") 45 /** Custom constraints to apply to preview ConstraintLayout. */ 46 fun applyPreviewConstraints( 47 clockPreviewConfig: ClockPreviewConfig, 48 constraints: ConstraintSet, 49 ): ConstraintSet 50 51 /** Apply specified AOD BurnIn parameters to this layout */ 52 fun applyAodBurnIn(aodBurnInModel: AodClockBurnInModel) 53 } 54 55 /** Data class to contain AOD BurnIn information for correct aod rendering */ 56 data class AodClockBurnInModel( 57 /** Scale that the clock should render at to mitigate burnin */ 58 val scale: Float, 59 60 /** X-Translation for the clock to mitigate burnin */ 61 val translationX: Float, 62 63 /** Y-Translation for the clock to mitigate burnin */ 64 val translationY: Float, 65 ) 66 67 /** A ClockFaceLayout that applies the default lockscreen layout to a single view */ 68 class DefaultClockFaceLayout(val view: View) : ClockFaceLayout { 69 override val views = listOf(view) 70 71 override fun applyConstraints(constraints: ConstraintSet): ConstraintSet { 72 if (views.size != 1) { 73 throw IllegalArgumentException( 74 "Should have only one container view when using DefaultClockFaceLayout" 75 ) 76 } 77 return constraints 78 } 79 80 override fun applyPreviewConstraints( 81 clockPreviewConfig: ClockPreviewConfig, 82 constraints: ConstraintSet, 83 ): ConstraintSet { 84 return applyDefaultPreviewConstraints(clockPreviewConfig, constraints) 85 } 86 87 override fun applyAodBurnIn(aodBurnInModel: AodClockBurnInModel) { 88 // Default clock doesn't need detailed control of view 89 } 90 91 companion object { 92 fun applyDefaultPreviewConstraints( 93 clockPreviewConfig: ClockPreviewConfig, 94 constraints: ConstraintSet, 95 ): ConstraintSet { 96 constraints.apply { 97 val context = clockPreviewConfig.previewContext 98 val lockscreenClockViewLargeId = getId(context, "lockscreen_clock_view_large") 99 constrainWidth(lockscreenClockViewLargeId, WRAP_CONTENT) 100 constrainHeight(lockscreenClockViewLargeId, WRAP_CONTENT) 101 constrainMaxHeight(lockscreenClockViewLargeId, 0) 102 103 val largeClockTopMargin = 104 SystemBarUtils.getStatusBarHeight(context) + 105 getDimen(context, "small_clock_padding_top") + 106 getDimen(context, "keyguard_smartspace_top_offset") + 107 getDimen(context, "date_weather_view_height") + 108 getDimen(context, "enhanced_smartspace_height") 109 connect(lockscreenClockViewLargeId, TOP, PARENT_ID, TOP, largeClockTopMargin) 110 connect(lockscreenClockViewLargeId, START, PARENT_ID, START) 111 connect(lockscreenClockViewLargeId, END, PARENT_ID, END) 112 113 // In preview, we'll show UDFPS icon for UDFPS devices 114 // and nothing for non-UDFPS devices, 115 // and we're not planning to add this vide in clockHostView 116 // so we only need position of device entry icon to constrain clock 117 // Copied calculation codes from applyConstraints in DefaultDeviceEntrySection 118 val bottomPaddingPx = getDimen(context, "lock_icon_margin_bottom") 119 val defaultDensity = 120 DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() / 121 DisplayMetrics.DENSITY_DEFAULT.toFloat() 122 val lockIconRadiusPx = (defaultDensity * 36).toInt() 123 val clockBottomMargin = bottomPaddingPx + 2 * lockIconRadiusPx 124 125 connect(lockscreenClockViewLargeId, BOTTOM, PARENT_ID, BOTTOM, clockBottomMargin) 126 val smallClockViewId = getId(context, "lockscreen_clock_view") 127 constrainWidth(smallClockViewId, WRAP_CONTENT) 128 constrainHeight(smallClockViewId, getDimen(context, "small_clock_height")) 129 connect( 130 smallClockViewId, 131 START, 132 PARENT_ID, 133 START, 134 getDimen(context, "clock_padding_start") + 135 getDimen(context, "status_view_margin_horizontal"), 136 ) 137 val smallClockTopMargin = 138 getSmallClockTopPadding( 139 clockPreviewConfig = clockPreviewConfig, 140 SystemBarUtils.getStatusBarHeight(context), 141 ) 142 connect(smallClockViewId, TOP, PARENT_ID, TOP, smallClockTopMargin) 143 } 144 return constraints 145 } 146 147 fun getId(context: Context, name: String): Int { 148 val packageName = context.packageName 149 val res = context.packageManager.getResourcesForApplication(packageName) 150 val id = res.getIdentifier(name, "id", packageName) 151 return id 152 } 153 154 fun getDimen(context: Context, name: String): Int { 155 val packageName = context.packageName 156 val res = context.resources 157 val id = res.getIdentifier(name, "dimen", packageName) 158 return if (id == 0) 0 else res.getDimensionPixelSize(id) 159 } 160 161 fun getSmallClockTopPadding( 162 clockPreviewConfig: ClockPreviewConfig, 163 statusBarHeight: Int, 164 ): Int { 165 return if (clockPreviewConfig.isShadeLayoutWide) { 166 getDimen(clockPreviewConfig.previewContext, "keyguard_split_shade_top_margin") - 167 if (clockPreviewConfig.isSceneContainerFlagEnabled) statusBarHeight else 0 168 } else { 169 getDimen(clockPreviewConfig.previewContext, "keyguard_clock_top_margin") + 170 if (!clockPreviewConfig.isSceneContainerFlagEnabled) statusBarHeight else 0 171 } 172 } 173 } 174 } 175