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.keyguard.ui.binder
18 
19 import android.animation.ValueAnimator
20 import android.graphics.Point
21 import com.android.systemui.CoreStartable
22 import com.android.systemui.Flags
23 import com.android.systemui.dagger.SysUISingleton
24 import com.android.systemui.dagger.qualifiers.Application
25 import com.android.systemui.keyguard.ui.view.SideFpsProgressBar
26 import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
27 import com.android.systemui.log.SideFpsLogger
28 import com.android.systemui.statusbar.commandline.Command
29 import com.android.systemui.statusbar.commandline.CommandRegistry
30 import com.android.systemui.util.kotlin.Quint
31 import java.io.PrintWriter
32 import javax.inject.Inject
33 import kotlinx.coroutines.CoroutineScope
34 import kotlinx.coroutines.Job
35 import kotlinx.coroutines.flow.collectLatest
36 import kotlinx.coroutines.flow.combine
37 import kotlinx.coroutines.launch
38 
39 private const val sfpsProgressBarCommand = "sfps-progress-bar"
40 
41 @SysUISingleton
42 class SideFpsProgressBarViewBinder
43 @Inject
44 constructor(
45     private val viewModel: SideFpsProgressBarViewModel,
46     private val view: SideFpsProgressBar,
47     @Application private val applicationScope: CoroutineScope,
48     private val logger: SideFpsLogger,
49     private val commandRegistry: CommandRegistry,
50 ) : CoreStartable {
51 
52     override fun start() {
53         if (!Flags.restToUnlock()) {
54             return
55         }
56         // When the rest to unlock feature is disabled by the user, stop any coroutines that are
57         // not required.
58         var layoutJob: Job? = null
59         var progressJob: Job? = null
60         commandRegistry.registerCommand(sfpsProgressBarCommand) { SfpsProgressBarCommand() }
61         applicationScope.launch {
62             viewModel.isProlongedTouchRequiredForAuthentication.collectLatest { enabled ->
63                 logger.isProlongedTouchRequiredForAuthenticationChanged(enabled)
64                 if (enabled) {
65                     layoutJob = launch {
66                         combine(
67                                 viewModel.isVisible,
68                                 viewModel.progressBarLocation,
69                                 viewModel.rotation,
70                                 viewModel.isFingerprintAuthRunning,
71                                 viewModel.progressBarLength,
72                                 ::Quint
73                             )
74                             .collectLatest { (visible, location, rotation, fpDetectRunning, length)
75                                 ->
76                                 updateView(
77                                     visible,
78                                     location,
79                                     fpDetectRunning,
80                                     length,
81                                     viewModel.progressBarThickness,
82                                     rotation,
83                                 )
84                             }
85                     }
86                     progressJob = launch {
87                         viewModel.progress.collectLatest { view.setProgress(it) }
88                     }
89                 } else {
90                     view.hide()
91                     layoutJob?.cancel()
92                     progressJob?.cancel()
93                 }
94             }
95         }
96     }
97 
98     private fun updateView(
99         visible: Boolean,
100         location: Point,
101         fpDetectRunning: Boolean,
102         length: Int,
103         thickness: Int,
104         rotation: Float,
105     ) {
106         logger.sfpsProgressBarStateChanged(visible, location, fpDetectRunning, length, rotation)
107         view.updateView(visible, location, length, thickness, rotation)
108     }
109 
110     inner class SfpsProgressBarCommand : Command {
111         private var animator: ValueAnimator? = null
112         override fun execute(pw: PrintWriter, args: List<String>) {
113             if (args.isEmpty() || args[0] == "show" && args.size != 6) {
114                 pw.println("invalid command")
115                 help(pw)
116             } else {
117                 when (args[0]) {
118                     "show" -> {
119                         animator?.cancel()
120                         updateView(
121                             visible = true,
122                             location = Point(Integer.parseInt(args[1]), Integer.parseInt(args[2])),
123                             fpDetectRunning = true,
124                             length = Integer.parseInt(args[3]),
125                             thickness = Integer.parseInt(args[4]),
126                             rotation = Integer.parseInt(args[5]).toFloat(),
127                         )
128                         animator =
129                             ValueAnimator.ofFloat(0.0f, 1.0f).apply {
130                                 repeatMode = ValueAnimator.REVERSE
131                                 repeatCount = ValueAnimator.INFINITE
132                                 addUpdateListener { view.setProgress(it.animatedValue as Float) }
133                             }
134                         animator?.start()
135                     }
136                     "hide" -> {
137                         animator?.cancel()
138                         updateView(
139                             visible = false,
140                             location = Point(0, 0),
141                             fpDetectRunning = false,
142                             length = 0,
143                             thickness = 0,
144                             rotation = 0.0f,
145                         )
146                     }
147                 }
148             }
149         }
150 
151         override fun help(pw: PrintWriter) {
152             pw.println("Usage: adb shell cmd statusbar $sfpsProgressBarCommand <command>")
153             pw.println("Available commands:")
154             pw.println("  show x y width height rotation")
155             pw.println("  hide")
156         }
157     }
158 }
159