switch to alsa.omap3 module
[android_pandora.git] / apps / AndroidSupportV2 / src / android / support / v2 / app / DialogFragment.java
1 /*
2  * Copyright (C) 2011 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.support.v2.app;
18
19 import android.app.Dialog;
20 import android.content.Context;
21 import android.content.DialogInterface;
22 import android.os.Bundle;
23 import android.view.LayoutInflater;
24 import android.view.View;
25 import android.view.ViewGroup;
26 import android.view.Window;
27 import android.view.WindowManager;
28
29 /**
30  * Static library support version of the framework's {@link android.app.DialogFragment}.
31  * Used to write apps that run on platforms prior to Android 3.0.  When running
32  * on Android 3.0 or above, this implementation is still used; it does not try
33  * to switch to the framework's implementation.  See the framework SDK
34  * documentation for a class overview.
35  */
36 public class DialogFragment extends Fragment
37         implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
38
39     /**
40      * Style for {@link #setStyle(int, int)}: a basic,
41      * normal dialog.
42      */
43     public static final int STYLE_NORMAL = 0;
44
45     /**
46      * Style for {@link #setStyle(int, int)}: don't include
47      * a title area.
48      */
49     public static final int STYLE_NO_TITLE = 1;
50
51     /**
52      * Style for {@link #setStyle(int, int)}: don't draw
53      * any frame at all; the view hierarchy returned by {@link #onCreateView}
54      * is entirely responsible for drawing the dialog.
55      */
56     public static final int STYLE_NO_FRAME = 2;
57
58     /**
59      * Style for {@link #setStyle(int, int)}: like
60      * {@link #STYLE_NO_FRAME}, but also disables all input to the dialog.
61      * The user can not touch it, and its window will not receive input focus.
62      */
63     public static final int STYLE_NO_INPUT = 3;
64
65     private static final String SAVED_DIALOG_STATE_TAG = "android:savedDialogState";
66     private static final String SAVED_STYLE = "android:style";
67     private static final String SAVED_THEME = "android:theme";
68     private static final String SAVED_CANCELABLE = "android:cancelable";
69     private static final String SAVED_SHOWS_DIALOG = "android:showsDialog";
70     private static final String SAVED_BACK_STACK_ID = "android:backStackId";
71
72     int mStyle = STYLE_NORMAL;
73     int mTheme = 0;
74     boolean mCancelable = true;
75     boolean mShowsDialog = true;
76     int mBackStackId = -1;
77
78     Dialog mDialog;
79     boolean mDestroyed;
80     boolean mRemoved;
81
82     public DialogFragment() {
83     }
84
85     /**
86      * Call to customize the basic appearance and behavior of the
87      * fragment's dialog.  This can be used for some common dialog behaviors,
88      * taking care of selecting flags, theme, and other options for you.  The
89      * same effect can be achieve by manually setting Dialog and Window
90      * attributes yourself.  Calling this after the fragment's Dialog is
91      * created will have no effect.
92      *
93      * @param style Selects a standard style: may be {@link #STYLE_NORMAL},
94      * {@link #STYLE_NO_TITLE}, {@link #STYLE_NO_FRAME}, or
95      * {@link #STYLE_NO_INPUT}.
96      * @param theme Optional custom theme.  If 0, an appropriate theme (based
97      * on the style) will be selected for you.
98      */
99     public void setStyle(int style, int theme) {
100         mStyle = style;
101         if (mStyle == STYLE_NO_FRAME || mStyle == STYLE_NO_INPUT) {
102             mTheme = android.R.style.Theme_Panel;
103         }
104         if (theme != 0) {
105             mTheme = theme;
106         }
107     }
108
109     /**
110      * Display the dialog, adding the fragment to the given FragmentManager.  This
111      * is a convenience for explicitly creating a transaction, adding the
112      * fragment to it with the given tag, and committing it.  This does
113      * <em>not</em> add the transaction to the back stack.  When the fragment
114      * is dismissed, a new transaction will be executed to remove it from
115      * the activity.
116      * @param manager The FragmentManager this fragment will be added to.
117      * @param tag The tag for this fragment, as per
118      * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
119      */
120     public void show(FragmentManager manager, String tag) {
121         FragmentTransaction ft = manager.beginTransaction();
122         ft.add(this, tag);
123         ft.commit();
124     }
125
126     /**
127      * Display the dialog, adding the fragment using an existing transaction
128      * and then committing the transaction.
129      * @param transaction An existing transaction in which to add the fragment.
130      * @param tag The tag for this fragment, as per
131      * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
132      * @return Returns the identifier of the committed transaction, as per
133      * {@link FragmentTransaction#commit() FragmentTransaction.commit()}.
134      */
135     public int show(FragmentTransaction transaction, String tag) {
136         transaction.add(this, tag);
137         mRemoved = false;
138         mBackStackId = transaction.commit();
139         return mBackStackId;
140     }
141
142     /**
143      * Dismiss the fragment and its dialog.  If the fragment was added to the
144      * back stack, all back stack state up to and including this entry will
145      * be popped.  Otherwise, a new transaction will be committed to remove
146      * the fragment.
147      */
148     public void dismiss() {
149         dismissInternal(false);
150     }
151
152     void dismissInternal(boolean allowStateLoss) {
153         if (mDialog != null) {
154             mDialog.dismiss();
155             mDialog = null;
156         }
157         mRemoved = true;
158         if (mBackStackId >= 0) {
159             getFragmentManager().popBackStack(mBackStackId,
160                     FragmentManager.POP_BACK_STACK_INCLUSIVE);
161             mBackStackId = -1;
162         } else {
163             FragmentTransaction ft = getFragmentManager().beginTransaction();
164             ft.remove(this);
165             if (allowStateLoss) {
166                 ft.commitAllowingStateLoss();
167             } else {
168                 ft.commit();
169             }
170         }
171     }
172     
173     public Dialog getDialog() {
174         return mDialog;
175     }
176
177     public int getTheme() {
178         return mTheme;
179     }
180
181     /**
182      * Control whether the shown Dialog is cancelable.  Use this instead of
183      * directly calling {@link Dialog#setCancelable(boolean)
184      * Dialog.setCancelable(boolean)}, because DialogFragment needs to change
185      * its behavior based on this.
186      *
187      * @param cancelable If true, the dialog is cancelable.  The default
188      * is true.
189      */
190     public void setCancelable(boolean cancelable) {
191         mCancelable = cancelable;
192         if (mDialog != null) mDialog.setCancelable(cancelable);
193     }
194
195     /**
196      * Return the current value of {@link #setCancelable(boolean)}.
197      */
198     public boolean isCancelable() {
199         return mCancelable;
200     }
201
202     /**
203      * Controls whether this fragment should be shown in a dialog.  If not
204      * set, no Dialog will be created in {@link #onActivityCreated(Bundle)},
205      * and the fragment's view hierarchy will thus not be added to it.  This
206      * allows you to instead use it as a normal fragment (embedded inside of
207      * its activity).
208      *
209      * <p>This is normally set for you based on whether the fragment is
210      * associated with a container view ID passed to
211      * {@link FragmentTransaction#add(int, Fragment) FragmentTransaction.add(int, Fragment)}.
212      * If the fragment was added with a container, setShowsDialog will be
213      * initialized to false; otherwise, it will be true.
214      *
215      * @param showsDialog If true, the fragment will be displayed in a Dialog.
216      * If false, no Dialog will be created and the fragment's view hierarchly
217      * left undisturbed.
218      */
219     public void setShowsDialog(boolean showsDialog) {
220         mShowsDialog = showsDialog;
221     }
222
223     /**
224      * Return the current value of {@link #setShowsDialog(boolean)}.
225      */
226     public boolean getShowsDialog() {
227         return mShowsDialog;
228     }
229
230     @Override
231     public void onCreate(Bundle savedInstanceState) {
232         super.onCreate(savedInstanceState);
233
234         mShowsDialog = mContainerId == 0;
235
236         if (savedInstanceState != null) {
237             mStyle = savedInstanceState.getInt(SAVED_STYLE, STYLE_NORMAL);
238             mTheme = savedInstanceState.getInt(SAVED_THEME, 0);
239             mCancelable = savedInstanceState.getBoolean(SAVED_CANCELABLE, true);
240             mShowsDialog = savedInstanceState.getBoolean(SAVED_SHOWS_DIALOG, mShowsDialog);
241             mBackStackId = savedInstanceState.getInt(SAVED_BACK_STACK_ID, -1);
242         }
243         
244     }
245
246     /** @hide */
247     @Override
248     public LayoutInflater getLayoutInflater(Bundle savedInstanceState) {
249         if (!mShowsDialog) {
250             return super.getLayoutInflater(savedInstanceState);
251         }
252
253         mDialog = onCreateDialog(savedInstanceState);
254         mDestroyed = false;
255         switch (mStyle) {
256             case STYLE_NO_INPUT:
257                 mDialog.getWindow().addFlags(
258                         WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
259                         WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
260                 // fall through...
261             case STYLE_NO_FRAME:
262             case STYLE_NO_TITLE:
263                 mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
264         }
265         return (LayoutInflater)mDialog.getContext().getSystemService(
266                 Context.LAYOUT_INFLATER_SERVICE);
267     }
268     
269     /**
270      * Override to build your own custom Dialog container.  This is typically
271      * used to show an AlertDialog instead of a generic Dialog; when doing so,
272      * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} does not need
273      * to be implemented since the AlertDialog takes care of its own content.
274      * 
275      * <p>This method will be called after {@link #onCreate(Bundle)} and
276      * before {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}.  The
277      * default implementation simply instantiates and returns a {@link Dialog}
278      * class.
279      * 
280      * <p><em>Note: DialogFragment own the {@link Dialog#setOnCancelListener
281      * Dialog.setOnCancelListener} and {@link Dialog#setOnDismissListener
282      * Dialog.setOnDismissListener} callbacks.  You must not set them yourself.</em>
283      * To find out about these events, override {@link #onCancel(DialogInterface)}
284      * and {@link #onDismiss(DialogInterface)}.</p>
285      * 
286      * @param savedInstanceState The last saved instance state of the Fragment,
287      * or null if this is a freshly created Fragment.
288      * 
289      * @return Return a new Dialog instance to be displayed by the Fragment.
290      */
291     public Dialog onCreateDialog(Bundle savedInstanceState) {
292         return new Dialog(getActivity(), getTheme());
293     }
294
295     public void onCancel(DialogInterface dialog) {
296     }
297
298     public void onDismiss(DialogInterface dialog) {
299         if (!mRemoved) {
300             // Note: we need to use allowStateLoss, because the dialog
301             // dispatches this asynchronously so we can receive the call
302             // after the activity is paused.  Worst case, when the user comes
303             // back to the activity they see the dialog again.
304             dismissInternal(true);
305         }
306     }
307
308     @Override
309     public void onActivityCreated(Bundle savedInstanceState) {
310         super.onActivityCreated(savedInstanceState);
311
312         if (!mShowsDialog) {
313             return;
314         }
315
316         View view = getView();
317         if (view != null) {
318             if (view.getParent() != null) {
319                 throw new IllegalStateException("DialogFragment can not be attached to a container view");
320             }
321             mDialog.setContentView(view);
322         }
323         mDialog.setOwnerActivity(getActivity());
324         mDialog.setCancelable(mCancelable);
325         mDialog.setOnCancelListener(this);
326         mDialog.setOnDismissListener(this);
327         if (savedInstanceState != null) {
328             Bundle dialogState = savedInstanceState.getBundle(SAVED_DIALOG_STATE_TAG);
329             if (dialogState != null) {
330                 mDialog.onRestoreInstanceState(dialogState);
331             }
332         }
333     }
334
335     @Override
336     public void onStart() {
337         super.onStart();
338         if (mDialog != null) {
339             mRemoved = false;
340             mDialog.show();
341         }
342     }
343
344     @Override
345     public void onSaveInstanceState(Bundle outState) {
346         super.onSaveInstanceState(outState);
347         if (mDialog != null) {
348             Bundle dialogState = mDialog.onSaveInstanceState();
349             if (dialogState != null) {
350                 outState.putBundle(SAVED_DIALOG_STATE_TAG, dialogState);
351             }
352         }
353         if (mStyle != STYLE_NORMAL) {
354             outState.putInt(SAVED_STYLE, mStyle);
355         }
356         if (mTheme != 0) {
357             outState.putInt(SAVED_THEME, mTheme);
358         }
359         if (!mCancelable) {
360             outState.putBoolean(SAVED_CANCELABLE, mCancelable);
361         }
362         if (!mShowsDialog) {
363             outState.putBoolean(SAVED_SHOWS_DIALOG, mShowsDialog);
364         }
365         if (mBackStackId != -1) {
366             outState.putInt(SAVED_BACK_STACK_ID, mBackStackId);
367         }
368     }
369
370     @Override
371     public void onStop() {
372         super.onStop();
373         if (mDialog != null) {
374             mDialog.hide();
375         }
376     }
377
378     /**
379      * Remove dialog.
380      */
381     @Override
382     public void onDestroyView() {
383         super.onDestroyView();
384         mDestroyed = true;
385         if (mDialog != null) {
386             // Set removed here because this dismissal is just to hide
387             // the dialog -- we don't want this to cause the fragment to
388             // actually be removed.
389             mRemoved = true;
390             mDialog.dismiss();
391             mDialog = null;
392         }
393     }
394 }