1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.charging; 18 19 import android.content.Context; 20 import android.graphics.Canvas; 21 import android.graphics.Paint; 22 import android.util.AttributeSet; 23 import android.view.View; 24 import android.view.animation.Interpolator; 25 26 import com.android.settingslib.Utils; 27 import com.android.systemui.Interpolators; 28 import com.android.systemui.R; 29 30 final class WirelessChargingView extends View { 31 32 private Interpolator mInterpolator; 33 private float mPathGone; 34 private float mInterpolatedPathGone; 35 private long mAnimationStartTime; 36 private long mScaleDotsDuration; 37 38 private boolean mFinishedAnimatingDots = false; 39 private int mNumDots; 40 41 private double mAngleOffset; 42 private double mCurrAngleOffset; 43 44 private int mDotsRadiusStart; 45 private int mDotsRadiusEnd; 46 private int mCurrDotRadius; 47 48 private int mMainCircleStartRadius; 49 private int mMainCircleEndRadius; 50 private int mMainCircleCurrentRadius; 51 52 private int mCenterX; 53 private int mCenterY; 54 55 private Paint mPaint; 56 private Context mContext; 57 WirelessChargingView(Context context)58 public WirelessChargingView(Context context) { 59 super(context); 60 init(context, null); 61 } 62 WirelessChargingView(Context context, AttributeSet attr)63 public WirelessChargingView(Context context, AttributeSet attr) { 64 super(context, attr); 65 init(context, attr); 66 } 67 WirelessChargingView(Context context, AttributeSet attr, int styleAttr)68 public WirelessChargingView(Context context, AttributeSet attr, int styleAttr) { 69 super(context, attr, styleAttr); 70 init(context, attr); 71 } 72 init(Context context, AttributeSet attr)73 public void init(Context context, AttributeSet attr) { 74 mContext = context; 75 76 mDotsRadiusStart = context.getResources().getDimensionPixelSize( 77 R.dimen.wireless_charging_dots_radius_start); 78 mDotsRadiusEnd = context.getResources().getDimensionPixelSize( 79 R.dimen.wireless_charging_dots_radius_end); 80 81 mMainCircleStartRadius = context.getResources().getDimensionPixelSize( 82 R.dimen.wireless_charging_circle_radius_start); 83 mMainCircleEndRadius = context.getResources().getDimensionPixelSize( 84 R.dimen.wireless_charging_circle_radius_end); 85 mMainCircleCurrentRadius = mMainCircleStartRadius; 86 87 mAngleOffset = context.getResources().getInteger(R.integer.wireless_charging_angle_offset); 88 mScaleDotsDuration = (long) context.getResources().getInteger( 89 R.integer.wireless_charging_scale_dots_duration); 90 mNumDots = context.getResources().getInteger(R.integer.wireless_charging_num_dots); 91 92 setupPaint(); 93 mInterpolator = Interpolators.LINEAR_OUT_SLOW_IN; 94 } 95 setupPaint()96 private void setupPaint() { 97 mPaint = new Paint(); 98 mPaint.setColor(Utils.getColorAttr(mContext, R.attr.wallpaperTextColor)); 99 } 100 setPaintColor(int color)101 public void setPaintColor(int color) { 102 mPaint.setColor(color); 103 } 104 105 @Override onDraw(final Canvas canvas)106 protected void onDraw(final Canvas canvas) { 107 super.onDraw(canvas); 108 109 if (mAnimationStartTime == 0) { 110 mAnimationStartTime = System.currentTimeMillis(); 111 } 112 113 updateDrawingParameters(); 114 drawCircles(canvas); 115 116 if (!mFinishedAnimatingDots) { 117 invalidate(); 118 } 119 } 120 121 /** 122 * Draws a larger circle of radius {@link WirelessChargingView#mMainCircleEndRadius} composed of 123 * {@link WirelessChargingView#mNumDots} smaller circles 124 * @param canvas 125 */ drawCircles(Canvas canvas)126 private void drawCircles(Canvas canvas) { 127 mCenterX = canvas.getWidth() / 2; 128 mCenterY = canvas.getHeight() / 2; 129 130 // draws mNumDots to compose a larger, main circle 131 for (int circle = 0; circle < mNumDots; circle++) { 132 double angle = ((mCurrAngleOffset) * Math.PI / 180) + (circle * ((2 * Math.PI) 133 / mNumDots)); 134 135 int x = (int) (mCenterX + Math.cos(angle) * (mMainCircleCurrentRadius + 136 mCurrDotRadius)); 137 int y = (int) (mCenterY + Math.sin(angle) * (mMainCircleCurrentRadius + 138 mCurrDotRadius)); 139 140 canvas.drawCircle(x, y, mCurrDotRadius, mPaint); 141 } 142 143 if (mMainCircleCurrentRadius >= mMainCircleEndRadius) { 144 mFinishedAnimatingDots = true; 145 } 146 } 147 updateDrawingParameters()148 private void updateDrawingParameters() { 149 long now = System.currentTimeMillis(); 150 long timeSinceStart = now - mAnimationStartTime; 151 mPathGone = getPathGone(now); 152 mInterpolatedPathGone = mInterpolator.getInterpolation(mPathGone); 153 154 // Position Dots: update main circle radius (that the dots compose) 155 if (mPathGone < 1.0f) { 156 mMainCircleCurrentRadius = mMainCircleStartRadius + (int) (mInterpolatedPathGone * 157 (mMainCircleEndRadius - mMainCircleStartRadius)); 158 } else { 159 mMainCircleCurrentRadius = mMainCircleEndRadius; 160 } 161 162 // Scale Dots: update dot radius 163 if (timeSinceStart < mScaleDotsDuration) { 164 mCurrDotRadius = mDotsRadiusStart + (int) (mInterpolator.getInterpolation((float) 165 timeSinceStart / mScaleDotsDuration) * (mDotsRadiusEnd - mDotsRadiusStart)); 166 } else { 167 mCurrDotRadius = mDotsRadiusEnd; 168 } 169 170 // Rotation Dot Group: dots rotate from 0 to 20 degrees 171 mCurrAngleOffset = mAngleOffset * mPathGone; 172 } 173 174 /** 175 * @return decimal depicting how far along the creation of the larger circle (of circles) is 176 * For values < 1.0, the larger circle is being drawn 177 * For values > 1.0 the larger circle has been drawn and further animation can occur 178 */ getPathGone(long now)179 private float getPathGone(long now) { 180 return (float) (now - mAnimationStartTime) / (WirelessChargingAnimation.DURATION); 181 } 182 }