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 17package com.android.systemui.charging; 18 19import android.content.Context; 20import android.graphics.Canvas; 21import android.graphics.Paint; 22import android.util.AttributeSet; 23import android.view.View; 24import android.view.animation.Interpolator; 25 26import com.android.settingslib.Utils; 27import com.android.systemui.Interpolators; 28import com.android.systemui.R; 29 30final 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 58 public WirelessChargingView(Context context) { 59 super(context); 60 init(context, null); 61 } 62 63 public WirelessChargingView(Context context, AttributeSet attr) { 64 super(context, attr); 65 init(context, attr); 66 } 67 68 public WirelessChargingView(Context context, AttributeSet attr, int styleAttr) { 69 super(context, attr, styleAttr); 70 init(context, attr); 71 } 72 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 96 private void setupPaint() { 97 mPaint = new Paint(); 98 mPaint.setColor(Utils.getColorAttr(mContext, R.attr.wallpaperTextColor)); 99 } 100 101 public void setPaintColor(int color) { 102 mPaint.setColor(color); 103 } 104 105 @Override 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 */ 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 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 */ 179 private float getPathGone(long now) { 180 return (float) (now - mAnimationStartTime) / (WirelessChargingAnimation.DURATION); 181 } 182}