blob: 391f672823292784ea8b51fc2a86b61040c2c7c2 [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
/**
* A fragment that displays a dialog window, floating on top of its
* activity's window. This fragment contains a Dialog object, which it
* displays as appropriate based on the fragment's state. Control of
* the dialog (deciding when to show, hide, dismiss it) should be done through
* the API here, not with direct calls on the dialog.
*
* <p>Implementations should override this class and implement
* {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} to supply the
* content of the dialog. Alternatively, they can override
* {@link #onCreateDialog(Bundle)} to create an entirely custom dialog, such
* as an AlertDialog, with its own content.
*/
public class DialogFragment extends Fragment
implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
/**
* Style for {@link #DialogFragment(int, int)} constructor: a basic,
* normal dialog.
*/
public static final int STYLE_NORMAL = 0;
/**
* Style for {@link #DialogFragment(int, int)} constructor: don't include
* a title area.
*/
public static final int STYLE_NO_TITLE = 1;
/**
* Style for {@link #DialogFragment(int, int)} constructor: don't draw
* any frame at all; the view hierarchy returned by {@link #onCreateView}
* is entirely responsible for drawing the dialog.
*/
public static final int STYLE_NO_FRAME = 2;
/**
* Style for {@link #DialogFragment(int, int)} constructor: like
* {@link #STYLE_NO_FRAME}, but also disables all input to the dialog.
* The user can not touch it, and its window will not receive input focus.
*/
public static final int STYLE_NO_INPUT = 3;
private static final String SAVED_DIALOG_STATE_TAG = "android:savedDialogState";
private static final String SAVED_STYLE = "android:style";
private static final String SAVED_THEME = "android:theme";
private static final String SAVED_CANCELABLE = "android:cancelable";
private static final String SAVED_BACK_STACK_ID = "android:backStackId";
int mStyle = STYLE_NORMAL;
int mTheme = 0;
boolean mCancelable = true;
int mBackStackId = -1;
Dialog mDialog;
boolean mDestroyed;
public DialogFragment() {
}
/**
* Constructor to customize the basic appearance and behavior of the
* fragment's dialog. This can be used for some common dialog behaviors,
* taking care of selecting flags, theme, and other options for you. The
* same effect can be achieve by manually setting Dialog and Window
* attributes yourself.
*
* @param style Selects a standard style: may be {@link #STYLE_NORMAL},
* {@link #STYLE_NO_TITLE}, {@link #STYLE_NO_FRAME}, or
* {@link #STYLE_NO_INPUT}.
* @param theme Optional custom theme. If 0, an appropriate theme (based
* on the style) will be selected for you.
*/
public DialogFragment(int style, int theme) {
mStyle = style;
if (mStyle == STYLE_NO_FRAME || mStyle == STYLE_NO_INPUT) {
mTheme = android.R.style.Theme_Dialog_NoFrame;
}
if (theme != 0) {
mTheme = theme;
}
}
/**
* Display the dialog, adding the fragment to the given activity. This
* is a convenience for explicitly creating a transaction, adding the
* fragment to it with the given tag, and committing it. This does
* <em>not</em> add the transaction to the back stack. When the fragment
* is dismissed, a new transaction will be executed to remove it from
* the activity.
* @param activity The activity this fragment will be added to.
* @param tag The tag for this fragment, as per
* {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
*/
public void show(Activity activity, String tag) {
FragmentTransaction ft = activity.openFragmentTransaction();
ft.add(this, tag);
ft.commit();
}
/**
* Display the dialog, adding the fragment to the given activity using
* an existing transaction and then committing the transaction.
* @param activity The activity this fragment will be added to.
* @param transaction An existing transaction in which to add the fragment.
* @param tag The tag for this fragment, as per
* {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
* @return Returns the identifier of the committed transaction, as per
* {@link FragmentTransaction#commit() FragmentTransaction.commit()}.
*/
public int show(Activity activity, FragmentTransaction transaction, String tag) {
transaction.add(this, tag);
mBackStackId = transaction.commit();
return mBackStackId;
}
/**
* Dismiss the fragment and its dialog. If the fragment was added to the
* back stack, all back stack state up to and including this entry will
* be popped. Otherwise, a new transaction will be committed to remove
* the fragment.
*/
public void dismiss() {
if (mDialog != null) {
mDialog.dismiss();
}
if (mBackStackId >= 0) {
getActivity().popBackStack(mBackStackId, Activity.POP_BACK_STACK_INCLUSIVE);
mBackStackId = -1;
} else {
FragmentTransaction ft = getActivity().openFragmentTransaction();
ft.remove(this);
ft.commit();
}
}
public Dialog getDialog() {
return mDialog;
}
public int getTheme() {
return mTheme;
}
public void setCancelable(boolean cancelable) {
mCancelable = cancelable;
if (mDialog != null) mDialog.setCancelable(cancelable);
}
public boolean getCancelable() {
return mCancelable;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mStyle = savedInstanceState.getInt(SAVED_STYLE, mStyle);
mTheme = savedInstanceState.getInt(SAVED_THEME, mTheme);
mCancelable = savedInstanceState.getBoolean(SAVED_CANCELABLE, mCancelable);
mBackStackId = savedInstanceState.getInt(SAVED_BACK_STACK_ID, mBackStackId);
}
}
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new Dialog(getActivity(), getTheme());
}
public void onCancel(DialogInterface dialog) {
if (mBackStackId >= 0) {
// If this fragment is part of the back stack, then cancelling
// the dialog means popping off the back stack.
getActivity().popBackStack(mBackStackId, Activity.POP_BACK_STACK_INCLUSIVE);
mBackStackId = -1;
}
}
public void onDismiss(DialogInterface dialog) {
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mDialog = onCreateDialog(savedInstanceState);
mDestroyed = false;
switch (mStyle) {
case STYLE_NO_INPUT:
mDialog.getWindow().addFlags(
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
// fall through...
case STYLE_NO_FRAME:
case STYLE_NO_TITLE:
mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
}
View view = getView();
if (view != null) {
if (view.getParent() != null) {
throw new IllegalStateException("DialogFragment can not be attached to a container view");
}
mDialog.setContentView(view);
}
mDialog.setOwnerActivity(getActivity());
mDialog.setCancelable(mCancelable);
mDialog.setOnCancelListener(this);
mDialog.setOnDismissListener(this);
if (savedInstanceState != null) {
Bundle dialogState = savedInstanceState.getBundle(SAVED_DIALOG_STATE_TAG);
if (dialogState != null) {
mDialog.onRestoreInstanceState(dialogState);
}
}
}
@Override
public void onStart() {
super.onStart();
mDialog.show();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mDialog != null) {
Bundle dialogState = mDialog.onSaveInstanceState();
if (dialogState != null) {
outState.putBundle(SAVED_DIALOG_STATE_TAG, dialogState);
}
}
outState.putInt(SAVED_STYLE, mStyle);
outState.putInt(SAVED_THEME, mTheme);
outState.putBoolean(SAVED_CANCELABLE, mCancelable);
outState.putInt(SAVED_BACK_STACK_ID, mBackStackId);
}
@Override
public void onStop() {
super.onStop();
mDialog.hide();
}
/**
* Detach from list view.
*/
@Override
public void onDestroyView() {
super.onDestroyView();
mDestroyed = true;
mDialog.dismiss();
mDialog = null;
}
}