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
17package android.content;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21import java.lang.Comparable;
22
23/**
24 * Identifier for a specific application component
25 * ({@link android.app.Activity}, {@link android.app.Service},
26 * {@link android.content.BroadcastReceiver}, or
27 * {@link android.content.ContentProvider}) that is available.  Two
28 * pieces of information, encapsulated here, are required to identify
29 * a component: the package (a String) it exists in, and the class (a String)
30 * name inside of that package.
31 *
32 */
33public final class ComponentName implements Parcelable, Cloneable, Comparable<ComponentName> {
34    private final String mPackage;
35    private final String mClass;
36
37    /**
38     * Create a new component identifier.
39     *
40     * @param pkg The name of the package that the component exists in.  Can
41     * not be null.
42     * @param cls The name of the class inside of <var>pkg</var> that
43     * implements the component.  Can not be null.
44     */
45    public ComponentName(String pkg, String cls) {
46        if (pkg == null) throw new NullPointerException("package name is null");
47        if (cls == null) throw new NullPointerException("class name is null");
48        mPackage = pkg;
49        mClass = cls;
50    }
51
52    /**
53     * Create a new component identifier from a Context and class name.
54     *
55     * @param pkg A Context for the package implementing the component,
56     * from which the actual package name will be retrieved.
57     * @param cls The name of the class inside of <var>pkg</var> that
58     * implements the component.
59     */
60    public ComponentName(Context pkg, String cls) {
61        if (cls == null) throw new NullPointerException("class name is null");
62        mPackage = pkg.getPackageName();
63        mClass = cls;
64    }
65
66    /**
67     * Create a new component identifier from a Context and Class object.
68     *
69     * @param pkg A Context for the package implementing the component, from
70     * which the actual package name will be retrieved.
71     * @param cls The Class object of the desired component, from which the
72     * actual class name will be retrieved.
73     */
74    public ComponentName(Context pkg, Class<?> cls) {
75        mPackage = pkg.getPackageName();
76        mClass = cls.getName();
77    }
78
79    public ComponentName clone() {
80        return new ComponentName(mPackage, mClass);
81    }
82
83    /**
84     * Return the package name of this component.
85     */
86    public String getPackageName() {
87        return mPackage;
88    }
89
90    /**
91     * Return the class name of this component.
92     */
93    public String getClassName() {
94        return mClass;
95    }
96
97    /**
98     * Return the class name, either fully qualified or in a shortened form
99     * (with a leading '.') if it is a suffix of the package.
100     */
101    public String getShortClassName() {
102        if (mClass.startsWith(mPackage)) {
103            int PN = mPackage.length();
104            int CN = mClass.length();
105            if (CN > PN && mClass.charAt(PN) == '.') {
106                return mClass.substring(PN, CN);
107            }
108        }
109        return mClass;
110    }
111
112    /**
113     * Return a String that unambiguously describes both the package and
114     * class names contained in the ComponentName.  You can later recover
115     * the ComponentName from this string through
116     * {@link #unflattenFromString(String)}.
117     *
118     * @return Returns a new String holding the package and class names.  This
119     * is represented as the package name, concatenated with a '/' and then the
120     * class name.
121     *
122     * @see #unflattenFromString(String)
123     */
124    public String flattenToString() {
125        return mPackage + "/" + mClass;
126    }
127
128    /**
129     * The same as {@link #flattenToString()}, but abbreviates the class
130     * name if it is a suffix of the package.  The result can still be used
131     * with {@link #unflattenFromString(String)}.
132     *
133     * @return Returns a new String holding the package and class names.  This
134     * is represented as the package name, concatenated with a '/' and then the
135     * class name.
136     *
137     * @see #unflattenFromString(String)
138     */
139    public String flattenToShortString() {
140        return mPackage + "/" + getShortClassName();
141    }
142
143    /**
144     * Recover a ComponentName from a String that was previously created with
145     * {@link #flattenToString()}.  It splits the string at the first '/',
146     * taking the part before as the package name and the part after as the
147     * class name.  As a special convenience (to use, for example, when
148     * parsing component names on the command line), if the '/' is immediately
149     * followed by a '.' then the final class name will be the concatenation
150     * of the package name with the string following the '/'.  Thus
151     * "com.foo/.Blah" becomes package="com.foo" class="com.foo.Blah".
152     *
153     * @param str The String that was returned by flattenToString().
154     * @return Returns a new ComponentName containing the package and class
155     * names that were encoded in <var>str</var>
156     *
157     * @see #flattenToString()
158     */
159    public static ComponentName unflattenFromString(String str) {
160        int sep = str.indexOf('/');
161        if (sep < 0 || (sep+1) >= str.length()) {
162            return null;
163        }
164        String pkg = str.substring(0, sep);
165        String cls = str.substring(sep+1);
166        if (cls.length() > 0 && cls.charAt(0) == '.') {
167            cls = pkg + cls;
168        }
169        return new ComponentName(pkg, cls);
170    }
171
172    /**
173     * Return string representation of this class without the class's name
174     * as a prefix.
175     */
176    public String toShortString() {
177        return "{" + mPackage + "/" + mClass + "}";
178    }
179
180    @Override
181    public String toString() {
182        return "ComponentInfo{" + mPackage + "/" + mClass + "}";
183    }
184
185    @Override
186    public boolean equals(Object obj) {
187        try {
188            if (obj != null) {
189                ComponentName other = (ComponentName)obj;
190                // Note: no null checks, because mPackage and mClass can
191                // never be null.
192                return mPackage.equals(other.mPackage)
193                        && mClass.equals(other.mClass);
194            }
195        } catch (ClassCastException e) {
196        }
197        return false;
198    }
199
200    @Override
201    public int hashCode() {
202        return mPackage.hashCode() + mClass.hashCode();
203    }
204
205    public int compareTo(ComponentName that) {
206        int v;
207        v = this.mPackage.compareTo(that.mPackage);
208        if (v != 0) {
209            return v;
210        }
211        return this.mClass.compareTo(that.mClass);
212    }
213
214    public int describeContents() {
215        return 0;
216    }
217
218    public void writeToParcel(Parcel out, int flags) {
219        out.writeString(mPackage);
220        out.writeString(mClass);
221    }
222
223    /**
224     * Write a ComponentName to a Parcel, handling null pointers.  Must be
225     * read with {@link #readFromParcel(Parcel)}.
226     *
227     * @param c The ComponentName to be written.
228     * @param out The Parcel in which the ComponentName will be placed.
229     *
230     * @see #readFromParcel(Parcel)
231     */
232    public static void writeToParcel(ComponentName c, Parcel out) {
233        if (c != null) {
234            c.writeToParcel(out, 0);
235        } else {
236            out.writeString(null);
237        }
238    }
239
240    /**
241     * Read a ComponentName from a Parcel that was previously written
242     * with {@link #writeToParcel(ComponentName, Parcel)}, returning either
243     * a null or new object as appropriate.
244     *
245     * @param in The Parcel from which to read the ComponentName
246     * @return Returns a new ComponentName matching the previously written
247     * object, or null if a null had been written.
248     *
249     * @see #writeToParcel(ComponentName, Parcel)
250     */
251    public static ComponentName readFromParcel(Parcel in) {
252        String pkg = in.readString();
253        return pkg != null ? new ComponentName(pkg, in) : null;
254    }
255
256    public static final Parcelable.Creator<ComponentName> CREATOR
257            = new Parcelable.Creator<ComponentName>() {
258        public ComponentName createFromParcel(Parcel in) {
259            return new ComponentName(in);
260        }
261
262        public ComponentName[] newArray(int size) {
263            return new ComponentName[size];
264        }
265    };
266
267    /**
268     * Instantiate a new ComponentName from the data in a Parcel that was
269     * previously written with {@link #writeToParcel(Parcel, int)}.  Note that you
270     * must not use this with data written by
271     * {@link #writeToParcel(ComponentName, Parcel)} since it is not possible
272     * to handle a null ComponentObject here.
273     *
274     * @param in The Parcel containing the previously written ComponentName,
275     * positioned at the location in the buffer where it was written.
276     */
277    public ComponentName(Parcel in) {
278        mPackage = in.readString();
279        if (mPackage == null) throw new NullPointerException(
280                "package name is null");
281        mClass = in.readString();
282        if (mClass == null) throw new NullPointerException(
283                "class name is null");
284    }
285
286    private ComponentName(String pkg, Parcel in) {
287        mPackage = pkg;
288        mClass = in.readString();
289    }
290}
291