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.decor
18
19 import android.graphics.Color
20 import android.graphics.Path
21 import android.util.PathParser
22 import com.android.systemui.statusbar.commandline.ParseableCommand
23 import com.android.systemui.statusbar.commandline.Type
24 import com.android.systemui.statusbar.commandline.map
25 import java.io.PrintWriter
26
27 /** Debug screen-decor command to be handled by the SystemUI command line interface */
28 class ScreenDecorCommand(
29 private val callback: Callback,
30 ) : ParseableCommand(SCREEN_DECOR_CMD_NAME) {
31 val debug: Boolean? by
32 param(
33 longName = "debug",
34 description =
35 "Enter or exits debug mode. Effectively makes the corners visible and allows " +
36 "for overriding the path data for the anti-aliasing corner paths and display " +
37 "cutout.",
38 valueParser = Type.Boolean,
39 )
40
41 val color: Int? by
42 param(
43 longName = "color",
44 shortName = "c",
45 description =
46 "Set a specific color for the debug assets. See Color#parseString() for " +
47 "accepted inputs.",
<lambda>null48 valueParser = Type.String.map { it.toColorIntOrNull() }
49 )
50
51 val roundedTop: RoundedCornerSubCommand? by subCommand(RoundedCornerSubCommand("rounded-top"))
52
53 val roundedBottom: RoundedCornerSubCommand? by
54 subCommand(RoundedCornerSubCommand("rounded-bottom"))
55
56 val faceAuthScreen: Int? by
57 param(
58 longName = "faceAuthScreen",
59 description =
60 "Specify a screen to show face auth animation. 0:outer(default screen), 1:inner",
61 valueParser = Type.Int,
62 )
63
executenull64 override fun execute(pw: PrintWriter) {
65 callback.onExecute(this, pw)
66 }
67
toStringnull68 override fun toString(): String {
69 return "ScreenDecorCommand(" +
70 "debug=$debug, " +
71 "color=$color, " +
72 "faceAuthScreen=$faceAuthScreen, " +
73 "roundedTop=$roundedTop, " +
74 "roundedBottom=$roundedBottom)"
75 }
76
77 /** For use in ScreenDecorations.java, define a Callback */
78 interface Callback {
onExecutenull79 fun onExecute(cmd: ScreenDecorCommand, pw: PrintWriter)
80 }
81
82 companion object {
83 const val SCREEN_DECOR_CMD_NAME = "screen-decor"
84 }
85 }
86
87 /**
88 * Defines a subcommand suitable for `rounded-top` and `rounded-bottom`. They both have the same
89 * API.
90 */
91 class RoundedCornerSubCommand(name: String) : ParseableCommand(name) {
92 val height by
93 param(
94 longName = "height",
95 description = "The height of a corner, in pixels.",
96 valueParser = Type.Int,
97 )
98 .required()
99
100 val width by
101 param(
102 longName = "width",
103 description =
104 "The width of the corner, in pixels. Likely should be equal to the height.",
105 valueParser = Type.Int,
106 )
107 .required()
108
109 val pathData by
110 param(
111 longName = "path-data",
112 shortName = "d",
113 description =
114 "PathParser-compatible path string to be rendered as the corner drawable. " +
115 "This path should be a closed arc oriented as the top-left corner " +
116 "of the device",
<lambda>null117 valueParser = Type.String.map { it.toPathOrNull() }
118 )
119 .required()
120
121 val viewportHeight: Float? by
122 param(
123 longName = "viewport-height",
124 description =
125 "The height of the viewport for the given path string. " +
126 "If null, the corner height will be used.",
127 valueParser = Type.Float,
128 )
129
130 val scaleY: Float
<lambda>null131 get() = viewportHeight?.let { height.toFloat() / it } ?: 1.0f
132
133 val viewportWidth: Float? by
134 param(
135 longName = "viewport-width",
136 description =
137 "The width of the viewport for the given path string. " +
138 "If null, the corner width will be used.",
139 valueParser = Type.Float,
140 )
141
142 val scaleX: Float
<lambda>null143 get() = viewportWidth?.let { width.toFloat() / it } ?: 1.0f
144
executenull145 override fun execute(pw: PrintWriter) {
146 // Not needed for a subcommand
147 }
148
toStringnull149 override fun toString(): String {
150 return "RoundedCornerSubCommand(" +
151 "height=$height," +
152 " width=$width," +
153 " pathData='$pathData'," +
154 " viewportHeight=$viewportHeight," +
155 " viewportWidth=$viewportWidth)"
156 }
157
toRoundedCornerDebugModelnull158 fun toRoundedCornerDebugModel(): DebugRoundedCornerModel =
159 DebugRoundedCornerModel(
160 path = pathData,
161 width = width,
162 height = height,
163 scaleX = scaleX,
164 scaleY = scaleY,
165 )
166 }
167
168 fun String.toPathOrNull(): Path? =
169 try {
170 PathParser.createPathFromPathData(this)
171 } catch (e: Exception) {
172 null
173 }
174
toColorIntOrNullnull175 fun String.toColorIntOrNull(): Int? =
176 try {
177 Color.parseColor(this)
178 } catch (e: Exception) {
179 null
180 }
181