• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright 2024 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.wm.shell.compatui.letterbox
18 
19 import android.content.Context
20 import android.graphics.Color
21 import com.android.internal.protolog.ProtoLog
22 import com.android.window.flags.Flags
23 import com.android.wm.shell.dagger.WMSingleton
24 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_APP_COMPAT
25 import com.android.wm.shell.sysui.ShellCommandHandler
26 import com.android.wm.shell.sysui.ShellCommandHandler.ShellCommandActionHandler
27 import com.android.wm.shell.sysui.ShellInit
28 import java.io.PrintWriter
29 import javax.inject.Inject
30 
31 /**
32  * Handles the shell commands for the CompatUI.
33  *
34  * <p> Use with [adb shell wm shell letterbox &lt;command&gt;].
35  */
36 @WMSingleton
37 class LetterboxCommandHandler @Inject constructor(
38     private val context: Context,
39     shellInit: ShellInit,
40     shellCommandHandler: ShellCommandHandler,
41     private val letterboxConfiguration: LetterboxConfiguration
42 ) : ShellCommandActionHandler {
43 
44     companion object {
45         @JvmStatic
46         private val TAG = "LetterboxCommandHandler"
47     }
48 
49     init {
50         if (Flags.appCompatRefactoring()) {
51             ProtoLog.v(
52                 WM_SHELL_APP_COMPAT,
53                 "%s: %s",
54                 TAG,
55                 "Initializing LetterboxCommandHandler"
56             )
57             shellInit.addInitCallback({
58                 shellCommandHandler.addCommandCallback("letterbox", this, this)
59             }, this)
60         }
61     }
62 
63     override fun onShellCommand(args: Array<out String>?, pw: PrintWriter?): Boolean {
64         if (args == null || pw == null) {
65             pw!!.println("Missing arguments.")
66             return false
67         }
68         return when (args.size) {
69             1 -> onNoParamsCommand(args[0], pw)
70             2 -> onSingleParamCommand(args[0], args[1], pw)
71             else -> {
72                 pw.println("Invalid command: " + args[0])
73                 return false
74             }
75         }
76     }
77 
78     override fun printShellCommandHelp(pw: PrintWriter?, prefix: String?) {
79         pw?.println(
80             """
81                     $prefix backgroundColor color"
82                     $prefix      Color of letterbox which is to be used when letterbox background
83                     $prefix      type is 'solid-color'. See Color#parseColor for allowed color
84                     $prefix      formats (#RRGGBB and some colors by name, e.g. magenta or olive).
85                     $prefix backgroundColorResource resource_name"
86                     $prefix      Color resource name of letterbox background which is used when
87                     $prefix      background type is 'solid-color'. Parameter is a color resource
88                     $prefix      name, for example, @android:color/system_accent2_50.
89                     $prefix backgroundColorReset"
90                     $prefix      Resets the background color to the default value."
91                     $prefix cornerRadius"
92                     $prefix      Corners radius (in pixels) for activities in the letterbox mode."
93                     $prefix      If cornerRadius < 0, it will be ignored and corners of the"
94                     $prefix      activity won't be rounded."
95                     $prefix cornerRadiusReset"
96                     $prefix      Resets the rounded corners radius to the default value."
97                 """.trimIndent()
98         )
99     }
100 
101     private fun onSingleParamCommand(command: String, value: String, pw: PrintWriter): Boolean {
102         when (command) {
103             "backgroundColor" -> {
104                 return invokeWhenValid(
105                     pw,
106                     value,
107                     ::strToColor,
108                     { color ->
109                         letterboxConfiguration.setLetterboxBackgroundColor(color)
110                     },
111                     { c -> "$c is not a valid color." }
112                 )
113             }
114 
115             "backgroundColorResource" -> return invokeWhenValid(
116                 pw,
117                 value,
118                 ::nameToColorId,
119                 { color ->
120                     letterboxConfiguration.setLetterboxBackgroundColorResourceId(color)
121                 },
122                 { c ->
123                     "$c is not a valid resource. Color in '@android:color/resource_name'" +
124                             " format should be provided as an argument."
125                 }
126             )
127 
128             "cornerRadius" -> return invokeWhenValid(
129                 pw,
130                 value,
131                 ::strToInt{ it >= 0 },
132                 { radius ->
133                     letterboxConfiguration.setLetterboxActivityCornersRadius(radius)
134                 },
135                 { r ->
136                     "$r is not a valid radius. It must be an integer >= 0."
137                 }
138             )
139 
140             else -> {
141                 pw.println("Invalid command: $value")
142                 return false
143             }
144         }
145     }
146 
147     private fun onNoParamsCommand(command: String, pw: PrintWriter): Boolean {
148         when (command) {
149             "backgroundColor" -> {
150                 pw.println(
151                     "    Background color: " + Integer.toHexString(
152                         letterboxConfiguration.getLetterboxBackgroundColor()
153                             .toArgb()
154                     )
155                 )
156                 return true
157             }
158 
159             "backgroundColorReset" -> {
160                 letterboxConfiguration.resetLetterboxBackgroundColor()
161                 return true
162             }
163 
164             "cornerRadius" -> {
165                 pw.println(
166                     "    Rounded corners radius: " +
167                         "${letterboxConfiguration.getLetterboxActivityCornersRadius()} px."
168                 )
169                 return true
170             }
171 
172             "cornerRadiusReset" -> {
173                 letterboxConfiguration.resetLetterboxActivityCornersRadius()
174                 return true
175             }
176 
177             else -> {
178                 pw.println("Invalid command: $command")
179                 return false
180             }
181         }
182     }
183 
184     private fun <T> invokeWhenValid(
185         pw: PrintWriter,
186         input: String,
187         converter: (String) -> T?,
188         consumer: (T) -> Unit,
189         errorMessage: (String) -> String = { value -> " Wrong input value: $value." }
190     ): Boolean {
191         converter(input)?.let {
192             consumer(it)
193             return true
194         }
195         pw.println(errorMessage(input))
196         return false
197     }
198 
199     // Converts a String to Color if possible or it returns null otherwise.
200     private fun strToColor(str: String): Color? =
201         try {
202             Color.valueOf(Color.parseColor(str))
203         } catch (e: IllegalArgumentException) {
204             null
205         }
206 
207     // Converts a resource id to Color if possible or it returns null otherwise.
208     private fun nameToColorId(str: String): Int? =
209         try {
210             context.resources.getIdentifier(str, "color", "com.android.internal")
211         } catch (e: IllegalArgumentException) {
212             null
213         }
214 
215     // Converts a String to Int which if possible or it returns null otherwise.
216     // If a predicate is set, it also returns [null] if the predicate evaluate to [false].
217     private fun strToInt(predicate: (Int) -> Boolean = { _ -> true }): (String) -> Int? = { str ->
218         try {
219             val value = str.toInt()
220             if (predicate(value)) value else null
221         } catch (e: IllegalArgumentException) {
222             null
223         }
224     }
225 }
226