Java tutorial
/* * ================================================================================= * Copyright (C) 2012 Martin Albedinsky [Wolf-ITechnologies] * ================================================================================= * Licensed under the Apache License, Version 2.0 or later (further "License" only); * --------------------------------------------------------------------------------- * You may use this file only in compliance with the License. More details and copy * of this License you may obtain at * * http://www.apache.org/licenses/LICENSE-2.0 * * You can redistribute, modify or publish any part of the code written in this * file but as it is described in the License, the software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES or CONDITIONS OF * ANY KIND. * * See the License for the specific language governing permissions and limitations * under the License. * ================================================================================= */ package com.wit.and.dialog.internal; import android.app.Activity; import android.content.Context; import android.content.DialogInterface; import android.content.res.Resources; import android.content.res.TypedArray; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.RelativeLayout.LayoutParams; import com.wit.and.dialog.Dialog; import com.wit.and.dialog.IDialog; import com.wit.and.dialog.R; import com.wit.and.dialog.manage.DialogOptions; /** * <h4>Class Overview</h4> * <p> * Base implementation of customizable instance of {@link DialogFragment}. This implementation * allows to create native modal dialog window with title, body and buttons view. Handles base * logic of building dialog view, creation of dialog options from arguments, and also handles * setting of listeners and firing callbacks for them. See {@link Dialog} for common dialog * implementation or its derived classes for specific purpose implementations. * </p> * <h3>Basic call hierarchy:</h3> * <ul> * <li><b>1)</b> {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}</li> * <li><b>2)</b> {@link #onCreateDialogView(LayoutInflater, ViewGroup, Bundle)}</li> * <li><b>3)</b> {@link #onCreateTitleView(LayoutInflater, ViewGroup, Bundle)}</li> * <li><b>4)</b> {@link #onCreateBodyView(LayoutInflater, ViewGroup, Bundle)}</li> * <li><b>5)</b> {@link #onCreateButtonsView(com.wit.and.dialog.Dialog.Buttons, LayoutInflater, ViewGroup, Bundle)}</li> * <li><b>6)</b> {@link #onViewCreated(android.view.View, android.os.Bundle)}</li> * <li><b>7)</b> {@link #onInitViews(View)}</li> * <li><b>8)</b> {@link #onRestoreInstanceState(Bundle)}</li> * <li><b>9)</b> {@link #onStyleDialog(View, TypedArray)}</li> * <li><b>10)</b> {@link #onSetUpButtons(View)}</li> * <li><b>11)</b> {@link #onBindDialog(View, Bundle)}</li> * <li><b>12)</b> {@link #onActivityCreated(android.os.Bundle)}</li> * </ul> * * @author Martin Albedinsky * @see DialogFragment * @see com.wit.and.dialog.manage.DialogManager * @see com.wit.and.dialog.manage.DialogManager.IDialogFactory * @see com.wit.and.dialog.manage.DialogFactory */ public abstract class BaseDialog extends DialogFragment implements IDialog { /** * Constants ============================= */ /** * Bundle identifiers. */ /** */ protected static final String BUNDLE_OPTIONS = "com.wit.and.dialog.internal.BaseDialog.Bundle.Options"; protected static final String BUNDLE_ID = "com.wit.and.dialog.internal.BaseDialog.Bundle.ID"; protected static final String BUNDLE_PARENT_FRAGMENT_TAG = "com.wit.and.dialog.internal.BaseDialog.Bundle.ParentFragmentTag"; protected static final String BUNDLE_DIALOG_THEME = "com.wit.and.dialog.internal.BaseDialog.Bundle.DialogTheme"; /** * Log TAG. */ private static final String TAG = BaseDialog.class.getSimpleName(); /** * Indicates if debug private output trough log-cat is enabled. */ private static final boolean DEBUG = false; /** * Ids of the dialog views. */ /** * Indicates if logging for user output trough log-cat is enabled. */ // private static final boolean USER_LOG = true; private static final String BUNDLE_DISMISS_AFTER_RECREATE = "com.wit.and.dialog.internal.BaseDialog.Bundle.DismissAfterRecreate"; /** * Default identifiers for dialog view parts if we build dialog as relative * layout. */ /** */ private static final int DIALOG_VIEW_DEFAULT_ID = 0xaa3560f; private static final int TITLE_VIEW_DEFAULT_ID = 0xaa3561f; private static final int BODY_VIEW_DEFAULT_ID = 0xaa3562f; private static final int BUTTONS_VIEW_DEFAULT_ID = 0xaa3563f; /** * Enums ================================= */ /** * <p> * Dialog button. * </p> */ public enum DialogButton { /** * <p> * </p> */ POSITIVE, /** * <p> * </p> */ NEGATIVE, /** * <p> * </p> */ NEUTRAL, /** * <p> * </p> */ UNDEFINED } /** * <p> * </p> */ protected enum DialogLayoutCorrection { /** * <p> * </p> */ WITH_TITLE_AND_BUTTONS, /** * <p> * </p> */ WITH_TITLE, /** * <p> * </p> */ WITHOUT_TITLE, /** * <p> * </p> */ WITH_BUTTONS, /** * <p> * </p> */ WITHOUT_BUTTONS, /** * <p> * </p> */ WITHOUT_BOTH } /** * Static members ======================== */ /** * Members =============================== */ /** * Id of the dialog view. */ private int mDialogViewID = DIALOG_VIEW_DEFAULT_ID; /** * Id of the dialog to identify different instances of the dialogs. Default * set to random <code>-1</code>. */ private int mDialogID = -1; /** * FastOptions for this dialog. Like dialog title, message text and so on. */ private DialogOptions mOptions = null; /** * Actual correction for the dialog layout (only relative layout is * corrected). */ private DialogLayoutCorrection mLayoutCorrection = DialogLayoutCorrection.WITHOUT_BOTH; /** * Dialog theme. */ private int mDialogTheme = R.style.And_Theme_Dialog; /** * Layout inflater to later handling of inflating some changed layouts (like * buttons view). */ private LayoutInflater mInflater; /** * Tag of the fragment if this dialog was showed by dialog manger * initialized in the fragment. */ private String mParentFragmentTag = null; /** * Dialog view. */ private View mTitleView, mBodyView, mButtonsView, mDialogView; /** * Listeners ----------------------------- */ /** * Base dialog listener. */ private OnDialogListener iListener, iParentListener; /** * Dialog listener to receive cancel action. */ private OnDialogCancelListener iCancelListener, iParentCancelListener; /** * Arrays -------------------------------- */ /** * Booleans ------------------------------ */ /** * Indicates if the dialog view is already binded with dialog data. */ private boolean bViewBinded = false; /** * Indicates if the whole dialog view is already inflated. */ private boolean bViewInflated = false; /** * Indicates if the style should be parsed from the theme. */ private boolean bObtainOuterStyle = true; private boolean bCalled = false; /** * Constructors ========================== */ /** * <p> * </p> */ protected BaseDialog() { } /** * Methods =============================== */ /** * Public -------------------------------- */ /** */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Check bundle. if (savedInstanceState != null) { // Check if we should dismiss the dialog. if (savedInstanceState.getBoolean(BUNDLE_DISMISS_AFTER_RECREATE, false)) { this.dismissAllowingStateLoss(); } // Recreate dialog options. this.createOptions(savedInstanceState); // Restore dialog theme. if (savedInstanceState.containsKey(BUNDLE_DIALOG_THEME)) { mDialogTheme = savedInstanceState.getInt(BUNDLE_DIALOG_THEME); } } else { // Create dialog options from arguments. this.createOptions(this.getArguments()); /** * Dialog theme priority (from the highest to the lowest): * <ul> * <li>1) Custom theme (set by setStyle())</li> * <li>2) Custom theme (obtained form dialog options)</li> * <li>3) Custom theme (obtained from the activity/application theme)</li> * <li>4) Default theme</li> * </ul> */ if (bObtainOuterStyle) { // Style wasn't set by setStyle() method, check for theme in dialog options. mDialogTheme = mOptions.getDialogTheme(); if (mDialogTheme <= 0) { // Dialog options doesn't contains valid dialog theme. mDialogTheme = R.style.And_Theme_Dialog; // Get dialog theme from application theme. final Context context = getActivity(); final Resources.Theme theme = context.getTheme(); if (theme != null) { final TypedArray styleArray = theme .obtainStyledAttributes(new int[] { R.attr.dialogTheme }); if (styleArray != null) { mDialogTheme = styleArray.getResourceId(0, mDialogTheme); styleArray.recycle(); } } } } } // Set super theme. super.setStyle(Dialog.STYLE_NO_FRAME, mDialogTheme); this.bObtainOuterStyle = false; } /** */ @Override public final View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the dialog main view. View dialogView = onCreateDialogView(inflater, container, savedInstanceState); // Check valid dialog view. if (dialogView == null) { Log.e(TAG, "Dialog layout or dialog view not inflated correctly."); return super.onCreateView(inflater, container, savedInstanceState); } else { // Save inflater for later usage. this.mInflater = inflater; // Build dialog view. this.buildDialogView(dialogView, inflater, savedInstanceState); // Mark our created view. if (dialogView.getId() != View.NO_ID) { this.mDialogViewID = dialogView.getId(); } else { dialogView.setId(mDialogViewID); } // Return inflated dialog view. return dialogView; } } /** */ @Override public void onAttach(Activity activity) { super.onAttach(activity); // This resolves dialog listener even, when any listener wasn't set, but // if this dialog was showed in the context of activity (fragment) which is // currently implementing OnDialogListener or OnDialogNeutralListener or // OnDialogCancelListener, we take that as that activity (fragment) want // to be informed about dialog callback. Fragment fragment = getCurrentFragment(); // Resolve parent listener. boolean resolved = false; if (fragment != null) { if (fragment instanceof OnDialogListener) { this.iParentListener = (OnDialogListener) fragment; resolved = true; } if (fragment instanceof OnDialogCancelListener) { this.iParentCancelListener = (OnDialogCancelListener) fragment; resolved = true; } } if (!resolved && activity != null) { if (activity instanceof OnDialogListener) { this.iParentListener = (OnDialogListener) activity; } if (activity instanceof OnDialogCancelListener) { this.iParentCancelListener = (OnDialogCancelListener) activity; } } } @Override public final void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // Save dialog view. this.mDialogView = view.findViewById(mDialogViewID); // Inflate dialog view objects. this.performInitViews(); if (savedInstanceState != null) { // Recreate all dialog necessary data. performRestoreInstanceState(savedInstanceState); } // Update style. this.performStyleDialog(); // Reset layout correction and perform dialog binding. this.mLayoutCorrection = DialogLayoutCorrection.WITHOUT_BOTH; this.performBindDialog(savedInstanceState); } /** */ @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // Save dialog options data. this.fillBundleWithOptions(outState); // Save dialog tag and id. outState.putString(BUNDLE_PARENT_FRAGMENT_TAG, mParentFragmentTag); // Secure save the parent fragment tag. In some cases maybe the // onAttachToAdapter() called before onCreateView() where is the parent // tag restored in the onRestoreInstanceState(). getArguments().putString(BUNDLE_PARENT_FRAGMENT_TAG, mParentFragmentTag); // Save dialog id. outState.putInt(BUNDLE_ID, mDialogID); // Internal state. outState.putBoolean(BUNDLE_DISMISS_AFTER_RECREATE, mOptions.shouldDismissOnRestore()); // Save dialog theme. if (!bObtainOuterStyle) { outState.putInt(BUNDLE_DIALOG_THEME, mDialogTheme); } // Save persistent listeners. if (iListener != null && iListener instanceof OnDialogPersistentListener) { ((OnDialogPersistentListener) iListener).saveIntoState(outState); } if (iCancelListener != null && iCancelListener instanceof OnDialogPersistentListener) { ((OnDialogPersistentListener) iCancelListener).saveIntoState(outState); } } /** */ @Override public void cancel() { this.dismiss(); } /** * Getters + Setters --------------------- */ /** * <h5><i>public void setOnDialogListener(OnDialogListener * listener)</i></h5> * <p> * Registers a callback to be invoked when some of the dialog buttons * is being clicked. * </p> * * @param listener Listener callback. */ public void setOnDialogListener(OnDialogListener listener) { this.iListener = listener; } /** * <h5><i>public void setOnCancelListener(OnDialogCancelListener * listener)</i></h5> * <p> * Registers a callback to be invoked when the dialog is being canceled by * the user. * </p> * * @param listener Listener callback. */ public void setOnCancelListener(OnDialogCancelListener listener) { this.iCancelListener = listener; } /** */ @Override public final void setDialogID(int dialogID) { this.mDialogID = dialogID; } /** * <p> * Sets the tag of the parent fragment. * </p> * * @param tag Tag of the fragment which use the dialog manager to show this * dialog. */ public final void setParentFragmentTag(String tag) { this.mParentFragmentTag = tag; } /** * <p> * Returns the tag of the parent fragment. * </p> * * @return Parent fragment tag if this dialog was showed by * {@link com.wit.and.dialog.manage.DialogManager} from fragment context, * <code>null</code> otherwise. */ public final String getParentFragmentTag() { return mParentFragmentTag; } /** */ @Override public final int getDialogID() { return mDialogID; } /** * <p> * Returns actual dialog options with all dialog data (title, message, icon * resource id, ...). You can modify this data trough this options. * </p> */ public DialogOptions getOptions() { return (mOptions == null) ? getArguments().<DialogOptions>getParcelable(BUNDLE_OPTIONS) : mOptions; } /** * <p> * Returns whole dialog view in which are all dialog views (title, body, buttons) placed. * Note that this can be {@link android.widget.LinearLayout} or {@link android.widget.RelativeLayout}. * </p> * * @return Dialog view created inside {@link #onCreateDialogView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)}. */ public View getDialogView() { return mDialogView; } /** */ @Override public final void setStyle(int style, int theme) { super.setStyle(style, theme); this.mDialogTheme = theme; // Save state. this.bObtainOuterStyle = false; // Refresh dialog style. if (bViewBinded) { performStyleDialog(); } } /** * <p> * Sets dialog theme for this dialog. Theme will be applied only * to this dialog instance. * </p> * <p> * Theme should have as its parent set to {@link com.wit.and.dialog.R.style#And_Theme_Dialog} or related * (colored) dialog themes like {@link com.wit.and.dialog.R.style#And_Theme_Dialog_Lime}. * </p> * * @param theme Theme to apply. */ public final void setDialogTheme(int theme) { setStyle(Dialog.STYLE_NO_FRAME, theme); } /** * <p> * Returns current dialog theme resource id. * </p> * * @return Dialog theme. */ public int getDialogTheme() { return mDialogTheme; } /** */ @Override public void onCancel(DialogInterface dialog) { super.onCancel(dialog); // Invoke for listener. if (this.iCancelListener != null) { this.iCancelListener.onDialogCancelled(this); } // Invoke for parent listener. if (iParentCancelListener != null) { this.iParentCancelListener.onDialogCancelled(this); } } /** * Protected ----------------------------- */ /** * <h5><i>protected static Bundle createArguments(DialogOptions * options)</i></h5> * <p> * Creates dialog arguments from the given dialog options. * </p> * * @param options Dialog options from which will be arguments created. * @return Dialog arguments as Bundle. */ protected static Bundle createArguments(DialogOptions options) { Bundle args = new Bundle(); // Fill dialog arguments. if (options != null) { fillBundleWithOptions(args, options); } return args; } /** * */ protected final void performInitViews() { onInitViews(mDialogView); } /** * */ protected final void performSetUpButtons() { onSetUpButtons(mDialogView); } /** * <p> * Dispatches message that dialog layout should be corrected. Correction is performed only * in case that the given dialog view isn't instance of {@link LinearLayout}. * </p> * * @param requestedCorrection Requested correction to be applied to the dialog layout. * @param dialogView Currently inflated or actual dialog main view. */ protected final void dispatchCorrectLayout(DialogLayoutCorrection requestedCorrection, View dialogView) { // Correct only if main dialog view is not an RelativeLayout. if (!(dialogView instanceof LinearLayout)) { performCorrectLayout(requestedCorrection, dialogView); } } /** * <p> * Returns the layout inflater initialized when this dialog was created. * </p> * * @return Actual layout inflater. */ protected final LayoutInflater getLayoutInflater() { return mInflater; } /** * <p> * Returns actual merged layout correction if you call this after * <code>super.onCorrectRelativeLayout()</code> or last merged correction if * you call <code>super</code> after your custom layout correction. * </p> * * @return Merged layout correction state. */ protected final DialogLayoutCorrection getMergedLayoutCorrection() { return mLayoutCorrection; } /** * <p> * Checks if the dialog view is binded. * </p> * * @return True if the dialog view is already binded otherwise false. */ protected final boolean isViewBinded() { return bViewBinded; } /** * <p> * Returns flag indicating if dialog view is inflated. * </p> * * @return <code>True</code> if dialog view is already infalted, <code>false</code> otherwise. */ protected final boolean isViewInflated() { return bViewInflated; } /** * <p> * Returns the view depends on the given id. * </p> * <p> * <b>Note: </b> This can be called only in the {@link #onInitViews(View)} * method or after it. Earlier calling this method will return null. * </p> * * @param viewID Dialog view id. * @return The view which has the given id, or <code>null</code> if there is * no view with the given id. */ protected View findViewByID(int viewID) { return (mDialogView != null) ? mDialogView.findViewById(viewID) : null; } /** * <h5><i>protected View onCreateDialogView(LayoutInflater inflater, * ViewGroup container, Bundle savedInstanceState)</i></h5> * <p> * Create the dialog main view into which will be placed title, body and * buttons view. * </p> * * @param inflater Layout inflater. * @param container Inflated dialog main view. * @param savedInstanceState Saved state. * @return Inflated dialog view. */ protected View onCreateDialogView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { LinearLayout layout = new LinearLayout(inflater.getContext()); layout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)); layout.setOrientation(LinearLayout.VERTICAL); return layout; } /** * <p> * Performs invocation of styling methods to style dialog view. * {@link #onStyleDialog(android.view.View, android.content.res.TypedArray)} is * invoked immediately if parsed typed array is valid. * </p> */ protected final void performStyleDialog() { TypedArray parsedArray = getActivity().obtainStyledAttributes(mDialogTheme, R.styleable.And_Theme_Dialog); if (parsedArray != null) { // Style. onStyleDialog(mDialogView, parsedArray); parsedArray.recycle(); } } /** * <p> * Invoked when some of the buttons in the base dialog view was clicked. * </p> * * @param button Dialog button identifier. */ protected void onButtonClick(DialogButton button) { // Fire for listener. if (iListener != null) { iListener.onDialogButtonClick(this, button); } // Fire for parent listener. if (iParentListener != null) { iParentListener.onDialogButtonClick(this, button); } } /** * <p> * Corrects the dialog view (if is instance of <code>RelativeLayout</code>). * This actually place the dialog main views (title, body, buttons) into * correct place. If you decide override this for better dialog view * performance it is necessary to call <code>super</code> here. * </p> * * @param requestedCorrection Requested operation for correction in the dialog view as * relative layout. * @param dialogView Inflated dialog view. */ protected void onCorrectLayout(DialogLayoutCorrection requestedCorrection, View dialogView) { // Save call state. this.bCalled = true; // Perform only if we have valid body view. if (mBodyView == null || mLayoutCorrection == requestedCorrection || !(dialogView instanceof RelativeLayout)) return; // Create layout parameters for body and buttons view. RelativeLayout.LayoutParams bodyParams = new RelativeLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT); RelativeLayout.LayoutParams buttonsParams = new RelativeLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT); if (mBodyView.getLayoutParams() != null) { bodyParams = new RelativeLayout.LayoutParams(mBodyView.getLayoutParams()); } if (mButtonsView != null && mButtonsView.getLayoutParams() != null) { buttonsParams = new RelativeLayout.LayoutParams(mButtonsView.getLayoutParams()); } // Default merge. DialogLayoutCorrection helper = this.mLayoutCorrection; this.mLayoutCorrection = requestedCorrection; // Merge actual state of the relative layout correction. switch (requestedCorrection) { case WITH_TITLE_AND_BUTTONS: case WITHOUT_BOTH: break; case WITH_TITLE: if (helper == DialogLayoutCorrection.WITH_BUTTONS) { this.mLayoutCorrection = DialogLayoutCorrection.WITH_TITLE_AND_BUTTONS; } break; case WITHOUT_TITLE: if (helper == DialogLayoutCorrection.WITH_BUTTONS) { this.mLayoutCorrection = DialogLayoutCorrection.WITH_TITLE_AND_BUTTONS; } else if (helper == DialogLayoutCorrection.WITHOUT_BUTTONS) { this.mLayoutCorrection = DialogLayoutCorrection.WITHOUT_BOTH; } break; case WITH_BUTTONS: if (helper == DialogLayoutCorrection.WITH_TITLE_AND_BUTTONS) { return; } else if (helper == DialogLayoutCorrection.WITH_TITLE) { this.mLayoutCorrection = DialogLayoutCorrection.WITH_TITLE_AND_BUTTONS; } break; case WITHOUT_BUTTONS: if (helper == DialogLayoutCorrection.WITHOUT_TITLE) { this.mLayoutCorrection = DialogLayoutCorrection.WITHOUT_BOTH; } break; } // We just will set new parameters to the body view, which can depends // on the title or buttons view existence in the layout but in some // situations we don't have for example title view in the dialog view // (if the title text is empty). switch (mLayoutCorrection) { case WITH_TITLE_AND_BUTTONS: if (mTitleView != null) { bodyParams.addRule(RelativeLayout.BELOW, mTitleView.getId()); } buttonsParams.addRule(RelativeLayout.BELOW, mBodyView.getId()); break; case WITHOUT_TITLE: case WITH_BUTTONS: bodyParams.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE); buttonsParams.addRule(RelativeLayout.BELOW, mBodyView.getId()); break; case WITHOUT_BUTTONS: case WITH_TITLE: if (mTitleView != null) { bodyParams.addRule(RelativeLayout.BELOW, mTitleView.getId()); } break; case WITHOUT_BOTH: bodyParams.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE); break; } if (DEBUG) Log.d(TAG, "Dialog layout corrected to state " + mLayoutCorrection); // Set new parameters to the body view. mBodyView.setLayoutParams(bodyParams); // Set new parameters to the buttons view if we have it. if (mButtonsView != null) { mButtonsView.setLayoutParams(buttonsParams); } } /** * <p> * Adds the given inflated child view into inflated main dialog view. * </p> * * @param dialogView Inflated dialog view. * @param childView Inflated dialog child view. */ protected final void addView(View dialogView, View childView) { this.addView(dialogView, childView, null); } /** * <p> * Adds the given inflated child view into inflated main dialog view. * </p> * * @param dialogView Inflated dialog view. * @param childView Inflated dialog child view. * @param childParams Layout parameters to set to child view. */ protected final void addView(View dialogView, View childView, LayoutParams childParams) { // Check valid child view. if (childView == null) return; // Check dialog view layout instance. if (dialogView instanceof LinearLayout) { // Check for layout parameters. if (childParams == null) { ((LinearLayout) dialogView).addView(childView); } else { ((LinearLayout) dialogView).addView(childView, childParams); } } else if (dialogView instanceof RelativeLayout) { // Check for layout parameters. if (childParams == null) { ((RelativeLayout) dialogView).addView(childView); } else { ((RelativeLayout) dialogView).addView(childView, childParams); } } if (DEBUG) Log.d(TAG, "addView() id = " + childView.getId()); } /** * <p> * Restores the dialog from the saved bundle. Invoked between * {@link #onInitViews(android.view.View)} and * {@link #onStyleDialog(android.view.View, android.content.res.TypedArray)}. * </p> * * @param savedInstanceState Saved dialog instance state. * @see #onBindDialog(View, Bundle) */ protected void onRestoreInstanceState(Bundle savedInstanceState) { // Save call state. this.bCalled = true; // Restore dialog tag id. this.mParentFragmentTag = savedInstanceState.getString(BUNDLE_PARENT_FRAGMENT_TAG); this.mDialogID = savedInstanceState.getInt(BUNDLE_ID); // Restore persistent listeners. if (savedInstanceState.containsKey(OnDialogPersistentListener.BUNDLE_TAG)) { this.iListener = OnDialogPersistentListener.fromSavedState(savedInstanceState); } if (savedInstanceState.containsKey(OnDialogPersistentCancelListener.BUNDLE_TAG)) { this.iCancelListener = OnDialogPersistentCancelListener.fromSavedState(savedInstanceState); } } /** * <p> * Returns the current fragment which is showed under this dialog and is * also responsible for the showing of this dialog. * </p> * * @return Current visible fragment or <code>null</code> if the parent * fragment tag isn't valid. */ protected Fragment getCurrentFragment() { if (mParentFragmentTag == null && getArguments() != null) { // Try to get tag from the arguments. mParentFragmentTag = getArguments().getString(BUNDLE_PARENT_FRAGMENT_TAG); } return this.getFragmentManager().findFragmentByTag(this.mParentFragmentTag); } /** * <p> * Checks if dialog should be dismissed. * </p> */ protected final void checkIfDismiss() { // Check if we should let this dialog showing. if (!mOptions.shouldRemain()) { this.dismiss(); } } /** * Private ------------------------------- */ /** * Fills the given bundle with the given dialog options data to save them * because of later usage to recreate them after it was this dialog * destroyed (after orientation change). * * @param bundle The bundle to fill with dialog options. * @param options Dialog options to save. */ private static void fillBundleWithOptions(Bundle bundle, DialogOptions options) { // Dialog options object implements parcel-able so we can just put it // into bundle and then restore it. bundle.putParcelable(BUNDLE_OPTIONS, options); } /** * Builds whole dialog view. */ private void buildDialogView(View dialogView, LayoutInflater inflater, Bundle savedInstanceState) { if (((ViewGroup) dialogView).getChildCount() > 0) // Do not build dialog view if it is already binded with some views. return; // Check if the main dialog view doesn't contain any view. if (dialogView instanceof LinearLayout) { // Build dialog view as linear layout. this.buildAsLinearLayout(dialogView, inflater, (ViewGroup) dialogView, savedInstanceState); } else if (dialogView instanceof RelativeLayout) { // Build dialog view as relative layout. this.buildAsRelativeLayout(dialogView, inflater, (ViewGroup) dialogView, savedInstanceState); } // Save state. this.bViewInflated = true; } /** * Builds dialog view as linear layout. This only adds the base dialog views * (title, body, buttons) in correct order into dialog view. */ private void buildAsLinearLayout(View dialogView, LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Build the dialog from the base dialog views (title, body, buttons). this.addView(dialogView, this.mTitleView = performCreateTitleView(inflater, container, savedInstanceState)); this.addView(dialogView, this.mBodyView = performCreateBodyView(inflater, container, savedInstanceState)); this.addView(dialogView, this.mButtonsView = performCreateButtonsView(mOptions.getButtonsType(), inflater, container, savedInstanceState)); // Check valid body view. if (mBodyView == null) throw new IllegalStateException("Missing body view. Dialog must have at least body view."); } /** * Builds dialog view as relative layout. This adds the base dialog views * (title, body, buttons) in correct order into dialog view. Adding views * into relative layout is a little bit more complicated as into linear * layout so we need to create some layout parameters and use vies * identifiers to place them correctly. */ private void buildAsRelativeLayout(View dialogView, LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { this.mTitleView = performCreateTitleView(inflater, container, savedInstanceState); this.mButtonsView = performCreateButtonsView(mOptions.getButtonsType(), inflater, container, savedInstanceState); this.mBodyView = performCreateBodyView(inflater, container, savedInstanceState); // Check valid body view. if (mBodyView == null) throw new IllegalStateException("Missing body view. Dialog must have at least body view."); // Check if the view has the id to identify it in relative layout // hierarchy. if (mTitleView != null && mTitleView.getId() == View.NO_ID) { mTitleView.setId(TITLE_VIEW_DEFAULT_ID); } if (mBodyView.getId() == View.NO_ID) { mBodyView.setId(BODY_VIEW_DEFAULT_ID); } if (mButtonsView != null && mButtonsView.getId() == View.NO_ID) { mButtonsView.setId(BUTTONS_VIEW_DEFAULT_ID); } // Correct layout parameters. performCorrectLayout(DialogLayoutCorrection.WITH_TITLE_AND_BUTTONS, dialogView); // Build dialog view. this.addView(dialogView, mTitleView); this.addView(dialogView, mButtonsView); this.addView(dialogView, mBodyView); } /** * Creates new dialog options from the saved bundle or dialog arguments. * * @param arguments Dialog arguments. */ private void createOptions(Bundle arguments) { // Check valid arguments. if (arguments == null) return; // Restore data. if (arguments.getParcelable(BUNDLE_OPTIONS) != null) { // Dialog options object implements parcelable so we can just // restore it as it was saved in the bundle. this.mOptions = arguments.getParcelable(BUNDLE_OPTIONS); } } /** * Fills the given bundle with the actual dialog options data to save them * because of later usage to recreate them after it was this dialog * destroyed (after orientation change). * * @param bundle The bundle to fill with dialog options. */ private void fillBundleWithOptions(Bundle bundle) { fillBundleWithOptions(bundle, this.mOptions); } /** * Performs dialog binding. * * @param savedInstanceState Saved dialog instance state. */ private void performBindDialog(Bundle savedInstanceState) { // Bind dialog buttons and set its listeners. performSetUpButtons(); // Bind with content. onBindDialog(mDialogView, savedInstanceState); // Save state. this.bViewBinded = true; } /** * Performs initialization of title view. */ private View performCreateTitleView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return onCreateTitleView(inflater, container, savedInstanceState); } /** * Performs initialization of body view. */ private View performCreateBodyView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return onCreateBodyView(inflater, container, savedInstanceState); } /** * Performs initialization of buttons view. */ private View performCreateButtonsView(Dialog.Buttons buttonsType, LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return onCreateButtonsView(buttonsType, inflater, container, savedInstanceState); } /** * Performs restoring of dialog saved state. * * @param savedState Dialog saved state. */ private void performRestoreInstanceState(Bundle savedState) { this.bCalled = false; onRestoreInstanceState(savedState); if (!bCalled) { throw new IllegalStateException( "DialogFragment " + this + " did not call trough to super.onRestoreInstanceState()"); } } /** * Performs correction of dialog layout. * * @param correction Requested correction. * @param dialogView Current dialog view. */ private void performCorrectLayout(DialogLayoutCorrection correction, View dialogView) { this.bCalled = false; onCorrectLayout(correction, dialogView); if (!bCalled) { throw new IllegalStateException( "DialogFragment " + this + " did not call trough to super.onCorrectLayout()"); } } /** * Abstract methods ---------------------- */ /** * <h5><i>protected View onCreateTitleView(LayoutInflater inflater, * ViewGroup container, Bundle savedInstanceState)</i></h5> * <p> * Invoked to create title view. Created view should contain only views specific for dialog title. * </p> * <p> * <b>Note: </b>This method is called only if the inflated main dialog view * doesn't contain any views. * </p> * * @param inflater Valid layout inflater. * @param container Inflated dialog main layout. * @param savedInstanceState Saved dialog instance state. May be <code>null</code>. * @return Inflated dialog title view. */ protected abstract View onCreateTitleView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState); /** * <h5><i>protected View onCreateBodyView(LayoutInflater inflater, ViewGroup * container, Bundle savedInstanceState)</i></h5> * <p> * Invoked to create body view. Created view should contain only views specific for dialog body, like * just simple {@link android.widget.TextView} or more complex layout. * </p> * <p> * <b>Note: </b>This method is called only if the inflated main dialog view * doesn't contain any views. * </p> * * @param inflater Valid layout inflater. * @param container Inflated dialog main layout. * @param savedInstanceState Saved dialog instance state. May be <code>null</code>. * @return Inflated dialog body view. */ protected abstract View onCreateBodyView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState); /** * <h5><i>protected View onCreateButtonsView(Buttons buttonsType, * LayoutInflater inflater, ViewGroup container, Bundle * savedInstanceState)</i></h5> * <p> * Invoked to create buttons view. Created view should contain only views specific for dialog title like * some buttons with dividers. * </p> * <p> * <b>Note: </b>This method is called only if the inflated main dialog view * doesn't contain any views. * </p> * * @param buttonsType Type of buttons to show in dialog. * @param inflater Valid layout inflater. * @param container Inflated dialog main layout. * @param savedInstanceState Saved dialog instance state. May be <code>null</code>. * @return Inflated dialog buttons view. */ protected abstract View onCreateButtonsView(Dialog.Buttons buttonsType, LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState); /** * <p> * Invoked to initialize dialog views. * </p> * * @param dialogView Inflated dialog view. */ protected abstract void onInitViews(View dialogView); /** * <p> * Invoked to set up buttons. Dialog listeners should be assigned to buttons click here. * </p> * * @param dialogView Inflated dialog view. */ protected abstract void onSetUpButtons(View dialogView); /** * <h5><i>protected void onBindDialog(View dialogView, Bundle * savedInstanceState)</i></h5> * <p> * Invoked to bind dialog view with data from dialog options. * </p> * * @param dialogView Inflated dialog view. * @param savedInstanceState Saved instance state. * @see #getOptions() */ protected abstract void onBindDialog(View dialogView, Bundle savedInstanceState); /** * Invoked to apply style to dialog view. Note, that here passed typed array * will be automatically recycled after successful calls of this method inside * all derived classes, so don't recycle it. * </p> * * @param dialogView Inflated dialog view. * @param parsedArray Parsed dialog style typed array. Can be immediately used. */ protected abstract void onStyleDialog(View dialogView, TypedArray parsedArray); /** * Inner classes ========================= */ /** * Interface ============================= */ }