Java tutorial
/* * Copyright (C) 2016 Pranav Pandey * * 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 com.pranavpandey.smallapp.dialog; import com.pranavpandey.smallapp.R; import com.pranavpandey.smallapp.SmallUtils; import com.pranavpandey.smallapp.view.ColoredImageView; import com.pranavpandey.smallapp.view.ColoredTextView; import com.pranavpandey.smallapp.view.HeaderGridView; import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.graphics.drawable.Drawable; import android.support.annotation.DrawableRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.support.v4.content.ContextCompat; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.widget.Adapter; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.CompoundButton; import android.widget.LinearLayout; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.ScrollView; import android.widget.TextView; import android.widget.WrapperListAdapter; /** * A class which creates a dialog to show different actions to perform various * operations by using an adapter containing all the actions which will be displayed * either in a <code>List</code> or <code>Grid</code>. You can use Base action * item adapter or any other custom adapter according to your need. Set a click * listener to dispatch click events so that you can perform actions. * * <p>You can also set extra info to display as header of <code>List</code> or <code>Grid</code> * which is also clickable.</p> * * @see {@link android.app.AlertDialog}, * {@link com.pranavpandey.smallapp.adapter.BaseActionItemAdapter}, * {@link OnActionItemClickListener}, {@link #setExtraInfo(int, int, OnExtraInfoClickListener)}, * {@link OnExtraInfoClickListener}. */ public class ActionDialog { /** * Interface definition for a callback to be invoked when an action * item is clicked. */ public interface OnActionItemClickListener { /** * Called when an action item is clicked. * * @param dialog The dialog where the selection was made. * @param adapter The adapter in this AdapterView. * @param parent The AdapterView where the click happened. * @param view The view within the AdapterView that was clicked (this * will be a view provided by the adapter). * @param position The position of the view in the adapter. * @param id The row id of the item that was clicked. */ public void onActionItemClick(DialogInterface dialog, Adapter adapter, AdapterView<?> parent, View view, int position, long id); } /** * Interface definition for a callback to be invoked when extra * info is clicked. */ public interface OnExtraInfoClickListener { /** * Called when extra info is clicked. * * @param view The view that was clicked. */ public void onExtraInfoClick(View view); } /** * Interface definition for a callback to be invoked when the checked state * of a compound button in check button layout changed. */ public interface OnCheckButtonChangedListener { /** * Called when the checked state of a compound button in the check button * layout has changed. * * @param view The view that was clicked. * @param buttonView The compound button view whose state has changed. * @param isChecked The new checked state of buttonView. * @param textView The text view to update description according to the state * of the compound button. */ public void onCheckButtonChanged(View view, CompoundButton buttonView, boolean isChecked, TextView textView); } /** * Enum to define the type of this dialog. Either a * <code>List</code> or <code>Grid</code>. * * @see {@link Type#LIST} or {@link Type#GRID}. */ public static enum Type { /** * Actions will be displayed in the vertical list. * * @see {@link android.widget.ListView}. */ LIST, /** * Actions will be displayed in the vertical Grid. * * @see {@link android.widget.GridView}. */ GRID } /** * Context to retrieve resources and to build the * final {@link ActionDialog}. */ private Context mContext; /** * Dialog to be displayed. */ private AlertDialog mDialog; /** * AlertDialog builder to attach with the dialog. Provide * info about title, buttons, etc. in this builder. */ private AlertDialog.Builder mDialogBuilder; /** * Type of the ActionDialog. Either <code>List</code> or * <code>Grid</code> based on {@link #Type}. */ private Type mType; /** * View of this dialog. */ private View mView; /** * <code>Grid</code> to display all the action items. */ private HeaderGridView mActionGridView; /** * <code>List</code> to display all the action items. */ private ListView mActionListView; /** * Adapter in the {@link Type#LIST} or {@link Type#GRID}. */ private ListAdapter mAdapter; /** * Listener used to dispatch action item click events. */ private @Nullable OnActionItemClickListener mActionItemListener; /** * Listener used to dispatch extra info click events. */ private @Nullable OnExtraInfoClickListener mExtraInfoListener; /** * Listener used to dispatch check button layout click events. */ private @Nullable OnCheckButtonChangedListener mCheckButtonListener; /** * Extra info icon. */ private Drawable mExtraInfoIcon; /** * Extra info text. */ private CharSequence mExtraInfoText; /** * Check button text. */ private CharSequence mCheckButtonText; /** * <code>true</code> if extra info is set to display as header of * {@link Type#LIST} or {@link Type#GRID}. */ private boolean mExtraInfo = false; /** * <code>true</code> if auto dismiss dialog when an action item * is clicked. */ private boolean mAutoDismiss = true; /** * <code>true</code> ff a custom dialog will be displayed from * the extended class. It extends the functionality of this * dialog. It is well implemented in {@link OpenIntentDialog}. * * @see {@link OpenIntentDialog#createDialog(View)}. */ private boolean mCustomDialog = false; /** * <code>true</code> if check button layout is enabled. Enable it to * remember user selection for later use. It is well implemented in * {@link OpenIntentDialog}. * * @see {@link OpenIntentDialog#createDialog(View)}. */ private boolean mCheckButtonLayout = false; /** * Checked state of the button in check button layout. * <code>true</code> if it is checked. */ private boolean mCheckButtonState = false; /** * LayoutInflater to inflate views. */ private LayoutInflater mInflater; /** * Constructor using a context, alert dialog builder and the dialog type to * display an Action dialog which shows a set of actions to perform different * operations. * * @see {@link android.app.AlertDialog.Builder}, {@link Type}. */ public ActionDialog(Context context, AlertDialog.Builder dialogBuilder, Type type) { this.mContext = context; this.mDialogBuilder = dialogBuilder; this.mType = type; this.mInflater = LayoutInflater.from(mContext); } /** * Creates a {@link ActionDialog} with the arguments supplied to the * constructor by using the supplied adapter. * * @param rootView Root view to which the dialog should attach. */ public void createDialog(@NonNull View rootView) { if (mType == Type.GRID) { mView = mInflater.inflate(R.layout.sas_dialog_action_grid, new LinearLayout(mContext), false); mActionGridView = (HeaderGridView) mView.findViewById(R.id.action_grid); if (mExtraInfo) { ((ColoredTextView) mView.findViewById(R.id.extra_info_desc)).setText(mExtraInfoText); ((ColoredImageView) mView.findViewById(R.id.extra_info_icon)).setImageDrawable(mExtraInfoIcon); mActionGridView.setExpanded(true); if (mExtraInfoListener != null) { mView.findViewById(R.id.extra_info_layout).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mExtraInfoListener.onExtraInfoClick(v); autoDismiss(); } }); } mView.findViewById(R.id.extra_info_layout).setVisibility(View.VISIBLE); final ScrollView scrollView = (ScrollView) mView.findViewById(R.id.action_grid_scroll); scrollView.post(new Runnable() { @Override public void run() { scrollView.smoothScrollTo(0, 0); } }); } mActionGridView.setAdapter(mAdapter); mActionGridView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (mActionItemListener != null) { mActionItemListener.onActionItemClick(mDialog, mAdapter, parent, view, position, id); } autoDismiss(); } }); } else { mView = mInflater.inflate(R.layout.sas_dialog_action_list, new LinearLayout(mContext), false); mActionListView = (ListView) mView.findViewById(R.id.action_list); if (mExtraInfo) { View mHeader = mInflater.inflate(R.layout.sas_dialog_extra_info_list, mActionListView, false); ((ColoredTextView) mHeader.findViewById(R.id.extra_info_desc)).setText(mExtraInfoText); ((ColoredImageView) mHeader.findViewById(R.id.extra_info_icon)).setImageDrawable(mExtraInfoIcon); mActionListView.addHeaderView(mHeader, null, mExtraInfoListener != null ? true : false); } mActionListView.setAdapter(mAdapter); mActionListView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (mExtraInfo) { position--; if (position < 0) { if (mExtraInfoListener != null) { mExtraInfoListener.onExtraInfoClick(view); autoDismiss(); } return; } } if (mActionItemListener != null) { mActionItemListener.onActionItemClick(mDialog, mAdapter, parent, view, position, id); } autoDismiss(); } }); } if (mCheckButtonLayout) { final TextView chkText = (TextView) mView.findViewById(R.id.check_text); final View chkLayout = (View) mView.findViewById(R.id.check_layout); final CompoundButton chkButton = (CompoundButton) mView.findViewById(R.id.check_button); chkText.setText(mCheckButtonText); chkLayout.setVisibility(View.VISIBLE); chkLayout.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mCheckButtonState = !chkButton.isChecked(); chkButton.setChecked(mCheckButtonState); if (mCheckButtonListener != null) { mCheckButtonListener.onCheckButtonChanged(chkLayout, chkButton, chkButton.isChecked(), chkText); } } }); chkButton.setChecked(mCheckButtonState); } mDialog = SmallUtils.createDialog(mDialogBuilder.create(), rootView.getWindowToken(), mView); } /** * Set actions adapter for this dialog with on click listener to dispatch * click events. You can use Base action item adapter or any other custom * adapter according to your need. * * @param adapter Action adapter for this dialog. * @param actionItemListener Listener to dispatch click events. * * @return ActionDialog object to allow for chaining of calls to set * methods. * * @see {@link com.pranavpandey.smallapp.adapter.BaseActionItemAdapter}, * {@link #getAdapter()}. */ public ActionDialog setAdapter(@NonNull ListAdapter adapter, OnActionItemClickListener actionItemListener) { this.mAdapter = adapter; this.mActionItemListener = actionItemListener; return this; } /** * Set actions adapter for this dialog. You can use Base action item adapter * or any other custom adapter according to your need. * * @param adapter Action adapter for this dialog. * * @return ActionDialog object to allow for chaining of calls to set * methods. * * @see {@link com.pranavpandey.smallapp.adapter.BaseActionItemAdapter}, * {@link #getAdapter()}. */ public ActionDialog setAdapter(@NonNull ListAdapter adapter) { this.mAdapter = adapter; if (mType == Type.GRID) { if (mActionGridView != null) mActionGridView.setAdapter(adapter); } else { if (mActionListView != null) mActionListView.setAdapter(adapter); } return this; } /** * Set extra info to display as header of {@link Type#LIST} or {@link Type#GRID}. * * @param icon Extra info image drawable. * @param text Extra info text. * @param extraInfoListener Extra info listener to dispatch click events. Pass * <code>null</code> for no action. * * @return ActionDialog object to allow for chaining of calls to set * methods. */ public ActionDialog setExtraInfo(@Nullable Drawable icon, @Nullable CharSequence text, @Nullable OnExtraInfoClickListener extraInfoListener) { this.mExtraInfoIcon = icon; this.mExtraInfoText = text; this.mExtraInfoListener = extraInfoListener; this.mExtraInfo = true; return this; } /** * Set extra info to display as header of {@link Type#LIST} or {@link Type#GRID}. * * @param iconId Extra info image drawable id. * @param textId Extra info text id. * @param extraInfoListener Extra info listener to dispatch click events. Pass * <code>null</code> for no action. * * @return ActionDialog object to allow for chaining of calls to set * methods. */ public ActionDialog setExtraInfo(@DrawableRes int iconId, @StringRes int textId, @Nullable OnExtraInfoClickListener extraInfoListener) { this.mExtraInfoIcon = ContextCompat.getDrawable(mContext, iconId); this.mExtraInfoText = mContext.getText(textId); this.mExtraInfoListener = extraInfoListener; this.mExtraInfo = true; return this; } /** * Set extra info to display as header of {@link Type#LIST} or {@link Type#GRID}. * * @param iconId Extra info image drawable id. * @param text Extra info text. * @param extraInfoListener Extra info listener to dispatch click events. Pass * <code>null</code> for no action. * * @return ActionDialog object to allow for chaining of calls to set * methods. */ public ActionDialog setExtraInfo(@DrawableRes int iconId, @Nullable CharSequence text, OnExtraInfoClickListener extraInfoListener) { this.mExtraInfoIcon = ContextCompat.getDrawable(mContext, iconId); this.mExtraInfoText = text; this.mExtraInfoListener = extraInfoListener; this.mExtraInfo = true; return this; } /** * Set extra info to display as header of {@link Type#LIST} or {@link Type#GRID}. * * @param icon Extra info image drawable. * @param textId Extra info text id. * @param extraInfoListener Extra info listener to dispatch click events. Pass * <code>null</code> for no action. * * @return ActionDialog object to allow for chaining of calls to set * methods. */ public ActionDialog setExtraInfo(@Nullable Drawable icon, @StringRes int textId, @Nullable OnExtraInfoClickListener extraInfoListener) { this.mExtraInfoIcon = icon; this.mExtraInfoText = mContext.getText(textId); this.mExtraInfoListener = extraInfoListener; this.mExtraInfo = true; return this; } /** * Set check button layout to remember user selection. * * @param text Check button text. * @param defaultState The checked default state of the check button. * @param checkButtonListener Check button listener to dispatch state change events. Pass * <code>null</code> for no action. * * @return ActionDialog object to allow for chaining of calls to set * methods. */ public ActionDialog setCheckButtonLayout(@Nullable String text, boolean defaultState, @Nullable OnCheckButtonChangedListener checkButtonListener) { this.mCheckButtonText = text; this.mCheckButtonListener = checkButtonListener; this.mCheckButtonState = defaultState; this.mCheckButtonLayout = true; return this; } /** * Set check button layout to remember user selection. * * @param textId Check button text id. * @param defaultState The checked default state of the check button. * @param checkButtonListener Check button listener to dispatch state change events. Pass * <code>null</code> for no action. * * @return ActionDialog object to allow for chaining of calls to set * methods. */ public ActionDialog setCheckButtonLayout(@StringRes int textId, boolean defaultState, @Nullable OnCheckButtonChangedListener checkButtonListener) { return setCheckButtonLayout(mContext.getText(textId).toString(), defaultState, checkButtonListener); } /** * Set the value of {@link #mAutoDismiss}. * * @param isAutoDismiss Value to be set. Either <code>true</code> or <code>false</code>. * * @return ActionDialog object to allow for chaining of calls to set * methods. */ public ActionDialog setAutoDismiss(boolean isAutoDismiss) { this.mAutoDismiss = isAutoDismiss; return this; } /** * Set the value of {@link #mAutoDismiss}. * * @param isAutoDismiss Value to be set. Either <code>true</code> or <code>false</code>. * * @return ActionDialog object to allow for chaining of calls to set * methods. */ public ActionDialog setCustomDialog(boolean isCustomDialog) { this.mCustomDialog = isCustomDialog; return this; } /** * Dismiss dialog if {@link #mAutoDismiss} is set to * <code>true</code>. */ protected void autoDismiss() { if (mAutoDismiss) { dismissDialog(); } } /** * Dismiss the dialog. */ public void dismissDialog() { if (mDialog != null) { mDialog.dismiss(); } } /** * Creates a {@link ActionDialog} with the arguments supplied to the constructor * and {@link Dialog#show()} the dialog. * * @param rootView Root view to which the dialog should attach. */ public void show(@NonNull View rootView) { createDialog(rootView); if (!mCustomDialog) { mDialog.show(); } } /** * Get this dialog to do further operations. * * @return {@link #mDialog}. */ public AlertDialog getDialog() { return mDialog; } /** * Get context to used to create this dialog. * * @return Context used to create this dialog. */ public Context getContext() { return mContext; } /** * Returns the adapter currently in use in the <code>List</code> or <code>Grid</code>. * The returned adapter might not be the same adapter passed to * {@link #setAdapter(ListAdapter)} but might be a {@link WrapperListAdapter}. * * @return The adapter currently used to display data in this ActionDialog. * * @see #setAdapter(ListAdapter) */ public ListAdapter getAdapter() { return mAdapter; } /** * @return The callback to be invoked when an action item in this dialog * has been clicked, or <code>null</code> if no callback has been set. */ @Nullable public OnActionItemClickListener getActionItemListener() { return mActionItemListener; } /** * @return The callback to be invoked when extra info in this dialog * has been clicked, or <code>null</code> if no callback has been set. */ @Nullable public OnExtraInfoClickListener getExtraInfoListener() { return mExtraInfoListener; } /** * @return The type of the dialog. Either <code>LIST</code> or * <code>GRID</code>. */ public Type getType() { return mType; } /** * @return The AlertDialog builder to set custom views. */ public AlertDialog.Builder getDialogBuilder() { return mDialogBuilder; } /** * @return <code>true</code> if a custom dialog will be displayed by using * the values of this dialog. */ public boolean isCustomDialog() { return mCustomDialog; } /** * @return <code>true</code> if extra info is set to display as header of * {@link Type#LIST} or {@link Type#GRID}. */ public boolean isExtraInfo() { return mExtraInfo; } /** * @return If check button layout is enabled to remember user selection. */ public boolean isCheckButtonLayout() { return mCheckButtonLayout; } /** * @return If check is checked to remember user selection. */ public boolean isCheckButtonChecked() { return mCheckButtonState; } /** * @return The icon set to display as Extra info. */ public Drawable getExtraInfoIcon() { return mExtraInfoIcon; } /** * @return The text set to display as Extra info. */ public CharSequence getExtraInfoText() { return mExtraInfoText; } }