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