1 /* 2 * 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 package com.android.systemui.keyguard.ui.view.layout.sections 18 19 import android.content.Context 20 import android.view.View 21 import android.view.ViewGroup 22 import android.view.ViewTreeObserver.OnGlobalLayoutListener 23 import android.widget.LinearLayout 24 import androidx.constraintlayout.widget.Barrier 25 import androidx.constraintlayout.widget.ConstraintLayout 26 import androidx.constraintlayout.widget.ConstraintSet 27 import androidx.constraintlayout.widget.ConstraintSet.GONE 28 import androidx.constraintlayout.widget.ConstraintSet.VISIBLE 29 import com.android.systemui.customization.R as customR 30 import com.android.systemui.dagger.SysUISingleton 31 import com.android.systemui.keyguard.KeyguardUnlockAnimationController 32 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor 33 import com.android.systemui.keyguard.domain.interactor.KeyguardSmartspaceInteractor 34 import com.android.systemui.keyguard.shared.model.KeyguardSection 35 import com.android.systemui.keyguard.ui.binder.KeyguardSmartspaceViewBinder 36 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel 37 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel 38 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel 39 import com.android.systemui.res.R as R 40 import com.android.systemui.shade.ShadeDisplayAware 41 import com.android.systemui.shared.R as sharedR 42 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController 43 import dagger.Lazy 44 import javax.inject.Inject 45 import kotlinx.coroutines.DisposableHandle 46 47 @SysUISingleton 48 open class SmartspaceSection 49 @Inject 50 constructor( 51 @ShadeDisplayAware val context: Context, 52 val keyguardClockViewModel: KeyguardClockViewModel, 53 val keyguardSmartspaceViewModel: KeyguardSmartspaceViewModel, 54 private val keyguardSmartspaceInteractor: KeyguardSmartspaceInteractor, 55 val smartspaceController: LockscreenSmartspaceController, 56 val keyguardUnlockAnimationController: KeyguardUnlockAnimationController, 57 private val blueprintInteractor: Lazy<KeyguardBlueprintInteractor>, 58 private val keyguardRootViewModel: KeyguardRootViewModel, 59 ) : KeyguardSection() { 60 private var smartspaceView: View? = null 61 private var dateView: ViewGroup? = null 62 private var dateViewLargeClock: ViewGroup? = null 63 64 private var smartspaceVisibilityListener: OnGlobalLayoutListener? = null 65 private var pastVisibility: Int = -1 66 private var disposableHandle: DisposableHandle? = null 67 onRebuildBeginnull68 override fun onRebuildBegin() { 69 smartspaceController.suppressDisconnects = true 70 } 71 onRebuildEndnull72 override fun onRebuildEnd() { 73 smartspaceController.suppressDisconnects = false 74 } 75 addViewsnull76 override fun addViews(constraintLayout: ConstraintLayout) { 77 if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return 78 smartspaceView = smartspaceController.buildAndConnectView(constraintLayout) 79 dateView = 80 smartspaceController.buildAndConnectDateView(constraintLayout, false) as? ViewGroup 81 var weatherViewLargeClock: View? = null 82 val weatherView: View? = 83 smartspaceController.buildAndConnectWeatherView(constraintLayout, false) 84 if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) { 85 weatherViewLargeClock = 86 smartspaceController.buildAndConnectWeatherView(constraintLayout, true) 87 dateViewLargeClock = 88 smartspaceController.buildAndConnectDateView(constraintLayout, true) as? ViewGroup 89 } 90 pastVisibility = smartspaceView?.visibility ?: View.GONE 91 constraintLayout.addView(smartspaceView) 92 if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) { 93 dateView?.visibility = View.GONE 94 dateViewLargeClock?.visibility = View.GONE 95 constraintLayout.addView(dateViewLargeClock) 96 if (keyguardSmartspaceViewModel.isDateWeatherDecoupled) { 97 // Place weather right after the date, before the extras (alarm and dnd) 98 val index = if (dateViewLargeClock?.childCount == 0) 0 else 1 99 dateViewLargeClock?.addView(weatherViewLargeClock, index) 100 } 101 102 if ( 103 KeyguardSmartspaceViewModel.dateWeatherBelowSmallClock( 104 context.resources.configuration, 105 keyguardClockViewModel.hasCustomWeatherDataDisplay.value, 106 ) 107 ) { 108 (dateView as? LinearLayout)?.orientation = LinearLayout.HORIZONTAL 109 } else { 110 (dateView as? LinearLayout)?.orientation = LinearLayout.VERTICAL 111 } 112 } 113 114 if (keyguardSmartspaceViewModel.isDateWeatherDecoupled) { 115 constraintLayout.addView(dateView) 116 // Place weather right after the date, before the extras (alarm and dnd) 117 val index = if (dateView?.childCount == 0) 0 else 1 118 dateView?.addView(weatherView, index) 119 } 120 keyguardUnlockAnimationController.lockscreenSmartspace = smartspaceView 121 smartspaceVisibilityListener = OnGlobalLayoutListener { 122 smartspaceView?.let { 123 val newVisibility = it.visibility 124 if (pastVisibility != newVisibility) { 125 keyguardSmartspaceInteractor.setBcSmartspaceVisibility(newVisibility) 126 pastVisibility = newVisibility 127 } 128 } 129 } 130 smartspaceView?.viewTreeObserver?.addOnGlobalLayoutListener(smartspaceVisibilityListener) 131 } 132 bindDatanull133 override fun bindData(constraintLayout: ConstraintLayout) { 134 if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return 135 disposableHandle?.dispose() 136 disposableHandle = 137 KeyguardSmartspaceViewBinder.bind( 138 constraintLayout, 139 keyguardRootViewModel, 140 keyguardClockViewModel, 141 keyguardSmartspaceViewModel, 142 blueprintInteractor.get(), 143 ) 144 } 145 applyConstraintsnull146 override fun applyConstraints(constraintSet: ConstraintSet) { 147 if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return 148 val dateWeatherPaddingStart = KeyguardSmartspaceViewModel.getDateWeatherStartMargin(context) 149 val smartspaceHorizontalPadding = 150 KeyguardSmartspaceViewModel.getSmartspaceHorizontalMargin(context) 151 val dateWeatherBelowSmallClock = 152 KeyguardSmartspaceViewModel.dateWeatherBelowSmallClock( 153 context.resources.configuration, 154 keyguardClockViewModel.hasCustomWeatherDataDisplay.value, 155 ) 156 constraintSet.apply { 157 constrainHeight(sharedR.id.date_smartspace_view, ConstraintSet.WRAP_CONTENT) 158 constrainWidth(sharedR.id.date_smartspace_view, ConstraintSet.WRAP_CONTENT) 159 if (dateWeatherBelowSmallClock) { 160 connect( 161 sharedR.id.date_smartspace_view, 162 ConstraintSet.START, 163 ConstraintSet.PARENT_ID, 164 ConstraintSet.START, 165 dateWeatherPaddingStart, 166 ) 167 } 168 169 constrainHeight(sharedR.id.bc_smartspace_view, ConstraintSet.WRAP_CONTENT) 170 constrainWidth(sharedR.id.bc_smartspace_view, ConstraintSet.MATCH_CONSTRAINT) 171 connect( 172 sharedR.id.bc_smartspace_view, 173 ConstraintSet.START, 174 ConstraintSet.PARENT_ID, 175 ConstraintSet.START, 176 smartspaceHorizontalPadding, 177 ) 178 connect( 179 sharedR.id.bc_smartspace_view, 180 ConstraintSet.END, 181 if (keyguardSmartspaceViewModel.isShadeLayoutWide.value) R.id.split_shade_guideline 182 else ConstraintSet.PARENT_ID, 183 ConstraintSet.END, 184 smartspaceHorizontalPadding, 185 ) 186 if (keyguardClockViewModel.hasCustomWeatherDataDisplay.value) { 187 if (dateWeatherBelowSmallClock) { 188 clear(sharedR.id.date_smartspace_view, ConstraintSet.TOP) 189 connect( 190 sharedR.id.date_smartspace_view, 191 ConstraintSet.BOTTOM, 192 sharedR.id.bc_smartspace_view, 193 ConstraintSet.TOP, 194 ) 195 } 196 } else { 197 clear(sharedR.id.date_smartspace_view, ConstraintSet.BOTTOM) 198 if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) { 199 if (dateWeatherBelowSmallClock) { 200 connect( 201 sharedR.id.date_smartspace_view, 202 ConstraintSet.TOP, 203 customR.id.lockscreen_clock_view, 204 ConstraintSet.BOTTOM, 205 ) 206 connect( 207 sharedR.id.bc_smartspace_view, 208 ConstraintSet.TOP, 209 sharedR.id.date_smartspace_view, 210 ConstraintSet.BOTTOM, 211 ) 212 } else { 213 connect( 214 sharedR.id.bc_smartspace_view, 215 ConstraintSet.TOP, 216 customR.id.lockscreen_clock_view, 217 ConstraintSet.BOTTOM, 218 ) 219 } 220 } else { 221 connect( 222 sharedR.id.date_smartspace_view, 223 ConstraintSet.TOP, 224 customR.id.lockscreen_clock_view, 225 ConstraintSet.BOTTOM, 226 ) 227 connect( 228 sharedR.id.bc_smartspace_view, 229 ConstraintSet.TOP, 230 sharedR.id.date_smartspace_view, 231 ConstraintSet.BOTTOM, 232 ) 233 } 234 } 235 236 if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) { 237 if (keyguardClockViewModel.isLargeClockVisible.value) { 238 setVisibility(sharedR.id.date_smartspace_view, GONE) 239 constrainHeight( 240 sharedR.id.date_smartspace_view_large, 241 ConstraintSet.WRAP_CONTENT, 242 ) 243 constrainWidth( 244 sharedR.id.date_smartspace_view_large, 245 ConstraintSet.WRAP_CONTENT, 246 ) 247 constrainHeight( 248 sharedR.id.weather_smartspace_view_large, 249 ConstraintSet.WRAP_CONTENT, 250 ) 251 constrainWidth( 252 sharedR.id.weather_smartspace_view_large, 253 ConstraintSet.WRAP_CONTENT, 254 ) 255 connect( 256 sharedR.id.date_smartspace_view_large, 257 ConstraintSet.TOP, 258 customR.id.lockscreen_clock_view_large, 259 ConstraintSet.BOTTOM, 260 context.resources.getDimensionPixelSize(R.dimen.smartspace_padding_vertical), 261 ) 262 263 connect( 264 sharedR.id.date_smartspace_view_large, 265 ConstraintSet.START, 266 customR.id.lockscreen_clock_view_large, 267 ConstraintSet.START, 268 ) 269 connect( 270 sharedR.id.date_smartspace_view_large, 271 ConstraintSet.END, 272 customR.id.lockscreen_clock_view_large, 273 ConstraintSet.END, 274 ) 275 setHorizontalChainStyle( 276 sharedR.id.date_smartspace_view_large, 277 ConstraintSet.CHAIN_PACKED, 278 ) 279 } else { 280 if (dateWeatherBelowSmallClock) { 281 connect( 282 sharedR.id.date_smartspace_view, 283 ConstraintSet.START, 284 ConstraintSet.PARENT_ID, 285 ConstraintSet.START, 286 dateWeatherPaddingStart, 287 ) 288 } else { 289 setVisibility(sharedR.id.date_smartspace_view_large, GONE) 290 constrainHeight(sharedR.id.date_smartspace_view, ConstraintSet.WRAP_CONTENT) 291 constrainWidth(sharedR.id.date_smartspace_view, ConstraintSet.WRAP_CONTENT) 292 connect( 293 sharedR.id.date_smartspace_view, 294 ConstraintSet.START, 295 customR.id.lockscreen_clock_view, 296 ConstraintSet.END, 297 context.resources.getDimensionPixelSize( 298 R.dimen.smartspace_padding_horizontal 299 ), 300 ) 301 connect( 302 sharedR.id.date_smartspace_view, 303 ConstraintSet.TOP, 304 customR.id.lockscreen_clock_view, 305 ConstraintSet.TOP, 306 ) 307 connect( 308 sharedR.id.date_smartspace_view, 309 ConstraintSet.BOTTOM, 310 customR.id.lockscreen_clock_view, 311 ConstraintSet.BOTTOM, 312 ) 313 } 314 } 315 } 316 317 if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) { 318 if (dateWeatherBelowSmallClock) { 319 createBarrier( 320 R.id.smart_space_barrier_bottom, 321 Barrier.BOTTOM, 322 0, 323 *intArrayOf(sharedR.id.bc_smartspace_view, sharedR.id.date_smartspace_view), 324 ) 325 createBarrier( 326 R.id.smart_space_barrier_top, 327 Barrier.TOP, 328 0, 329 *intArrayOf(sharedR.id.bc_smartspace_view, sharedR.id.date_smartspace_view), 330 ) 331 } else { 332 createBarrier( 333 R.id.smart_space_barrier_bottom, 334 Barrier.BOTTOM, 335 0, 336 sharedR.id.bc_smartspace_view, 337 ) 338 createBarrier( 339 R.id.smart_space_barrier_top, 340 Barrier.TOP, 341 0, 342 sharedR.id.bc_smartspace_view, 343 ) 344 } 345 } else { 346 createBarrier( 347 R.id.smart_space_barrier_bottom, 348 Barrier.BOTTOM, 349 0, 350 *intArrayOf(sharedR.id.bc_smartspace_view, sharedR.id.date_smartspace_view), 351 ) 352 createBarrier( 353 R.id.smart_space_barrier_top, 354 Barrier.TOP, 355 0, 356 *intArrayOf(sharedR.id.bc_smartspace_view, sharedR.id.date_smartspace_view), 357 ) 358 } 359 } 360 updateVisibility(constraintSet) 361 } 362 removeViewsnull363 override fun removeViews(constraintLayout: ConstraintLayout) { 364 if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return 365 366 val list = 367 if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) { 368 listOf(smartspaceView, dateView, dateViewLargeClock) 369 } else { 370 listOf(smartspaceView, dateView) 371 } 372 list.forEach { 373 it?.let { 374 if (it.parent == constraintLayout) { 375 constraintLayout.removeView(it) 376 } 377 } 378 } 379 smartspaceView?.viewTreeObserver?.removeOnGlobalLayoutListener(smartspaceVisibilityListener) 380 smartspaceVisibilityListener = null 381 382 disposableHandle?.dispose() 383 } 384 updateVisibilitynull385 private fun updateVisibility(constraintSet: ConstraintSet) { 386 387 // This may update the visibility of the smartspace views 388 smartspaceController.requestSmartspaceUpdate() 389 val weatherId: Int 390 val dateId: Int 391 if ( 392 com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout() && 393 keyguardClockViewModel.isLargeClockVisible.value 394 ) { 395 weatherId = sharedR.id.weather_smartspace_view_large 396 dateId = sharedR.id.date_smartspace_view_large 397 } else { 398 weatherId = sharedR.id.weather_smartspace_view 399 dateId = sharedR.id.date_smartspace_view 400 } 401 402 constraintSet.apply { 403 val showWeather = keyguardSmartspaceViewModel.isWeatherVisible.value 404 setVisibility(weatherId, if (showWeather) VISIBLE else GONE) 405 setAlpha(weatherId, if (showWeather) 1f else 0f) 406 407 val showDateView = !keyguardClockViewModel.hasCustomWeatherDataDisplay.value 408 setVisibility(dateId, if (showDateView) VISIBLE else GONE) 409 setAlpha(dateId, if (showDateView) 1f else 0f) 410 411 if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) { 412 if (keyguardClockViewModel.isLargeClockVisible.value) { 413 setVisibility(sharedR.id.date_smartspace_view, GONE) 414 } else { 415 setVisibility(sharedR.id.date_smartspace_view_large, GONE) 416 } 417 } 418 } 419 } 420 } 421