com.github.shareme.gwsmaterialuikit.library.material.app.SimpleDialog.java Source code

Java tutorial

Introduction

Here is the source code for com.github.shareme.gwsmaterialuikit.library.material.app.SimpleDialog.java

Source

/*
 * Copyright 2015 Rey Pham.
 * Modifications Copyright(C) 2016 Fred Grott
 *
 * 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.github.shareme.gwsmaterialuikit.library.material.app;

import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.view.ViewCompat;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ScrollView;

import com.github.shareme.gwsmaterialuikit.library.R;
import com.github.shareme.gwsmaterialuikit.library.material.drawable.BlankDrawable;
import com.github.shareme.gwsmaterialuikit.library.material.widget.CheckBox;
import com.github.shareme.gwsmaterialuikit.library.material.widget.CompoundButton;
import com.github.shareme.gwsmaterialuikit.library.material.widget.ListView;
import com.github.shareme.gwsmaterialuikit.library.material.widget.RadioButton;
import com.github.shareme.gwsmaterialuikit.library.material.widget.TextView;

/**
 * Created by Rey on 12/16/2014.
 */
public class SimpleDialog extends Dialog {

    private TextView mMessage;
    private InternalScrollView mScrollView;
    private InternalListView mListView;
    private InternalAdapter mAdapter;

    private int mMessageTextAppearanceId;
    private int mMessageTextColor;

    private int mRadioButtonStyle;
    private int mCheckBoxStyle;
    private int mItemHeight;
    private int mItemTextAppearance;

    private int mMode;

    protected static final int MODE_NONE = 0;
    protected static final int MODE_MESSAGE = 1;
    protected static final int MODE_ITEMS = 2;
    protected static final int MODE_MULTI_ITEMS = 3;
    protected static final int MODE_CUSTOM = 4;

    /**
     * Interface definition for a callback to be invoked when the checked state of an item changed.
     */
    public interface OnSelectionChangedListener {
        /**
         * Called when the checked state of an item changed.
         * @param index The index of item.
         * @param selected The checked state.
         */
        public void onSelectionChanged(int index, boolean selected);
    }

    private OnSelectionChangedListener mOnSelectionChangedListener;

    public SimpleDialog(Context context) {
        super(context, R.style.Material_App_Dialog_Simple_Light);
    }

    public SimpleDialog(Context context, int style) {
        super(context, style);
    }

    @Override
    protected void onCreate() {
        messageTextAppearance(R.style.TextAppearance_AppCompat_Body1);
        itemHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
        itemTextAppearance(R.style.TextAppearance_AppCompat_Body1);
    }

    @Override
    public Dialog applyStyle(int resId) {
        super.applyStyle(resId);

        if (resId == 0)
            return this;

        TypedArray a = getContext().obtainStyledAttributes(resId, R.styleable.SimpleDialog);
        int textAppearance = 0;
        int textColor = 0;
        boolean textColorDefined = false;

        for (int i = 0, count = a.getIndexCount(); i < count; i++) {
            int attr = a.getIndex(i);

            if (attr == R.styleable.SimpleDialog_di_messageTextAppearance)
                textAppearance = a.getResourceId(attr, 0);
            else if (attr == R.styleable.SimpleDialog_di_messageTextColor) {
                textColor = a.getColor(attr, 0);
                textColorDefined = true;
            } else if (attr == R.styleable.SimpleDialog_di_radioButtonStyle)
                radioButtonStyle(a.getResourceId(attr, 0));
            else if (attr == R.styleable.SimpleDialog_di_checkBoxStyle)
                checkBoxStyle(a.getResourceId(attr, 0));
            else if (attr == R.styleable.SimpleDialog_di_itemHeight)
                itemHeight(a.getDimensionPixelSize(attr, 0));
            else if (attr == R.styleable.SimpleDialog_di_itemTextAppearance)
                itemTextAppearance(a.getResourceId(attr, 0));
        }

        a.recycle();

        if (textAppearance != 0)
            messageTextAppearance(textAppearance);

        if (textColorDefined)
            messageTextColor(textColor);

        return this;
    }

    @Override
    public Dialog clearContent() {
        super.clearContent();
        mMode = MODE_NONE;
        return this;
    }

    @Override
    public Dialog title(CharSequence title) {
        boolean titleVisible = !TextUtils.isEmpty(title);
        contentMargin(mContentPadding, titleVisible ? 0 : mContentPadding, mContentPadding, 0);
        return super.title(title);
    }

    @Override
    public Dialog contentView(View v) {
        if (mScrollView == null)
            initScrollView();

        if (mScrollView.getChildAt(0) != v && v != null) {
            mScrollView.removeAllViews();
            mScrollView.addView(v);
            mMode = MODE_CUSTOM;
            super.contentView(mScrollView);
        }

        return this;
    }

    @SuppressWarnings("WrongConstant")
    private void initScrollView() {
        mScrollView = new InternalScrollView(getContext());
        mScrollView.setPadding(0, 0, 0, mContentPadding - mActionPadding);
        mScrollView.setClipToPadding(false);
        mScrollView.setFillViewport(true);
        mScrollView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
        ViewCompat.setLayoutDirection(mScrollView, ViewCompat.LAYOUT_DIRECTION_INHERIT);
    }

    private void initMessageView() {
        mMessage = new TextView(getContext());
        mMessage.setTextAppearance(getContext(), mMessageTextAppearanceId);
        mMessage.setTextColor(mMessageTextColor);
        mMessage.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
    }

    /**
     * Set a message text to this SimpleDialog.
     * @param message
     * @return The SimpleDialog for chaining methods.
     */
    public SimpleDialog message(CharSequence message) {
        if (mScrollView == null)
            initScrollView();

        if (mMessage == null)
            initMessageView();

        if (mScrollView.getChildAt(0) != mMessage) {
            mScrollView.removeAllViews();
            mScrollView.addView(mMessage);
        }

        mMessage.setText(message);
        if (!TextUtils.isEmpty(message)) {
            mMode = MODE_MESSAGE;
            super.contentView(mScrollView);
        }
        return this;
    }

    /**
     * Set a message text to this SimpleDialog.
     * @param id The resourceId of text.
     * @return The SimpleDialog for chaining methods.
     */
    public SimpleDialog message(int id) {
        return message(id == 0 ? null : getContext().getResources().getString(id));
    }

    /**
     * Sets the text color, size, style of the message view from the specified TextAppearance resource.
     * @param resId The resourceId value.
     * @return The SimpleDialog for chaining methods.
     */
    public SimpleDialog messageTextAppearance(int resId) {
        if (mMessageTextAppearanceId != resId) {
            mMessageTextAppearanceId = resId;
            if (mMessage != null)
                mMessage.setTextAppearance(getContext(), mMessageTextAppearanceId);
        }
        return this;
    }

    /**
     * Sets the text color of the message view.
     * @param color The color value.
     * @return The SimpleDialog for chaining methods.
     */
    public SimpleDialog messageTextColor(int color) {
        if (mMessageTextColor != color) {
            mMessageTextColor = color;
            if (mMessage != null)
                mMessage.setTextColor(color);
        }
        return this;
    }

    /**
     * Sets the style of radio button.
     * @param resId The resourceId of style.
     * @return The SimpleDialog for chaining methods.
     */
    public SimpleDialog radioButtonStyle(int resId) {
        if (mRadioButtonStyle != resId) {
            mRadioButtonStyle = resId;
            if (mAdapter != null && mMode == MODE_ITEMS)
                mAdapter.notifyDataSetChanged();
        }
        return this;
    }

    /**
     * Sets the style of check box.
     * @param resId The resourceId of style.
     * @return The SimpleDialog for chaining methods.
     */
    public SimpleDialog checkBoxStyle(int resId) {
        if (mCheckBoxStyle != resId) {
            mCheckBoxStyle = resId;
            if (mAdapter != null && mMode == MODE_MULTI_ITEMS)
                mAdapter.notifyDataSetChanged();
        }
        return this;
    }

    /**
     * Sets the height of item
     * @param height The size in pixels.
     * @return The SimpleDialog for chaining methods.
     */
    public SimpleDialog itemHeight(int height) {
        if (mItemHeight != height) {
            mItemHeight = height;
            if (mAdapter != null)
                mAdapter.notifyDataSetChanged();
        }
        return this;
    }

    /**
     * Sets the text color, size, style of the item view from the specified TextAppearance resource.
     * @param resId The resourceId value.
     * @return The SimpleDialog for chaining methods.
     */
    public SimpleDialog itemTextAppearance(int resId) {
        if (mItemTextAppearance != resId) {
            mItemTextAppearance = resId;
            if (mAdapter != null)
                mAdapter.notifyDataSetChanged();
        }
        return this;
    }

    private void initListView() {
        mListView = new InternalListView(getContext());
        mListView.setDividerHeight(0);
        mListView.setCacheColorHint(0x00000000);
        mListView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
        mListView.setClipToPadding(false);
        mListView.setSelector(BlankDrawable.getInstance());
        mListView.setPadding(0, 0, 0, mContentPadding - mActionPadding);
        mListView.setVerticalFadingEdgeEnabled(false);
        mListView.setOverScrollMode(ListView.OVER_SCROLL_NEVER);
        ViewCompat.setLayoutDirection(mListView, ViewCompat.LAYOUT_DIRECTION_INHERIT);

        mAdapter = new InternalAdapter();
        mListView.setAdapter(mAdapter);
    }

    /**
     * Set the list of items in single-choice mode.
     * @param items The list of items.
     * @param selectedIndex The index of selected item.
     * @return The SimpleDialog for chaining methods.
     */
    public SimpleDialog items(CharSequence[] items, int selectedIndex) {
        if (mListView == null)
            initListView();

        mMode = MODE_ITEMS;
        mAdapter.setItems(items, selectedIndex);
        super.contentView(mListView);
        return this;
    }

    /**
     * Set the list of items in multi-choice mode.
     * @param items The list of items.
     * @param selectedIndexes The indexes of selected items.
     * @return The SimpleDialog for chaining methods.
     */
    public SimpleDialog multiChoiceItems(CharSequence[] items, int... selectedIndexes) {
        if (mListView == null)
            initListView();

        mMode = MODE_MULTI_ITEMS;
        mAdapter.setItems(items, selectedIndexes);
        super.contentView(mListView);
        return this;
    }

    /**
     * Set a listener will be called when the checked state of a item is changed.
     * @param listener The {@link OnSelectionChangedListener} will be called.
     * @return The SimpleDialog for chaining methods.
     */
    public SimpleDialog onSelectionChangedListener(OnSelectionChangedListener listener) {
        mOnSelectionChangedListener = listener;
        return this;
    }

    /**
     * @return The list of index of all selected items.
     */
    public int[] getSelectedIndexes() {
        return mAdapter == null ? null : mAdapter.getSelectedIndexes();
    }

    /**
     * @return The list of value of all selected items.
     */
    public CharSequence[] getSelectedValues() {
        return mAdapter.getSelectedValues();
    }

    /**
     * @return The index of selected item.
     */
    public int getSelectedIndex() {
        return mAdapter == null ? -1 : mAdapter.getLastSelectedIndex();
    }

    /**
     * @return The value of selected item.
     */
    public CharSequence getSelectedValue() {
        return mAdapter.getLastSelectedValue();
    }

    private class InternalScrollView extends ScrollView {

        private boolean mIsRtl = false;

        public InternalScrollView(Context context) {
            super(context);
        }

        public void onRtlPropertiesChanged(int layoutDirection) {
            boolean rtl = layoutDirection == LAYOUT_DIRECTION_RTL;
            if (mIsRtl != rtl) {
                mIsRtl = rtl;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                    View v = getChildAt(0);
                    if (v != null && v == mMessage)
                        mMessage.setTextDirection(mIsRtl ? View.TEXT_DIRECTION_RTL : View.TEXT_DIRECTION_LTR);
                }
                requestLayout();
            }
        }

        public boolean isLayoutRtl() {
            return mIsRtl;
        }

        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            super.onLayout(changed, l, t, r, b);

            View child = getChildAt(0);
            showDivider(child != null
                    && child.getMeasuredHeight() > getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
        }
    }

    private class InternalListView extends ListView {

        private boolean mIsRtl = false;

        public InternalListView(Context context) {
            super(context);
        }

        public void onRtlPropertiesChanged(int layoutDirection) {
            boolean rtl = layoutDirection == LAYOUT_DIRECTION_RTL;
            if (mIsRtl != rtl) {
                mIsRtl = rtl;
                requestLayout();
            }
        }

        public boolean isLayoutRtl() {
            return mIsRtl;
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int heightMode = View.MeasureSpec.getMode(heightMeasureSpec);
            if (heightMode == View.MeasureSpec.UNSPECIFIED) {
                if (mItemHeight != ViewGroup.LayoutParams.WRAP_CONTENT)
                    heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
                            mItemHeight * getAdapter().getCount() + getPaddingTop() + getPaddingBottom(),
                            View.MeasureSpec.EXACTLY);
            }

            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }

        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            super.onLayout(changed, l, t, r, b);

            int totalHeight = 0;
            int childCount = getChildCount();

            for (int i = 0; i < childCount; i++)
                totalHeight += getChildAt(i).getMeasuredHeight();

            showDivider(totalHeight > getMeasuredHeight()
                    || (totalHeight == getMeasuredHeight() && getAdapter().getCount() > childCount));
        }

    }

    private class InternalAdapter extends BaseAdapter implements CompoundButton.OnCheckedChangeListener {

        private CharSequence[] mItems;
        private boolean[] mSelected;
        private int mLastSelectedIndex;

        public void setItems(CharSequence[] items, int... selectedIndexes) {
            mItems = items;

            if (mSelected == null || mSelected.length != items.length)
                mSelected = new boolean[items.length];

            for (int i = 0; i < mSelected.length; i++)
                mSelected[i] = false;

            if (selectedIndexes != null)
                for (int index : selectedIndexes)
                    if (index >= 0 && index < mSelected.length) {
                        mSelected[index] = true;
                        mLastSelectedIndex = index;
                    }

            notifyDataSetChanged();
        }

        public int getLastSelectedIndex() {
            return mLastSelectedIndex;
        }

        public CharSequence getLastSelectedValue() {
            return mItems[mLastSelectedIndex];
        }

        public int[] getSelectedIndexes() {
            int count = 0;
            for (int i = 0; i < mSelected.length; i++)
                if (mSelected[i])
                    count++;

            if (count == 0)
                return null;

            int[] result = new int[count];
            count = 0;
            for (int i = 0; i < mSelected.length; i++)
                if (mSelected[i]) {
                    result[count] = i;
                    count++;
                }

            return result;
        }

        public CharSequence[] getSelectedValues() {
            int count = 0;
            for (int i = 0; i < mSelected.length; i++)
                if (mSelected[i])
                    count++;

            if (count == 0)
                return null;

            CharSequence[] result = new CharSequence[count];
            count = 0;
            for (int i = 0; i < mSelected.length; i++)
                if (mSelected[i]) {
                    result[count] = mItems[i];
                    count++;
                }

            return result;
        }

        @Override
        public int getCount() {
            return mItems == null ? 0 : mItems.length;
        }

        @Override
        public Object getItem(int position) {
            return mItems == null ? 0 : mItems[position];
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            CompoundButton v = (CompoundButton) convertView;
            if (v == null) {
                if (mMode == MODE_MULTI_ITEMS) {
                    v = new CheckBox(parent.getContext());
                    v.applyStyle(mCheckBoxStyle);
                } else {
                    v = new RadioButton(parent.getContext());
                    v.applyStyle(mRadioButtonStyle);
                }
                if (mItemHeight != ViewGroup.LayoutParams.WRAP_CONTENT)
                    v.setMinHeight(mItemHeight);
                v.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
                    v.setTextDirection(((InternalListView) parent).isLayoutRtl() ? View.TEXT_DIRECTION_RTL
                            : View.TEXT_DIRECTION_LTR);
                v.setTextAppearance(v.getContext(), mItemTextAppearance);
                v.setCompoundDrawablePadding(mContentPadding);
            }

            v.setTag(position);
            v.setText(mItems[position]);
            if (v instanceof CheckBox)
                ((CheckBox) v).setCheckedImmediately(mSelected[position]);
            else
                ((RadioButton) v).setCheckedImmediately(mSelected[position]);

            v.setOnCheckedChangeListener(this);

            return v;
        }

        @Override
        public void onCheckedChanged(android.widget.CompoundButton v, boolean isChecked) {
            int position = (Integer) v.getTag();
            if (mSelected[position] != isChecked) {
                mSelected[position] = isChecked;

                if (mOnSelectionChangedListener != null)
                    mOnSelectionChangedListener.onSelectionChanged(position, mSelected[position]);
            }

            if (mMode == MODE_ITEMS && isChecked && mLastSelectedIndex != position) {
                mSelected[mLastSelectedIndex] = false;

                if (mOnSelectionChangedListener != null)
                    mOnSelectionChangedListener.onSelectionChanged(mLastSelectedIndex, false);

                CompoundButton child = (CompoundButton) mListView
                        .getChildAt(mLastSelectedIndex - mListView.getFirstVisiblePosition());
                if (child != null)
                    child.setChecked(false);

                mLastSelectedIndex = position;
            }
        }
    }

    public static class Builder extends Dialog.Builder implements OnSelectionChangedListener {

        protected int mMode;
        protected CharSequence mMessage;
        protected CharSequence[] mItems;
        protected int[] mSelectedIndexes;

        public Builder() {
            super(R.style.Material_App_Dialog_Simple_Light);
        }

        public Builder(int styleId) {
            super(styleId);
        }

        public Builder message(CharSequence message) {
            mMode = MODE_MESSAGE;
            mMessage = message;
            return this;
        }

        public Builder items(CharSequence[] items, int selectedIndex) {
            mMode = MODE_ITEMS;
            mItems = items;
            mSelectedIndexes = new int[] { selectedIndex };
            return this;
        }

        public Builder multiChoiceItems(CharSequence[] items, int... selectedIndexes) {
            mMode = MODE_MULTI_ITEMS;
            mItems = items;
            mSelectedIndexes = selectedIndexes;
            return this;
        }

        public int getSelectedIndex() {
            if (mMode == MODE_ITEMS || mMode == MODE_MULTI_ITEMS)
                return mSelectedIndexes[0];

            return -1;
        }

        public CharSequence getSelectedValue() {
            int index = getSelectedIndex();
            return index >= 0 ? mItems[index] : null;
        }

        public int[] getSelectedIndexes() {
            if (mMode == MODE_ITEMS || mMode == MODE_MULTI_ITEMS)
                return mSelectedIndexes;

            return null;
        }

        public CharSequence[] getSelectedValues() {
            int[] indexes = getSelectedIndexes();
            if (indexes == null || indexes.length == 0)
                return null;

            CharSequence[] result = new CharSequence[indexes.length];
            for (int i = 0; i < indexes.length; i++)
                result[i] = mItems[indexes[i]];

            return result;
        }

        @Override
        protected Dialog onBuild(Context context, int styleId) {
            SimpleDialog dialog = new SimpleDialog(context, styleId);

            switch (mMode) {
            case MODE_MESSAGE:
                dialog.message(mMessage);
                break;
            case MODE_ITEMS:
                dialog.items(mItems, mSelectedIndexes == null ? 0 : mSelectedIndexes[0]);
                dialog.onSelectionChangedListener(this);
                break;
            case MODE_MULTI_ITEMS:
                dialog.multiChoiceItems(mItems, mSelectedIndexes);
                dialog.onSelectionChangedListener(this);
                break;
            }

            return dialog;
        }

        @Override
        public void onSelectionChanged(int index, boolean selected) {
            switch (mMode) {
            case MODE_ITEMS:
                if (selected) {
                    if (mSelectedIndexes == null)
                        mSelectedIndexes = new int[] { index };
                    else
                        mSelectedIndexes[0] = index;
                }
                break;
            case MODE_MULTI_ITEMS:
                mSelectedIndexes = ((SimpleDialog) mDialog).getSelectedIndexes();
                break;
            }
        }

        protected Builder(Parcel in) {
            super(in);
        }

        @Override
        protected void onReadFromParcel(Parcel in) {
            mMode = in.readInt();
            switch (mMode) {
            case MODE_MESSAGE:
                mMessage = (CharSequence) in.readParcelable(null);
                break;
            case MODE_ITEMS: {
                Parcelable[] values = in.readParcelableArray(null);
                if (values != null && values.length > 0) {
                    mItems = new CharSequence[values.length];
                    for (int i = 0; i < mItems.length; i++)
                        mItems[i] = (CharSequence) values[i];
                } else
                    mItems = null;
                mSelectedIndexes = new int[] { in.readInt() };
                break;
            }
            case MODE_MULTI_ITEMS: {
                Parcelable[] values = in.readParcelableArray(null);
                if (values != null && values.length > 0) {
                    mItems = new CharSequence[values.length];
                    for (int i = 0; i < mItems.length; i++)
                        mItems[i] = (CharSequence) values[i];
                } else
                    mItems = null;
                int length = in.readInt();
                if (length > 0) {
                    mSelectedIndexes = new int[length];
                    in.readIntArray(mSelectedIndexes);
                }
                break;
            }
            }
        }

        @Override
        protected void onWriteToParcel(Parcel dest, int flags) {
            dest.writeInt(mMode);
            switch (mMode) {
            case MODE_MESSAGE:
                dest.writeValue(mMessage);
                break;
            case MODE_ITEMS:
                dest.writeArray(mItems);
                dest.writeInt(mSelectedIndexes == null ? 0 : mSelectedIndexes[0]);
                break;
            case MODE_MULTI_ITEMS:
                dest.writeArray(mItems);
                int length = mSelectedIndexes == null ? 0 : mSelectedIndexes.length;
                dest.writeInt(length);
                if (length > 0)
                    dest.writeIntArray(mSelectedIndexes);
                break;
            }
        }

        public static final Creator<Builder> CREATOR = new Creator<Builder>() {
            public Builder createFromParcel(Parcel in) {
                return new Builder(in);
            }

            public Builder[] newArray(int size) {
                return new Builder[size];
            }
        };

    }
}