1package com.xtremelabs.robolectric.shadows;
2
3import android.content.ComponentName;
4import android.content.Context;
5import android.content.Intent;
6import android.net.Uri;
7import android.os.Bundle;
8import android.os.Parcel;
9import android.os.Parcelable;
10import android.os.Parcelable.Creator;
11
12import com.xtremelabs.robolectric.Robolectric;
13import com.xtremelabs.robolectric.internal.Implementation;
14import com.xtremelabs.robolectric.internal.Implements;
15import com.xtremelabs.robolectric.internal.RealObject;
16import com.xtremelabs.robolectric.util.Join;
17
18import java.io.*;
19import java.util.*;
20
21import static android.content.Intent.*;
22import static com.xtremelabs.robolectric.Robolectric.shadowOf;
23
24@SuppressWarnings({"UnusedDeclaration"})
25@Implements(Intent.class)
26public class ShadowIntent {
27    @RealObject private Intent realIntent;
28
29    private final Bundle extras = new Bundle();
30    private String action;
31    private ComponentName componentName;
32    private String type;
33    private Uri data;
34    private int flags;
35    private Class<?> intentClass;
36    private String packageName;
37    private final Set<String> categories = new HashSet<String>();
38
39    public void __constructor__(Context packageContext, Class cls) {
40        componentName = new ComponentName(packageContext, cls);
41        intentClass = cls;
42    }
43
44    public void __constructor__(String action, Uri uri) {
45        this.action = action;
46        data = uri;
47    }
48
49    public void __constructor__(String action) {
50        __constructor__(action, null);
51    }
52
53    public void __constructor__(Intent intent) {
54        ShadowIntent other = shadowOf(intent);
55        extras.putAll(other.extras);
56        action = other.action;
57        componentName = other.componentName;
58        type = other.type;
59        data = other.data;
60        flags = other.flags;
61        intentClass = other.intentClass;
62        packageName = other.packageName;
63        categories.addAll(other.categories);
64    }
65
66    @Implementation
67    public static Intent createChooser(Intent target, CharSequence title) {
68        Intent intent = new Intent(Intent.ACTION_CHOOSER);
69        intent.putExtra(Intent.EXTRA_INTENT, target);
70        if (title != null) {
71            intent.putExtra(Intent.EXTRA_TITLE, title);
72        }
73        return intent;
74    }
75
76    @Implementation
77    public Intent setAction(String action) {
78        this.action = action;
79        return realIntent;
80    }
81
82    @Implementation
83    public String getAction() {
84        return action;
85    }
86
87    @Implementation
88    public Intent setType(String type) {
89        this.type = type;
90        this.data = null;
91        return realIntent;
92    }
93
94    @Implementation
95    public Intent setDataAndType(Uri data, String type) {
96        this.data = data;
97        this.type = type;
98        return realIntent;
99    }
100
101    @Implementation
102    public String getType() {
103        return type;
104    }
105
106    @Implementation
107    public Intent addCategory(String category) {
108        categories.add(category);
109        return realIntent;
110    }
111
112    @Implementation
113    public void removeCategory(String category) {
114        categories.remove(category);
115    }
116
117    @Implementation
118    public boolean hasCategory(String category) {
119        return categories.contains(category);
120    }
121
122    @Implementation
123    public Set<String> getCategories() {
124        return categories;
125    }
126
127    @Implementation
128    public Intent setPackage(String packageName) {
129        this.packageName = packageName;
130        return realIntent;
131    }
132
133    @Implementation
134    public String getPackage() {
135        return packageName;
136    }
137
138    @Implementation
139    public Uri getData() {
140        return data;
141    }
142
143    @Implementation
144    public Intent setClass(Context packageContext, Class<?> cls) {
145        this.intentClass = cls;
146        return realIntent;
147    }
148
149    @Implementation
150    public Intent setClassName(String packageName, String className) {
151        componentName = new ComponentName(packageName, className);
152        try {
153            this.intentClass = Class.forName(className);
154        } catch (ClassNotFoundException e) {
155            // ignore
156        }
157        return realIntent;
158    }
159
160    @Implementation
161    public Intent setClassName(Context packageContext, String className) {
162        componentName = new ComponentName(packageContext.getPackageName(), className);
163        return realIntent;
164    }
165
166    @Implementation
167    public Intent setData(Uri data) {
168        this.data = data;
169        this.type = null;
170        return realIntent;
171    }
172
173    @Implementation
174    public int getFlags() {
175        return flags;
176    }
177
178    @Implementation
179    public Intent setFlags(int flags) {
180        this.flags = flags;
181        return realIntent;
182    }
183
184    @Implementation
185    public Intent addFlags(int flags) {
186        this.flags |= flags;
187        return realIntent;
188    }
189
190    @Implementation
191    public Intent putExtras(Bundle src) {
192        extras.putAll(src);
193        return realIntent;
194    }
195
196    @Implementation
197    public Intent putExtras(Intent src) {
198        ShadowIntent srcShadowIntent = shadowOf(src);
199        extras.putAll(srcShadowIntent.extras);
200        return realIntent;
201    }
202
203    @Implementation
204    public Bundle getExtras() {
205        return extras != null ? new Bundle(extras) : null;
206    }
207
208    @Implementation
209    public Intent putExtra(String key, int value) {
210        extras.putInt(key, value);
211        return realIntent;
212    }
213
214    @Implementation
215    public Intent putExtra(String key, double value) {
216        extras.putDouble(key, value);
217        return realIntent;
218    }
219
220    @Implementation
221    public Intent putExtra(String key, float value) {
222        extras.putFloat(key, value);
223        return realIntent;
224    }
225
226    @Implementation
227    public Intent putExtra(String key, long value) {
228        extras.putLong(key, value);
229        return realIntent;
230    }
231
232    @Implementation
233    public Intent putExtra(String key, Serializable value) {
234        extras.putSerializable(key, serializeCycle(value));
235        return realIntent;
236    }
237
238    @Implementation
239    public Intent putExtra(String key, Parcelable value) {
240        extras.putParcelable(key, value);
241        return realIntent;
242    }
243
244    @Implementation
245    public Intent putExtra(String key, Parcelable[] value) {
246        extras.putParcelableArray(key, value);
247        return realIntent;
248    }
249
250    @Implementation
251    public Intent putExtra(String key, String value) {
252        extras.putString(key, value);
253        return realIntent;
254    }
255
256    @Implementation
257    public Intent putExtra(String key, String[] value) {
258        extras.putStringArray(key, value);
259        return realIntent;
260    }
261
262    @Implementation
263    public Intent putExtra(String key, Bundle value) {
264        extras.putBundle(key, value);
265        return realIntent;
266    }
267
268    @Implementation
269    public Intent putExtra(String key, boolean value) {
270        extras.putBoolean(key, value);
271        return realIntent;
272    }
273
274    @Implementation
275    public Intent putExtra(String key, int[] value) {
276        extras.putIntArray(key, value);
277        return realIntent;
278    }
279
280    @Implementation
281    public Intent putExtra(String key, long[] value) {
282        extras.putLongArray(key, value);
283        return realIntent;
284    }
285
286    @Implementation
287    public int[] getIntArrayExtra(String name) {
288        return extras.getIntArray(name);
289    }
290
291    @Implementation
292    public long[] getLongArrayExtra(String name) {
293        return extras.getLongArray(name);
294    }
295
296    @Implementation
297    public boolean getBooleanExtra(String name, boolean defaultValue) {
298        return extras.getBoolean(name, defaultValue);
299    }
300
301    @Implementation
302    public String[] getStringArrayExtra(String name) {
303        return extras.getStringArray(name);
304    }
305
306    @Implementation
307    public Intent putExtra(String key, CharSequence value) {
308        extras.putCharSequence(key, value);
309        return realIntent;
310    }
311
312    @Implementation
313    public CharSequence getCharSequenceExtra(String name) {
314        return extras.getCharSequence(name);
315    }
316
317    @Implementation
318    public void putExtra(String key, byte[] value) {
319        extras.putByteArray(key, value);
320    }
321
322    @Implementation
323    public Intent putStringArrayListExtra(String key, ArrayList<String> value) {
324        extras.putStringArrayList(key, value);
325        return realIntent;
326    }
327
328    @Implementation
329    public ArrayList<String> getStringArrayListExtra(String name) {
330        return extras.getStringArrayList(name);
331    }
332
333    @Implementation
334    public Intent putIntegerArrayListExtra(String key, ArrayList<Integer> value) {
335        extras.putIntegerArrayList(key, value);
336        return realIntent;
337    }
338
339    @Implementation
340    public ArrayList<Integer> getIntegerArrayListExtra(String name) {
341        return extras.getIntegerArrayList(name);
342    }
343
344    @Implementation
345    public Intent putParcelableArrayListExtra(String key, ArrayList<Parcelable> value) {
346        extras.putParcelableArrayList(key, value);
347        return realIntent;
348    }
349
350    @Implementation
351    public ArrayList<Parcelable> getParcelableArrayListExtra(String key) {
352        return extras.getParcelableArrayList(key);
353    }
354
355    @Implementation
356    public boolean hasExtra(String name) {
357        return extras.containsKey(name);
358    }
359
360    @Implementation
361    public String getStringExtra(String name) {
362        return extras.getString(name);
363    }
364
365    @Implementation
366    public Parcelable getParcelableExtra(String name) {
367        return extras.getParcelable(name);
368    }
369
370    @Implementation
371    public Parcelable[] getParcelableArrayExtra(String name) {
372        return extras.getParcelableArray(name);
373    }
374
375    @Implementation
376    public int getIntExtra(String name, int defaultValue) {
377        return extras.getInt(name, defaultValue);
378    }
379
380    @Implementation
381    public long getLongExtra(String name, long defaultValue) {
382        return extras.getLong(name, defaultValue);
383    }
384
385    @Implementation
386    public double getDoubleExtra(String name, double defaultValue) {
387        return extras.getDouble(name, defaultValue);
388    }
389
390    @Implementation
391    public Bundle getBundleExtra(String name) {
392        return extras.getBundle(name);
393    }
394
395    @Implementation
396    public float getFloatExtra(String name, float defaultValue) {
397        return extras.getFloat(name, defaultValue);
398    }
399
400    @Implementation
401    public byte[] getByteArrayExtra(String name) {
402        return extras.getByteArray(name);
403    }
404
405    @Implementation
406    public Serializable getSerializableExtra(String name) {
407        return extras.getSerializable(name);
408    }
409
410    @Implementation
411    public void removeExtra(String name) {
412        extras.remove(name);
413    }
414
415    @Implementation
416    public Intent setComponent(ComponentName componentName) {
417        this.componentName = componentName;
418        return realIntent;
419    }
420
421    @Implementation
422    public ComponentName getComponent() {
423        return componentName;
424    }
425
426    @Implementation
427    public int fillIn(Intent otherIntent, int flags) {
428        int changes = 0;
429        ShadowIntent other = shadowOf(otherIntent);
430
431        if (other.action != null && (action == null || (flags & FILL_IN_ACTION) != 0)) {
432            action = other.action;
433            changes |= FILL_IN_ACTION;
434        }
435        if ((other.data != null || other.type != null)
436                && ((data == null && type == null) || (flags & FILL_IN_DATA) != 0)) {
437            data = other.data;
438            type = other.type;
439            changes |= FILL_IN_DATA;
440        }
441        if (!other.categories.isEmpty()
442                && (categories.isEmpty() || (flags & FILL_IN_CATEGORIES) != 0)) {
443            categories.addAll(other.categories);
444            changes |= FILL_IN_CATEGORIES;
445        }
446        if (other.packageName != null
447                && (packageName == null || (flags & FILL_IN_PACKAGE) != 0)) {
448            packageName = other.packageName;
449            changes |= FILL_IN_PACKAGE;
450        }
451        if (other.componentName != null && (flags & FILL_IN_COMPONENT) != 0) {
452            componentName = other.componentName;
453            changes |= FILL_IN_COMPONENT;
454        }
455
456        extras.putAll(other.extras);
457        return changes;
458    }
459
460    @Implementation
461    // cribbed from Android source
462    public boolean filterEquals(Intent other) {
463        if (other == null) {
464            return false;
465        }
466        if (getAction() != other.getAction()) {
467            if (getAction() != null) {
468                if (!getAction().equals(other.getAction())) {
469                    return false;
470                }
471            } else {
472                if (!other.getAction().equals(getAction())) {
473                    return false;
474                }
475            }
476        }
477        if (getData() != other.getData()) {
478            if (getData() != null) {
479                if (!getData().equals(other.getData())) {
480                    return false;
481                }
482            } else {
483                if (!other.getData().equals(getData())) {
484                    return false;
485                }
486            }
487        }
488        if (getType() != other.getType()) {
489            if (getType() != null) {
490                if (!getType().equals(other.getType())) {
491                    return false;
492                }
493            } else {
494                if (!other.getType().equals(getType())) {
495                    return false;
496                }
497            }
498        }
499        if (getPackage() != other.getPackage()) {
500            if (getPackage() != null) {
501                if (!getPackage().equals(other.getPackage())) {
502                    return false;
503                }
504            } else {
505                if (!other.getPackage().equals(getPackage())) {
506                    return false;
507                }
508            }
509        }
510        if (getComponent() != other.getComponent()) {
511            if (getComponent() != null) {
512                if (!getComponent().equals(other.getComponent())) {
513                    return false;
514                }
515            } else {
516                if (!other.getComponent().equals(getComponent())) {
517                    return false;
518                }
519            }
520        }
521        if (getCategories() != other.getCategories()) {
522            if (getCategories() != null) {
523                if (!getCategories().equals(other.getCategories())) {
524                    return false;
525                }
526            } else {
527                if (!other.getCategories().equals(getCategories())) {
528                    return false;
529                }
530            }
531        }
532
533        return true;
534    }
535
536    /**
537     * Compares an {@code Intent} with a {@code ShadowIntent} (obtained via a call to
538     * {@link Robolectric#shadowOf(android.content.Intent)})
539     *
540     * @param o a {@code ShadowIntent}
541     * @return whether they are equivalent
542     */
543    @Deprecated
544    public boolean realIntentEquals(ShadowIntent o) {
545        if (this == o) return true;
546        if (o == null || getClass() != o.getClass()) return false;
547
548        if (action != null ? !action.equals(o.action) : o.action != null) return false;
549        if (componentName != null ? !componentName.equals(o.componentName) : o.componentName != null)
550            return false;
551        if (data != null ? !data.equals(o.data) : o.data != null) return false;
552        if (extras != null ? !extras.equals(o.extras) : o.extras != null) return false;
553        if (type != null ? !type.equals(o.type) : o.type != null) return false;
554        if (categories != null ? !categories.equals(o.categories) : o.categories != null) return false;
555
556        return true;
557    }
558
559    @Override
560    @Implementation
561    public int hashCode() {
562        int result = extras != null ? extras.hashCode() : 0;
563        result = 31 * result + (action != null ? action.hashCode() : 0);
564        result = 31 * result + (componentName != null ? componentName.hashCode() : 0);
565        result = 31 * result + (data != null ? data.hashCode() : 0);
566        result = 31 * result + (type != null ? type.hashCode() : 0);
567        result = 31 * result + flags;
568        return result;
569    }
570
571    @Override
572    @Implementation
573    public boolean equals(Object o) {
574        if (!(o instanceof Intent)) return false;
575        return realIntentEquals(shadowOf((Intent) o));
576    }
577
578    /**
579     * Non-Android accessor that returns the {@code Class} object set by
580     * {@link #setClass(android.content.Context, Class)}
581     *
582     * @return the {@code Class} object set by
583     *         {@link #setClass(android.content.Context, Class)}
584     */
585    public Class<?> getIntentClass() {
586        return intentClass;
587    }
588
589    @Override
590    @Implementation
591    public String toString() {
592        return "Intent{" +
593                Join.join(
594                        ", ",
595                        ifWeHave(componentName, "componentName"),
596                        ifWeHave(action, "action"),
597                        ifWeHave(extras, "extras"),
598                        ifWeHave(data, "data"),
599                        ifWeHave(type, "type")
600                ) +
601                '}';
602    }
603
604    @Implementation
605    public void writeToParcel(Parcel out, int flags) {
606        out.writeString(action);
607        if (data != null) {
608            out.writeInt(1);
609            Uri.writeToParcel(out, data);
610        } else {
611            out.writeInt(-1);
612        }
613        out.writeString(type);
614        out.writeInt(flags);
615        out.writeString(packageName);
616        ComponentName.writeToParcel(componentName, out);
617        out.writeInt(categories.size());
618
619        for (String category : categories) {
620            out.writeString(category);
621        }
622        out.writeBundle(extras);
623    }
624
625    @Implementation
626    public void readFromParcel(Parcel in) {
627        setAction(in.readString());
628        if (in.readInt() == 1) {
629            data = Uri.CREATOR.createFromParcel(in);
630        }
631        type = in.readString();
632        flags = in.readInt();
633        packageName = in.readString();
634        componentName = ComponentName.readFromParcel(in);
635        int N = in.readInt();
636        for (int i = 0; i < N; i++) {
637            categories.add(in.readString().intern());
638        }
639        extras.putAll(in.readBundle());
640    }
641
642    private Serializable serializeCycle(Serializable serializable) {
643        try {
644            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
645            ObjectOutputStream output = new ObjectOutputStream(byteArrayOutputStream);
646            output.writeObject(serializable);
647            output.close();
648
649            byte[] bytes = byteArrayOutputStream.toByteArray();
650            ObjectInputStream input = new ObjectInputStream(new ByteArrayInputStream(bytes));
651            return (Serializable) input.readObject();
652        } catch (IOException e) {
653            throw new RuntimeException(e);
654        } catch (ClassNotFoundException e) {
655            throw new RuntimeException(e);
656        }
657    }
658
659    private String ifWeHave(Object o, String name) {
660        if (o == null) return null;
661        if (o instanceof Map && ((Map) o).isEmpty()) return null;
662        return name + "=" + o;
663    }
664
665    public static final Creator<Intent> CREATOR =
666        new Creator<Intent>() {
667            @Override
668            public Intent createFromParcel(Parcel source) {
669                Intent intent = new Intent();
670                intent.readFromParcel(source);
671                return intent;
672            }
673
674            @Override
675            public Intent[] newArray(int size) {
676                return new Intent[size];
677            }
678        };
679
680    public static void reset() {
681        Robolectric.Reflection.setFinalStaticField(Intent.class, "CREATOR", CREATOR);
682    }
683}
684