Home | History | Annotate | Download | only in content
      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 import android.util.proto.ProtoOutputStream;
     25 
     26 import java.io.PrintWriter;
     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     /** Put this here so that individual services don't have to reimplement this. @hide */
    287     public void writeToProto(ProtoOutputStream proto, long fieldId) {
    288         final long token = proto.start(fieldId);
    289         proto.write(ComponentNameProto.PACKAGE_NAME, mPackage);
    290         proto.write(ComponentNameProto.CLASS_NAME, mClass);
    291         proto.end(token);
    292     }
    293 
    294     @Override
    295     public boolean equals(Object obj) {
    296         try {
    297             if (obj != null) {
    298                 ComponentName other = (ComponentName)obj;
    299                 // Note: no null checks, because mPackage and mClass can
    300                 // never be null.
    301                 return mPackage.equals(other.mPackage)
    302                         && mClass.equals(other.mClass);
    303             }
    304         } catch (ClassCastException e) {
    305         }
    306         return false;
    307     }
    308 
    309     @Override
    310     public int hashCode() {
    311         return mPackage.hashCode() + mClass.hashCode();
    312     }
    313 
    314     public int compareTo(ComponentName that) {
    315         int v;
    316         v = this.mPackage.compareTo(that.mPackage);
    317         if (v != 0) {
    318             return v;
    319         }
    320         return this.mClass.compareTo(that.mClass);
    321     }
    322 
    323     public int describeContents() {
    324         return 0;
    325     }
    326 
    327     public void writeToParcel(Parcel out, int flags) {
    328         out.writeString(mPackage);
    329         out.writeString(mClass);
    330     }
    331 
    332     /**
    333      * Write a ComponentName to a Parcel, handling null pointers.  Must be
    334      * read with {@link #readFromParcel(Parcel)}.
    335      *
    336      * @param c The ComponentName to be written.
    337      * @param out The Parcel in which the ComponentName will be placed.
    338      *
    339      * @see #readFromParcel(Parcel)
    340      */
    341     public static void writeToParcel(ComponentName c, Parcel out) {
    342         if (c != null) {
    343             c.writeToParcel(out, 0);
    344         } else {
    345             out.writeString(null);
    346         }
    347     }
    348 
    349     /**
    350      * Read a ComponentName from a Parcel that was previously written
    351      * with {@link #writeToParcel(ComponentName, Parcel)}, returning either
    352      * a null or new object as appropriate.
    353      *
    354      * @param in The Parcel from which to read the ComponentName
    355      * @return Returns a new ComponentName matching the previously written
    356      * object, or null if a null had been written.
    357      *
    358      * @see #writeToParcel(ComponentName, Parcel)
    359      */
    360     public static ComponentName readFromParcel(Parcel in) {
    361         String pkg = in.readString();
    362         return pkg != null ? new ComponentName(pkg, in) : null;
    363     }
    364 
    365     public static final Parcelable.Creator<ComponentName> CREATOR
    366             = new Parcelable.Creator<ComponentName>() {
    367         public ComponentName createFromParcel(Parcel in) {
    368             return new ComponentName(in);
    369         }
    370 
    371         public ComponentName[] newArray(int size) {
    372             return new ComponentName[size];
    373         }
    374     };
    375 
    376     /**
    377      * Instantiate a new ComponentName from the data in a Parcel that was
    378      * previously written with {@link #writeToParcel(Parcel, int)}.  Note that you
    379      * must not use this with data written by
    380      * {@link #writeToParcel(ComponentName, Parcel)} since it is not possible
    381      * to handle a null ComponentObject here.
    382      *
    383      * @param in The Parcel containing the previously written ComponentName,
    384      * positioned at the location in the buffer where it was written.
    385      */
    386     public ComponentName(Parcel in) {
    387         mPackage = in.readString();
    388         if (mPackage == null) throw new NullPointerException(
    389                 "package name is null");
    390         mClass = in.readString();
    391         if (mClass == null) throw new NullPointerException(
    392                 "class name is null");
    393     }
    394 
    395     private ComponentName(String pkg, Parcel in) {
    396         mPackage = pkg;
    397         mClass = in.readString();
    398     }
    399 
    400     /**
    401      * Interface for classes associated with a component name.
    402      * @hide
    403      */
    404     @FunctionalInterface
    405     public interface WithComponentName {
    406         /** Return the associated component name. */
    407         ComponentName getComponentName();
    408     }
    409 }
    410