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 android.tools.traces.component 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 flicker 23 * 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 not be null. 26 * @param className The name of the class inside <var>pkg</var> that implements the component. 27 */ 28 data class ComponentName(override val packageName: String, override val className: String) : 29 IComponentName { 30 /** 31 * Obtains the activity name from the component name. 32 * 33 * See [ComponentName.toWindowName] for additional information 34 */ toActivityNamenull35 override fun toActivityName(): String { 36 return when { 37 packageName.isNotEmpty() && className.isNotEmpty() -> { 38 val sb = StringBuilder(packageName.length + className.length) 39 appendShortString(sb, packageName, className) 40 return sb.toString() 41 } 42 packageName.isNotEmpty() -> packageName 43 className.isNotEmpty() -> className 44 else -> error("Component name should have an activity of class name") 45 } 46 } 47 48 /** 49 * Obtains the window name from the component name. 50 * 51 * [ComponentName] builds the string representation as PKG/CLASS, however this doesn't work for 52 * system components such as IME, NavBar and StatusBar, Toast. 53 * 54 * If the component doesn't have a package name, assume it's a system component and return only 55 * the class name 56 */ toWindowNamenull57 override fun toWindowName(): String { 58 return when { 59 packageName.isNotEmpty() && className.isNotEmpty() -> "$packageName/$className" 60 packageName.isNotEmpty() -> packageName 61 className.isNotEmpty() -> className 62 else -> error("Component name should have an activity of class name") 63 } 64 } 65 toShortWindowNamenull66 internal fun toShortWindowName(): String { 67 return when { 68 packageName.isNotEmpty() && className.isNotEmpty() -> 69 "$packageName/${className.removePrefix(packageName)}" 70 packageName.isNotEmpty() -> packageName 71 className.isNotEmpty() -> className 72 else -> error("Component name should have an activity of class name") 73 } 74 } 75 76 /** 77 * Obtains the layer name from the component name. 78 * 79 * See [toWindowName] for additional information 80 */ toLayerNamenull81 override fun toLayerName(): String { 82 var result = this.toWindowName() 83 if (result.contains("/") && !result.contains("#")) { 84 result = "$result#" 85 } 86 87 return result 88 } 89 appendShortStringnull90 private fun appendShortString(sb: StringBuilder, packageName: String, className: String) { 91 sb.append(packageName).append('/') 92 appendShortClassName(sb, packageName, className) 93 } 94 appendShortClassNamenull95 private fun appendShortClassName(sb: StringBuilder, packageName: String, className: String) { 96 if (className.startsWith(packageName)) { 97 val packageNameLength = packageName.length 98 val classNameLength = className.length 99 if (classNameLength > packageNameLength && className[packageNameLength] == '.') { 100 sb.append(className, packageNameLength, classNameLength) 101 return 102 } 103 } 104 sb.append(className) 105 } 106 toStringnull107 override fun toString(): String = toShortWindowName() 108 109 companion object { 110 fun fromLayerName(layerName: String): IComponentName { 111 var name = layerName 112 var packageName = "" 113 var className = "" 114 if (name.contains("/")) { 115 if (name.contains("#")) { 116 name = name.removeSuffix("#") 117 } 118 val splitString = name.split('/') 119 packageName = splitString[0] 120 className = splitString[1] 121 } else { 122 className = name 123 } 124 return ComponentName(packageName, className) 125 } 126 } 127 } 128