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
executenull56 override fun execute(pw: PrintWriter) {
57 callback.onExecute(this, pw)
58 }
59
toStringnull60 override fun toString(): String {
61 return "ScreenDecorCommand(" +
62 "debug=$debug, " +
63 "color=$color, " +
64 "roundedTop=$roundedTop, " +
65 "roundedBottom=$roundedBottom)"
66 }
67
68 /** For use in ScreenDecorations.java, define a Callback */
69 interface Callback {
onExecutenull70 fun onExecute(cmd: ScreenDecorCommand, pw: PrintWriter)
71 }
72
73 companion object {
74 const val SCREEN_DECOR_CMD_NAME = "screen-decor"
75 }
76 }
77
78 /**
79 * Defines a subcommand suitable for `rounded-top` and `rounded-bottom`. They both have the same
80 * API.
81 */
82 class RoundedCornerSubCommand(name: String) : ParseableCommand(name) {
83 val height by
84 param(
85 longName = "height",
86 description = "The height of a corner, in pixels.",
87 valueParser = Type.Int,
88 )
89 .required()
90
91 val width by
92 param(
93 longName = "width",
94 description =
95 "The width of the corner, in pixels. Likely should be equal to the height.",
96 valueParser = Type.Int,
97 )
98 .required()
99
100 val pathData by
101 param(
102 longName = "path-data",
103 shortName = "d",
104 description =
105 "PathParser-compatible path string to be rendered as the corner drawable. " +
106 "This path should be a closed arc oriented as the top-left corner " +
107 "of the device",
<lambda>null108 valueParser = Type.String.map { it.toPathOrNull() }
109 )
110 .required()
111
112 val viewportHeight: Float? by
113 param(
114 longName = "viewport-height",
115 description =
116 "The height of the viewport for the given path string. " +
117 "If null, the corner height will be used.",
118 valueParser = Type.Float,
119 )
120
121 val scaleY: Float
<lambda>null122 get() = viewportHeight?.let { height.toFloat() / it } ?: 1.0f
123
124 val viewportWidth: Float? by
125 param(
126 longName = "viewport-width",
127 description =
128 "The width of the viewport for the given path string. " +
129 "If null, the corner width will be used.",
130 valueParser = Type.Float,
131 )
132
133 val scaleX: Float
<lambda>null134 get() = viewportWidth?.let { width.toFloat() / it } ?: 1.0f
135
executenull136 override fun execute(pw: PrintWriter) {
137 // Not needed for a subcommand
138 }
139
toStringnull140 override fun toString(): String {
141 return "RoundedCornerSubCommand(" +
142 "height=$height," +
143 " width=$width," +
144 " pathData='$pathData'," +
145 " viewportHeight=$viewportHeight," +
146 " viewportWidth=$viewportWidth)"
147 }
148
toRoundedCornerDebugModelnull149 fun toRoundedCornerDebugModel(): DebugRoundedCornerModel =
150 DebugRoundedCornerModel(
151 path = pathData,
152 width = width,
153 height = height,
154 scaleX = scaleX,
155 scaleY = scaleY,
156 )
157 }
158
159 fun String.toPathOrNull(): Path? =
160 try {
161 PathParser.createPathFromPathData(this)
162 } catch (e: Exception) {
163 null
164 }
165
toColorIntOrNullnull166 fun String.toColorIntOrNull(): Int? =
167 try {
168 Color.parseColor(this)
169 } catch (e: Exception) {
170 null
171 }
172