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