1 /* 2 * Copyright (C) 2006 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.content; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.os.Parcel; 22 import android.os.Parcelable; 23 import android.text.TextUtils; 24 25 import java.io.PrintWriter; 26 import java.lang.Comparable; 27 28 /** 29 * Identifier for a specific application component 30 * ({@link android.app.Activity}, {@link android.app.Service}, 31 * {@link android.content.BroadcastReceiver}, or 32 * {@link android.content.ContentProvider}) that is available. Two 33 * pieces of information, encapsulated here, are required to identify 34 * a component: the package (a String) it exists in, and the class (a String) 35 * name inside of that package. 36 * 37 */ 38 public final class ComponentName implements Parcelable, Cloneable, Comparable<ComponentName> { 39 private final String mPackage; 40 private final String mClass; 41 42 /** 43 * Create a new component identifier where the class name may be specified 44 * as either absolute or relative to the containing package. 45 * 46 * <p>Relative package names begin with a <code>'.'</code> character. For a package 47 * <code>"com.example"</code> and class name <code>".app.MyActivity"</code> this method 48 * will return a ComponentName with the package <code>"com.example"</code>and class name 49 * <code>"com.example.app.MyActivity"</code>. Fully qualified class names are also 50 * permitted.</p> 51 * 52 * @param pkg the name of the package the component exists in 53 * @param cls the name of the class inside of <var>pkg</var> that implements 54 * the component 55 * @return the new ComponentName 56 */ 57 public static @NonNull ComponentName createRelative(@NonNull String pkg, @NonNull String cls) { 58 if (TextUtils.isEmpty(cls)) { 59 throw new IllegalArgumentException("class name cannot be empty"); 60 } 61 62 final String fullName; 63 if (cls.charAt(0) == '.') { 64 // Relative to the package. Prepend the package name. 65 fullName = pkg + cls; 66 } else { 67 // Fully qualified package name. 68 fullName = cls; 69 } 70 return new ComponentName(pkg, fullName); 71 } 72 73 /** 74 * Create a new component identifier where the class name may be specified 75 * as either absolute or relative to the containing package. 76 * 77 * <p>Relative package names begin with a <code>'.'</code> character. For a package 78 * <code>"com.example"</code> and class name <code>".app.MyActivity"</code> this method 79 * will return a ComponentName with the package <code>"com.example"</code>and class name 80 * <code>"com.example.app.MyActivity"</code>. Fully qualified class names are also 81 * permitted.</p> 82 * 83 * @param pkg a Context for the package implementing the component 84 * @param cls the name of the class inside of <var>pkg</var> that implements 85 * the component 86 * @return the new ComponentName 87 */ 88 public static @NonNull ComponentName createRelative(@NonNull Context pkg, @NonNull String cls) { 89 return createRelative(pkg.getPackageName(), cls); 90 } 91 92 /** 93 * Create a new component identifier. 94 * 95 * @param pkg The name of the package that the component exists in. Can 96 * not be null. 97 * @param cls The name of the class inside of <var>pkg</var> that 98 * implements the component. Can not be null. 99 */ 100 public ComponentName(@NonNull String pkg, @NonNull String cls) { 101 if (pkg == null) throw new NullPointerException("package name is null"); 102 if (cls == null) throw new NullPointerException("class name is null"); 103 mPackage = pkg; 104 mClass = cls; 105 } 106 107 /** 108 * Create a new component identifier from a Context and class name. 109 * 110 * @param pkg A Context for the package implementing the component, 111 * from which the actual package name will be retrieved. 112 * @param cls The name of the class inside of <var>pkg</var> that 113 * implements the component. 114 */ 115 public ComponentName(@NonNull Context pkg, @NonNull String cls) { 116 if (cls == null) throw new NullPointerException("class name is null"); 117 mPackage = pkg.getPackageName(); 118 mClass = cls; 119 } 120 121 /** 122 * Create a new component identifier from a Context and Class object. 123 * 124 * @param pkg A Context for the package implementing the component, from 125 * which the actual package name will be retrieved. 126 * @param cls The Class object of the desired component, from which the 127 * actual class name will be retrieved. 128 */ 129 public ComponentName(@NonNull Context pkg, @NonNull Class<?> cls) { 130 mPackage = pkg.getPackageName(); 131 mClass = cls.getName(); 132 } 133 134 public ComponentName clone() { 135 return new ComponentName(mPackage, mClass); 136 } 137 138 /** 139 * Return the package name of this component. 140 */ 141 public @NonNull String getPackageName() { 142 return mPackage; 143 } 144 145 /** 146 * Return the class name of this component. 147 */ 148 public @NonNull String getClassName() { 149 return mClass; 150 } 151 152 /** 153 * Return the class name, either fully qualified or in a shortened form 154 * (with a leading '.') if it is a suffix of the package. 155 */ 156 public String getShortClassName() { 157 if (mClass.startsWith(mPackage)) { 158 int PN = mPackage.length(); 159 int CN = mClass.length(); 160 if (CN > PN && mClass.charAt(PN) == '.') { 161 return mClass.substring(PN, CN); 162 } 163 } 164 return mClass; 165 } 166 167 private static void appendShortClassName(StringBuilder sb, String packageName, 168 String className) { 169 if (className.startsWith(packageName)) { 170 int PN = packageName.length(); 171 int CN = className.length(); 172 if (CN > PN && className.charAt(PN) == '.') { 173 sb.append(className, PN, CN); 174 return; 175 } 176 } 177 sb.append(className); 178 } 179 180 private static void printShortClassName(PrintWriter pw, String packageName, 181 String className) { 182 if (className.startsWith(packageName)) { 183 int PN = packageName.length(); 184 int CN = className.length(); 185 if (CN > PN && className.charAt(PN) == '.') { 186 pw.write(className, PN, CN-PN); 187 return; 188 } 189 } 190 pw.print(className); 191 } 192 193 /** 194 * Return a String that unambiguously describes both the package and 195 * class names contained in the ComponentName. You can later recover 196 * the ComponentName from this string through 197 * {@link #unflattenFromString(String)}. 198 * 199 * @return Returns a new String holding the package and class names. This 200 * is represented as the package name, concatenated with a '/' and then the 201 * class name. 202 * 203 * @see #unflattenFromString(String) 204 */ 205 public @NonNull String flattenToString() { 206 return mPackage + "/" + mClass; 207 } 208 209 /** 210 * The same as {@link #flattenToString()}, but abbreviates the class 211 * name if it is a suffix of the package. The result can still be used 212 * with {@link #unflattenFromString(String)}. 213 * 214 * @return Returns a new String holding the package and class names. This 215 * is represented as the package name, concatenated with a '/' and then the 216 * class name. 217 * 218 * @see #unflattenFromString(String) 219 */ 220 public @NonNull String flattenToShortString() { 221 StringBuilder sb = new StringBuilder(mPackage.length() + mClass.length()); 222 appendShortString(sb, mPackage, mClass); 223 return sb.toString(); 224 } 225 226 /** @hide */ 227 public void appendShortString(StringBuilder sb) { 228 appendShortString(sb, mPackage, mClass); 229 } 230 231 /** @hide */ 232 public static void appendShortString(StringBuilder sb, String packageName, String className) { 233 sb.append(packageName).append('/'); 234 appendShortClassName(sb, packageName, className); 235 } 236 237 /** @hide */ 238 public static void printShortString(PrintWriter pw, String packageName, String className) { 239 pw.print(packageName); 240 pw.print('/'); 241 printShortClassName(pw, packageName, className); 242 } 243 244 /** 245 * Recover a ComponentName from a String that was previously created with 246 * {@link #flattenToString()}. It splits the string at the first '/', 247 * taking the part before as the package name and the part after as the 248 * class name. As a special convenience (to use, for example, when 249 * parsing component names on the command line), if the '/' is immediately 250 * followed by a '.' then the final class name will be the concatenation 251 * of the package name with the string following the '/'. Thus 252 * "com.foo/.Blah" becomes package="com.foo" class="com.foo.Blah". 253 * 254 * @param str The String that was returned by flattenToString(). 255 * @return Returns a new ComponentName containing the package and class 256 * names that were encoded in <var>str</var> 257 * 258 * @see #flattenToString() 259 */ 260 public static @Nullable ComponentName unflattenFromString(@NonNull String str) { 261 int sep = str.indexOf('/'); 262 if (sep < 0 || (sep+1) >= str.length()) { 263 return null; 264 } 265 String pkg = str.substring(0, sep); 266 String cls = str.substring(sep+1); 267 if (cls.length() > 0 && cls.charAt(0) == '.') { 268 cls = pkg + cls; 269 } 270 return new ComponentName(pkg, cls); 271 } 272 273 /** 274 * Return string representation of this class without the class's name 275 * as a prefix. 276 */ 277 public String toShortString() { 278 return "{" + mPackage + "/" + mClass + "}"; 279 } 280 281 @Override 282 public String toString() { 283 return "ComponentInfo{" + mPackage + "/" + mClass + "}"; 284 } 285 286 @Override 287 public boolean equals(Object obj) { 288 try { 289 if (obj != null) { 290 ComponentName other = (ComponentName)obj; 291 // Note: no null checks, because mPackage and mClass can 292 // never be null. 293 return mPackage.equals(other.mPackage) 294 && mClass.equals(other.mClass); 295 } 296 } catch (ClassCastException e) { 297 } 298 return false; 299 } 300 301 @Override 302 public int hashCode() { 303 return mPackage.hashCode() + mClass.hashCode(); 304 } 305 306 public int compareTo(ComponentName that) { 307 int v; 308 v = this.mPackage.compareTo(that.mPackage); 309 if (v != 0) { 310 return v; 311 } 312 return this.mClass.compareTo(that.mClass); 313 } 314 315 public int describeContents() { 316 return 0; 317 } 318 319 public void writeToParcel(Parcel out, int flags) { 320 out.writeString(mPackage); 321 out.writeString(mClass); 322 } 323 324 /** 325 * Write a ComponentName to a Parcel, handling null pointers. Must be 326 * read with {@link #readFromParcel(Parcel)}. 327 * 328 * @param c The ComponentName to be written. 329 * @param out The Parcel in which the ComponentName will be placed. 330 * 331 * @see #readFromParcel(Parcel) 332 */ 333 public static void writeToParcel(ComponentName c, Parcel out) { 334 if (c != null) { 335 c.writeToParcel(out, 0); 336 } else { 337 out.writeString(null); 338 } 339 } 340 341 /** 342 * Read a ComponentName from a Parcel that was previously written 343 * with {@link #writeToParcel(ComponentName, Parcel)}, returning either 344 * a null or new object as appropriate. 345 * 346 * @param in The Parcel from which to read the ComponentName 347 * @return Returns a new ComponentName matching the previously written 348 * object, or null if a null had been written. 349 * 350 * @see #writeToParcel(ComponentName, Parcel) 351 */ 352 public static ComponentName readFromParcel(Parcel in) { 353 String pkg = in.readString(); 354 return pkg != null ? new ComponentName(pkg, in) : null; 355 } 356 357 public static final Parcelable.Creator<ComponentName> CREATOR 358 = new Parcelable.Creator<ComponentName>() { 359 public ComponentName createFromParcel(Parcel in) { 360 return new ComponentName(in); 361 } 362 363 public ComponentName[] newArray(int size) { 364 return new ComponentName[size]; 365 } 366 }; 367 368 /** 369 * Instantiate a new ComponentName from the data in a Parcel that was 370 * previously written with {@link #writeToParcel(Parcel, int)}. Note that you 371 * must not use this with data written by 372 * {@link #writeToParcel(ComponentName, Parcel)} since it is not possible 373 * to handle a null ComponentObject here. 374 * 375 * @param in The Parcel containing the previously written ComponentName, 376 * positioned at the location in the buffer where it was written. 377 */ 378 public ComponentName(Parcel in) { 379 mPackage = in.readString(); 380 if (mPackage == null) throw new NullPointerException( 381 "package name is null"); 382 mClass = in.readString(); 383 if (mClass == null) throw new NullPointerException( 384 "class name is null"); 385 } 386 387 private ComponentName(String pkg, Parcel in) { 388 mPackage = pkg; 389 mClass = in.readString(); 390 } 391 } 392