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 package com.android.systemui.statusbar.pipeline.shared.ui.binder 18 19 import android.animation.Animator 20 import android.animation.AnimatorListenerAdapter 21 import android.view.View 22 import android.widget.ImageView 23 import androidx.lifecycle.Lifecycle 24 import androidx.lifecycle.repeatOnLifecycle 25 import com.android.systemui.Flags 26 import com.android.systemui.common.ui.binder.IconViewBinder 27 import com.android.systemui.dagger.SysUISingleton 28 import com.android.systemui.lifecycle.repeatWhenAttached 29 import com.android.systemui.res.R 30 import com.android.systemui.statusbar.chips.ui.binder.ChipChronometerBinder 31 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel 32 import com.android.systemui.statusbar.chips.ui.view.ChipChronometer 33 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor 34 import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel 35 import javax.inject.Inject 36 import kotlinx.coroutines.launch 37 38 /** 39 * Interface to assist with binding the [CollapsedStatusBarFragment] to 40 * [CollapsedStatusBarViewModel]. Used only to enable easy testing of [CollapsedStatusBarFragment]. 41 */ 42 interface CollapsedStatusBarViewBinder { 43 /** 44 * Binds the view to the view-model. [listener] will be notified whenever an event that may 45 * change the status bar visibility occurs. 46 */ 47 fun bind( 48 view: View, 49 viewModel: CollapsedStatusBarViewModel, 50 listener: StatusBarVisibilityChangeListener, 51 ) 52 } 53 54 @SysUISingleton 55 class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBarViewBinder { bindnull56 override fun bind( 57 view: View, 58 viewModel: CollapsedStatusBarViewModel, 59 listener: StatusBarVisibilityChangeListener, 60 ) { 61 view.repeatWhenAttached { 62 repeatOnLifecycle(Lifecycle.State.CREATED) { 63 launch { 64 viewModel.isTransitioningFromLockscreenToOccluded.collect { 65 listener.onStatusBarVisibilityMaybeChanged() 66 } 67 } 68 69 launch { 70 viewModel.transitionFromLockscreenToDreamStartedEvent.collect { 71 listener.onTransitionFromLockscreenToDreamStarted() 72 } 73 } 74 75 if (NotificationsLiveDataStoreRefactor.isEnabled) { 76 val displayId = view.display.displayId 77 val lightsOutView: View = view.requireViewById(R.id.notification_lights_out) 78 launch { 79 viewModel.areNotificationsLightsOut(displayId).collect { show -> 80 animateLightsOutView(lightsOutView, show) 81 } 82 } 83 } 84 85 if (Flags.statusBarScreenSharingChips()) { 86 val chipView: View = view.requireViewById(R.id.ongoing_activity_chip) 87 val chipIconView: ImageView = 88 chipView.requireViewById(R.id.ongoing_activity_chip_icon) 89 val chipTimeView: ChipChronometer = 90 chipView.requireViewById(R.id.ongoing_activity_chip_time) 91 launch { 92 viewModel.ongoingActivityChip.collect { chipModel -> 93 when (chipModel) { 94 is OngoingActivityChipModel.Shown -> { 95 IconViewBinder.bind(chipModel.icon, chipIconView) 96 ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView) 97 chipView.setOnClickListener(chipModel.onClickListener) 98 99 listener.onOngoingActivityStatusChanged( 100 hasOngoingActivity = true 101 ) 102 } 103 is OngoingActivityChipModel.Hidden -> { 104 chipTimeView.stop() 105 listener.onOngoingActivityStatusChanged( 106 hasOngoingActivity = false 107 ) 108 } 109 } 110 } 111 } 112 } 113 } 114 } 115 } 116 animateLightsOutViewnull117 private fun animateLightsOutView(view: View, visible: Boolean) { 118 view.animate().cancel() 119 120 val alpha = if (visible) 1f else 0f 121 val duration = if (visible) 750L else 250L 122 val visibility = if (visible) View.VISIBLE else View.GONE 123 124 if (visible) { 125 view.alpha = 0f 126 view.visibility = View.VISIBLE 127 } 128 129 view 130 .animate() 131 .alpha(alpha) 132 .setDuration(duration) 133 .setListener( 134 object : AnimatorListenerAdapter() { 135 override fun onAnimationEnd(animation: Animator) { 136 view.alpha = alpha 137 view.visibility = visibility 138 // Unset the listener, otherwise this may persist for 139 // another view property animation 140 view.animate().setListener(null) 141 } 142 } 143 ) 144 .start() 145 } 146 } 147 148 /** Listener for various events that may affect the status bar's visibility. */ 149 interface StatusBarVisibilityChangeListener { 150 /** 151 * Called when the status bar visibility might have changed due to the device moving to a 152 * different state. 153 */ onStatusBarVisibilityMaybeChangednull154 fun onStatusBarVisibilityMaybeChanged() 155 156 /** Called when a transition from lockscreen to dream has started. */ 157 fun onTransitionFromLockscreenToDreamStarted() 158 159 /** Called when the status of the ongoing activity chip (active or not active) has changed. */ 160 fun onOngoingActivityStatusChanged(hasOngoingActivity: Boolean) 161 } 162