Java tutorial
/* * ================================================================================= * Copyright (C) 2013 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.manage; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.util.Log; import com.wit.and.dialog.EditDialog; import com.wit.and.dialog.IDialog; import com.wit.and.dialog.IEditDialog; import com.wit.and.dialog.IListDialog; import com.wit.and.dialog.ILoginDialog; import com.wit.and.dialog.ListDialog; import com.wit.and.dialog.LoginDialog; import com.wit.and.dialog.internal.BaseDialog; import java.util.ArrayList; import java.util.List; /** * <h4>Class Overview</h4> * <p> * Manager used to handle showing and hiding of dialog fragments in the fragment * activity or fragment. Cooperates with the {@link IDialogFactory } which * provides instances of dialogs to show. * </p> * * @author Martin Albedinsky * @see DialogFactory * @see com.wit.and.dialog.Dialog */ public class DialogManager { /** * Constants ============================= */ /** * <p> * Default tag for the showing on dialog fragments. * </p> */ public static final String DIALOG_FRAGMENT_TAG = "com.wit.and.dialog.manage.DialogManager.DialogFragment.TAG"; /** * Log TAG. */ private static final String TAG = DialogManager.class.getSimpleName(); /** * Indicates if debug private output trough log-cat is enabled. */ private static final boolean DEBUG = true; /** * Indicates if logging for user output trough log-cat is enabled. */ private static final boolean USER_LOG = true; /** * Enums ================================= */ /** * Static members ======================== */ /** * Members =============================== */ /** * Fragment manager to handle showing dialog fragments. */ private FragmentManager mFragmentManager = null; /** * Dialog factory to handle obtaining of dialog fragment instances to show. */ private IDialogFactory mDialogFactory = null; /** * Tag of the parent fragment in case of dialog manager used in fragment. */ private String mParentFragmentTag = null; /** * Listeners ----------------------------- */ /** * Listener to handle callback when the showed dialogs were recreated. */ private OnDialogRestoreListener iRestoreListener = null; /** * Arrays -------------------------------- */ /** * Booleans ------------------------------ */ /** * */ private boolean bDialogsRestoreCheckPerformed = false; /** * Constructors ========================== */ /** * <p> * Same as * {@link #DialogManager(FragmentManager, IDialogFactory)}, but with not * initialized factory. * </p> * * @param fragmentManager Support fragment manager. */ public DialogManager(FragmentManager fragmentManager) { this(fragmentManager, null); } /** * <p> * Constructs the manager to handle dialog fragments. * </p> * <p> * <b>Note: </b>always use {@link FragmentManager} provided by * {@link FragmentActivity}. * </p> * <p> * <b>DialogManager parent:</b> * <ul> * <li>FragmentActivity: * <code>yourActivity.getSupportFragmentManager()</code></li> * <li>Fragment: <code>yourFragment.getFragmentManager()</code></li> * </ul> * </p> * * @param fragmentManager Support fragment manager. * @param factory Dialog factory to provide dialog fragment instances. */ public DialogManager(FragmentManager fragmentManager, IDialogFactory factory) { this.mFragmentManager = fragmentManager; this.mDialogFactory = factory; } /** * Methods =============================== */ /** * Public -------------------------------- */ /** * <p> * Same as {@link #showDialog(int, com.wit.and.dialog.manage.DialogManager.ListenerOptions)} * with empty listeners. * </p> * * @param dialogID Dialog id. * @return <code>True</code> if dialog was currently showed, * <code>false</code> if there is currently showing dialog with the * same tag. * @throws IllegalStateException */ public final boolean showDialog(int dialogID) { return showDialog(dialogID, new ListenerOptions()); } /** * <p> * Same as {@link #showDialog(int, DialogOptions, com.wit.and.dialog.manage.DialogManager.ListenerOptions)} * with empty listeners. * </p> * * @param dialogID Dialog id. * @param options Dialog options. * @return <code>True</code> if dialog was currently showed, * <code>false</code> if there is currently showing dialog with the * same tag. * @throws IllegalStateException */ public final boolean showDialog(int dialogID, DialogOptions options) { return showDialog(dialogID, options, new ListenerOptions()); } /** * <p> * Same as {@link #showDialog(int, DialogOptions, com.wit.and.dialog.manage.DialogManager.ListenerOptions)} * with no options. * </p> * * @param dialogID Dialog id. * @param listeners Callback to be invoked when specific dialog action occurs. * @return <code>True</code> if dialog was currently showed, * <code>false</code> if there is currently showing dialog with the * same tag. * @throws IllegalStateException */ public final boolean showDialog(int dialogID, ListenerOptions listeners) { return showDialog(dialogID, null, listeners); } /** * <p> * Shows dialog fragment mapped to the given <var>dialogID</var> in the current * dialog factory. Given options will be passed to that factory and instance of * requested dialog will be obtained with dialog tag from it. Also the given * listeners will be assigned to that dialog instance. * </p> * * @param dialogID Id of dialog to show. Id should be provided by {@link IDialogFactory} * @param options Dialog options. * @param listeners Callback to be invoked when specific dialog action occurs. * @return <code>True</code> if dialog was currently showed, * <code>false</code> if there is currently showing dialog with the * same tag. * @throws IllegalStateException */ public final boolean showDialog(int dialogID, DialogOptions options, ListenerOptions listeners) { return this.performShowFactoryDialog(dialogID, options, listeners); } /** * <p> * Same as {@link #showDialog(android.support.v4.app.DialogFragment, com.wit.and.dialog.manage.DialogManager.ListenerOptions)} * with empty listeners. * </p> * * @param dialog Dialog to show. * @return <code>True</code> if dialog was currently showed, * <code>false</code> if there is currently showing dialog with the * same tag. */ public final boolean showDialog(DialogFragment dialog) { return this.showDialog(dialog, new ListenerOptions()); } /** * <p> * Same as {@link #showDialog(android.support.v4.app.DialogFragment, com.wit.and.dialog.manage.DialogManager.ListenerOptions, String)}, * where {@link #DIALOG_FRAGMENT_TAG} will be used as dialog tag. * </p> * * @param dialog Dialog to show. * @param listeners Set of listeners to assign to the given dialog. * @return <code>True</code> if dialog was currently showed, * <code>false</code> if there is currently showing dialog with the * same tag. */ public final boolean showDialog(DialogFragment dialog, ListenerOptions listeners) { return this.showDialog(dialog, listeners, DIALOG_FRAGMENT_TAG); } /** * <p> * Same as {@link #showDialog(android.support.v4.app.DialogFragment, com.wit.and.dialog.manage.DialogManager.ListenerOptions, String)} * with empty listeners. * </p> * * @param dialog Dialog to show. * @param dialogTag Tag under which should be the given dialog showed. * @return <code>True</code> if dialog was currently showed, * <code>false</code> if there is currently showing dialog with the * same tag. */ public final boolean showDialog(DialogFragment dialog, String dialogTag) { return this.showDialog(dialog, new ListenerOptions(), dialogTag); } /** * <p> * Shows the given dialog fragment under the given dialog tag. Also the given * listeners will be assigned to that dialog instance. * </p> * * @param dialog Dialog to show. * @param listeners Set of listeners to assign to the given dialog. * @param dialogTag Tag under which should be the given dialog showed. * @return <code>True</code> if dialog was currently showed, * <code>false</code> if there is currently showing dialog with the * same tag. */ public final boolean showDialog(DialogFragment dialog, ListenerOptions listeners, String dialogTag) { return this.performShowDialog(dialog, listeners, dialogTag); } /** * <p> * Dismiss dialog fragment by the given id. * Same as {@link #dismissDialog(String)}, where dialog tag is obtained * from current dialog factory. * </p> * * @param dialogID Id from the {@link DialogFactory} responsible for dialog. * @return <code>True</code> if there is some dialog fragment active in the fragment * manager under the given <var>dialogID</var> and was successfully dismissed, * <code>false</code> otherwise. * @throws java.lang.IllegalStateException * @see #dismissDialog(android.support.v4.app.DialogFragment) * @see #dismissDialog(String) */ public final boolean dismissDialog(int dialogID) { if (mDialogFactory == null) throw new IllegalStateException( "No factory to provide dialog tag for dialog with id(" + dialogID + ")"); return dismissDialog(mDialogFactory.getDialogTag(dialogID)); } /** * <p> * Dismiss dialog fragment by the given tag. * Same as {@link #dismissDialog(android.support.v4.app.DialogFragment)}. * </p> * * @param dialogTag Tag under which was dialog displayed. * @return <code>True</code> if there is some dialog fragment visible in the fragment * manager under the given <var>dialogTag</var> and was successfully dismissed, * <code>false</code> otherwise. * @see #dismissDialog(android.support.v4.app.DialogFragment) * @see #dismissDialog(int) */ public final boolean dismissDialog(String dialogTag) { Fragment dialog = mFragmentManager.findFragmentByTag(dialogTag == null ? DIALOG_FRAGMENT_TAG : dialogTag); return dialog != null && dialog instanceof DialogFragment && dismissDialog((DialogFragment) dialog); } /** * <p> * Dismiss the given dialog fragment. * </p> * * @param dialog Dialog fragment to dismiss. * @return <code>True</code> if dismissing succeed, <code>false</code> otherwise. * @see #dismissDialog(int) * @see #dismissDialog(String) */ public final boolean dismissDialog(DialogFragment dialog) { // Check dialog. if (dialog != null && dialog.isVisible()) { // Try to dismiss dialog. try { dialog.dismissAllowingStateLoss(); return true; } catch (IllegalStateException e) { e.printStackTrace(); } } return false; } /** * <p> * </p> */ public final boolean dispatchParentRestored() { // Check for restored dialog fragments. boolean restored = !bDialogsRestoreCheckPerformed && checkRestoredDialogs(); // Dispatch. onParentRestored(); return restored; } /** * Getters + Setters --------------------- */ /** * <p> * Sets the fragment manager to handle showing dialog fragments. * </p> * <b>Note: </b>always use {@link FragmentManager} provided by fragment * activity.</p> * <p> * <b>DialogManager parent:</b> * <ul> * <li>FragmentActivity: * <code>yourActivity.getSupportFragmentManager()</code></li> * <li>Fragment: <code>yourFragment.getFragmentManager()</code></li> * </ul> * </p> * * @param fragmentManager Support fragment manager. */ public final void setFragmentManager(FragmentManager fragmentManager) { this.mFragmentManager = fragmentManager; } /** * <p> * Sets dialogs factory which will provides the dialog fragment instances * for this manager. * </p> * * @param factory Factory used when this manager shows the dialog depends on the * <var>id</var>. */ public final void setDialogFactory(IDialogFactory factory) { this.mDialogFactory = factory; } /** * <p> * Register listener to handle callback to be invoked when a dialog * fragment is recreated. * </p> * * @param listener Listener callback. */ public final void setOnDialogRestoreListener(OnDialogRestoreListener listener) { this.iRestoreListener = listener; } /** * <p> * Returns dialog factory of this manager. * </p> * * @return Dialog factory or <code>null</code> if there wasn't set dialog * factory yet. */ public final IDialogFactory getDialogFactory() { return mDialogFactory; } /** * <p> * Sets the tag of the parent fragment. * </p> * * @param tag Tag of the fragment which use this dialog manager. */ public final void setParentFragmentTag(String tag) { this.mParentFragmentTag = tag; } /** * <p> * Same as {@link #getDialog(String)}. Dialog tag is obtained from the dialog factory. * </p> * * @param dialogID Dialog id from the correspond {@link DialogFactory} which was used to show requested dialog. * @return Dialog fragment if there is some in the fragment manager with resolved tag or <code>null</code> if not. */ public DialogFragment getDialog(int dialogID) { if (mDialogFactory == null) throw new IllegalStateException( "No factory to provide dialog tag for dialog with id(" + dialogID + ")"); return getDialog(mDialogFactory.getDialogTag(dialogID)); } /** * <p> * Returns dialog fragment with the given tag. * </p> * * @param dialogTAG Dialog tag under which was dialog showed. * @return Dialog fragment if there is some in the fragment manager with given tag or <code>null</code> if not. */ public DialogFragment getDialog(String dialogTAG) { return (DialogFragment) mFragmentManager.findFragmentByTag(dialogTAG); } /** * <p> * Returns tag of top (last showed) visible dialog fragment. * </p> * * @return Tag of top dialog fragment. * @see #getTopVisibleDialog() * @see #getVisibleDialogs() */ public String getTopVisibleDialogTag() { List<DialogFragment> visible = getVisibleDialogs(); return (visible.size() > 0) ? visible.get(visible.size() - 1).getTag() : null; } /** * <p> * Returns top visible dialog fragment. * </p> * * @return Dialog fragment which is currently at the top of visible dialogs * or <code>null</code> if there are no currently visible any dialogs * @see #getTopVisibleDialogTag() */ public DialogFragment getTopVisibleDialog() { List<DialogFragment> visible = getVisibleDialogs(); return (visible.size() > 0) ? visible.get(visible.size() - 1) : null; } /** * <p> * </p> * * @return */ public List<DialogFragment> getVisibleDialogs() { List<DialogFragment> dialogs = getDialogs(); List<DialogFragment> visible = new ArrayList<DialogFragment>(); if (dialogs.size() > 0) { for (DialogFragment dialog : dialogs) { if (dialog.isVisible()) { visible.add(dialog); } } } return visible; } /** * <p> * </p> * * @return */ public List<DialogFragment> getDialogs() { List<DialogFragment> dialogs = new ArrayList<DialogFragment>(); if (mFragmentManager != null) { List<Fragment> fragments = mFragmentManager.getFragments(); if (fragments != null) { // Find the dialog fragments between current fragments. for (Fragment fragment : fragments) { if (fragment != null && fragment instanceof DialogFragment) { dialogs.add((DialogFragment) fragment); } } } } return dialogs; } /** * Protected ----------------------------- */ /** * <p> * </p> */ protected void onParentRestored() { } /** * <p> * Invoked to resolve given listeners for the given dialog. * </p> * * @param dialog Dialog to which should be given listeners assigned. * @param listeners Set of listeners to assign to the given dialog. */ protected void onResolveListeners(DialogFragment dialog, ListenerOptions listeners) { } /** * <p> * Returns current fragment manager. * </p> * * @return Fragment manager. * @see #setFragmentManager(android.support.v4.app.FragmentManager) * @see #DialogManager(android.support.v4.app.FragmentManager) * @see #DialogManager(android.support.v4.app.FragmentManager, com.wit.and.dialog.manage.DialogManager.IDialogFactory) */ protected final FragmentManager getFragmentManager() { return mFragmentManager; } /** * <p> * Invoked to show the given dialog fragment under the given tag. * </p> * * @param dialog Dialog fragment to show. * @param dialogTag Tag under which should be the given dialog showed. * @return <code>True</code> if dialog was currently showed, * <code>false</code> if there is currently showing dialog with the * same tag. */ protected boolean onShowDialog(DialogFragment dialog, String dialogTag) { // Check valid dialog. if (dialog == null) return false; // Set parent fragment tag to be possible resolve implicit listener as // fragment. if (dialog instanceof BaseDialog) { ((BaseDialog) dialog).setParentFragmentTag(mParentFragmentTag); } // Correct dialog tag. dialogTag = (dialogTag == null) ? DIALOG_FRAGMENT_TAG : dialogTag; if (DEBUG) Log.d(TAG, "showDialog(tag('" + dialogTag + "'))"); // Check for existing dialog fragment. DialogFragment currentDialog = (DialogFragment) mFragmentManager.findFragmentByTag(dialogTag); if (currentDialog != null) { if (USER_LOG) Log.i(TAG, String.format("Request to show dialog with tag '" + dialogTag + "' failed. Dialog with this tag is already being showing. Set another tag or dismiss the previous dialog with same tag.")); return false; } // Show dialog. dialog.show(mFragmentManager.beginTransaction(), dialogTag); return true; } /** * Resolves the setting of the given listeners to the given dialog. Given * dialog must be instanceof {@link BaseDialog} to be listeners successfully * resolved and set. * * @param dialog Dialog fragment. * @param listeners Set of listeners to assign to the given dialog. * @return Given dialog instance with listeners set. */ static <D extends DialogFragment> D resolveListeners(D dialog, ListenerOptions listeners) { if (!listeners.isEmpty()) { if (dialog instanceof BaseDialog) { if (listeners.dialogListener != null) { ((BaseDialog) dialog).setOnDialogListener(listeners.dialogListener); } if (listeners.cancelListener != null) { ((BaseDialog) dialog).setOnCancelListener(listeners.cancelListener); } // Others specific dialog listeners. if (listeners.listener != null) { if (dialog instanceof ListDialog && listeners.listener instanceof IListDialog.OnListListener) { ((ListDialog) dialog) .setOnListDialogListener(listeners.<IListDialog.OnListListener>getListener()); } else if (dialog instanceof LoginDialog && listeners.listener instanceof ILoginDialog.OnAuthenticationListener) { ((LoginDialog) dialog).setOnAuthenticationListener( listeners.<ILoginDialog.OnAuthenticationListener>getListener()); } else if (dialog instanceof EditDialog && listeners.listener instanceof IEditDialog.OnEditListener) { ((EditDialog) dialog) .setOnEditListener(listeners.<IEditDialog.OnEditListener>getListener()); } } } } return dialog; } /** * Private ------------------------------- */ /** * Resolved dialog listeners. * * @param dialog Dialog fragment. * @param listeners Set of listeners to assign to the given dialog. * @param <D> Type of dialog. * @return Same dialog with resolved listeners. */ @SuppressWarnings("unchecked") private <D extends DialogFragment> D resolveListenersInternal(DialogFragment dialog, ListenerOptions listeners) { dialog = resolveListeners(dialog, listeners); onResolveListeners(dialog, listeners); return (D) dialog; } /** * Performs showing of the dialog obtained from the dialog factory with the * given parameters. * * @param dialogID Id of dialog. * @param options Dialog options. * @param listeners Set of listeners to assign to the given dialog. * @return <code>True</code> if dialog was successfully showed, <code>false</code> otherwise. * @throws IllegalStateException */ private boolean performShowFactoryDialog(int dialogID, DialogOptions options, ListenerOptions listeners) { // Check if we have dialog factory. if (mDialogFactory == null) { throw new IllegalStateException( "No dialog factory found. Please set factory before you use any showDialog(int, ...) methods."); } // First obtain dialog instance then dialog tag. DialogFragment dialog = mDialogFactory.createDialogInstance(dialogID, options); if (dialog == null) { // Invalid dialog instance. Log.e(TAG, "No such dialog fragment instance for the requested dialog id(" + dialogID + "). Please check your dialog factory."); return false; } String dialogTag = mDialogFactory.getDialogTag(dialogID); return showDialog(dialog, listeners, dialogTag); } /** * Performs showing of the given dialog fragment with the given parameters. * * @param dialog Dialog to show. * @param listeners Set of listeners to assign to the given dialog. * @param dialogTag Tag under which should be the given dialog showed. * @return <code>True</code> if dialog was successfully showed, <code>false</code> otherwise. */ private boolean performShowDialog(DialogFragment dialog, ListenerOptions listeners, String dialogTag) { // Check valid dialog. if (dialog == null) { if (USER_LOG) Log.i(TAG, "Invalid dialog instance(null) to show."); return false; } // Set listeners to the dialog. dialog = this.resolveListenersInternal(dialog, listeners); // Show dialog and return it's options. return onShowDialog(dialog, dialogTag); } /** * Performs check for restored dialogs. * * @return <code>True</code> if some dialog was restored, <code>false</code> otherwise. */ private boolean checkRestoredDialogs() { if (bDialogsRestoreCheckPerformed) return false; boolean restored = false; List<DialogFragment> restoredDialogs = getDialogs(); if (restoredDialogs.size() > 0) { for (DialogFragment dialog : restoredDialogs) { if (USER_LOG) Log.i(TAG, "Restored dialog(" + dialog.getTag() + ")."); // Fire on restored dialog callback. if (iRestoreListener != null) { if (dialog instanceof IDialog) { iRestoreListener.onDialogRestored(dialog, ((IDialog) dialog).getDialogID()); } else { iRestoreListener.onDialogRestored(dialog, dialog.getId()); } } restored = true; } } // Save check state. this.bDialogsRestoreCheckPerformed = true; return restored; } /** * Abstract methods ---------------------- */ /** * Inner classes ========================= */ /** * <h4>Class Overview</h4> * <p> * Fast listener options to set the listener for the {@link com.wit.and.dialog.Dialog}. * </p> * * @author Martin Albedinsky * @see DialogManager */ public static final class ListenerOptions { /** * Members =============================== */ /** * <p> * Dialog listener. * </p> */ protected IDialog.OnDialogListener dialogListener = null; /** * <p> * Cancel dialog listener. * </p> */ protected IDialog.OnDialogCancelListener cancelListener = null; /** * <p> * Custom on dialog listener. * </p> */ protected IDialog.OnBaseDialogListener listener = null; /** * Constructors ========================== */ /** * <p> * Constructs empty fast dialog listener options. * </p> */ public ListenerOptions() { } /** * Methods =============================== */ /** * Public -------------------------------- */ /** * <p> * Return flag indicating if some of the listeners is set. * </p> * * @return <code>True</code> if some of the listeners is set, * <code>false</code> otherwise. */ public boolean isEmpty() { return dialogListener == null && cancelListener == null && listener == null; } /** * Getters + Setters --------------------- */ /** * <p> * Sets the given callback as dialog listener for the {@link com.wit.and.dialog.Dialog}. * </p> * * @param listener Listener callback. * @return This options. */ public ListenerOptions dialogListener(IDialog.OnDialogListener listener) { this.dialogListener = listener; return this; } /** * <p> * Sets the given callback as cancel listener for the {@link com.wit.and.dialog.Dialog}. * </p> * * @param listener Listener callback. * @return This options. */ public ListenerOptions cancelListener(IDialog.OnDialogCancelListener listener) { this.cancelListener = listener; return this; } /** * <p> * Sets the given callback as specific listener for the {@link com.wit.and.dialog.Dialog} derived class. * </p> * * @return This options. */ public ListenerOptions listener(IDialog.OnBaseDialogListener listener) { this.listener = listener; return this; } /** * Protected ----------------------------- */ /** * <p> * Returns specific listener for one of the derived classes of * {@link com.wit.and.dialog.Dialog}. * </p> * * @param <L> Type of the requested listener. * @return Listener callback. */ @SuppressWarnings("unchecked") <L extends IDialog.OnBaseDialogListener> L getListener() { return (L) listener; } } /** * Interface ============================= */ /** * <h4>Interface Overview</h4> * <p> * </p> * * @author Martin Albedinsky * @see DialogManager */ public static interface IDialogFactory { /** * Methods =============================== */ /** * <p> * Returns instance of the dialog fragment. Instance of the dialog * depends on the given <var>dialogID</var>. * </p> * * @param dialogID Id of dialog. * @param options Dialog options with data or <code>null</code> if options should * be populated in the factory. * @see DialogManager */ public DialogFragment createDialogInstance(int dialogID, DialogOptions options); /** * <p> * Returns tag for dialog fragment associated with the given * <var>dialogID</var>. * </p> * * @param dialogID Id of dialog for which is tag requested. */ public String getDialogTag(int dialogID); } /** * <h4>Interface Overview</h4> * <p> * </p> * * @author Martin Albedinsky * @see DialogManager */ public static interface OnDialogRestoreListener { /** * Methods =============================== */ /** * <p> * Invoked when some dialog was restored after orientation change. * </p> * <p> * <b>Contract:</b><br/> * If you want to handle restored dialogs, you must all that dialogs show by * {@link com.wit.and.dialog.manage.DialogManager}. Only dialog which was showed by * dialog manager can be ... as restored. * </p> * * @param dialog Dialog fragment which was restored after orientation change and is still visible. * @param dialogID Id of restored dialog. */ public void onDialogRestored(DialogFragment dialog, int dialogID); } }