1 /* <lambda>null2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 18 package com.android.systemui.keyguard.ui.binder 19 20 import android.content.Context 21 import android.util.DisplayMetrics 22 import android.view.View 23 import android.view.View.INVISIBLE 24 import android.view.View.VISIBLE 25 import android.view.ViewGroup 26 import android.view.ViewGroup.LayoutParams.WRAP_CONTENT 27 import androidx.constraintlayout.widget.ConstraintLayout 28 import androidx.constraintlayout.widget.ConstraintSet 29 import androidx.constraintlayout.widget.ConstraintSet.BOTTOM 30 import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID 31 import androidx.constraintlayout.widget.ConstraintSet.START 32 import androidx.constraintlayout.widget.ConstraintSet.TOP 33 import androidx.core.view.isVisible 34 import androidx.lifecycle.Lifecycle 35 import androidx.lifecycle.repeatOnLifecycle 36 import com.android.app.tracing.coroutines.launch 37 import com.android.internal.policy.SystemBarUtils 38 import com.android.systemui.customization.R as customizationR 39 import com.android.systemui.keyguard.shared.model.ClockSizeSetting 40 import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRenderer 41 import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection.Companion.getDimen 42 import com.android.systemui.keyguard.ui.view.layout.sections.setVisibility 43 import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockViewModel 44 import com.android.systemui.lifecycle.repeatWhenAttached 45 import com.android.systemui.plugins.clocks.ClockController 46 import com.android.systemui.res.R 47 import com.android.systemui.shared.clocks.ClockRegistry 48 import com.android.systemui.util.Utils 49 import kotlin.reflect.KSuspendFunction1 50 51 /** Binder for the small clock view, large clock view. */ 52 object KeyguardPreviewClockViewBinder { 53 @JvmStatic 54 fun bind( 55 largeClockHostView: View, 56 smallClockHostView: View, 57 viewModel: KeyguardPreviewClockViewModel, 58 ) { 59 largeClockHostView.repeatWhenAttached { 60 repeatOnLifecycle(Lifecycle.State.STARTED) { 61 launch("$TAG#viewModel.isLargeClockVisible") { 62 viewModel.isLargeClockVisible.collect { largeClockHostView.isVisible = it } 63 } 64 } 65 } 66 67 smallClockHostView.repeatWhenAttached { 68 repeatOnLifecycle(Lifecycle.State.STARTED) { 69 launch("$TAG#viewModel.isSmallClockVisible") { 70 viewModel.isSmallClockVisible.collect { smallClockHostView.isVisible = it } 71 } 72 } 73 } 74 } 75 76 @JvmStatic 77 fun bind( 78 context: Context, 79 rootView: ConstraintLayout, 80 viewModel: KeyguardPreviewClockViewModel, 81 clockRegistry: ClockRegistry, 82 updateClockAppearance: KSuspendFunction1<ClockController, Unit>, 83 ) { 84 rootView.repeatWhenAttached { 85 repeatOnLifecycle(Lifecycle.State.STARTED) { 86 var lastClock: ClockController? = null 87 launch("$TAG#viewModel.previewClock") { 88 viewModel.previewClock.collect { currentClock -> 89 lastClock?.let { clock -> 90 (clock.largeClock.layout.views + clock.smallClock.layout.views) 91 .forEach { rootView.removeView(it) } 92 } 93 lastClock = currentClock 94 updateClockAppearance(currentClock) 95 96 if (viewModel.shouldHighlightSelectedAffordance) { 97 (currentClock.largeClock.layout.views + 98 currentClock.smallClock.layout.views) 99 .forEach { it.alpha = KeyguardPreviewRenderer.DIM_ALPHA } 100 } 101 currentClock.largeClock.layout.views.forEach { 102 (it.parent as? ViewGroup)?.removeView(it) 103 rootView.addView(it) 104 } 105 106 currentClock.smallClock.layout.views.forEach { 107 (it.parent as? ViewGroup)?.removeView(it) 108 rootView.addView(it) 109 } 110 applyPreviewConstraints(context, rootView, currentClock, viewModel) 111 } 112 } 113 .invokeOnCompletion { 114 // recover seed color especially for Transit clock 115 lastClock?.events?.onSeedColorChanged(clockRegistry.seedColor) 116 } 117 } 118 } 119 } 120 121 private fun applyClockDefaultConstraints(context: Context, constraints: ConstraintSet) { 122 constraints.apply { 123 constrainWidth(R.id.lockscreen_clock_view_large, ConstraintSet.WRAP_CONTENT) 124 constrainHeight(R.id.lockscreen_clock_view_large, ConstraintSet.MATCH_CONSTRAINT) 125 val largeClockTopMargin = 126 SystemBarUtils.getStatusBarHeight(context) + 127 context.resources.getDimensionPixelSize( 128 customizationR.dimen.small_clock_padding_top 129 ) + 130 context.resources.getDimensionPixelSize( 131 R.dimen.keyguard_smartspace_top_offset 132 ) + 133 getDimen(context, DATE_WEATHER_VIEW_HEIGHT) + 134 getDimen(context, ENHANCED_SMARTSPACE_HEIGHT) 135 connect(R.id.lockscreen_clock_view_large, TOP, PARENT_ID, TOP, largeClockTopMargin) 136 connect(R.id.lockscreen_clock_view_large, START, PARENT_ID, START) 137 connect( 138 R.id.lockscreen_clock_view_large, 139 ConstraintSet.END, 140 PARENT_ID, 141 ConstraintSet.END 142 ) 143 144 // In preview, we'll show UDFPS icon for UDFPS devices 145 // and nothing for non-UDFPS devices, 146 // but we need position of device entry icon to constrain clock 147 if (getConstraint(R.id.lock_icon_view) != null) { 148 connect(R.id.lockscreen_clock_view_large, BOTTOM, R.id.lock_icon_view, TOP) 149 } else { 150 // Copied calculation codes from applyConstraints in DefaultDeviceEntrySection 151 val bottomPaddingPx = 152 context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom) 153 val defaultDensity = 154 DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() / 155 DisplayMetrics.DENSITY_DEFAULT.toFloat() 156 val lockIconRadiusPx = (defaultDensity * 36).toInt() 157 val clockBottomMargin = bottomPaddingPx + 2 * lockIconRadiusPx 158 connect( 159 R.id.lockscreen_clock_view_large, 160 BOTTOM, 161 PARENT_ID, 162 BOTTOM, 163 clockBottomMargin 164 ) 165 } 166 167 constrainWidth(R.id.lockscreen_clock_view, WRAP_CONTENT) 168 constrainHeight( 169 R.id.lockscreen_clock_view, 170 context.resources.getDimensionPixelSize(customizationR.dimen.small_clock_height) 171 ) 172 connect( 173 R.id.lockscreen_clock_view, 174 START, 175 PARENT_ID, 176 START, 177 context.resources.getDimensionPixelSize(customizationR.dimen.clock_padding_start) + 178 context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal) 179 ) 180 val smallClockTopMargin = 181 context.resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) + 182 Utils.getStatusBarHeaderHeightKeyguard(context) 183 connect(R.id.lockscreen_clock_view, TOP, PARENT_ID, TOP, smallClockTopMargin) 184 } 185 } 186 187 private fun applyPreviewConstraints( 188 context: Context, 189 rootView: ConstraintLayout, 190 previewClock: ClockController, 191 viewModel: KeyguardPreviewClockViewModel 192 ) { 193 val cs = ConstraintSet().apply { clone(rootView) } 194 applyClockDefaultConstraints(context, cs) 195 previewClock.largeClock.layout.applyPreviewConstraints(cs) 196 previewClock.smallClock.layout.applyPreviewConstraints(cs) 197 198 // When selectedClockSize is the initial value, make both clocks invisible to avoid 199 // flickering 200 val largeClockVisibility = 201 when (viewModel.selectedClockSize.value) { 202 ClockSizeSetting.DYNAMIC -> VISIBLE 203 ClockSizeSetting.SMALL -> INVISIBLE 204 null -> INVISIBLE 205 } 206 val smallClockVisibility = 207 when (viewModel.selectedClockSize.value) { 208 ClockSizeSetting.DYNAMIC -> INVISIBLE 209 ClockSizeSetting.SMALL -> VISIBLE 210 null -> INVISIBLE 211 } 212 cs.apply { 213 setVisibility(previewClock.largeClock.layout.views, largeClockVisibility) 214 setVisibility(previewClock.smallClock.layout.views, smallClockVisibility) 215 } 216 cs.applyTo(rootView) 217 } 218 219 private const val DATE_WEATHER_VIEW_HEIGHT = "date_weather_view_height" 220 private const val ENHANCED_SMARTSPACE_HEIGHT = "enhanced_smartspace_height" 221 private const val TAG = "KeyguardPreviewClockViewBinder" 222 } 223