org.opensilk.common.widget.FloatingActionButton.java Source code

Java tutorial

Introduction

Here is the source code for org.opensilk.common.widget.FloatingActionButton.java

Source

/*
 * Copyright (c) 2014 OpenSilk Productions LLC
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.opensilk.common.widget;

import android.animation.AnimatorInflater;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.RippleDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.StateListDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.os.Build;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v4.view.MotionEventCompat;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageButton;

import org.opensilk.common.R;
import org.opensilk.common.util.ThemeUtils;
import org.opensilk.common.util.VersionUtils;

/**
 * Created by drew on 10/30/14.
 */
public class FloatingActionButton extends ImageButton {

    public interface OnDoubleClickListener {
        void onDoubleClick(View view);
    }

    public static final int COLOR_ACCENT = 0;
    public static final int COLOR_WHITE = 1;
    public static final int COLOR_BLACK = 2;

    protected int mColor;
    //Used to create the state list for compat drawable
    //https://code.google.com/p/android/issues/detail?id=26251
    protected int mColorNormal;
    protected int mColorPressed;

    private OnDoubleClickListener mOnDoubleClickListener;
    private GestureDetectorCompat mGestureDetector;

    public FloatingActionButton(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mGestureDetector = new GestureDetectorCompat(getContext(), new GestureListener());
        TypedArray attr = context.obtainStyledAttributes(attrs, R.styleable.FloatingActionButton, defStyleAttr, 0);
        if (attr != null) {
            try {
                mColor = attr.getInt(R.styleable.FloatingActionButton_fabColor, COLOR_ACCENT);
                switch (mColor) {
                case COLOR_BLACK:
                    mColorNormal = Color.BLACK;
                    break;
                case COLOR_WHITE:
                    mColorNormal = Color.WHITE;
                    break;
                case COLOR_ACCENT:
                default:
                    mColorNormal = ThemeUtils.getColorAccent(getContext());
                }
                mColorPressed = ThemeUtils.getColorControlNormal(getContext());
            } finally {
                attr.recycle();
            }
        }
        init();
    }

    protected void init() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            switch (mColor) {
            case COLOR_BLACK:
                setBackgroundResource(R.drawable.fab_ripple_black);
                break;
            case COLOR_WHITE:
                setBackgroundResource(R.drawable.fab_ripple_white);
                break;
            case COLOR_ACCENT:
            default:
                setBackgroundResource(R.drawable.fab_ripple_accent);
                break;
            }
            setStateListAnimator(AnimatorInflater.loadStateListAnimator(getContext(), R.animator.fab_elevation));
            setElevation(getResources().getDimension(R.dimen.fab_elevation));
            //            setImageResource(mIcon);
            return;
        } else {
            setBackgroundCompat(createSelectableDrawable());
            //            setImageResource(mIcon);
        }
    }

    /**
     * >= api 21
     */
    @SuppressWarnings("NewApi")
    protected RippleDrawable createRippleDrawable() {
        ShapeDrawable drawable = new ShapeDrawable(new OvalShape());
        drawable.getPaint().setColor(mColorNormal);
        return new RippleDrawable(createRippleStateList(), drawable, null);
    }

    protected ColorStateList createRippleStateList() {
        int[][] states = new int[1][];
        int[] colors = new int[1];
        states[0] = new int[0];
        colors[0] = mColorPressed;
        return new ColorStateList(states, colors);
    }

    /**
     * <= api 19
     */
    protected Drawable createSelectableDrawable() {

        ShapeDrawable drawableNormal = new ShapeDrawable(new OvalShape());
        drawableNormal.getPaint().setColor(mColorNormal);

        StateListDrawable stateDrawable = new StateListDrawable();

        ShapeDrawable drawableHighlight = new ShapeDrawable(new OvalShape());
        drawableHighlight.getPaint().setColor(mColorPressed);

        stateDrawable.addState(new int[] { android.R.attr.state_pressed }, drawableHighlight);
        stateDrawable.addState(new int[0], null);

        LayerDrawable layerDrawable = new LayerDrawable(new Drawable[] { drawableNormal, stateDrawable });

        return layerDrawable;
    }

    @Override
    public void setImageLevel(int level) {
        if (getDrawable() != null && getDrawable().getLevel() == level)
            return;
        maybeDoCircularReveal();
        super.setImageLevel(level);
    }

    @Override
    public void setImageState(int[] state, boolean merge) {
        if (getDrawable() != null && getDrawable().getState() == state)
            return;
        maybeDoCircularReveal();
        super.setImageState(state, merge);
    }

    @SuppressWarnings("NewApi")
    protected void maybeDoCircularReveal() {
        if (true)
            return; //XXX STUBBED till i make it look better
        if (VersionUtils.hasLollipop()) {
            ViewAnimationUtils.createCircularReveal(this, getWidth() / 2, getHeight() / 2, 0, getHeight() * 2)
                    .start();
        }
    }

    @SuppressLint("NewApi")
    @SuppressWarnings("deprecation")
    protected void setBackgroundCompat(Drawable drawable) {
        if (VersionUtils.hasJellyBean()) {
            setBackground(drawable);
        } else {
            setBackgroundDrawable(drawable);
        }
    }

    public void setOnDoubleClickListener(OnDoubleClickListener l) {
        if (!isClickable())
            setClickable(true);
        mOnDoubleClickListener = l;
    }

    protected boolean performDoubleClick() {
        if (mOnDoubleClickListener != null) {
            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
            playSoundEffect(SoundEffectConstants.CLICK);
            mOnDoubleClickListener.onDoubleClick(this);
            return true;
        } else {
            return performClick();
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (MotionEventCompat.getActionMasked(event)) {
        case MotionEvent.ACTION_DOWN:
            setPressed(true);
            break;
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
            setPressed(false);
            break;
        }
        return mGestureDetector.onTouchEvent(event);
    }

    private class GestureListener extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onDown(MotionEvent e) {
            return isClickable();
        }

        @Override
        public void onShowPress(MotionEvent e) {
        }

        @Override
        public void onLongPress(MotionEvent e) {
            performLongClick();
        }

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            return isClickable() && performClick();
        }

        @Override
        public boolean onDoubleTap(MotionEvent e) {
            return isClickable() && performDoubleClick();
        }

    }
}