Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2010 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.app; |
| 18 | |
Dianne Hackborn | 7187ccb | 2011-01-24 23:58:13 -0800 | [diff] [blame] | 19 | import android.content.Context; |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 20 | import android.content.DialogInterface; |
| 21 | import android.os.Bundle; |
| 22 | import android.view.LayoutInflater; |
| 23 | import android.view.View; |
| 24 | import android.view.ViewGroup; |
| 25 | import android.view.Window; |
| 26 | import android.view.WindowManager; |
| 27 | |
Dianne Hackborn | bfe2e3f | 2011-03-01 16:40:54 -0800 | [diff] [blame] | 28 | import java.io.FileDescriptor; |
| 29 | import java.io.PrintWriter; |
| 30 | |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 31 | /** |
| 32 | * A fragment that displays a dialog window, floating on top of its |
| 33 | * activity's window. This fragment contains a Dialog object, which it |
| 34 | * displays as appropriate based on the fragment's state. Control of |
| 35 | * the dialog (deciding when to show, hide, dismiss it) should be done through |
| 36 | * the API here, not with direct calls on the dialog. |
| 37 | * |
| 38 | * <p>Implementations should override this class and implement |
| 39 | * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} to supply the |
| 40 | * content of the dialog. Alternatively, they can override |
| 41 | * {@link #onCreateDialog(Bundle)} to create an entirely custom dialog, such |
| 42 | * as an AlertDialog, with its own content. |
Dianne Hackborn | 7277820 | 2010-08-20 18:26:01 -0700 | [diff] [blame] | 43 | * |
| 44 | * <p>Topics covered here: |
| 45 | * <ol> |
| 46 | * <li><a href="#Lifecycle">Lifecycle</a> |
| 47 | * <li><a href="#BasicDialog">Basic Dialog</a> |
| 48 | * <li><a href="#AlertDialog">Alert Dialog</a> |
| 49 | * <li><a href="#DialogOrEmbed">Selecting Between Dialog or Embedding</a> |
| 50 | * </ol> |
| 51 | * |
| 52 | * <a name="Lifecycle"></a> |
| 53 | * <h3>Lifecycle</h3> |
| 54 | * |
| 55 | * <p>DialogFragment does various things to keep the fragment's lifecycle |
| 56 | * driving it, instead of the Dialog. Note that dialogs are generally |
| 57 | * autonomous entities -- they are their own window, receiving their own |
| 58 | * input events, and often deciding on their own when to disappear (by |
| 59 | * receiving a back key event or the user clicking on a button). |
| 60 | * |
| 61 | * <p>DialogFragment needs to ensure that what is happening with the Fragment |
| 62 | * and Dialog states remains consistent. To do this, it watches for dismiss |
Scott Main | fd51698 | 2012-07-03 16:25:52 -0700 | [diff] [blame] | 63 | * events from the dialog and takes care of removing its own state when they |
Dianne Hackborn | 7277820 | 2010-08-20 18:26:01 -0700 | [diff] [blame] | 64 | * happen. This means you should use {@link #show(FragmentManager, String)} |
| 65 | * or {@link #show(FragmentTransaction, String)} to add an instance of |
| 66 | * DialogFragment to your UI, as these keep track of how DialogFragment should |
| 67 | * remove itself when the dialog is dismissed. |
| 68 | * |
| 69 | * <a name="BasicDialog"></a> |
| 70 | * <h3>Basic Dialog</h3> |
| 71 | * |
| 72 | * <p>The simplest use of DialogFragment is as a floating container for the |
| 73 | * fragment's view hierarchy. A simple implementation may look like this: |
| 74 | * |
| 75 | * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialog.java |
| 76 | * dialog} |
| 77 | * |
| 78 | * <p>An example showDialog() method on the Activity could be: |
| 79 | * |
| 80 | * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialog.java |
| 81 | * add_dialog} |
| 82 | * |
| 83 | * <p>This removes any currently shown dialog, creates a new DialogFragment |
| 84 | * with an argument, and shows it as a new state on the back stack. When the |
| 85 | * transaction is popped, the current DialogFragment and its Dialog will be |
| 86 | * destroyed, and the previous one (if any) re-shown. Note that in this case |
| 87 | * DialogFragment will take care of popping the transaction of the Dialog |
| 88 | * is dismissed separately from it. |
| 89 | * |
| 90 | * <a name="AlertDialog"></a> |
| 91 | * <h3>Alert Dialog</h3> |
| 92 | * |
| 93 | * <p>Instead of (or in addition to) implementing {@link #onCreateView} to |
| 94 | * generate the view hierarchy inside of a dialog, you may implement |
| 95 | * {@link #onCreateDialog(Bundle)} to create your own custom Dialog object. |
| 96 | * |
| 97 | * <p>This is most useful for creating an {@link AlertDialog}, allowing you |
| 98 | * to display standard alerts to the user that are managed by a fragment. |
| 99 | * A simple example implementation of this is: |
| 100 | * |
| 101 | * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentAlertDialog.java |
| 102 | * dialog} |
| 103 | * |
| 104 | * <p>The activity creating this fragment may have the following methods to |
| 105 | * show the dialog and receive results from it: |
| 106 | * |
| 107 | * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentAlertDialog.java |
| 108 | * activity} |
| 109 | * |
| 110 | * <p>Note that in this case the fragment is not placed on the back stack, it |
| 111 | * is just added as an indefinitely running fragment. Because dialogs normally |
| 112 | * are modal, this will still operate as a back stack, since the dialog will |
| 113 | * capture user input until it is dismissed. When it is dismissed, DialogFragment |
| 114 | * will take care of removing itself from its fragment manager. |
| 115 | * |
| 116 | * <a name="DialogOrEmbed"></a> |
| 117 | * <h3>Selecting Between Dialog or Embedding</h3> |
| 118 | * |
| 119 | * <p>A DialogFragment can still optionally be used as a normal fragment, if |
| 120 | * desired. This is useful if you have a fragment that in some cases should |
| 121 | * be shown as a dialog and others embedded in a larger UI. This behavior |
| 122 | * will normally be automatically selected for you based on how you are using |
| 123 | * the fragment, but can be customized with {@link #setShowsDialog(boolean)}. |
| 124 | * |
| 125 | * <p>For example, here is a simple dialog fragment: |
| 126 | * |
| 127 | * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogOrActivity.java |
| 128 | * dialog} |
| 129 | * |
| 130 | * <p>An instance of this fragment can be created and shown as a dialog: |
| 131 | * |
| 132 | * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogOrActivity.java |
| 133 | * show_dialog} |
| 134 | * |
| 135 | * <p>It can also be added as content in a view hierarchy: |
| 136 | * |
| 137 | * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogOrActivity.java |
| 138 | * embed} |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 139 | */ |
| 140 | public class DialogFragment extends Fragment |
| 141 | implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { |
| 142 | |
| 143 | /** |
Jean-Baptiste Queru | 72dc780 | 2010-08-13 09:25:19 -0700 | [diff] [blame] | 144 | * Style for {@link #setStyle(int, int)}: a basic, |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 145 | * normal dialog. |
| 146 | */ |
| 147 | public static final int STYLE_NORMAL = 0; |
| 148 | |
| 149 | /** |
Jean-Baptiste Queru | 72dc780 | 2010-08-13 09:25:19 -0700 | [diff] [blame] | 150 | * Style for {@link #setStyle(int, int)}: don't include |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 151 | * a title area. |
| 152 | */ |
| 153 | public static final int STYLE_NO_TITLE = 1; |
| 154 | |
| 155 | /** |
Jean-Baptiste Queru | 72dc780 | 2010-08-13 09:25:19 -0700 | [diff] [blame] | 156 | * Style for {@link #setStyle(int, int)}: don't draw |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 157 | * any frame at all; the view hierarchy returned by {@link #onCreateView} |
| 158 | * is entirely responsible for drawing the dialog. |
| 159 | */ |
| 160 | public static final int STYLE_NO_FRAME = 2; |
| 161 | |
| 162 | /** |
Jean-Baptiste Queru | 72dc780 | 2010-08-13 09:25:19 -0700 | [diff] [blame] | 163 | * Style for {@link #setStyle(int, int)}: like |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 164 | * {@link #STYLE_NO_FRAME}, but also disables all input to the dialog. |
| 165 | * The user can not touch it, and its window will not receive input focus. |
| 166 | */ |
| 167 | public static final int STYLE_NO_INPUT = 3; |
| 168 | |
| 169 | private static final String SAVED_DIALOG_STATE_TAG = "android:savedDialogState"; |
| 170 | private static final String SAVED_STYLE = "android:style"; |
| 171 | private static final String SAVED_THEME = "android:theme"; |
| 172 | private static final String SAVED_CANCELABLE = "android:cancelable"; |
Dianne Hackborn | 7277820 | 2010-08-20 18:26:01 -0700 | [diff] [blame] | 173 | private static final String SAVED_SHOWS_DIALOG = "android:showsDialog"; |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 174 | private static final String SAVED_BACK_STACK_ID = "android:backStackId"; |
| 175 | |
| 176 | int mStyle = STYLE_NORMAL; |
| 177 | int mTheme = 0; |
| 178 | boolean mCancelable = true; |
Dianne Hackborn | 7277820 | 2010-08-20 18:26:01 -0700 | [diff] [blame] | 179 | boolean mShowsDialog = true; |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 180 | int mBackStackId = -1; |
| 181 | |
| 182 | Dialog mDialog; |
Dianne Hackborn | bfe2e3f | 2011-03-01 16:40:54 -0800 | [diff] [blame] | 183 | boolean mViewDestroyed; |
| 184 | boolean mDismissed; |
| 185 | boolean mShownByMe; |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 186 | |
| 187 | public DialogFragment() { |
| 188 | } |
| 189 | |
| 190 | /** |
Dianne Hackborn | b7a2e47 | 2010-08-12 16:20:42 -0700 | [diff] [blame] | 191 | * Call to customize the basic appearance and behavior of the |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 192 | * fragment's dialog. This can be used for some common dialog behaviors, |
| 193 | * taking care of selecting flags, theme, and other options for you. The |
| 194 | * same effect can be achieve by manually setting Dialog and Window |
Dianne Hackborn | b7a2e47 | 2010-08-12 16:20:42 -0700 | [diff] [blame] | 195 | * attributes yourself. Calling this after the fragment's Dialog is |
| 196 | * created will have no effect. |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 197 | * |
| 198 | * @param style Selects a standard style: may be {@link #STYLE_NORMAL}, |
| 199 | * {@link #STYLE_NO_TITLE}, {@link #STYLE_NO_FRAME}, or |
| 200 | * {@link #STYLE_NO_INPUT}. |
| 201 | * @param theme Optional custom theme. If 0, an appropriate theme (based |
| 202 | * on the style) will be selected for you. |
| 203 | */ |
Dianne Hackborn | b7a2e47 | 2010-08-12 16:20:42 -0700 | [diff] [blame] | 204 | public void setStyle(int style, int theme) { |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 205 | mStyle = style; |
| 206 | if (mStyle == STYLE_NO_FRAME || mStyle == STYLE_NO_INPUT) { |
Adam Powell | 6e90a36 | 2011-08-14 16:48:32 -0700 | [diff] [blame] | 207 | mTheme = com.android.internal.R.style.Theme_DeviceDefault_Dialog_NoFrame; |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 208 | } |
| 209 | if (theme != 0) { |
| 210 | mTheme = theme; |
| 211 | } |
| 212 | } |
| 213 | |
| 214 | /** |
Dianne Hackborn | 7277820 | 2010-08-20 18:26:01 -0700 | [diff] [blame] | 215 | * Display the dialog, adding the fragment to the given FragmentManager. This |
| 216 | * is a convenience for explicitly creating a transaction, adding the |
| 217 | * fragment to it with the given tag, and committing it. This does |
| 218 | * <em>not</em> add the transaction to the back stack. When the fragment |
| 219 | * is dismissed, a new transaction will be executed to remove it from |
| 220 | * the activity. |
| 221 | * @param manager The FragmentManager this fragment will be added to. |
| 222 | * @param tag The tag for this fragment, as per |
| 223 | * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}. |
| 224 | */ |
| 225 | public void show(FragmentManager manager, String tag) { |
Dianne Hackborn | bfe2e3f | 2011-03-01 16:40:54 -0800 | [diff] [blame] | 226 | mDismissed = false; |
| 227 | mShownByMe = true; |
Dianne Hackborn | 48e7b45 | 2011-01-17 12:28:35 -0800 | [diff] [blame] | 228 | FragmentTransaction ft = manager.beginTransaction(); |
Dianne Hackborn | 7277820 | 2010-08-20 18:26:01 -0700 | [diff] [blame] | 229 | ft.add(this, tag); |
| 230 | ft.commit(); |
| 231 | } |
| 232 | |
Jeff Sharkey | 063f850 | 2015-08-13 16:57:29 -0700 | [diff] [blame] | 233 | /** {@hide} */ |
| 234 | public void showAllowingStateLoss(FragmentManager manager, String tag) { |
| 235 | mDismissed = false; |
| 236 | mShownByMe = true; |
| 237 | FragmentTransaction ft = manager.beginTransaction(); |
| 238 | ft.add(this, tag); |
| 239 | ft.commitAllowingStateLoss(); |
| 240 | } |
| 241 | |
Dianne Hackborn | 7277820 | 2010-08-20 18:26:01 -0700 | [diff] [blame] | 242 | /** |
| 243 | * Display the dialog, adding the fragment using an existing transaction |
| 244 | * and then committing the transaction. |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 245 | * @param transaction An existing transaction in which to add the fragment. |
| 246 | * @param tag The tag for this fragment, as per |
| 247 | * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}. |
| 248 | * @return Returns the identifier of the committed transaction, as per |
| 249 | * {@link FragmentTransaction#commit() FragmentTransaction.commit()}. |
| 250 | */ |
Dianne Hackborn | 7277820 | 2010-08-20 18:26:01 -0700 | [diff] [blame] | 251 | public int show(FragmentTransaction transaction, String tag) { |
Dianne Hackborn | bfe2e3f | 2011-03-01 16:40:54 -0800 | [diff] [blame] | 252 | mDismissed = false; |
| 253 | mShownByMe = true; |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 254 | transaction.add(this, tag); |
Dianne Hackborn | bfe2e3f | 2011-03-01 16:40:54 -0800 | [diff] [blame] | 255 | mViewDestroyed = false; |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 256 | mBackStackId = transaction.commit(); |
| 257 | return mBackStackId; |
| 258 | } |
| 259 | |
| 260 | /** |
| 261 | * Dismiss the fragment and its dialog. If the fragment was added to the |
| 262 | * back stack, all back stack state up to and including this entry will |
| 263 | * be popped. Otherwise, a new transaction will be committed to remove |
| 264 | * the fragment. |
| 265 | */ |
| 266 | public void dismiss() { |
Dianne Hackborn | ab36acb | 2010-11-05 14:12:11 -0700 | [diff] [blame] | 267 | dismissInternal(false); |
| 268 | } |
| 269 | |
Dianne Hackborn | cf407ad | 2011-03-11 13:17:57 -0800 | [diff] [blame] | 270 | /** |
| 271 | * Version of {@link #dismiss()} that uses |
| 272 | * {@link FragmentTransaction#commitAllowingStateLoss() |
| 273 | * FragmentTransaction.commitAllowingStateLoss()}. See linked |
| 274 | * documentation for further details. |
| 275 | */ |
| 276 | public void dismissAllowingStateLoss() { |
| 277 | dismissInternal(true); |
| 278 | } |
| 279 | |
Dianne Hackborn | ab36acb | 2010-11-05 14:12:11 -0700 | [diff] [blame] | 280 | void dismissInternal(boolean allowStateLoss) { |
Dianne Hackborn | bfe2e3f | 2011-03-01 16:40:54 -0800 | [diff] [blame] | 281 | if (mDismissed) { |
| 282 | return; |
| 283 | } |
| 284 | mDismissed = true; |
| 285 | mShownByMe = false; |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 286 | if (mDialog != null) { |
| 287 | mDialog.dismiss(); |
Dianne Hackborn | def1537 | 2010-08-15 12:43:52 -0700 | [diff] [blame] | 288 | mDialog = null; |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 289 | } |
Dianne Hackborn | bfe2e3f | 2011-03-01 16:40:54 -0800 | [diff] [blame] | 290 | mViewDestroyed = true; |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 291 | if (mBackStackId >= 0) { |
Dianne Hackborn | def1537 | 2010-08-15 12:43:52 -0700 | [diff] [blame] | 292 | getFragmentManager().popBackStack(mBackStackId, |
| 293 | FragmentManager.POP_BACK_STACK_INCLUSIVE); |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 294 | mBackStackId = -1; |
| 295 | } else { |
Dianne Hackborn | 48e7b45 | 2011-01-17 12:28:35 -0800 | [diff] [blame] | 296 | FragmentTransaction ft = getFragmentManager().beginTransaction(); |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 297 | ft.remove(this); |
Dianne Hackborn | 6908cd1 | 2010-11-08 15:11:16 -0800 | [diff] [blame] | 298 | if (allowStateLoss) { |
| 299 | ft.commitAllowingStateLoss(); |
| 300 | } else { |
| 301 | ft.commit(); |
| 302 | } |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 303 | } |
| 304 | } |
Dianne Hackborn | ab36acb | 2010-11-05 14:12:11 -0700 | [diff] [blame] | 305 | |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 306 | public Dialog getDialog() { |
| 307 | return mDialog; |
| 308 | } |
| 309 | |
| 310 | public int getTheme() { |
| 311 | return mTheme; |
| 312 | } |
| 313 | |
Dianne Hackborn | 7277820 | 2010-08-20 18:26:01 -0700 | [diff] [blame] | 314 | /** |
| 315 | * Control whether the shown Dialog is cancelable. Use this instead of |
| 316 | * directly calling {@link Dialog#setCancelable(boolean) |
| 317 | * Dialog.setCancelable(boolean)}, because DialogFragment needs to change |
| 318 | * its behavior based on this. |
| 319 | * |
| 320 | * @param cancelable If true, the dialog is cancelable. The default |
| 321 | * is true. |
| 322 | */ |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 323 | public void setCancelable(boolean cancelable) { |
| 324 | mCancelable = cancelable; |
| 325 | if (mDialog != null) mDialog.setCancelable(cancelable); |
| 326 | } |
| 327 | |
Dianne Hackborn | 7277820 | 2010-08-20 18:26:01 -0700 | [diff] [blame] | 328 | /** |
| 329 | * Return the current value of {@link #setCancelable(boolean)}. |
| 330 | */ |
Dianne Hackborn | 327fbd2 | 2011-01-17 14:38:50 -0800 | [diff] [blame] | 331 | public boolean isCancelable() { |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 332 | return mCancelable; |
| 333 | } |
| 334 | |
Dianne Hackborn | 7277820 | 2010-08-20 18:26:01 -0700 | [diff] [blame] | 335 | /** |
| 336 | * Controls whether this fragment should be shown in a dialog. If not |
| 337 | * set, no Dialog will be created in {@link #onActivityCreated(Bundle)}, |
| 338 | * and the fragment's view hierarchy will thus not be added to it. This |
| 339 | * allows you to instead use it as a normal fragment (embedded inside of |
| 340 | * its activity). |
| 341 | * |
| 342 | * <p>This is normally set for you based on whether the fragment is |
| 343 | * associated with a container view ID passed to |
| 344 | * {@link FragmentTransaction#add(int, Fragment) FragmentTransaction.add(int, Fragment)}. |
| 345 | * If the fragment was added with a container, setShowsDialog will be |
| 346 | * initialized to false; otherwise, it will be true. |
| 347 | * |
| 348 | * @param showsDialog If true, the fragment will be displayed in a Dialog. |
| 349 | * If false, no Dialog will be created and the fragment's view hierarchly |
| 350 | * left undisturbed. |
| 351 | */ |
| 352 | public void setShowsDialog(boolean showsDialog) { |
| 353 | mShowsDialog = showsDialog; |
| 354 | } |
| 355 | |
| 356 | /** |
| 357 | * Return the current value of {@link #setShowsDialog(boolean)}. |
| 358 | */ |
| 359 | public boolean getShowsDialog() { |
| 360 | return mShowsDialog; |
| 361 | } |
| 362 | |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 363 | @Override |
Adam Powell | 31479e3 | 2016-04-22 11:27:31 -0700 | [diff] [blame] | 364 | public void onAttach(Context context) { |
| 365 | super.onAttach(context); |
Dianne Hackborn | bfe2e3f | 2011-03-01 16:40:54 -0800 | [diff] [blame] | 366 | if (!mShownByMe) { |
| 367 | // If not explicitly shown through our API, take this as an |
| 368 | // indication that the dialog is no longer dismissed. |
| 369 | mDismissed = false; |
| 370 | } |
| 371 | } |
| 372 | |
| 373 | @Override |
| 374 | public void onDetach() { |
| 375 | super.onDetach(); |
| 376 | if (!mShownByMe && !mDismissed) { |
| 377 | // The fragment was not shown by a direct call here, it is not |
| 378 | // dismissed, and now it is being detached... well, okay, thou |
| 379 | // art now dismissed. Have fun. |
| 380 | mDismissed = true; |
| 381 | } |
| 382 | } |
| 383 | |
| 384 | @Override |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 385 | public void onCreate(Bundle savedInstanceState) { |
| 386 | super.onCreate(savedInstanceState); |
Dianne Hackborn | 7277820 | 2010-08-20 18:26:01 -0700 | [diff] [blame] | 387 | |
| 388 | mShowsDialog = mContainerId == 0; |
| 389 | |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 390 | if (savedInstanceState != null) { |
Dianne Hackborn | 7277820 | 2010-08-20 18:26:01 -0700 | [diff] [blame] | 391 | mStyle = savedInstanceState.getInt(SAVED_STYLE, STYLE_NORMAL); |
| 392 | mTheme = savedInstanceState.getInt(SAVED_THEME, 0); |
| 393 | mCancelable = savedInstanceState.getBoolean(SAVED_CANCELABLE, true); |
| 394 | mShowsDialog = savedInstanceState.getBoolean(SAVED_SHOWS_DIALOG, mShowsDialog); |
| 395 | mBackStackId = savedInstanceState.getInt(SAVED_BACK_STACK_ID, -1); |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 396 | } |
| 397 | } |
| 398 | |
Dianne Hackborn | 7187ccb | 2011-01-24 23:58:13 -0800 | [diff] [blame] | 399 | /** @hide */ |
| 400 | @Override |
| 401 | public LayoutInflater getLayoutInflater(Bundle savedInstanceState) { |
| 402 | if (!mShowsDialog) { |
| 403 | return super.getLayoutInflater(savedInstanceState); |
| 404 | } |
| 405 | |
| 406 | mDialog = onCreateDialog(savedInstanceState); |
Dianne Hackborn | 7187ccb | 2011-01-24 23:58:13 -0800 | [diff] [blame] | 407 | switch (mStyle) { |
| 408 | case STYLE_NO_INPUT: |
| 409 | mDialog.getWindow().addFlags( |
| 410 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | |
| 411 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); |
| 412 | // fall through... |
| 413 | case STYLE_NO_FRAME: |
| 414 | case STYLE_NO_TITLE: |
| 415 | mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); |
| 416 | } |
Dianne Hackborn | 25193d3 | 2011-03-01 15:01:30 -0800 | [diff] [blame] | 417 | if (mDialog != null) { |
| 418 | return (LayoutInflater)mDialog.getContext().getSystemService( |
| 419 | Context.LAYOUT_INFLATER_SERVICE); |
| 420 | } |
Todd Kennedy | a5fc6f0 | 2015-04-14 18:22:54 -0700 | [diff] [blame] | 421 | return (LayoutInflater) mHost.getContext().getSystemService( |
Dianne Hackborn | 7187ccb | 2011-01-24 23:58:13 -0800 | [diff] [blame] | 422 | Context.LAYOUT_INFLATER_SERVICE); |
| 423 | } |
| 424 | |
| 425 | /** |
| 426 | * Override to build your own custom Dialog container. This is typically |
| 427 | * used to show an AlertDialog instead of a generic Dialog; when doing so, |
| 428 | * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} does not need |
| 429 | * to be implemented since the AlertDialog takes care of its own content. |
| 430 | * |
| 431 | * <p>This method will be called after {@link #onCreate(Bundle)} and |
| 432 | * before {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}. The |
| 433 | * default implementation simply instantiates and returns a {@link Dialog} |
| 434 | * class. |
| 435 | * |
Dianne Hackborn | f812fee | 2011-01-25 14:54:29 -0800 | [diff] [blame] | 436 | * <p><em>Note: DialogFragment own the {@link Dialog#setOnCancelListener |
| 437 | * Dialog.setOnCancelListener} and {@link Dialog#setOnDismissListener |
| 438 | * Dialog.setOnDismissListener} callbacks. You must not set them yourself.</em> |
| 439 | * To find out about these events, override {@link #onCancel(DialogInterface)} |
| 440 | * and {@link #onDismiss(DialogInterface)}.</p> |
| 441 | * |
Dianne Hackborn | 7187ccb | 2011-01-24 23:58:13 -0800 | [diff] [blame] | 442 | * @param savedInstanceState The last saved instance state of the Fragment, |
| 443 | * or null if this is a freshly created Fragment. |
| 444 | * |
| 445 | * @return Return a new Dialog instance to be displayed by the Fragment. |
| 446 | */ |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 447 | public Dialog onCreateDialog(Bundle savedInstanceState) { |
| 448 | return new Dialog(getActivity(), getTheme()); |
| 449 | } |
| 450 | |
| 451 | public void onCancel(DialogInterface dialog) { |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 452 | } |
| 453 | |
| 454 | public void onDismiss(DialogInterface dialog) { |
Dianne Hackborn | bfe2e3f | 2011-03-01 16:40:54 -0800 | [diff] [blame] | 455 | if (!mViewDestroyed) { |
Dianne Hackborn | ab36acb | 2010-11-05 14:12:11 -0700 | [diff] [blame] | 456 | // Note: we need to use allowStateLoss, because the dialog |
| 457 | // dispatches this asynchronously so we can receive the call |
| 458 | // after the activity is paused. Worst case, when the user comes |
| 459 | // back to the activity they see the dialog again. |
| 460 | dismissInternal(true); |
Dianne Hackborn | def1537 | 2010-08-15 12:43:52 -0700 | [diff] [blame] | 461 | } |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 462 | } |
| 463 | |
| 464 | @Override |
| 465 | public void onActivityCreated(Bundle savedInstanceState) { |
| 466 | super.onActivityCreated(savedInstanceState); |
Dianne Hackborn | 7277820 | 2010-08-20 18:26:01 -0700 | [diff] [blame] | 467 | |
| 468 | if (!mShowsDialog) { |
| 469 | return; |
| 470 | } |
| 471 | |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 472 | View view = getView(); |
| 473 | if (view != null) { |
| 474 | if (view.getParent() != null) { |
Adam Powell | 31479e3 | 2016-04-22 11:27:31 -0700 | [diff] [blame] | 475 | throw new IllegalStateException( |
| 476 | "DialogFragment can not be attached to a container view"); |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 477 | } |
| 478 | mDialog.setContentView(view); |
| 479 | } |
Adam Powell | 31479e3 | 2016-04-22 11:27:31 -0700 | [diff] [blame] | 480 | final Activity activity = getActivity(); |
| 481 | if (activity != null) { |
| 482 | mDialog.setOwnerActivity(activity); |
| 483 | } |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 484 | mDialog.setCancelable(mCancelable); |
Dianne Hackborn | f812fee | 2011-01-25 14:54:29 -0800 | [diff] [blame] | 485 | if (!mDialog.takeCancelAndDismissListeners("DialogFragment", this, this)) { |
| 486 | throw new IllegalStateException( |
| 487 | "You can not set Dialog's OnCancelListener or OnDismissListener"); |
| 488 | } |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 489 | if (savedInstanceState != null) { |
| 490 | Bundle dialogState = savedInstanceState.getBundle(SAVED_DIALOG_STATE_TAG); |
| 491 | if (dialogState != null) { |
| 492 | mDialog.onRestoreInstanceState(dialogState); |
| 493 | } |
| 494 | } |
| 495 | } |
| 496 | |
| 497 | @Override |
| 498 | public void onStart() { |
| 499 | super.onStart(); |
Dianne Hackborn | def1537 | 2010-08-15 12:43:52 -0700 | [diff] [blame] | 500 | if (mDialog != null) { |
Dianne Hackborn | bfe2e3f | 2011-03-01 16:40:54 -0800 | [diff] [blame] | 501 | mViewDestroyed = false; |
Dianne Hackborn | def1537 | 2010-08-15 12:43:52 -0700 | [diff] [blame] | 502 | mDialog.show(); |
| 503 | } |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 504 | } |
| 505 | |
| 506 | @Override |
| 507 | public void onSaveInstanceState(Bundle outState) { |
| 508 | super.onSaveInstanceState(outState); |
| 509 | if (mDialog != null) { |
| 510 | Bundle dialogState = mDialog.onSaveInstanceState(); |
| 511 | if (dialogState != null) { |
| 512 | outState.putBundle(SAVED_DIALOG_STATE_TAG, dialogState); |
| 513 | } |
| 514 | } |
Dianne Hackborn | 7277820 | 2010-08-20 18:26:01 -0700 | [diff] [blame] | 515 | if (mStyle != STYLE_NORMAL) { |
| 516 | outState.putInt(SAVED_STYLE, mStyle); |
| 517 | } |
| 518 | if (mTheme != 0) { |
| 519 | outState.putInt(SAVED_THEME, mTheme); |
| 520 | } |
| 521 | if (!mCancelable) { |
| 522 | outState.putBoolean(SAVED_CANCELABLE, mCancelable); |
| 523 | } |
| 524 | if (!mShowsDialog) { |
| 525 | outState.putBoolean(SAVED_SHOWS_DIALOG, mShowsDialog); |
| 526 | } |
| 527 | if (mBackStackId != -1) { |
| 528 | outState.putInt(SAVED_BACK_STACK_ID, mBackStackId); |
| 529 | } |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 530 | } |
| 531 | |
| 532 | @Override |
| 533 | public void onStop() { |
| 534 | super.onStop(); |
Dianne Hackborn | def1537 | 2010-08-15 12:43:52 -0700 | [diff] [blame] | 535 | if (mDialog != null) { |
| 536 | mDialog.hide(); |
| 537 | } |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 538 | } |
| 539 | |
| 540 | /** |
Dianne Hackborn | def1537 | 2010-08-15 12:43:52 -0700 | [diff] [blame] | 541 | * Remove dialog. |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 542 | */ |
| 543 | @Override |
| 544 | public void onDestroyView() { |
| 545 | super.onDestroyView(); |
Dianne Hackborn | def1537 | 2010-08-15 12:43:52 -0700 | [diff] [blame] | 546 | if (mDialog != null) { |
| 547 | // Set removed here because this dismissal is just to hide |
| 548 | // the dialog -- we don't want this to cause the fragment to |
| 549 | // actually be removed. |
Dianne Hackborn | bfe2e3f | 2011-03-01 16:40:54 -0800 | [diff] [blame] | 550 | mViewDestroyed = true; |
Dianne Hackborn | def1537 | 2010-08-15 12:43:52 -0700 | [diff] [blame] | 551 | mDialog.dismiss(); |
| 552 | mDialog = null; |
| 553 | } |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 554 | } |
Dianne Hackborn | bfe2e3f | 2011-03-01 16:40:54 -0800 | [diff] [blame] | 555 | |
| 556 | @Override |
| 557 | public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { |
| 558 | super.dump(prefix, fd, writer, args); |
| 559 | writer.print(prefix); writer.println("DialogFragment:"); |
| 560 | writer.print(prefix); writer.print(" mStyle="); writer.print(mStyle); |
| 561 | writer.print(" mTheme=0x"); writer.println(Integer.toHexString(mTheme)); |
| 562 | writer.print(prefix); writer.print(" mCancelable="); writer.print(mCancelable); |
| 563 | writer.print(" mShowsDialog="); writer.print(mShowsDialog); |
| 564 | writer.print(" mBackStackId="); writer.println(mBackStackId); |
| 565 | writer.print(prefix); writer.print(" mDialog="); writer.println(mDialog); |
| 566 | writer.print(prefix); writer.print(" mViewDestroyed="); writer.print(mViewDestroyed); |
| 567 | writer.print(" mDismissed="); writer.print(mDismissed); |
| 568 | writer.print(" mShownByMe="); writer.println(mShownByMe); |
| 569 | } |
Dianne Hackborn | dd913a5 | 2010-07-22 12:17:04 -0700 | [diff] [blame] | 570 | } |