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.", 48 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 override fun execute(pw: PrintWriter) { 57 callback.onExecute(this, pw) 58 } 59 60 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 { 70 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", 108 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 122 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 134 get() = viewportWidth?.let { width.toFloat() / it } ?: 1.0f 135 136 override fun execute(pw: PrintWriter) { 137 // Not needed for a subcommand 138 } 139 140 override fun toString(): String { 141 return "RoundedCornerSubCommand(" + 142 "height=$height," + 143 " width=$width," + 144 " pathData='$pathData'," + 145 " viewportHeight=$viewportHeight," + 146 " viewportWidth=$viewportWidth)" 147 } 148 149 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 166 fun String.toColorIntOrNull(): Int? = 167 try { 168 Color.parseColor(this) 169 } catch (e: Exception) { 170 null 171 } 172