/* * 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; } }