| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * 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.android.systemui.charging; |
| |
| import android.content.Context; |
| import android.graphics.Canvas; |
| import android.graphics.Paint; |
| import android.util.AttributeSet; |
| import android.view.View; |
| import android.view.animation.Interpolator; |
| |
| import com.android.settingslib.Utils; |
| import com.android.systemui.Interpolators; |
| import com.android.systemui.R; |
| |
| final class WirelessChargingView extends View { |
| |
| private Interpolator mInterpolator; |
| private float mPathGone; |
| private float mInterpolatedPathGone; |
| private long mAnimationStartTime; |
| private long mScaleDotsDuration; |
| |
| private boolean mFinishedAnimatingDots = false; |
| private int mNumDots; |
| |
| private double mAngleOffset; |
| private double mCurrAngleOffset; |
| |
| private int mDotsRadiusStart; |
| private int mDotsRadiusEnd; |
| private int mCurrDotRadius; |
| |
| private int mMainCircleStartRadius; |
| private int mMainCircleEndRadius; |
| private int mMainCircleCurrentRadius; |
| |
| private int mCenterX; |
| private int mCenterY; |
| |
| private Paint mPaint; |
| private Context mContext; |
| |
| public WirelessChargingView(Context context) { |
| super(context); |
| init(context, null); |
| } |
| |
| public WirelessChargingView(Context context, AttributeSet attr) { |
| super(context, attr); |
| init(context, attr); |
| } |
| |
| public WirelessChargingView(Context context, AttributeSet attr, int styleAttr) { |
| super(context, attr, styleAttr); |
| init(context, attr); |
| } |
| |
| public void init(Context context, AttributeSet attr) { |
| mContext = context; |
| |
| mDotsRadiusStart = context.getResources().getDimensionPixelSize( |
| R.dimen.wireless_charging_dots_radius_start); |
| mDotsRadiusEnd = context.getResources().getDimensionPixelSize( |
| R.dimen.wireless_charging_dots_radius_end); |
| |
| mMainCircleStartRadius = context.getResources().getDimensionPixelSize( |
| R.dimen.wireless_charging_circle_radius_start); |
| mMainCircleEndRadius = context.getResources().getDimensionPixelSize( |
| R.dimen.wireless_charging_circle_radius_end); |
| mMainCircleCurrentRadius = mMainCircleStartRadius; |
| |
| mAngleOffset = context.getResources().getInteger(R.integer.wireless_charging_angle_offset); |
| mScaleDotsDuration = (long) context.getResources().getInteger( |
| R.integer.wireless_charging_scale_dots_duration); |
| mNumDots = context.getResources().getInteger(R.integer.wireless_charging_num_dots); |
| |
| setupPaint(); |
| mInterpolator = Interpolators.LINEAR_OUT_SLOW_IN; |
| } |
| |
| private void setupPaint() { |
| mPaint = new Paint(); |
| mPaint.setColor(Utils.getColorAttr(mContext, R.attr.wallpaperTextColor)); |
| } |
| |
| @Override |
| protected void onDraw(final Canvas canvas) { |
| super.onDraw(canvas); |
| |
| if (mAnimationStartTime == 0) { |
| mAnimationStartTime = System.currentTimeMillis(); |
| } |
| |
| updateDrawingParameters(); |
| drawCircles(canvas); |
| |
| if (!mFinishedAnimatingDots) { |
| invalidate(); |
| } |
| } |
| |
| /** |
| * Draws a larger circle of radius {@link WirelessChargingView#mMainCircleEndRadius} composed of |
| * {@link WirelessChargingView#mNumDots} smaller circles |
| * @param canvas |
| */ |
| private void drawCircles(Canvas canvas) { |
| mCenterX = canvas.getWidth() / 2; |
| mCenterY = canvas.getHeight() / 2; |
| |
| // draws mNumDots to compose a larger, main circle |
| for (int circle = 0; circle < mNumDots; circle++) { |
| double angle = ((mCurrAngleOffset) * Math.PI / 180) + (circle * ((2 * Math.PI) |
| / mNumDots)); |
| |
| int x = (int) (mCenterX + Math.cos(angle) * (mMainCircleCurrentRadius + |
| mCurrDotRadius)); |
| int y = (int) (mCenterY + Math.sin(angle) * (mMainCircleCurrentRadius + |
| mCurrDotRadius)); |
| |
| canvas.drawCircle(x, y, mCurrDotRadius, mPaint); |
| } |
| |
| if (mMainCircleCurrentRadius >= mMainCircleEndRadius) { |
| mFinishedAnimatingDots = true; |
| } |
| } |
| |
| private void updateDrawingParameters() { |
| long now = System.currentTimeMillis(); |
| long timeSinceStart = now - mAnimationStartTime; |
| mPathGone = getPathGone(now); |
| mInterpolatedPathGone = mInterpolator.getInterpolation(mPathGone); |
| |
| // Position Dots: update main circle radius (that the dots compose) |
| if (mPathGone < 1.0f) { |
| mMainCircleCurrentRadius = mMainCircleStartRadius + (int) (mInterpolatedPathGone * |
| (mMainCircleEndRadius - mMainCircleStartRadius)); |
| } else { |
| mMainCircleCurrentRadius = mMainCircleEndRadius; |
| } |
| |
| // Scale Dots: update dot radius |
| if (timeSinceStart < mScaleDotsDuration) { |
| mCurrDotRadius = mDotsRadiusStart + (int) (mInterpolator.getInterpolation((float) |
| timeSinceStart / mScaleDotsDuration) * (mDotsRadiusEnd - mDotsRadiusStart)); |
| } else { |
| mCurrDotRadius = mDotsRadiusEnd; |
| } |
| |
| // Rotation Dot Group: dots rotate from 0 to 20 degrees |
| mCurrAngleOffset = mAngleOffset * mPathGone; |
| } |
| |
| /** |
| * @return decimal depicting how far along the creation of the larger circle (of circles) is |
| * For values < 1.0, the larger circle is being drawn |
| * For values > 1.0 the larger circle has been drawn and further animation can occur |
| */ |
| private float getPathGone(long now) { |
| return (float) (now - mAnimationStartTime) / (WirelessChargingAnimation.DURATION); |
| } |
| } |