package uk.me.lewisdeane.ldialogs; import android.content.Context; import android.graphics.Color; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.support.v7.appcompat.R; import android.util.TypedValue; import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; // Class extends BaseDialog which contains variables and properties applicable to all adaptations of our custom dialogs. public class CustomDialog extends BaseDialog { // Context for dialog to use. private Context mContext; // The layout view inflated from resources. private View mRootView; // View holding any custom views passed in. private View mCustomView; // Array of views containing the components of our dialog. private View[] mViews = new View[4]; // Array of strings to populate the corresponding view. private String[] mStrings = new String[]{"", "", "", ""}; // Array of linear layouts containing the 2 possible arrangements required // in case stacking needed. private LinearLayout[] mButtonContainers = new LinearLayout[2]; // Typeface used on the views. private Typeface mTypeface; // Our click listener which we can pass events to the user through. private ClickListener mCallbacks; // Theme containing the theme being used. private Theme mTheme = Theme.LIGHT; // Integers containing the hex colours to be used on the views. private int mPositiveColour, mNegativeColour, mTitleColour, mContentColour; // Positive background Drawable mPositiveBackground; // Integers containing the text sizes private int[] mTextSizes = new int[4]; // Alignment for title to use private Alignment mTitleAlignment = Alignment.LEFT; // Alignment for content to use private Alignment mContentAlignment = Alignment.LEFT; // Alignment for button to use private Alignment mButtonsAlignment = Alignment.RIGHT; // boolean containing whether or not its intended to be right to left. private final boolean RTL; /** * TODO: Add option on whether to force stack buttons. * TODO: If text overflows make it so it splits the buttons across the width. * TODO: Negative button correct colour by default? * TODO: Neutral Button. */ // We make our constructor private so we can only create it through the // builder inner class. private CustomDialog(Builder _builder) { // Call the super class to create our new dialog. super(new ContextThemeWrapper(_builder.mContext, _builder.mDarkTheme ? R.style.LDialogs_Dark : R.style.LDialogs_Light)); // Apply the things from the builder. this.mContext = _builder.mContext; this.mTheme = _builder.mDarkTheme ? Theme.DARK : Theme.LIGHT; this.mStrings[0] = _builder.mTitle; this.mStrings[1] = _builder.mContent; this.mStrings[2] = _builder.mPositiveText; this.mStrings[3] = _builder.mNegativeText; this.mPositiveColour = _builder.mPositiveColour; this.mNegativeColour = _builder.mNegativeColour; this.mTitleColour = _builder.mTitleColour; this.mContentColour = _builder.mContentColour; this.mTitleAlignment = _builder.mTitleAlignment; this.mTextSizes[0] = _builder.mTitleTextSize; this.mTextSizes[1] = _builder.mContentTextSize; this.mTextSizes[2] = _builder.mButtonTextSize; this.mTextSizes[3] = this.mTextSizes[2]; this.mContentAlignment = _builder.mContentAlignment; this.mButtonsAlignment = _builder.mButtonsAlignment; this.mTypeface = _builder.mTypeface; this.RTL = _builder.RTL; this.mPositiveBackground = _builder.mPositiveBackground; // Set up references to views and then set the view. init(); // Sets the properties of the views. setViewProperties(mViews, mStrings); // Check's if the different button arrangement is needed. checkIfButtonStackingNeeded(); // Sets the listeners for the buttons. setListeners(); // Apply the correct theme based on users preference. applyTheme(); } private void init() { // Reference root view by inflating the layout file. mRootView = LayoutInflater.from(mContext).inflate( R.layout.dialog_custom, null); // Reference the views. mViews[0] = mRootView.findViewById(R.id.dialog_custom_title); mViews[1] = mRootView.findViewById(R.id.dialog_custom_content); mViews[2] = mRootView.findViewById(R.id.dialog_custom_confirm); mViews[3] = mRootView.findViewById(R.id.dialog_custom_cancel); // Reference containers for the buttons. mButtonContainers[0] = (LinearLayout) mRootView .findViewById(R.id.dialog_custom_alongside_buttons); mButtonContainers[1] = (LinearLayout) mRootView .findViewById(R.id.dialog_custom_stacked_buttons); // Set alignment for buttons view. mButtonContainers[0] .setGravity(getGravityFromAlignment(mButtonsAlignment) | Gravity.CENTER_VERTICAL); mButtonContainers[1] .setGravity(getGravityFromAlignment(mButtonsAlignment) | Gravity.CENTER_VERTICAL); // Set alignment for title view. ((TextView) mViews[0]) .setGravity(getGravityFromAlignment(mTitleAlignment) | Gravity.CENTER_VERTICAL); // Set alignment for content view. ((TextView) mViews[1]) .setGravity(getGravityFromAlignment(mContentAlignment) | Gravity.CENTER_VERTICAL); // Set the view of our dialog with the one we've inflated. super.setView(mRootView); } private void setListeners() { // Set the listener that handles positive button clicks. mViews[2].setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // Call the method from our interface and dismiss. if (mCallbacks != null) mCallbacks.onConfirmClick(); dismiss(); } }); // Set the listener that handles negative button clicks. mViews[3].setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // Call the method from our interface and dismiss. if (mCallbacks != null) mCallbacks.onCancelClick(); dismiss(); } }); } private void checkIfButtonStackingNeeded() { // Check if the text on the button is too big for the button. boolean isStackingNeeded = ((Button) mViews[2]).getPaint().measureText( ((Button) mViews[2]).getText().toString()) > convertToPx(56) || ((Button) mViews[2]).getPaint().measureText( ((Button) mViews[3]).getText().toString()) > convertToPx(56); // Toggle visibility of the layouts based on whether switching is // needed. mButtonContainers[0].setVisibility(isStackingNeeded ? View.GONE : View.VISIBLE); mButtonContainers[1].setVisibility(isStackingNeeded ? View.VISIBLE : View.GONE); // Now the data may have changed we need to re-reference the buttons. updateButtonReferences(isStackingNeeded); } private void updateButtonReferences(boolean _isStackingNeeded) { // Re-reference the buttons. mViews[2] = mRootView .findViewById(_isStackingNeeded ? R.id.dialog_custom_confirm_stacked : R.id.dialog_custom_confirm); mViews[3] = mRootView .findViewById(_isStackingNeeded ? R.id.dialog_custom_cancel_stacked : R.id.dialog_custom_cancel); // Apply the data to the newly referenced views. setViewProperties(mViews, mStrings); } private float convertToPx(float _dp) { // Convert any density pixel value to it's corresponding pixel value. return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, _dp, mContext.getResources().getDisplayMetrics()); } private void setViewProperties(View[] _view, String[] _text) { // Loop through the views passed in and set the correct data to that // view. for (int i = 0; i < _view.length; i++) { // Gets the index from the mViews array as _view array may be a // different ordering and size to mViews. int index = getIndexFromView(_view[i]); // Hide the view if there is no data for this view. mViews[index].setVisibility(_text[i].equals("") ? View.GONE : View.VISIBLE); // Reference the new text correctly.. mStrings[index] = _text[i]; // If it's a button treat it as one rather than generic view. if (index / 2 > 0) { Button button = (Button) mViews[index]; button.setText(mStrings[index].toUpperCase()); button.setTypeface(mTypeface); button.setTextSize(TypedValue.COMPLEX_UNIT_SP, mTextSizes[index]); } // Otherwise treat view as a text view. else { TextView textView = (TextView) mViews[index]; textView.setText(mStrings[index]); textView.setTypeface(mTypeface); textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mTextSizes[index]); } } // Swap position of positive button and negative button if right to left required. if (RTL) { ((ViewGroup) mViews[3].getParent()).removeView(mViews[2]); ((ViewGroup) mViews[3].getParent()).addView(mViews[2], 0); } } private int getIndexFromView(View _view) { // Gets the correct index of the passed in view from the main array of // views. for (int i = 0; i < mViews.length; i++) { if (mViews[i] == _view) return i; } return 0; } private void applyTheme() { // Apply the correct colours based on theme and user preference. ((TextView) mViews[0]) .setTextColor(this.mTitleColour != 0 ? mTitleColour : (mTheme == Theme.LIGHT ? Color .parseColor(LightColours.TITLE.mColour) : Color .parseColor(DarkColours.TITLE.mColour))); ((TextView) mViews[1]) .setTextColor(this.mContentColour != 0 ? mContentColour : (mTheme == Theme.LIGHT ? Color .parseColor(LightColours.CONTENT.mColour) : Color.parseColor(DarkColours.CONTENT.mColour))); ((Button) mViews[2]) .setTextColor(this.mPositiveColour != 0 ? mPositiveColour : (mTheme == Theme.LIGHT ? Color .parseColor(LightColours.BUTTON.mColour) : Color.parseColor(DarkColours.BUTTON.mColour))); ((Button) mViews[3]) .setTextColor(this.mNegativeColour != 0 ? mNegativeColour : (mTheme == Theme.LIGHT ? Color .parseColor(LightColours.BUTTON.mColour) : Color.parseColor(DarkColours.BUTTON.mColour))); if (null != mPositiveBackground) { mViews[2].setBackgroundDrawable(mPositiveBackground); } } public CustomDialog setClickListener(ClickListener mCallbacks) { // Sets the listener that allows user to receive the click events. this.mCallbacks = mCallbacks; return this; } public CustomDialog setCustomView(View _view) { // Set weight of the custom view if (_view.getLayoutParams() == null) { LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT, 1.0f); _view.setLayoutParams(params); } else if (_view.getLayoutParams() instanceof LinearLayout.LayoutParams) { ((LinearLayout.LayoutParams) _view.getLayoutParams()).weight = 1.0f; } // If there's a custom view present - remove it. if (mCustomView != null) ((ViewGroup) mViews[0].getParent()).removeView(mCustomView); // Reference new custom view. mCustomView = _view; // Add it to the dialog. ((ViewGroup) mViews[0].getParent()).addView(mCustomView, 2); return this; } public interface ClickListener { public void onConfirmClick(); public void onCancelClick(); } public static class Builder { // Required fields for the dialog. private final Context mContext; private final String mTitle, mPositiveText; Typeface mTypeface; // Builder constructor that all dialogs will be created through. public Builder(Context _context, String _title, String _positiveText) { this.mContext = _context; this.mTitle = _title; this.mPositiveText = _positiveText; // Load typeface from assets to be used. mTypeface = Typeface.createFromAsset(this.mContext.getResources() .getAssets(), "fonts/katong/Katong.ttf"); } public Builder(Context _context, int _titleResId, int _positiveTextResId) { this.mContext = _context; this.mTitle = mContext.getString(_titleResId); this.mPositiveText = mContext.getString(_positiveTextResId); // Load typeface from assets to be used. mTypeface = Typeface.createFromAsset(this.mContext.getResources() .getAssets(), "fonts/katong/Katong.ttf"); } // Optional parameters initialised by default. private String mNegativeText = "", mContent = ""; private int mPositiveColour = 0, mNegativeColour = 0, mTitleColour = 0, mContentColour = 0, mTitleTextSize = 22, mContentTextSize = 18, mButtonTextSize = 14; private boolean mDarkTheme = false; private boolean RTL = false; private Alignment mTitleAlignment = Alignment.LEFT; private Alignment mContentAlignment = Alignment.LEFT; private Alignment mButtonsAlignment = Alignment.RIGHT; private Drawable mPositiveBackground; public Builder content(String _content) { this.mContent = _content; return this; } public Builder negativeText(String _negativeText) { this.mNegativeText = _negativeText; return this; } public Builder negativeText(int _negativeTextResId) { this.mNegativeText = mContext.getString(_negativeTextResId); return this; } public Builder positiveColor(String _positiveColour) { this.mPositiveColour = Color.parseColor(_positiveColour); return this; } public Builder negativeColor(String _negativeColour) { this.mNegativeColour = Color.parseColor(_negativeColour); return this; } public Builder titleColor(String _colour) { this.mTitleColour = Color.parseColor(_colour); return this; } public Builder contentColor(String _colour) { this.mContentColour = Color.parseColor(_colour); return this; } public Builder positiveColor(int _positiveColour) { this.mPositiveColour = _positiveColour; return this; } public Builder negativeColor(int _negativeColour) { this.mNegativeColour = _negativeColour; return this; } public Builder titleColor(int _colour) { this.mTitleColour = _colour; return this; } public Builder contentColor(int _colour) { this.mContentColour = _colour; return this; } public Builder positiveColorRes(int _positiveColour) { this.mPositiveColour = mContext.getResources().getColor( _positiveColour); return this; } public Builder negativeColorRes(int _negativeColour) { this.mNegativeColour = mContext.getResources().getColor( _negativeColour); return this; } public Builder titleColorRes(int _colour) { this.mTitleColour = mContext.getResources().getColor(_colour); return this; } public Builder contentColorRes(int _colour) { this.mContentColour = mContext.getResources().getColor(_colour); return this; } public Builder titleTextSize(int _textSize) { this.mTitleTextSize = _textSize; return this; } public Builder contentTextSize(int _textSize) { this.mContentTextSize = _textSize; return this; } public Builder buttonTextSize(int _textSize) { this.mButtonTextSize = _textSize; return this; } public Builder darkTheme(boolean _isDark) { this.mDarkTheme = _isDark; return this; } public Builder titleAlignment(Alignment _alignment) { this.mTitleAlignment = _alignment; return this; } public Builder contentAlignment(Alignment _alignment) { this.mContentAlignment = _alignment; return this; } public Builder buttonAlignment(Alignment _alignment) { this.mButtonsAlignment = _alignment; return this; } public Builder rightToLeft(boolean _rightToLeft) { this.RTL = _rightToLeft; if (_rightToLeft) { this.mTitleAlignment = Alignment.RIGHT; this.mContentAlignment = Alignment.RIGHT; this.mButtonsAlignment = Alignment.LEFT; } return this; } public Builder typeface(Typeface _typeface) { this.mTypeface = _typeface; return this; } public Builder positiveBackground(Drawable _positiveBkgd) { this.mPositiveBackground = _positiveBkgd; return this; } public Builder positiveBackground(int _positiveBkgd) { this.mPositiveBackground = mContext.getResources().getDrawable(_positiveBkgd); return this; } public CustomDialog build() { return new CustomDialog(this); } } }