com.appsimobile.appsii.module.home.SunriseDrawable.java Source code

Java tutorial

Introduction

Here is the source code for com.appsimobile.appsii.module.home.SunriseDrawable.java

Source

/*
 * Copyright 2015. Appsi Mobile
 *
 * 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.appsimobile.appsii.module.home;

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v4.graphics.ColorUtils;
import android.text.TextUtils;
import android.view.View;

import com.appsimobile.appsii.DrawableCompat;
import com.appsimobile.appsii.R;

import java.util.Locale;

/**
 * Created by nick on 22/01/15.
 */
public class SunriseDrawable extends Drawable {

    final Paint mArcPaint;

    final Paint mArcFillPaint;

    final Paint mLinePaint;

    final Paint mDotPaint;

    final int mTopOffset;

    final int mLeftOffset;

    final int mRightOffset;

    /**
     * Offset from the bottom
     */
    final int mBottomOffset;

    /**
     * The arc that displays the path of the sun
     */
    final Path mArc = new Path();

    /**
     * The path used to fill the arc with
     */
    final Path mArcFill = new Path();

    /**
     * The rect used to clip the mArcFill path with to make it fill until now
     */
    final Rect mClipRect = new Rect();

    /**
     * True when this drawable is in ltr mode. False otherwise
     */
    final boolean mIsRtl;

    /**
     * The image of the sun
     */
    Drawable mSunImage;

    /**
     * The sunrise time in minutes
     */
    int mRiseMinutes;

    /**
     * The sunset time in minutes
     */
    int mSetMinutes;

    /**
     * The current time in minutes
     */
    int mNowMinutes;

    /**
     * The size of the dot at the start and end in px
     */
    final int mDotRadius;

    int mBezier1x;

    int mBezier1y;

    int mBezier2x;

    int mBezier2y;

    int mBezier3x;

    int mBezier3y;

    int mBezier4x;

    int mBezier4y;

    final Context mContext;

    public SunriseDrawable(Context context) {

        mContext = context;

        Resources res = context.getResources();
        final TypedArray a = context.obtainStyledAttributes(new int[] { R.attr.colorPrimary, R.attr.colorAccent,
                R.attr.colorPrimaryDark, R.attr.appsiHomeWidgetPrimaryColor, });

        int primaryColor = a.getColor(0, Color.BLACK);
        int accentColor = a.getColor(1, Color.BLACK);
        int primaryColorDark = a.getColor(2, Color.BLACK);
        int textColor = a.getColor(3, Color.BLACK);
        a.recycle();

        mIsRtl = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL;

        float density = res.getDisplayMetrics().density;
        int sunBounds = (int) (density * 16);
        mTopOffset = (int) (density * 12);
        mLeftOffset = (int) (density * 24);
        mRightOffset = (int) (density * 24);
        mBottomOffset = (int) (density * 24);
        mDotRadius = (int) (density * 2);

        mSunImage = mContext.getResources().getDrawable(R.drawable.ic_weather_clear);
        mSunImage.setBounds(0, 0, sunBounds, sunBounds);

        mArcPaint = new Paint();
        mArcPaint.setColor(primaryColorDark);
        mArcPaint.setStyle(Paint.Style.STROKE);
        mArcPaint.setAntiAlias(true);

        mArcFillPaint = new Paint();
        mArcFillPaint.setColor(primaryColor);
        mArcFillPaint.setAlpha(64);
        mArcFillPaint.setStyle(Paint.Style.FILL);
        mArcFillPaint.setAntiAlias(true);

        mLinePaint = new Paint();
        mLinePaint.setColor(textColor);
        mLinePaint.setAlpha(128);
        mLinePaint.setStyle(Paint.Style.STROKE);

        mDotPaint = new Paint();
        mDotPaint.setStyle(Paint.Style.FILL);
        mDotPaint.setColor(primaryColorDark);
        mDotPaint.setAntiAlias(true);
    }

    void customTheme(int drawableResId, int colorPrimary, int textColor, int tintColor) {
        Resources res = mContext.getResources();
        mSunImage = res.getDrawable(drawableResId);
        DrawableCompat.setTintColorCompat(mSunImage, tintColor);
        mArcFillPaint.setColor(colorPrimary);
        mArcFillPaint.setColor(ColorUtils.setAlphaComponent(colorPrimary, 128));
        mLinePaint.setColor(textColor);
        mDotPaint.setColor(tintColor);

        float density = res.getDisplayMetrics().density;
        int sunBounds = (int) (density * 16);
        mSunImage.setBounds(0, 0, sunBounds, sunBounds);

        Callback callback = getCallback();
        if (callback != null) {
            callback.invalidateDrawable(this);
        }
    }

    public void setTime(int riseMinutes, int setMinutes, int nowMinutes) {
        mRiseMinutes = riseMinutes;
        mSetMinutes = setMinutes;
        mNowMinutes = nowMinutes;
        invalidateSelf();
    }

    @Override
    public void draw(Canvas canvas) {
        Rect bounds = getBounds();
        if (mIsRtl) {
            canvas.save();
            canvas.scale(-1, 1, bounds.centerX(), bounds.centerY());
        }
        float pct = drawArc(canvas, bounds);

        if (pct < 0) {
            pct = 0;
        } else if (pct > 1) {
            pct = 1;
        }

        int y = bounds.bottom - mBottomOffset;
        canvas.drawLine(bounds.left + mLeftOffset / 2, y, bounds.right - mRightOffset / 2, y, mLinePaint);

        drawSun(canvas, pct);

        if (mIsRtl) {
            canvas.restore();
        }

    }

    @Override
    public void setBounds(int left, int top, int right, int bottom) {
        super.setBounds(left, top, right, bottom);
        updateArcs(left, top, right, bottom);
    }

    private void updateArcs(int left, int top, int right, int bottom) {
        mBezier1x = left + mLeftOffset;
        mBezier1y = bottom - mBottomOffset;
        mBezier2y = 0; //top + mTopOffset;
        mBezier2x = (left + mLeftOffset + right - mRightOffset) / 3;
        mBezier3x = mBezier2x * 2;
        mBezier3y = mBezier2y;
        mBezier4x = right - mRightOffset;
        mBezier4y = mBezier1y;//bottom - mBottomOffset;

        mArc.reset();
        mArc.moveTo(mBezier1x, mBezier1y);
        mArc.cubicTo(mBezier2x, mBezier2y, mBezier3x, mBezier3y, mBezier4x, mBezier4y);

        mArcFill.reset();
        mArcFill.moveTo(mBezier1x, mBezier1y);
        mArcFill.cubicTo(mBezier2x, mBezier2y, mBezier3x, mBezier3y, mBezier4x, mBezier4y);
        mArcFill.close();

    }

    @Override
    public void setAlpha(int alpha) {

    }

    @Override
    public void setColorFilter(ColorFilter cf) {

    }

    @Override
    public boolean isAutoMirrored() {
        return true;
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }

    @Override
    public int getIntrinsicWidth() {
        return (mLeftOffset + mRightOffset) * 2;
    }

    @Override
    public int getIntrinsicHeight() {
        return (mTopOffset + mBottomOffset) * 2;
    }

    private float drawArc(Canvas canvas, Rect bounds) {
        canvas.drawPath(mArc, mArcPaint);

        int minuteSinceStart = mNowMinutes - mRiseMinutes;
        int totalMinutes = mSetMinutes - mRiseMinutes;
        float pct = (minuteSinceStart / (float) totalMinutes);

        int r = bounds.right - mRightOffset;
        int l = bounds.left + mLeftOffset;

        int px = (int) (pct * (r - l));

        mClipRect.set(0, 0, l + px, bounds.bottom);
        canvas.save(Canvas.CLIP_SAVE_FLAG);
        canvas.clipRect(mClipRect);
        canvas.drawPath(mArcFill, mArcFillPaint);
        canvas.restore();

        return pct;
    }

    private void drawSun(Canvas canvas, float pct) {

        // Draw the dots
        canvas.drawCircle(mBezier1x, mBezier1y, mDotRadius, mDotPaint);
        canvas.drawCircle(mBezier4x, mBezier4y, mDotRadius, mDotPaint);

        // calculate the center point of the sun image
        float x = bezierInterpolation(pct, mBezier1x, mBezier2x, mBezier3x, mBezier4x);
        float y = bezierInterpolation(pct, mBezier1y, mBezier2y, mBezier3y, mBezier4y);

        Rect sunBounds = mSunImage.getBounds();

        x -= sunBounds.width() / 2;
        y -= sunBounds.height() / 2;

        // if we are in rtl, we still want to show the image normally
        // so flip it back
        canvas.translate(x, y);
        if (mIsRtl) {
            canvas.save();
            canvas.scale(-1, 1, sunBounds.centerX(), sunBounds.centerY());
        }
        // draw the sun image
        mSunImage.draw(canvas);
        // restore the flip again
        if (mIsRtl) {
            canvas.restore();
        }
        canvas.translate(-x, -y);

    }

    float bezierInterpolation(float t, float a, float b, float c, float d) {
        float t2 = t * t;
        float t3 = t2 * t;
        return a + (-a * 3 + t * (3 * a - a * t)) * t + (3 * b + t * (-6 * b + b * 3 * t)) * t
                + (c * 3 - c * 3 * t) * t2 + d * t3;
    }
}