1package com.xtremelabs.robolectric.shadows;
2
3import android.app.Activity;
4import android.app.Dialog;
5import android.content.Context;
6import android.content.DialogInterface;
7import android.os.Bundle;
8import android.view.LayoutInflater;
9import android.view.View;
10import android.view.ViewGroup;
11import android.view.Window;
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.tester.android.view.TestWindow;
17
18import java.lang.reflect.Method;
19import java.util.ArrayList;
20import java.util.List;
21
22import static com.xtremelabs.robolectric.Robolectric.shadowOf;
23
24@SuppressWarnings({"UnusedDeclaration"})
25@Implements(Dialog.class)
26public class ShadowDialog {
27
28    @RealObject private Dialog realDialog;
29
30    private boolean isShowing;
31    Context context;
32    private int layoutId;
33    private int themeId;
34    private View inflatedView;
35    private boolean hasBeenDismissed;
36    private DialogInterface.OnDismissListener onDismissListener;
37    protected CharSequence title;
38    private DialogInterface.OnCancelListener onCancelListener;
39    private Window window;
40    private Activity ownerActivity;
41    private boolean isCancelable = true;
42    private boolean hasShownBefore;
43    private static final ArrayList<Dialog> shownDialogs = new ArrayList<Dialog>();
44    private boolean isCancelableOnTouchOutside;
45
46    public static void reset() {
47        setLatestDialog(null);
48        shownDialogs.clear();
49    }
50
51    public static Dialog getLatestDialog() {
52        ShadowDialog dialog = Robolectric.getShadowApplication().getLatestDialog();
53        return dialog == null ? null : dialog.realDialog;
54    }
55
56    public static void setLatestDialog(ShadowDialog latestDialog) {
57        Robolectric.getShadowApplication().setLatestDialog(latestDialog);
58    }
59
60    public void __constructor__(Context context) {
61        __constructor__(context, -1);
62    }
63
64    public void __constructor__(Context context, int themeId) {
65        this.context = context;
66        this.themeId = themeId;
67    }
68
69    @Implementation
70    public void setContentView(int layoutResID) {
71        layoutId = layoutResID;
72    }
73
74    @Implementation
75    public void setContentView(View view) {
76        inflatedView = view;
77    }
78
79    @Implementation
80    public void setTitle(int stringResourceId) {
81        this.title = context.getResources().getText(stringResourceId);
82    }
83
84    @Implementation(i18nSafe = false)
85    public void setTitle(CharSequence title) {
86        this.title = title;
87    }
88
89    @Implementation
90    public void setOwnerActivity(Activity activity) {
91        this.ownerActivity = activity;
92    }
93
94    @Implementation
95    public Activity getOwnerActivity() {
96        return this.ownerActivity;
97    }
98
99    @Implementation
100    public Context getContext() {
101        return context;
102    }
103
104    @Implementation
105    public void onBackPressed() {
106        cancel();
107    }
108
109    @Implementation
110    public void show() {
111        setLatestDialog(this);
112        shownDialogs.add(realDialog);
113        isShowing = true;
114        try {
115            if (!hasShownBefore) {
116                Method onCreateMethod = Dialog.class.getDeclaredMethod("onCreate", Bundle.class);
117                onCreateMethod.setAccessible(true);
118                onCreateMethod.invoke(realDialog, (Bundle) null);
119            }
120
121            Method onStartMethod = Dialog.class.getDeclaredMethod("onStart");
122            onStartMethod.setAccessible(true);
123            onStartMethod.invoke(realDialog);
124        } catch (Exception e) {
125            throw new RuntimeException(e);
126        }
127        hasShownBefore = true;
128    }
129
130    @Implementation
131    public void hide() {
132        isShowing = false;
133    }
134
135    @Implementation
136    public boolean isShowing() {
137        return isShowing;
138    }
139
140    @Implementation
141    public void dismiss() {
142        isShowing = false;
143        hasBeenDismissed = true;
144
145        if (onDismissListener != null) {
146            onDismissListener.onDismiss(realDialog);
147        }
148    }
149
150    @Implementation
151    public View findViewById(int viewId) {
152        if (inflatedView != null) {
153            return inflatedView.findViewById(viewId);
154        }
155        if (layoutId > 0 && context != null) {
156            inflatedView = ShadowLayoutInflater.from(context).inflate(layoutId, null);
157            return inflatedView.findViewById(viewId);
158        }
159        return null;
160    }
161
162    public void clickOn(int viewId) {
163        findViewById(viewId).performClick();
164    }
165
166    @Implementation
167    public void setOnDismissListener(DialogInterface.OnDismissListener onDismissListener) {
168        this.onDismissListener = onDismissListener;
169    }
170
171    @Implementation
172    public void setCancelable(boolean flag) {
173        isCancelable = flag;
174    }
175
176    @Implementation
177    public void setCanceledOnTouchOutside(boolean flag) {
178        isCancelableOnTouchOutside = flag;
179    }
180
181    public boolean isCancelable() {
182        return isCancelable;
183    }
184
185    public boolean isCancelableOnTouchOutside() {
186        return isCancelableOnTouchOutside;
187    }
188
189    @Implementation
190    public void cancel() {
191        if (onCancelListener != null) {
192            onCancelListener.onCancel(realDialog);
193        }
194        realDialog.dismiss();
195    }
196
197    @Implementation
198    public void setOnCancelListener(DialogInterface.OnCancelListener listener) {
199        this.onCancelListener = listener;
200    }
201
202    public DialogInterface.OnCancelListener getOnCancelListener() {
203        return onCancelListener;
204    }
205
206    @Implementation
207    public Window getWindow() {
208        if (window == null) {
209            window = new TestWindow(realDialog.getContext());
210        }
211        return window;
212    }
213
214
215    @Implementation
216    public LayoutInflater getLayoutInflater() {
217        return LayoutInflater.from(realDialog.getContext());
218    }
219
220    public int getLayoutId() {
221        return layoutId;
222    }
223
224    public int getThemeId() {
225        return themeId;
226    }
227
228    public boolean hasBeenDismissed() {
229        return hasBeenDismissed;
230    }
231
232    public CharSequence getTitle() {
233        return title;
234    }
235
236    public void clickOnText(int textId) {
237        if (inflatedView == null) {
238            inflatedView = ShadowLayoutInflater.from(context).inflate(layoutId, null);
239        }
240        String text = getContext().getResources().getString(textId);
241        if (!clickOnText(inflatedView, text)) {
242            throw new IllegalArgumentException("Text not found: " + text);
243        }
244    }
245
246    private boolean clickOnText(View view, String text) {
247        if (text.equals(shadowOf(view).innerText())) {
248            view.performClick();
249            return true;
250        }
251        if (view instanceof ViewGroup) {
252            ViewGroup viewGroup = (ViewGroup) view;
253            for (int i = 0; i < viewGroup.getChildCount(); i++) {
254                View child = viewGroup.getChildAt(i);
255                if (clickOnText(child, text)) {
256                    return true;
257                }
258            }
259        }
260        return false;
261    }
262
263    public static List<Dialog> getShownDialogs() {
264        return shownDialogs;
265    }
266}
267