2 * Copyright (C) 2011 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.support.v2.app;
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;
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.
36 public class DialogFragment extends Fragment
37 implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
40 * Style for {@link #setStyle(int, int)}: a basic,
43 public static final int STYLE_NORMAL = 0;
46 * Style for {@link #setStyle(int, int)}: don't include
49 public static final int STYLE_NO_TITLE = 1;
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.
56 public static final int STYLE_NO_FRAME = 2;
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.
63 public static final int STYLE_NO_INPUT = 3;
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";
72 int mStyle = STYLE_NORMAL;
74 boolean mCancelable = true;
75 boolean mShowsDialog = true;
76 int mBackStackId = -1;
82 public DialogFragment() {
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.
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.
99 public void setStyle(int style, int theme) {
101 if (mStyle == STYLE_NO_FRAME || mStyle == STYLE_NO_INPUT) {
102 mTheme = android.R.style.Theme_Panel;
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
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}.
120 public void show(FragmentManager manager, String tag) {
121 FragmentTransaction ft = manager.beginTransaction();
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()}.
135 public int show(FragmentTransaction transaction, String tag) {
136 transaction.add(this, tag);
138 mBackStackId = transaction.commit();
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
148 public void dismiss() {
149 dismissInternal(false);
152 void dismissInternal(boolean allowStateLoss) {
153 if (mDialog != null) {
158 if (mBackStackId >= 0) {
159 getFragmentManager().popBackStack(mBackStackId,
160 FragmentManager.POP_BACK_STACK_INCLUSIVE);
163 FragmentTransaction ft = getFragmentManager().beginTransaction();
165 if (allowStateLoss) {
166 ft.commitAllowingStateLoss();
173 public Dialog getDialog() {
177 public int getTheme() {
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.
187 * @param cancelable If true, the dialog is cancelable. The default
190 public void setCancelable(boolean cancelable) {
191 mCancelable = cancelable;
192 if (mDialog != null) mDialog.setCancelable(cancelable);
196 * Return the current value of {@link #setCancelable(boolean)}.
198 public boolean isCancelable() {
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
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.
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
219 public void setShowsDialog(boolean showsDialog) {
220 mShowsDialog = showsDialog;
224 * Return the current value of {@link #setShowsDialog(boolean)}.
226 public boolean getShowsDialog() {
231 public void onCreate(Bundle savedInstanceState) {
232 super.onCreate(savedInstanceState);
234 mShowsDialog = mContainerId == 0;
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);
248 public LayoutInflater getLayoutInflater(Bundle savedInstanceState) {
250 return super.getLayoutInflater(savedInstanceState);
253 mDialog = onCreateDialog(savedInstanceState);
257 mDialog.getWindow().addFlags(
258 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
259 WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
263 mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
265 return (LayoutInflater)mDialog.getContext().getSystemService(
266 Context.LAYOUT_INFLATER_SERVICE);
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.
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}
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>
286 * @param savedInstanceState The last saved instance state of the Fragment,
287 * or null if this is a freshly created Fragment.
289 * @return Return a new Dialog instance to be displayed by the Fragment.
291 public Dialog onCreateDialog(Bundle savedInstanceState) {
292 return new Dialog(getActivity(), getTheme());
295 public void onCancel(DialogInterface dialog) {
298 public void onDismiss(DialogInterface dialog) {
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);
309 public void onActivityCreated(Bundle savedInstanceState) {
310 super.onActivityCreated(savedInstanceState);
316 View view = getView();
318 if (view.getParent() != null) {
319 throw new IllegalStateException("DialogFragment can not be attached to a container view");
321 mDialog.setContentView(view);
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);
336 public void onStart() {
338 if (mDialog != null) {
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);
353 if (mStyle != STYLE_NORMAL) {
354 outState.putInt(SAVED_STYLE, mStyle);
357 outState.putInt(SAVED_THEME, mTheme);
360 outState.putBoolean(SAVED_CANCELABLE, mCancelable);
363 outState.putBoolean(SAVED_SHOWS_DIALOG, mShowsDialog);
365 if (mBackStackId != -1) {
366 outState.putInt(SAVED_BACK_STACK_ID, mBackStackId);
371 public void onStop() {
373 if (mDialog != null) {
382 public void onDestroyView() {
383 super.onDestroyView();
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.