switch to alsa.omap3 module
[android_pandora.git] / apps / AndroidSupportV2 / src / android / support / v2 / app / DialogFragment.java
CommitLineData
811a5a4a 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
17package android.support.v2.app;
18
19import android.app.Dialog;
20import android.content.Context;
21import android.content.DialogInterface;
22import android.os.Bundle;
23import android.view.LayoutInflater;
24import android.view.View;
25import android.view.ViewGroup;
26import android.view.Window;
27import 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 */
36public 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}