本片文章的源码和修改思路均来自 Robin-GG
这里只是将其整理出来,实际验证亲测可行。
效果图
修改文件清单
vendor/mediatek/proprietary/packages/apps/SystemUI/res/drawable-xhdpi/ vendor/mediatek/proprietary/packages/apps/SystemUI/res/layout/prize_charge_layout.xml vendor/mediatek/proprietary/packages/apps/SystemUI/res/layout/super_status_bar.xml vendor/mediatek/proprietary/packages/apps/SystemUI/res/values/ids.xml vendor/mediatek/proprietary/packages/apps/SystemUI/res/values/strings.xml vendor/mediatek/proprietary/packages/apps/SystemUI/res/values-zh-rCN/strings.xml vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardChargeAnimationView.java vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/power/PowerUI.java
具体实现
vendor/mediatek/proprietary/packages/apps/SystemUI/res/layout/prize_charge_layout.xml
<?xml version="1.0" encoding="utf-8"?> <com.android.systemui.statusbar.phone.KeyguardChargeAnimationView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" xmlns:sysui="http://schemas.android.com/apk/res-auto" android:id="@id/keyguard_charge_view" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#e6000000"> <ImageView android:id="@id/dot_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:padding="0dp" android:src="@drawable/prize_charge_dot" /> <ImageView android:id="@id/ring_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:padding="0dp" android:src="@drawable/prize_charge_ring" /> <LinearLayout android:id="@id/battery_layout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:layout_gravity="center" android:gravity="center" android:orientation="horizontal"> <ImageView android:id="@id/charge_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="1dp" android:layout_marginRight="3dp" /> <TextView android:id="@id/battery_level" android:layout_width="wrap_content" android:layout_height="wrap_content" android:fontFamily="sans-serif-light" android:textColor="#ffffff" android:textSize="28dp" /> </LinearLayout> <TextView android:id="@id/battery_prompt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/ring_view" android:layout_centerHorizontal="true" android:layout_marginTop="5dp" android:fontFamily="sans-serif-light" android:textColor="#ffffff" android:textSize="14dp" /> </com.android.systemui.statusbar.phone.KeyguardChargeAnimationView>
vendor/mediatek/proprietary/packages/apps/SystemUI/res/layout/super_status_bar.xml
<!-- This is the status bar window. --> <com.android.systemui.statusbar.phone.StatusBarWindowView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:sysui="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> <!-- cczheng add 16dp for charge animation--> <FrameLayout android:id="@+id/status_bar_container" android:paddingStart="16dp" android:paddingEnd="16dp" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/system_bar_background" /> <!-- cczheng add for charge animation--> <include android:visibility="invisible" android:layout_width="match_parent" android:layout_height="match_parent" layout="@layout/prize_charge_layout"/> </com.android.systemui.statusbar.phone.StatusBarWindowView>
vendor/mediatek/proprietary/packages/apps/SystemUI/res/values/ids.xml
<!-- cczheng add for charge animation --> <item type="id" name="keyguard_charge_view" /> <item type="id" name="dot_view" /> <item type="id" name="ring_view" /> <item type="id" name="battery_layout" /> <item type="id" name="charge_icon" /> <item type="id" name="battery_level" /> <item type="id" name="battery_prompt" />
vendor/mediatek/proprietary/packages/apps/SystemUI/res/values/strings.xml
vendor/mediatek/proprietary/packages/apps/SystemUI/res/values-zh-rCN/strings.xml
<!-- cczheng add for charge animation --> <string name="fcharge_prompt">Fast charging</string> <string name="charge_prompt">Charging</string>
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
import com.android.systemui.statusbar.phone.KeyguardChargeAnimationView; private KeyguardChargeAnimationView mKeyguardChargeAnimationView; private boolean isScreenOn = true; private boolean mDozing; public void setIndicationArea(ViewGroup indicationArea) { ..... //cczheng add for charge animation mKeyguardChargeAnimationView = indicationArea.getRootView().findViewById(R.id.keyguard_charge_view); updateIndication(false /* animate */); updateDisclosure(); public void setScreenOn(boolean isOn) { isScreenOn = isOn; } protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback { public static final int HIDE_DELAY_MS = 5000; @Override public void onRefreshBatteryInfo(BatteryStatus status) { boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING || status.status == BatteryManager.BATTERY_STATUS_FULL; boolean wasPluggedIn = mPowerPluggedIn; mPowerPluggedInWired = status.isPluggedInWired() && isChargingOrFull; mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull; mPowerCharged = status.isCharged(); mChargingWattage = status.maxChargingWattage; mChargingSpeed = status.getChargingSpeed(mContext); mBatteryLevel = status.level; try { mChargingTimeRemaining = mPowerPluggedIn ? mBatteryInfo.computeChargeTimeRemaining() : -1; } catch (RemoteException e) { Log.e(TAG, "Error calling IBatteryStats: ", e); mChargingTimeRemaining = -1; } android.util.Log.e("Keyguard", "isChargingOrFull="+isChargingOrFull); android.util.Log.e("Keyguard", "mVisible="+mVisible+" wasPluggedIn="+wasPluggedIn+" mPowerPluggedInWired="+mPowerPluggedInWired); android.util.Log.d("Keyguard", "mPowerPluggedIn="+mPowerPluggedIn+" mPowerCharged="+mPowerCharged+" mBatteryLevel="+mBatteryLevel); //cczheng add for charge animation if (mKeyguardChargeAnimationView != null) { if (mVisible && !wasPluggedIn && mPowerPluggedInWired) { mKeyguardChargeAnimationView.closeByDelay(); mKeyguardChargeAnimationView.setBattery(mBatteryLevel); mKeyguardChargeAnimationView.anim2Show(isScreenOn); } else if (!status.isPluggedIn()) { mKeyguardChargeAnimationView.anim2hide(); } } updateIndication(!wasPluggedIn && mPowerPluggedInWired);
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
else if (Intent.ACTION_SCREEN_OFF.equals(action)) { if (mNotificationShadeWindowController != null) { mNotificationShadeWindowController.setNotTouchable(false); } if (mBubbleController.isStackExpanded()) { mBubbleController.collapseStack(); } //cczheng add for charge animation if (mKeyguardIndicationController != null) { mKeyguardIndicationController.setScreenOn(false); } finishBarAnimations(); resetUserExpandedStates(); }
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/power/PowerUI.java
import android.app.KeyguardManager; import android.content.pm.ActivityInfo; import android.graphics.PixelFormat; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.os.Message; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import com.android.systemui.statusbar.phone.KeyguardChargeAnimationView; static final boolean DEBUG = true || Log.isLoggable(TAG, Log.DEBUG); public void init() { ... filter.addAction(Intent.ACTION_POWER_CONNECTED); filter.addAction(Intent.ACTION_POWER_DISCONNECTED); filter.addAction(Intent.ACTION_USER_SWITCHED); @Override public void onReceive(Context context, Intent intent) { .... } else if (Intent.ACTION_SCREEN_ON.equals(action)) { mScreenOffTime = -1; } else if (Intent.ACTION_POWER_CONNECTED.equals(action)) {//cczheng add KeyguardManager mKeyguardManager = (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE); boolean lockState = mKeyguardManager.inKeyguardRestrictedInputMode(); Log.d(TAG,"ACTION_POWER_CONNECTED lockState="+lockState + " mBatteryLevel="+mBatteryLevel); if (!lockState ){ addChargeWindowView(); } } else if (Intent.ACTION_POWER_DISCONNECTED.equals(action)) { Log.d(TAG,"ACTION_POWER_DISCONNECTED"); removeChargeWindowView(); } else if (Intent.ACTION_USER_SWITCHED.equals(action)) { mWarnings.userSwitched(); } else { Slog.w(TAG, "unknown intent: " + intent); } } } //cczheng add for chargeview start WindowManager mWindowManager; WindowManager.LayoutParams wmParams; View mRootView; KeyguardChargeAnimationView chargeView; boolean isShowChargingView; private void addChargeWindowView(){ wmParams = new WindowManager.LayoutParams(); mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); wmParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; wmParams.type = LayoutParams.TYPE_NAVIGATION_BAR_PANEL; wmParams.format = PixelFormat.RGBA_8888; wmParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_SHOW_WHEN_LOCKED | LayoutParams.FLAG_LAYOUT_NO_LIMITS | LayoutParams.FLAG_FULLSCREEN; wmParams.gravity = Gravity.START | Gravity.TOP; int statusBarHeight = 0; int resourceId = mContext.getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) { statusBarHeight = mContext.getResources().getDimensionPixelSize(resourceId); } wmParams.x = 0; wmParams.y = -statusBarHeight; wmParams.width = 720; wmParams.height = 1560; mRootView = LayoutInflater.from(mContext).inflate(R.layout.prize_charge_layout, null, false); mWindowManager.addView(mRootView, wmParams); isShowChargingView = true; chargeView = (KeyguardChargeAnimationView)mRootView.findViewById(R.id.keyguard_charge_view); chargeView.setBattery(mBatteryLevel); chargeView.anim2Show(true); cHandler.removeMessages(100); cHandler.sendEmptyMessageDelayed(100, 3000);//3s auto dismiss mRootView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { return true; } if (event.getAction() == MotionEvent.ACTION_UP) { removeChargeWindowView(); return true; } return true; } }); } private Handler cHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); removeChargeWindowView(); } }; private void removeChargeWindowView(){ cHandler.removeMessages(100); if (isShowChargingView && mRootView != null) { isShowChargingView = false; chargeView.anim2hide(); mWindowManager.removeViewImmediate(mRootView); mRootView = null; } } //cczheng add for chargeview end
自定义充电 KeyguardChargeAnimationView
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardChargeAnimationView.java
package com.android.systemui.statusbar.phone; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.Display; import android.view.MotionEvent; import android.view.WindowManager; import android.view.animation.LinearInterpolator; import android.view.animation.RotateAnimation; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import com.android.systemui.R; import java.util.ArrayList; import java.util.List; public class KeyguardChargeAnimationView extends RelativeLayout { public final static boolean isInFastCharger = false; private final int BORN_STAR_WIDTH = 300; private final int CREATE_STAR_MSG = 10002; private final int STARS_INTERVAL = 350; private int battery; private TextView batteryLevelTxt; private TextView batteryPromptTxt; private Bitmap batterySourceBmp; private Bitmap[] batteryStar; private final int chargeColor = -1; private ImageView chargeIcon; private ImageView dotView; private final int fastChargeColor = -12910685; private int height; private boolean isPause = false; private boolean isScreenOn = true; Handler mHandler = new Handler() { public void handleMessage(Message message) { super.handleMessage(message); if (message.what == 10002) { createOneStar(); } } }; private AnimationListener onAnimationListener; private Paint paint; private ImageView ringView; private int speedUpFactor = 3; private List<Star> starList; private long updateTime = 0; private int width; public abstract class AnimationListener { public abstract void onAnimationEnd(); public abstract void onAnimationStart(); } private Runnable hideKeyguardChargeAnimationView = new Runnable() { @Override public void run() { anim2hide(); } }; @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { return true; } if (ev.getAction() == MotionEvent.ACTION_UP) { anim2hide(); return true; } return super.dispatchTouchEvent(ev); } public void closeByDelay() { removeCallbacks(hideKeyguardChargeAnimationView); postDelayed(hideKeyguardChargeAnimationView, 5 * 1000); } public class Star { public Bitmap bitmap; public float horSpeed; public boolean isLeft; public float offx; public int swingDistance; public float verSpeed; public int x; public int y; public Star() { } } public void anim2Show(boolean screen_on) { if (battery < 100) { start(); } else { stop(); } if (!screen_on) { this.setAlpha(1.0f); setVisibility(VISIBLE); return; } if (getVisibility() == VISIBLE) { return; } this.setAlpha(0); this.setVisibility(VISIBLE); chargeViewHideAnimator = ValueAnimator.ofFloat(new float[]{0.0f, 1.0f}); chargeViewHideAnimator.setDuration(500); chargeViewHideAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { public void onAnimationUpdate(ValueAnimator valueAnimator) { setAlpha(((Float) valueAnimator.getAnimatedValue()).floatValue()); } }); chargeViewHideAnimator.addListener(new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animator) { super.onAnimationEnd(animator); chargeViewHideAnimator = null; setAlpha(1.0f); setVisibility(VISIBLE); } }); chargeViewHideAnimator.start(); } private ValueAnimator chargeViewHideAnimator; public void anim2hide() { removeCallbacks(hideKeyguardChargeAnimationView); if (getVisibility() != VISIBLE) { return; } chargeViewHideAnimator = ValueAnimator.ofFloat(new float[]{1.0f, 0.0f}); chargeViewHideAnimator.setDuration(500); chargeViewHideAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { public void onAnimationUpdate(ValueAnimator valueAnimator) { setAlpha(((Float) valueAnimator.getAnimatedValue()).floatValue()); } }); chargeViewHideAnimator.addListener(new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animator) { super.onAnimationEnd(animator); chargeViewHideAnimator = null; setAlpha(0.0f); setVisibility(INVISIBLE); stop(); } }); chargeViewHideAnimator.start(); } public KeyguardChargeAnimationView(Context context) { super(context); init(); } public KeyguardChargeAnimationView(Context context, AttributeSet attributeSet) { super(context, attributeSet); init(); } public KeyguardChargeAnimationView(Context context, AttributeSet attributeSet, int i) { super(context, attributeSet, i); init(); } public KeyguardChargeAnimationView(Context context, AttributeSet attributeSet, int i, int i2) { super(context, attributeSet, i, i2); init(); } private void init() { this.starList = new ArrayList(); this.paint = new Paint(1); this.paint.setAntiAlias(true); this.paint.setFilterBitmap(true); this.paint.setDither(true); this.batterySourceBmp = BitmapFactory.decodeResource(getResources(), R.drawable.prize_charge_batterysource); this.batteryStar = new Bitmap[]{BitmapFactory.decodeResource(getResources(), R.drawable.prize_charge_star_s), BitmapFactory.decodeResource(getResources(), R.drawable.prize_charge_star_m), BitmapFactory.decodeResource(getResources(), R.drawable.prize_charge_star_b)}; } /* access modifiers changed from: protected */ public void onFinishInflate() { super.onFinishInflate(); this.ringView = (ImageView) findViewById(R.id.ring_view); this.dotView = (ImageView) findViewById(R.id.dot_view); startViewRingAnimation(this.ringView, 30000); startViewRingAnimation(this.dotView, 60000); this.chargeIcon = (ImageView) findViewById(R.id.charge_icon); this.batteryLevelTxt = (TextView) findViewById(R.id.battery_level); this.batteryPromptTxt = (TextView) findViewById(R.id.battery_prompt); } private void startViewRingAnimation(ImageView imageView, long j) { if (imageView != null) { RotateAnimation rotateAnimation = new RotateAnimation(0.0f, 360.0f, 1, 0.5f, 1, 0.5f); rotateAnimation.setInterpolator(new LinearInterpolator()); rotateAnimation.setDuration(j); rotateAnimation.setRepeatCount(-1); rotateAnimation.setFillAfter(true); imageView.startAnimation(rotateAnimation); } } private void updateChargeIcon() { ImageView imageView = this.chargeIcon; if (imageView != null) { if (isInFastCharger) { imageView.setImageResource(R.drawable.prize_charge_fast_icon); this.chargeIcon.setColorFilter(-12910685); } else { imageView.setImageResource(R.drawable.prize_charge_icon); this.chargeIcon.setColorFilter(-1); } } } private void updateBatteryLevel() { TextView textView = this.batteryLevelTxt; if (textView != null) { StringBuilder sb = new StringBuilder(); sb.append(this.battery); sb.append("%"); textView.setText(sb.toString()); if (isInFastCharger) { this.batteryLevelTxt.setTextColor(-12910685); this.batteryPromptTxt.setText(getContext().getString(R.string.fcharge_prompt)); } else { this.batteryLevelTxt.setTextColor(-1); this.batteryPromptTxt.setText(getContext().getString(R.string.charge_prompt)); } } } public void start() { if ((this.isPause || this.speedUpFactor != 1) && this.isScreenOn) { this.isPause = false; this.speedUpFactor = 1; this.mHandler.removeMessages(10002); createOneStar(); AnimationListener animationListener = this.onAnimationListener; if (animationListener != null) { animationListener.onAnimationStart(); } } } public void stop() { this.isPause = true; this.speedUpFactor = 3; this.mHandler.removeMessages(10002); synchronized (this.starList) { this.starList.clear(); } } /* access modifiers changed from: private */ public void createOneStar() { synchronized (this.starList) { Star star = new Star(); star.bitmap = this.batteryStar[(int) (Math.random() * ((double) this.batteryStar.length))]; star.x = (int) (((double) ((this.width - 300) / 2)) + (Math.random() * ((double) (300 - star.bitmap.getWidth())))); star.y = this.height; star.verSpeed = (float) (isInFastCharger ? 4.5d : 2.5d + (Math.random() * 2.0d)); star.horSpeed = (float) (Math.random() + 0.5d); star.isLeft = Math.random() <= 0.5d; double d = (double) 100; star.swingDistance = (int) (d + (Math.random() * d)); if (getVisibility() == 0) { this.starList.add(star); } if (!this.isPause) { this.mHandler.removeMessages(10002); this.mHandler.sendEmptyMessageDelayed(10002, 350); if (this.starList.size() == 1) { invalidate(); } } } } /* access modifiers changed from: protected */ public void onDraw(Canvas canvas) { super.onDraw(canvas); if (this.width == 0 || this.height == 0) { Display defaultDisplay = ((WindowManager) getContext().getSystemService("window")).getDefaultDisplay(); DisplayMetrics displayMetrics = new DisplayMetrics(); defaultDisplay.getRealMetrics(displayMetrics); this.width = displayMetrics.widthPixels; this.height = displayMetrics.heightPixels; } int i = this.width; int i2 = this.height; if (i > i2) { this.width = i2; this.height = i; } if (this.width != 0 && this.height != 0) { synchronized (this.starList) { drawBatterySourceBmp(canvas); drawStars(canvas); updateChargeIcon(); updateBatteryLevel(); invalidate(); } } } private void drawStars(Canvas canvas) { if (this.battery != 100) { List<Star> list = this.starList; if (!(list == null || list.size() == 0)) { if (this.updateTime == 0) { this.updateTime = System.currentTimeMillis(); } long currentTimeMillis = System.currentTimeMillis(); int i = 0; boolean z = currentTimeMillis - this.updateTime >= 15; while (i < this.starList.size()) { Star star = (Star) this.starList.get(i); Bitmap bitmap = star.bitmap; if (bitmap != null && !bitmap.isRecycled()) { canvas.drawBitmap(star.bitmap, ((float) star.x) + star.offx, (float) star.y, this.paint); if (z) { this.updateTime = currentTimeMillis; if (updateStar(star)) { this.starList.remove(star); if (this.starList.size() == 0) { AnimationListener animationListener = this.onAnimationListener; if (animationListener != null) { animationListener.onAnimationEnd(); } } i--; } } } i++; } } } } private boolean updateStar(Star star) { if (star.isLeft) { star.offx -= star.horSpeed * ((float) this.speedUpFactor); if (star.offx <= ((float) (-star.swingDistance))) { star.isLeft = false; } if (((float) star.x) + star.offx <= ((float) ((this.width - 300) / 2))) { star.isLeft = false; } } else { star.offx += star.horSpeed * ((float) this.speedUpFactor); if (star.offx >= ((float) star.swingDistance)) { star.isLeft = true; } if (((float) star.x) + star.offx >= ((float) (((this.width + 300) / 2) - star.bitmap.getWidth()))) { star.isLeft = true; } } star.y = (int) (((float) star.y) - (star.verSpeed * ((float) this.speedUpFactor))); ImageView imageView = this.ringView; return star.y <= (imageView != null ? imageView.getBottom() - ((int) (((float) this.ringView.getHeight()) * 0.1f)) : 0); } private void drawBatterySourceBmp(Canvas canvas) { if (this.battery != 100) { Bitmap bitmap = this.batterySourceBmp; if (bitmap != null && !bitmap.isRecycled()) { canvas.drawBitmap(this.batterySourceBmp, (float) ((this.width - this.batterySourceBmp.getWidth()) / 2), (float) (this.height - this.batterySourceBmp.getHeight()), this.paint); } } } /* access modifiers changed from: protected */ public void onDetachedFromWindow() { super.onDetachedFromWindow(); } public void setBattery(int i) { this.battery = i; } }