1 /*
2  * Copyright (C) 2021 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.server.wm.traces.common
18 
19 /**
20  * Create a new component identifier.
21  *
22  * This is a version of Android's ComponentName class for flicker. This is necessary because
23  * flicker codebase it also compiled into KotlinJS for use into Winscope
24  *
25  * @param packageName The name of the package that the component exists in.  Can
26  * not be null.
27  * @param className The name of the class inside of <var>pkg</var> that
28  * implements the component.  Can not be null.
29  */
30 data class FlickerComponentName(
31     val packageName: String,
32     val className: String
33 ) {
34     /**
35      * Obtains the activity name from the component name.
36      *
37      * See [ComponentName.toWindowName] for additional information
38      */
toActivityNamenull39     fun toActivityName(): String {
40         return when {
41             packageName.isNotEmpty() && className.isNotEmpty() -> {
42                 val sb = StringBuilder(packageName.length + className.length)
43                 appendShortString(sb, packageName, className)
44                 return sb.toString()
45             }
46             packageName.isNotEmpty() -> packageName
47             className.isNotEmpty() -> className
48             else -> error("Component name should have an activity of class name")
49         }
50     }
51 
52     /**
53      * Obtains the window name from the component name.
54      *
55      * [ComponentName] builds the string representation as PKG/CLASS, however this doesn't
56      * work for system components such as IME, NavBar and StatusBar, Toast.
57      *
58      * If the component doesn't have a package name, assume it's a system component and return only
59      * the class name
60      */
toWindowNamenull61     fun toWindowName(): String {
62         return when {
63             packageName.isNotEmpty() && className.isNotEmpty() -> "$packageName/$className"
64             packageName.isNotEmpty() -> packageName
65             className.isNotEmpty() -> className
66             else -> error("Component name should have an activity of class name")
67         }
68     }
69 
70     /**
71      * Obtains the layer name from the component name.
72      *
73      * See [toWindowName] for additional information
74      */
toLayerNamenull75     fun toLayerName(): String {
76         var result = this.toWindowName()
77         if (result.contains("/") && !result.contains("#")) {
78             result = "$result#"
79         }
80 
81         return result
82     }
83 
appendShortStringnull84     private fun appendShortString(sb: StringBuilder, packageName: String, className: String) {
85         sb.append(packageName).append('/')
86         appendShortClassName(sb, packageName, className)
87     }
88 
appendShortClassNamenull89     private fun appendShortClassName(sb: StringBuilder, packageName: String, className: String) {
90         if (className.startsWith(packageName)) {
91             val packageNameLength = packageName.length
92             val classNameLength = className.length
93             if (classNameLength > packageNameLength && className[packageNameLength] == '.') {
94                 sb.append(className, packageNameLength, classNameLength)
95                 return
96             }
97         }
98         sb.append(className)
99     }
100 
101     companion object {
102         val NAV_BAR = FlickerComponentName("", "NavigationBar0")
103         val STATUS_BAR = FlickerComponentName("", "StatusBar")
104         val ROTATION = FlickerComponentName("", "RotationLayer")
105         val BACK_SURFACE = FlickerComponentName("", "BackColorSurface")
106         val IME = FlickerComponentName("", "InputMethod")
107         val SPLASH_SCREEN = FlickerComponentName("", "Splash Screen")
108         val SNAPSHOT = FlickerComponentName("", "SnapshotStartingWindow")
109         val WALLPAPER_BBQ_WRAPPER =
110                 FlickerComponentName("", "Wallpaper BBQ wrapper")
111 
unflattenFromStringnull112         fun unflattenFromString(str: String): FlickerComponentName {
113             val sep = str.indexOf('/')
114             if (sep < 0 || sep + 1 >= str.length) {
115                 error("Missing package/class separator")
116             }
117             val pkg = str.substring(0, sep)
118             var cls = str.substring(sep + 1)
119             if (cls.isNotEmpty() && cls[0] == '.') {
120                 cls = pkg + cls
121             }
122             return FlickerComponentName(pkg, cls)
123         }
124     }
125 }