android 4.4 batteryservice 電池電量顯示分析

最近工做接觸到這麼的東西,這是我對整個電池管理方面Java 層的分析。若是想了解底層的話,請看個人博客:
android 4.4 電池電量管理底層分析(C\C++層) (http://blog.csdn.NET/daweibalang717/article/details/41446993)
先貼一張類與類之間的關係圖:html

    Android開機過程當中會加載系統BatteryService ,說一下電池電量相關的,本文主要講述關於JAVA 層代碼。文件路徑:\frameworks\base\services\java\com\android\server\BatteryService.java   下面貼出源碼。我把註釋加上。我的理解,僅參考。
 
[java] view plaincopy
/* 
 * Copyright (C) 2006 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.server;  
  
import android.os.BatteryStats;  
import com.android.internal.app.IBatteryStats;  
import com.android.server.am.BatteryStatsService;  
  
import android.app.ActivityManagerNative;  
import android.content.ContentResolver;  
import android.content.Context;  
import android.content.Intent;  
import android.content.pm.PackageManager;  
import android.os.BatteryManager;  
import android.os.BatteryProperties;  
import android.os.Binder;  
import android.os.FileUtils;  
import android.os.Handler;  
import android.os.IBatteryPropertiesListener;  
import android.os.IBatteryPropertiesRegistrar;  
import android.os.IBinder;  
import android.os.DropBoxManager;  
import android.os.RemoteException;  
import android.os.ServiceManager;  
import android.os.SystemClock;  
import android.os.UEventObserver;  
import android.os.UserHandle;  
import android.provider.Settings;  
import android.util.EventLog;  
import android.util.Slog;  
  
import java.io.File;  
import java.io.FileDescriptor;  
import java.io.FileOutputStream;  
import java.io.IOException;  
import java.io.PrintWriter;  
  
  
/** 
 * <p>BatteryService monitors the charging status, and charge level of the device 
 * battery.  When these values change this service broadcasts the new values 
 * to all {@link android.content.BroadcastReceiver IntentReceivers} that are 
 * watching the {@link android.content.Intent#ACTION_BATTERY_CHANGED 
 * BATTERY_CHANGED} action.</p> 
 * <p>The new values are stored in the Intent data and can be retrieved by 
 * calling {@link android.content.Intent#getExtra Intent.getExtra} with the 
 * following keys:</p> 
 * <p>"scale" - int, the maximum value for the charge level</p> 
 * <p>"level" - int, charge level, from 0 through "scale" inclusive</p> 
 * <p>"status" - String, the current charging status.<br /> 
 * <p>"health" - String, the current battery health.<br /> 
 * <p>"present" - boolean, true if the battery is present<br /> 
 * <p>"icon-small" - int, suggested small icon to use for this state</p> 
 * <p>"plugged" - int, 0 if the device is not plugged in; 1 if plugged 
 * into an AC power adapter; 2 if plugged in via USB.</p> 
 * <p>"voltage" - int, current battery voltage in millivolts</p> 
 * <p>"temperature" - int, current battery temperature in tenths of 
 * a degree Centigrade</p> 
 * <p>"technology" - String, the type of battery installed, e.g. "Li-ion"</p> 
 * 
 * <p> 
 * The battery service may be called by the power manager while holding its locks so 
 * we take care to post all outcalls into the activity manager to a handler. 
 * 
 * FIXME: Ideally the power manager would perform all of its calls into the battery 
 * service asynchronously itself. 
 * </p> 
 */  
public final class BatteryService extends Binder {  
    private static final String TAG = BatteryService.class.getSimpleName();  
  
    private static final boolean DEBUG = false;  
  
    private static final int BATTERY_SCALE = 100;    // battery capacity is a percentage  
  
    // Used locally for determining when to make a last ditch effort to log  
    // discharge stats before the device dies.  
    private int mCriticalBatteryLevel;  
  
    private static final int DUMP_MAX_LENGTH = 24 * 1024;  
    private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "--unplugged" };  
  
    private static final String DUMPSYS_DATA_PATH = "/data/system/";  
  
    // This should probably be exposed in the API, though it's not critical  
    private static final int BATTERY_PLUGGED_NONE = 0;  
  
    private final Context mContext;  
    private final IBatteryStats mBatteryStats;  
    private final Handler mHandler;  
  
    private final Object mLock = new Object();  
  
    private BatteryProperties mBatteryProps;  
    private boolean mBatteryLevelCritical;  
    private int mLastBatteryStatus;  
    private int mLastBatteryHealth;  
    private boolean mLastBatteryPresent;  
    private int mLastBatteryLevel;  
    private int mLastBatteryVoltage;  
    private int mLastBatteryTemperature;  
    private boolean mLastBatteryLevelCritical;  
  
    private int mInvalidCharger;  
    private int mLastInvalidCharger;  
  
    private int mLowBatteryWarningLevel;  
    private int mLowBatteryCloseWarningLevel;  
    private int mShutdownBatteryTemperature;  
  
    private int mPlugType;  
    private int mLastPlugType = -1; // Extra state so we can detect first run  
  
    private long mDischargeStartTime;  
    private int mDischargeStartLevel;  
  
    private boolean mUpdatesStopped;  
  
    private Led mLed;  
  
    private boolean mSentLowBatteryBroadcast = false;  
  
    private BatteryListener mBatteryPropertiesListener;  
    private IBatteryPropertiesRegistrar mBatteryPropertiesRegistrar;  
//構造函數  
    public BatteryService(Context context, LightsService lights) {  
        mContext = context;  
        mHandler = new Handler(true /*async*/);  
        mLed = new Led(context, lights);//這個應該是指示燈,沒實驗  
        mBatteryStats = BatteryStatsService.getService();  
  
 //低電量臨界值,這個數我看的源碼版本值是4(在這個類裏只是用來寫日誌)  
        mCriticalBatteryLevel = mContext.getResources().getInteger(  
                com.android.internal.R.integer.config_criticalBatteryWarningLevel);  
  
//低電量告警值,值15,下面會根據這個變量發送低電量的廣播Intent.ACTION_BATTERY_LOW(這個跟系統低電量提醒不要緊,只是發出去了)  
        mLowBatteryWarningLevel = mContext.getResources().getInteger(  
                com.android.internal.R.integer.config_lowBatteryWarningLevel);  
  
//電量告警取消值,值20 , 就是手機電量大於等於20的話發送Intent.ACTION_BATTERY_OKAY  
        mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(  
                com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);  
  
//值是680 ,溫度太高,超過這個值就發送廣播,跳轉到將要關機提醒。  
       mShutdownBatteryTemperature = mContext.getResources().getInteger(  
                com.android.internal.R.integer.config_shutdownBatteryTemperature);  
  
        // watch for invalid charger messages if the invalid_charger switch exists  
        if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {  
            mInvalidChargerObserver.startObserving(  
                    "DEVPATH=/devices/virtual/switch/invalid_charger");  
        }  
//電池監聽,這個應該是註冊到底層去了。當底層電量改變會調用此監聽。而後執行update(BatteryProperties props);  
        mBatteryPropertiesListener = new BatteryListener();  
  
        IBinder b = ServiceManager.getService("batterypropreg");  
        mBatteryPropertiesRegistrar = IBatteryPropertiesRegistrar.Stub.asInterface(b);  
  
        try {  
//這裏註冊  
          mBatteryPropertiesRegistrar.registerListener(mBatteryPropertiesListener);  
        } catch (RemoteException e) {  
            // Should never happen.  
        }  
    }  
//開機後先去看看是否沒電了或者溫度過高了。若是是,就關機提示(關機提示我等會介紹)。  
    void systemReady() {  
        // check our power situation now that it is safe to display the shutdown dialog.  
        synchronized (mLock) {  
            shutdownIfNoPowerLocked();  
            shutdownIfOverTempLocked();  
        }  
    }  
//返回是否在充電,這個函數在PowerManagerService.java 中調用  
    /** 
     * Returns true if the device is plugged into any of the specified plug types. 
     */  
    public boolean isPowered(int plugTypeSet) {  
        synchronized (mLock) {  
            return isPoweredLocked(plugTypeSet);  
        }  
    }  
//就是這裏,經過充電器類型判斷是否充電  
    private boolean isPoweredLocked(int plugTypeSet) {  
//我這英語小白猜着翻譯下:就是開機後,電池狀態不明瞭,那咱們就認爲就在充電,以便設備正常工做。  
        // assume we are powered if battery state is unknown so  
        // the "stay on while plugged in" option will work.  
        if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {  
            return true;  
        }  
//充電器  
        if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mBatteryProps.chargerAcOnline) {  
            return true;  
        }  
//USB,插電腦上充電  
      if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mBatteryProps.chargerUsbOnline) {  
            return true;  
        }  
//電源是無線的。 (我沒見過...)  
        if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mBatteryProps.chargerWirelessOnline) {  
            return true;  
        }  
        return false;  
    }  
  
    /** 
     * Returns the current plug type. 
     */  
//充電器類型  
   public int getPlugType() {  
        synchronized (mLock) {  
            return mPlugType;  
        }  
    }  
  
    /** 
     * Returns battery level as a percentage. 
     */  
//電池屬性:電量等級(0-100)  
    public int getBatteryLevel() {  
        synchronized (mLock) {  
            return mBatteryProps.batteryLevel;  
        }  
    }  
  
    /** 
     * Returns true if battery level is below the first warning threshold. 
     */  
//低電量  
     public boolean isBatteryLow() {  
        synchronized (mLock) {  
            return mBatteryProps.batteryPresent && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel;  
        }  
    }  
  
    /** 
     * Returns a non-zero value if an  unsupported charger is attached. 
     */  
//不支持的充電器類型    
    public int getInvalidCharger() {  
        synchronized (mLock) {  
            return mInvalidCharger;  
        }  
    }  
  
//這裏就是沒電了,要關機的提示。  
    private void shutdownIfNoPowerLocked() {  
        // shut down gracefully if our battery is critically low and we are not powered.  
        // wait until the system has booted before attempting to display the shutdown dialog.  
        if (mBatteryProps.batteryLevel == 0 && (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING)) {  
            mHandler.post(new Runnable() {  
                @Override  
                public void run() {  
                    if (ActivityManagerNative.isSystemReady()) {  
                        Intent intent = new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN_LOWBATTERY");//ACTION_REQUEST_SHUTDOWN  
                        intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);  
                        intent.putExtra("cant_be_cancel_by_button", true);  
                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
                        mContext.startActivityAsUser(intent, UserHandle.CURRENT);  
                    }  
                }  
            });  
        }  
    }  
  
//溫度太高,關機提示(我的感受這裏有問題,溫度太高爲啥子跳轉到沒電關機提示界面)   
    private void shutdownIfOverTempLocked() {  
        // shut down gracefully if temperature is too high (> 68.0C by default)  
        // wait until the system has booted before attempting to display the  
        // shutdown dialog.  
        if (mBatteryProps.batteryTemperature > mShutdownBatteryTemperature) {  
            mHandler.post(new Runnable() {  
                @Override  
                public void run() {  
                    if (ActivityManagerNative.isSystemReady()) {  
                        Intent intent = new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN_LOWBATTERY");//ACTION_REQUEST_SHUTDOWN  
                        intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);  
                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
                        mContext.startActivityAsUser(intent, UserHandle.CURRENT);  
                    }  
                }  
            });  
        }  
    }  
//這個方法就是被JNI回調的。用來更新上層狀態的方法。  
    private void update(BatteryProperties props) {  
        synchronized (mLock) {  
            if (!mUpdatesStopped) {  
                mBatteryProps = props;  
                // Process the new values.  
                processValuesLocked();  
            }  
        }  
    }  
//嗯。這個就是最主要的方法了。  
    private void processValuesLocked() {  
        boolean logOutlier = false;  
        long dischargeDuration = 0;  
  
        mBatteryLevelCritical = (mBatteryProps.batteryLevel <= mCriticalBatteryLevel);  
//充電器類型   
       if (mBatteryProps.chargerAcOnline) {  
            mPlugType = BatteryManager.BATTERY_PLUGGED_AC;  
        } else if (mBatteryProps.chargerUsbOnline) {  
            mPlugType = BatteryManager.BATTERY_PLUGGED_USB;  
        } else if (mBatteryProps.chargerWirelessOnline) {  
            mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;  
        } else {  
            mPlugType = BATTERY_PLUGGED_NONE;  
        }  
  
        if (DEBUG) {//日誌,略過  
            Slog.d(TAG, "Processing new values: "  
                    + "chargerAcOnline=" + mBatteryProps.chargerAcOnline  
                    + ", chargerUsbOnline=" + mBatteryProps.chargerUsbOnline  
                    + ", chargerWirelessOnline=" + mBatteryProps.chargerWirelessOnline  
                    + ", batteryStatus=" + mBatteryProps.batteryStatus  
                    + ", batteryHealth=" + mBatteryProps.batteryHealth  
                    + ", batteryPresent=" + mBatteryProps.batteryPresent  
                    + ", batteryLevel=" + mBatteryProps.batteryLevel  
                    + ", batteryTechnology=" + mBatteryProps.batteryTechnology  
                    + ", batteryVoltage=" + mBatteryProps.batteryVoltage  
                    + ", batteryCurrentNow=" + mBatteryProps.batteryCurrentNow  
                    + ", batteryChargeCounter=" + mBatteryProps.batteryChargeCounter  
                    + ", batteryTemperature=" + mBatteryProps.batteryTemperature  
                    + ", mBatteryLevelCritical=" + mBatteryLevelCritical  
                    + ", mPlugType=" + mPlugType);  
        }  
  
        // Let the battery stats keep track of the current level.  
        try {  
//把電池屬性放到狀態裏面  
           mBatteryStats.setBatteryState(mBatteryProps.batteryStatus, mBatteryProps.batteryHealth,  
                    mPlugType, mBatteryProps.batteryLevel, mBatteryProps.batteryTemperature,  
                    mBatteryProps.batteryVoltage);  
        } catch (RemoteException e) {  
            // Should never happen.  
        }  
//沒電了  
        shutdownIfNoPowerLocked();  
//溫度太高了  
       shutdownIfOverTempLocked();  
  
        if (mBatteryProps.batteryStatus != mLastBatteryStatus ||  
                mBatteryProps.batteryHealth != mLastBatteryHealth ||  
                mBatteryProps.batteryPresent != mLastBatteryPresent ||  
                mBatteryProps.batteryLevel != mLastBatteryLevel ||  
                mPlugType != mLastPlugType ||  
                mBatteryProps.batteryVoltage != mLastBatteryVoltage ||  
                mBatteryProps.batteryTemperature != mLastBatteryTemperature ||  
                mInvalidCharger != mLastInvalidCharger) {  
  
            if (mPlugType != mLastPlugType) {//當前充電器類型與上次的不同  
//而且上次充電器類型是no one ,那就能夠知道,如今是插上充電器了。  
               if (mLastPlugType == BATTERY_PLUGGED_NONE) {  
                    // discharging -> charging  
  
                    // There's no value in this data unless we've discharged at least once and the  
                    // battery level has changed; so don't log until it does.  
                    if (mDischargeStartTime != 0 && mDischargeStartLevel != mBatteryProps.batteryLevel) {  
                        dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;  
                        logOutlier = true;  
                        EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,  
                                mDischargeStartLevel, mBatteryProps.batteryLevel);  
                        // make sure we see a discharge event before logging again  
                        mDischargeStartTime = 0;  
                    }  
//而且本次充電器類型是no one ,那就能夠知道,如今是拔掉充電器了。  
                } else if (mPlugType == BATTERY_PLUGGED_NONE) {  
                    // charging -> discharging or we just powered up  
                    mDischargeStartTime = SystemClock.elapsedRealtime();  
                    mDischargeStartLevel = mBatteryProps.batteryLevel;  
                }  
            }  
            if (mBatteryProps.batteryStatus != mLastBatteryStatus ||//寫日誌,略過  
                    mBatteryProps.batteryHealth != mLastBatteryHealth ||  
                    mBatteryProps.batteryPresent != mLastBatteryPresent ||  
                    mPlugType != mLastPlugType) {  
                EventLog.writeEvent(EventLogTags.BATTERY_STATUS,  
                        mBatteryProps.batteryStatus, mBatteryProps.batteryHealth, mBatteryProps.batteryPresent ? 1 : 0,  
                        mPlugType, mBatteryProps.batteryTechnology);  
            }  
            if (mBatteryProps.batteryLevel != mLastBatteryLevel) {  
                // Don't do this just from voltage or temperature changes, that is  
                // too noisy.  
                EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,  
                        mBatteryProps.batteryLevel, mBatteryProps.batteryVoltage, mBatteryProps.batteryTemperature);  
            }  
            if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&  
                    mPlugType == BATTERY_PLUGGED_NONE) {  
                // We want to make sure we log discharge cycle outliers  
                // if the battery is about to die.  
                dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;  
                logOutlier = true;  
            }  
//本次調用,當前的充電狀態  
            final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;  
//本次調用,上次調用的充電狀態    
            final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;  
  
            /* The ACTION_BATTERY_LOW broadcast is sent in these situations: 
             * - is just un-plugged (previously was plugged) and battery level is 
             *   less than or equal to WARNING, or 
             * - is not plugged and battery level falls to WARNING boundary 
             *   (becomes <= mLowBatteryWarningLevel). 
             */  
//用於發送低電量廣播的判斷  
            final boolean sendBatteryLow = !plugged//(按sendBatteryLow = true 來講) 當前沒有充電  
                    && mBatteryProps.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN//充電狀態不是UNKNOWN  
                    && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel//當前電量小於告警值 15  
                    && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);//上次狀態是充電或者上次電量等級大於告警值 15  
  
            sendIntentLocked();//發送電池電量改變的廣播Intent.ACTION_BATTERY_CHANGED  
  
            // Separate broadcast is sent for power connected / not connected  
            // since the standard intent will not wake any applications and some  
            // applications may want to have smart behavior based on this.  
            if (mPlugType != 0 && mLastPlugType == 0) {//插上充電器了  
                mHandler.post(new Runnable() {  
                    @Override  
                    public void run() {  
                        Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED);  
                        statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);  
                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);  
                    }  
                });  
            }  
            else if (mPlugType == 0 && mLastPlugType != 0) {//斷開充電器了  
                mHandler.post(new Runnable() {  
                    @Override  
                    public void run() {  
                        Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED);  
                        statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);  
                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);  
                    }  
                });  
            }  
//發送低電量提醒(這個跟系統低電量提醒不要緊,只是發出去了)  
            if (sendBatteryLow) {  
                mSentLowBatteryBroadcast = true;  
                mHandler.post(new Runnable() {  
                    @Override  
                    public void run() {  
                        Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW);  
                        statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);  
                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);  
                    }  
                });  
            } else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= mLowBatteryCloseWarningLevel) {//電量超過20了。電池狀態OK了  
                mSentLowBatteryBroadcast = false;  
                mHandler.post(new Runnable() {  
                    @Override  
                    public void run() {  
                        Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY);  
                        statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);  
                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);  
                    }  
                });  
            }  
  
            // Update the battery LED  
            mLed.updateLightsLocked();  
  
            // This needs to be done after sendIntent() so that we get the lastest battery stats.  
            if (logOutlier && dischargeDuration != 0) {  
                logOutlierLocked(dischargeDuration);  
            }  
  
            mLastBatteryStatus = mBatteryProps.batteryStatus;  
            mLastBatteryHealth = mBatteryProps.batteryHealth;  
            mLastBatteryPresent = mBatteryProps.batteryPresent;  
            mLastBatteryLevel = mBatteryProps.batteryLevel;  
            mLastPlugType = mPlugType;  
            mLastBatteryVoltage = mBatteryProps.batteryVoltage;  
            mLastBatteryTemperature = mBatteryProps.batteryTemperature;  
            mLastBatteryLevelCritical = mBatteryLevelCritical;  
            mLastInvalidCharger = mInvalidCharger;  
        }  
    }  
//電池電量改變,把屬性發出去(系統低電量提醒接收的是這個廣播)  
    private void sendIntentLocked() {  
        //  Pack up the values and broadcast them to everyone  
        final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);  
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY  
                | Intent.FLAG_RECEIVER_REPLACE_PENDING);  
  
        int icon = getIconLocked(mBatteryProps.batteryLevel);  
  
        intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryProps.batteryStatus);  
        intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryProps.batteryHealth);  
        intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryProps.batteryPresent);  
        intent.putExtra(BatteryManager.EXTRA_LEVEL, mBatteryProps.batteryLevel);  
        intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);  
        intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);  
        intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);  
        intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryProps.batteryVoltage);  
        intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryProps.batteryTemperature);  
        intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryProps.batteryTechnology);  
        intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);  
  
        if (DEBUG) {  
            Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED.  level:" + mBatteryProps.batteryLevel +  
                    ", scale:" + BATTERY_SCALE + ", status:" + mBatteryProps.batteryStatus +  
                    ", health:" + mBatteryProps.batteryHealth +  ", present:" + mBatteryProps.batteryPresent +  
                    ", voltage: " + mBatteryProps.batteryVoltage +  
                    ", temperature: " + mBatteryProps.batteryTemperature +  
                    ", technology: " + mBatteryProps.batteryTechnology +  
                    ", AC powered:" + mBatteryProps.chargerAcOnline + ", USB powered:" + mBatteryProps.chargerUsbOnline +  
                    ", Wireless powered:" + mBatteryProps.chargerWirelessOnline +  
                    ", icon:" + icon  + ", invalid charger:" + mInvalidCharger);  
        }  
  
        mHandler.post(new Runnable() {  
            @Override  
            public void run() {  
                ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);  
            }  
        });  
    }  
  
    private void logBatteryStatsLocked() {  
        IBinder batteryInfoService = ServiceManager.getService(BatteryStats.SERVICE_NAME);  
        if (batteryInfoService == null) return;  
  
        DropBoxManager db = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);  
        if (db == null || !db.isTagEnabled("BATTERY_DISCHARGE_INFO")) return;  
  
        File dumpFile = null;  
        FileOutputStream dumpStream = null;  
        try {  
            // dump the service to a file  
            dumpFile = new File(DUMPSYS_DATA_PATH + BatteryStats.SERVICE_NAME + ".dump");  
            dumpStream = new FileOutputStream(dumpFile);  
            batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS);  
            FileUtils.sync(dumpStream);  
  
            // add dump file to drop box  
            db.addFile("BATTERY_DISCHARGE_INFO", dumpFile, DropBoxManager.IS_TEXT);  
        } catch (RemoteException e) {  
            Slog.e(TAG, "failed to dump battery service", e);  
        } catch (IOException e) {  
            Slog.e(TAG, "failed to write dumpsys file", e);  
        } finally {  
            // make sure we clean up  
            if (dumpStream != null) {  
                try {  
                    dumpStream.close();  
                } catch (IOException e) {  
                    Slog.e(TAG, "failed to close dumpsys output stream");  
                }  
            }  
            if (dumpFile != null && !dumpFile.delete()) {  
                Slog.e(TAG, "failed to delete temporary dumpsys file: "  
                        + dumpFile.getAbsolutePath());  
            }  
        }  
    }  
  
    private void logOutlierLocked(long duration) {  
        ContentResolver cr = mContext.getContentResolver();  
        String dischargeThresholdString = Settings.Global.getString(cr,  
                Settings.Global.BATTERY_DISCHARGE_THRESHOLD);  
        String durationThresholdString = Settings.Global.getString(cr,  
                Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD);  
  
        if (dischargeThresholdString != null && durationThresholdString != null) {  
            try {  
                long durationThreshold = Long.parseLong(durationThresholdString);  
                int dischargeThreshold = Integer.parseInt(dischargeThresholdString);  
                if (duration <= durationThreshold &&  
                        mDischargeStartLevel - mBatteryProps.batteryLevel >= dischargeThreshold) {  
                    // If the discharge cycle is bad enough we want to know about it.  
                    logBatteryStatsLocked();  
                }  
                if (DEBUG) Slog.v(TAG, "duration threshold: " + durationThreshold +  
                        " discharge threshold: " + dischargeThreshold);  
                if (DEBUG) Slog.v(TAG, "duration: " + duration + " discharge: " +  
                        (mDischargeStartLevel - mBatteryProps.batteryLevel));  
            } catch (NumberFormatException e) {  
                Slog.e(TAG, "Invalid DischargeThresholds GService string: " +  
                        durationThresholdString + " or " + dischargeThresholdString);  
                return;  
            }  
        }  
    }  
  
    private int getIconLocked(int level) {  
        if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {  
            return com.android.internal.R.drawable.stat_sys_battery_charge;  
        } else if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {  
            return com.android.internal.R.drawable.stat_sys_battery;  
        } else if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING  
                || mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_FULL) {  
            if (isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)  
                    && mBatteryProps.batteryLevel >= 100) {  
                return com.android.internal.R.drawable.stat_sys_battery_charge;  
            } else {  
                return com.android.internal.R.drawable.stat_sys_battery;  
            }  
        } else {  
            return com.android.internal.R.drawable.stat_sys_battery_unknown;  
        }  
    }  
  
    @Override  
    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {  
        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)  
                != PackageManager.PERMISSION_GRANTED) {  
  
            pw.println("Permission Denial: can't dump Battery service from from pid="  
                    + Binder.getCallingPid()  
                    + ", uid=" + Binder.getCallingUid());  
            return;  
        }  
  
        synchronized (mLock) {  
            if (args == null || args.length == 0 || "-a".equals(args[0])) {  
                pw.println("Current Battery Service state:");  
                if (mUpdatesStopped) {  
                    pw.println("  (UPDATES STOPPED -- use 'reset' to restart)");  
                }  
                pw.println("  AC powered: " + mBatteryProps.chargerAcOnline);  
                pw.println("  USB powered: " + mBatteryProps.chargerUsbOnline);  
                pw.println("  Wireless powered: " + mBatteryProps.chargerWirelessOnline);  
                pw.println("  status: " + mBatteryProps.batteryStatus);  
                pw.println("  health: " + mBatteryProps.batteryHealth);  
                pw.println("  present: " + mBatteryProps.batteryPresent);  
                pw.println("  level: " + mBatteryProps.batteryLevel);  
                pw.println("  scale: " + BATTERY_SCALE);  
                pw.println("  voltage: " + mBatteryProps.batteryVoltage);  
  
                if (mBatteryProps.batteryCurrentNow != Integer.MIN_VALUE) {  
                    pw.println("  current now: " + mBatteryProps.batteryCurrentNow);  
                }  
  
                if (mBatteryProps.batteryChargeCounter != Integer.MIN_VALUE) {  
                    pw.println("  charge counter: " + mBatteryProps.batteryChargeCounter);  
                }  
  
                pw.println("  temperature: " + mBatteryProps.batteryTemperature);  
                pw.println("  technology: " + mBatteryProps.batteryTechnology);  
            } else if (args.length == 3 && "set".equals(args[0])) {  
                String key = args[1];  
                String value = args[2];  
                try {  
                    boolean update = true;  
                    if ("ac".equals(key)) {  
                        mBatteryProps.chargerAcOnline = Integer.parseInt(value) != 0;  
                    } else if ("usb".equals(key)) {  
                        mBatteryProps.chargerUsbOnline = Integer.parseInt(value) != 0;  
                    } else if ("wireless".equals(key)) {  
                        mBatteryProps.chargerWirelessOnline = Integer.parseInt(value) != 0;  
                    } else if ("status".equals(key)) {  
                        mBatteryProps.batteryStatus = Integer.parseInt(value);  
                    } else if ("level".equals(key)) {  
                        mBatteryProps.batteryLevel = Integer.parseInt(value);  
                    } else if ("invalid".equals(key)) {  
                        mInvalidCharger = Integer.parseInt(value);  
                    } else {  
                        pw.println("Unknown set option: " + key);  
                        update = false;  
                    }  
                    if (update) {  
                        long ident = Binder.clearCallingIdentity();  
                        try {  
                            mUpdatesStopped = true;  
                            processValuesLocked();  
                        } finally {  
                            Binder.restoreCallingIdentity(ident);  
                        }  
                    }  
                } catch (NumberFormatException ex) {  
                    pw.println("Bad value: " + value);  
                }  
            } else if (args.length == 1 && "reset".equals(args[0])) {  
                long ident = Binder.clearCallingIdentity();  
                try {  
                    mUpdatesStopped = false;  
                } finally {  
                    Binder.restoreCallingIdentity(ident);  
                }  
            } else {  
                pw.println("Dump current battery state, or:");  
                pw.println("  set ac|usb|wireless|status|level|invalid <value>");  
                pw.println("  reset");  
            }  
        }  
    }  
  
    private final UEventObserver mInvalidChargerObserver = new UEventObserver() {  
        @Override  
        public void onUEvent(UEventObserver.UEvent event) {  
            final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;  
            synchronized (mLock) {  
                if (mInvalidCharger != invalidCharger) {  
                    mInvalidCharger = invalidCharger;  
                }  
            }  
        }  
    };  
  
    private final class Led {  
        private final LightsService.Light mBatteryLight;  
  
        private final int mBatteryLowARGB;  
        private final int mBatteryMediumARGB;  
        private final int mBatteryFullARGB;  
        private final int mBatteryLedOn;  
        private final int mBatteryLedOff;  
  
        public Led(Context context, LightsService lights) {  
            mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);  
  
            mBatteryLowARGB = context.getResources().getInteger(  
                    com.android.internal.R.integer.config_notificationsBatteryLowARGB);  
            mBatteryMediumARGB = context.getResources().getInteger(  
                    com.android.internal.R.integer.config_notificationsBatteryMediumARGB);  
            mBatteryFullARGB = context.getResources().getInteger(  
                    com.android.internal.R.integer.config_notificationsBatteryFullARGB);  
            mBatteryLedOn = context.getResources().getInteger(  
                    com.android.internal.R.integer.config_notificationsBatteryLedOn);  
            mBatteryLedOff = context.getResources().getInteger(  
                    com.android.internal.R.integer.config_notificationsBatteryLedOff);  
        }  
  
        /** 
         * Synchronize on BatteryService. 
         */  
        public void updateLightsLocked() {  
            final int level = mBatteryProps.batteryLevel;  
            final int status = mBatteryProps.batteryStatus;  
            if (level < mLowBatteryWarningLevel) {  
                if (status == BatteryManager.BATTERY_STATUS_CHARGING) {  
                    // Solid red when battery is charging  
                    mBatteryLight.setColor(mBatteryLowARGB);  
                } else {  
                    // Flash red when battery is low and not charging  
                    mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED,  
                            mBatteryLedOn, mBatteryLedOff);  
                }  
            } else if (status == BatteryManager.BATTERY_STATUS_CHARGING  
                    || status == BatteryManager.BATTERY_STATUS_FULL) {  
                if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {  
                    // Solid green when full or charging and nearly full  
                    mBatteryLight.setColor(mBatteryFullARGB);  
                } else {  
                    // Solid orange when charging and halfway full  
                    mBatteryLight.setColor(mBatteryMediumARGB);  
                }  
            } else {  
                // No lights if not charging and not low  
                mBatteryLight.turnOff();  
            }  
        }  
    }  
  
    private final class BatteryListener extends IBatteryPropertiesListener.Stub {  
        public void batteryPropertiesChanged(BatteryProperties props) {  
            BatteryService.this.update(props);  
       }  
    }  
}  
[java] view plain copy
/* 
 * Copyright (C) 2006 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.server;  
  
import android.os.BatteryStats;  
import com.android.internal.app.IBatteryStats;  
import com.android.server.am.BatteryStatsService;  
  
import android.app.ActivityManagerNative;  
import android.content.ContentResolver;  
import android.content.Context;  
import android.content.Intent;  
import android.content.pm.PackageManager;  
import android.os.BatteryManager;  
import android.os.BatteryProperties;  
import android.os.Binder;  
import android.os.FileUtils;  
import android.os.Handler;  
import android.os.IBatteryPropertiesListener;  
import android.os.IBatteryPropertiesRegistrar;  
import android.os.IBinder;  
import android.os.DropBoxManager;  
import android.os.RemoteException;  
import android.os.ServiceManager;  
import android.os.SystemClock;  
import android.os.UEventObserver;  
import android.os.UserHandle;  
import android.provider.Settings;  
import android.util.EventLog;  
import android.util.Slog;  
  
import java.io.File;  
import java.io.FileDescriptor;  
import java.io.FileOutputStream;  
import java.io.IOException;  
import java.io.PrintWriter;  
  
  
/** 
 * <p>BatteryService monitors the charging status, and charge level of the device 
 * battery.  When these values change this service broadcasts the new values 
 * to all {@link android.content.BroadcastReceiver IntentReceivers} that are 
 * watching the {@link android.content.Intent#ACTION_BATTERY_CHANGED 
 * BATTERY_CHANGED} action.</p> 
 * <p>The new values are stored in the Intent data and can be retrieved by 
 * calling {@link android.content.Intent#getExtra Intent.getExtra} with the 
 * following keys:</p> 
 * <p>"scale" - int, the maximum value for the charge level</p> 
 * <p>"level" - int, charge level, from 0 through "scale" inclusive</p> 
 * <p>"status" - String, the current charging status.<br /> 
 * <p>"health" - String, the current battery health.<br /> 
 * <p>"present" - boolean, true if the battery is present<br /> 
 * <p>"icon-small" - int, suggested small icon to use for this state</p> 
 * <p>"plugged" - int, 0 if the device is not plugged in; 1 if plugged 
 * into an AC power adapter; 2 if plugged in via USB.</p> 
 * <p>"voltage" - int, current battery voltage in millivolts</p> 
 * <p>"temperature" - int, current battery temperature in tenths of 
 * a degree Centigrade</p> 
 * <p>"technology" - String, the type of battery installed, e.g. "Li-ion"</p> 
 * 
 * <p> 
 * The battery service may be called by the power manager while holding its locks so 
 * we take care to post all outcalls into the activity manager to a handler. 
 * 
 * FIXME: Ideally the power manager would perform all of its calls into the battery 
 * service asynchronously itself. 
 * </p> 
 */  
public final class BatteryService extends Binder {  
    private static final String TAG = BatteryService.class.getSimpleName();  
  
    private static final boolean DEBUG = false;  
  
    private static final int BATTERY_SCALE = 100;    // battery capacity is a percentage  
  
    // Used locally for determining when to make a last ditch effort to log  
    // discharge stats before the device dies.  
    private int mCriticalBatteryLevel;  
  
    private static final int DUMP_MAX_LENGTH = 24 * 1024;  
    private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "--unplugged" };  
  
    private static final String DUMPSYS_DATA_PATH = "/data/system/";  
  
    // This should probably be exposed in the API, though it's not critical  
    private static final int BATTERY_PLUGGED_NONE = 0;  
  
    private final Context mContext;  
    private final IBatteryStats mBatteryStats;  
    private final Handler mHandler;  
  
    private final Object mLock = new Object();  
  
    private BatteryProperties mBatteryProps;  
    private boolean mBatteryLevelCritical;  
    private int mLastBatteryStatus;  
    private int mLastBatteryHealth;  
    private boolean mLastBatteryPresent;  
    private int mLastBatteryLevel;  
    private int mLastBatteryVoltage;  
    private int mLastBatteryTemperature;  
    private boolean mLastBatteryLevelCritical;  
  
    private int mInvalidCharger;  
    private int mLastInvalidCharger;  
  
    private int mLowBatteryWarningLevel;  
    private int mLowBatteryCloseWarningLevel;  
    private int mShutdownBatteryTemperature;  
  
    private int mPlugType;  
    private int mLastPlugType = -1; // Extra state so we can detect first run  
  
    private long mDischargeStartTime;  
    private int mDischargeStartLevel;  
  
    private boolean mUpdatesStopped;  
  
    private Led mLed;  
  
    private boolean mSentLowBatteryBroadcast = false;  
  
    private BatteryListener mBatteryPropertiesListener;  
    private IBatteryPropertiesRegistrar mBatteryPropertiesRegistrar;  
//構造函數  
    public BatteryService(Context context, LightsService lights) {  
        mContext = context;  
        mHandler = new Handler(true /*async*/);  
        mLed = new Led(context, lights);//這個應該是指示燈,沒實驗  
        mBatteryStats = BatteryStatsService.getService();  
  
 //低電量臨界值,這個數我看的源碼版本值是4(在這個類裏只是用來寫日誌)  
        mCriticalBatteryLevel = mContext.getResources().getInteger(  
                com.android.internal.R.integer.config_criticalBatteryWarningLevel);  
  
//低電量告警值,值15,下面會根據這個變量發送低電量的廣播Intent.ACTION_BATTERY_LOW(這個跟系統低電量提醒不要緊,只是發出去了)  
        mLowBatteryWarningLevel = mContext.getResources().getInteger(  
                com.android.internal.R.integer.config_lowBatteryWarningLevel);  
  
//電量告警取消值,值20 , 就是手機電量大於等於20的話發送Intent.ACTION_BATTERY_OKAY  
        mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(  
                com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);  
  
//值是680 ,溫度太高,超過這個值就發送廣播,跳轉到將要關機提醒。  
       mShutdownBatteryTemperature = mContext.getResources().getInteger(  
                com.android.internal.R.integer.config_shutdownBatteryTemperature);  
  
        // watch for invalid charger messages if the invalid_charger switch exists  
        if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {  
            mInvalidChargerObserver.startObserving(  
                    "DEVPATH=/devices/virtual/switch/invalid_charger");  
        }  
//電池監聽,這個應該是註冊到底層去了。當底層電量改變會調用此監聽。而後執行update(BatteryProperties props);  
        mBatteryPropertiesListener = new BatteryListener();  
  
        IBinder b = ServiceManager.getService("batterypropreg");  
        mBatteryPropertiesRegistrar = IBatteryPropertiesRegistrar.Stub.asInterface(b);  
  
        try {  
//這裏註冊  
          mBatteryPropertiesRegistrar.registerListener(mBatteryPropertiesListener);  
        } catch (RemoteException e) {  
            // Should never happen.  
        }  
    }  
//開機後先去看看是否沒電了或者溫度過高了。若是是,就關機提示(關機提示我等會介紹)。  
    void systemReady() {  
        // check our power situation now that it is safe to display the shutdown dialog.  
        synchronized (mLock) {  
            shutdownIfNoPowerLocked();  
            shutdownIfOverTempLocked();  
        }  
    }  
//返回是否在充電,這個函數在PowerManagerService.java 中調用  
    /** 
     * Returns true if the device is plugged into any of the specified plug types. 
     */  
    public boolean isPowered(int plugTypeSet) {  
        synchronized (mLock) {  
            return isPoweredLocked(plugTypeSet);  
        }  
    }  
//就是這裏,經過充電器類型判斷是否充電  
    private boolean isPoweredLocked(int plugTypeSet) {  
//我這英語小白猜着翻譯下:就是開機後,電池狀態不明瞭,那咱們就認爲就在充電,以便設備正常工做。  
        // assume we are powered if battery state is unknown so  
        // the "stay on while plugged in" option will work.  
        if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {  
            return true;  
        }  
//充電器  
        if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mBatteryProps.chargerAcOnline) {  
            return true;  
        }  
//USB,插電腦上充電  
      if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mBatteryProps.chargerUsbOnline) {  
            return true;  
        }  
//電源是無線的。 (我沒見過...)  
        if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mBatteryProps.chargerWirelessOnline) {  
            return true;  
        }  
        return false;  
    }  
  
    /** 
     * Returns the current plug type. 
     */  
//充電器類型  
   public int getPlugType() {  
        synchronized (mLock) {  
            return mPlugType;  
        }  
    }  
  
    /** 
     * Returns battery level as a percentage. 
     */  
//電池屬性:電量等級(0-100)  
    public int getBatteryLevel() {  
        synchronized (mLock) {  
            return mBatteryProps.batteryLevel;  
        }  
    }  
  
    /** 
     * Returns true if battery level is below the first warning threshold. 
     */  
//低電量  
     public boolean isBatteryLow() {  
        synchronized (mLock) {  
            return mBatteryProps.batteryPresent && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel;  
        }  
    }  
  
    /** 
     * Returns a non-zero value if an  unsupported charger is attached. 
     */  
//不支持的充電器類型    
    public int getInvalidCharger() {  
        synchronized (mLock) {  
            return mInvalidCharger;  
        }  
    }  
  
//這裏就是沒電了,要關機的提示。  
    private void shutdownIfNoPowerLocked() {  
        // shut down gracefully if our battery is critically low and we are not powered.  
        // wait until the system has booted before attempting to display the shutdown dialog.  
        if (mBatteryProps.batteryLevel == 0 && (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING)) {  
            mHandler.post(new Runnable() {  
                @Override  
                public void run() {  
                    if (ActivityManagerNative.isSystemReady()) {  
                        Intent intent = new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN_LOWBATTERY");//ACTION_REQUEST_SHUTDOWN  
                        intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);  
                        intent.putExtra("cant_be_cancel_by_button", true);  
                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
                        mContext.startActivityAsUser(intent, UserHandle.CURRENT);  
                    }  
                }  
            });  
        }  
    }  
  
//溫度太高,關機提示(我的感受這裏有問題,溫度太高爲啥子跳轉到沒電關機提示界面)   
    private void shutdownIfOverTempLocked() {  
        // shut down gracefully if temperature is too high (> 68.0C by default)  
        // wait until the system has booted before attempting to display the  
        // shutdown dialog.  
        if (mBatteryProps.batteryTemperature > mShutdownBatteryTemperature) {  
            mHandler.post(new Runnable() {  
                @Override  
                public void run() {  
                    if (ActivityManagerNative.isSystemReady()) {  
                        Intent intent = new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN_LOWBATTERY");//ACTION_REQUEST_SHUTDOWN  
                        intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);  
                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
                        mContext.startActivityAsUser(intent, UserHandle.CURRENT);  
                    }  
                }  
            });  
        }  
    }  
//這個方法就是被JNI回調的。用來更新上層狀態的方法。  
    private void update(BatteryProperties props) {  
        synchronized (mLock) {  
            if (!mUpdatesStopped) {  
                mBatteryProps = props;  
                // Process the new values.  
                processValuesLocked();  
            }  
        }  
    }  
//嗯。這個就是最主要的方法了。  
    private void processValuesLocked() {  
        boolean logOutlier = false;  
        long dischargeDuration = 0;  
  
        mBatteryLevelCritical = (mBatteryProps.batteryLevel <= mCriticalBatteryLevel);  
//充電器類型   
       if (mBatteryProps.chargerAcOnline) {  
            mPlugType = BatteryManager.BATTERY_PLUGGED_AC;  
        } else if (mBatteryProps.chargerUsbOnline) {  
            mPlugType = BatteryManager.BATTERY_PLUGGED_USB;  
        } else if (mBatteryProps.chargerWirelessOnline) {  
            mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;  
        } else {  
            mPlugType = BATTERY_PLUGGED_NONE;  
        }  
  
        if (DEBUG) {//日誌,略過  
            Slog.d(TAG, "Processing new values: "  
                    + "chargerAcOnline=" + mBatteryProps.chargerAcOnline  
                    + ", chargerUsbOnline=" + mBatteryProps.chargerUsbOnline  
                    + ", chargerWirelessOnline=" + mBatteryProps.chargerWirelessOnline  
                    + ", batteryStatus=" + mBatteryProps.batteryStatus  
                    + ", batteryHealth=" + mBatteryProps.batteryHealth  
                    + ", batteryPresent=" + mBatteryProps.batteryPresent  
                    + ", batteryLevel=" + mBatteryProps.batteryLevel  
                    + ", batteryTechnology=" + mBatteryProps.batteryTechnology  
                    + ", batteryVoltage=" + mBatteryProps.batteryVoltage  
                    + ", batteryCurrentNow=" + mBatteryProps.batteryCurrentNow  
                    + ", batteryChargeCounter=" + mBatteryProps.batteryChargeCounter  
                    + ", batteryTemperature=" + mBatteryProps.batteryTemperature  
                    + ", mBatteryLevelCritical=" + mBatteryLevelCritical  
                    + ", mPlugType=" + mPlugType);  
        }  
  
        // Let the battery stats keep track of the current level.  
        try {  
//把電池屬性放到狀態裏面  
           mBatteryStats.setBatteryState(mBatteryProps.batteryStatus, mBatteryProps.batteryHealth,  
                    mPlugType, mBatteryProps.batteryLevel, mBatteryProps.batteryTemperature,  
                    mBatteryProps.batteryVoltage);  
        } catch (RemoteException e) {  
            // Should never happen.  
        }  
//沒電了  
        shutdownIfNoPowerLocked();  
//溫度太高了  
       shutdownIfOverTempLocked();  
  
        if (mBatteryProps.batteryStatus != mLastBatteryStatus ||  
                mBatteryProps.batteryHealth != mLastBatteryHealth ||  
                mBatteryProps.batteryPresent != mLastBatteryPresent ||  
                mBatteryProps.batteryLevel != mLastBatteryLevel ||  
                mPlugType != mLastPlugType ||  
                mBatteryProps.batteryVoltage != mLastBatteryVoltage ||  
                mBatteryProps.batteryTemperature != mLastBatteryTemperature ||  
                mInvalidCharger != mLastInvalidCharger) {  
  
            if (mPlugType != mLastPlugType) {//當前充電器類型與上次的不同  
//而且上次充電器類型是no one ,那就能夠知道,如今是插上充電器了。  
               if (mLastPlugType == BATTERY_PLUGGED_NONE) {  
                    // discharging -> charging  
  
                    // There's no value in this data unless we've discharged at least once and the  
                    // battery level has changed; so don't log until it does.  
                    if (mDischargeStartTime != 0 && mDischargeStartLevel != mBatteryProps.batteryLevel) {  
                        dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;  
                        logOutlier = true;  
                        EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,  
                                mDischargeStartLevel, mBatteryProps.batteryLevel);  
                        // make sure we see a discharge event before logging again  
                        mDischargeStartTime = 0;  
                    }  
//而且本次充電器類型是no one ,那就能夠知道,如今是拔掉充電器了。  
                } else if (mPlugType == BATTERY_PLUGGED_NONE) {  
                    // charging -> discharging or we just powered up  
                    mDischargeStartTime = SystemClock.elapsedRealtime();  
                    mDischargeStartLevel = mBatteryProps.batteryLevel;  
                }  
            }  
            if (mBatteryProps.batteryStatus != mLastBatteryStatus ||//寫日誌,略過  
                    mBatteryProps.batteryHealth != mLastBatteryHealth ||  
                    mBatteryProps.batteryPresent != mLastBatteryPresent ||  
                    mPlugType != mLastPlugType) {  
                EventLog.writeEvent(EventLogTags.BATTERY_STATUS,  
                        mBatteryProps.batteryStatus, mBatteryProps.batteryHealth, mBatteryProps.batteryPresent ? 1 : 0,  
                        mPlugType, mBatteryProps.batteryTechnology);  
            }  
            if (mBatteryProps.batteryLevel != mLastBatteryLevel) {  
                // Don't do this just from voltage or temperature changes, that is  
                // too noisy.  
                EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,  
                        mBatteryProps.batteryLevel, mBatteryProps.batteryVoltage, mBatteryProps.batteryTemperature);  
            }  
            if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&  
                    mPlugType == BATTERY_PLUGGED_NONE) {  
                // We want to make sure we log discharge cycle outliers  
                // if the battery is about to die.  
                dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;  
                logOutlier = true;  
            }  
//本次調用,當前的充電狀態  
            final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;  
//本次調用,上次調用的充電狀態    
            final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;  
  
            /* The ACTION_BATTERY_LOW broadcast is sent in these situations: 
             * - is just un-plugged (previously was plugged) and battery level is 
             *   less than or equal to WARNING, or 
             * - is not plugged and battery level falls to WARNING boundary 
             *   (becomes <= mLowBatteryWarningLevel). 
             */  
//用於發送低電量廣播的判斷  
            final boolean sendBatteryLow = !plugged//(按sendBatteryLow = true 來講) 當前沒有充電  
                    && mBatteryProps.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN//充電狀態不是UNKNOWN  
                    && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel//當前電量小於告警值 15  
                    && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);//上次狀態是充電或者上次電量等級大於告警值 15  
  
            sendIntentLocked();//發送電池電量改變的廣播Intent.ACTION_BATTERY_CHANGED  
  
            // Separate broadcast is sent for power connected / not connected  
            // since the standard intent will not wake any applications and some  
            // applications may want to have smart behavior based on this.  
            if (mPlugType != 0 && mLastPlugType == 0) {//插上充電器了  
                mHandler.post(new Runnable() {  
                    @Override  
                    public void run() {  
                        Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED);  
                        statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);  
                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);  
                    }  
                });  
            }  
            else if (mPlugType == 0 && mLastPlugType != 0) {//斷開充電器了  
                mHandler.post(new Runnable() {  
                    @Override  
                    public void run() {  
                        Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED);  
                        statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);  
                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);  
                    }  
                });  
            }  
//發送低電量提醒(這個跟系統低電量提醒不要緊,只是發出去了)  
            if (sendBatteryLow) {  
                mSentLowBatteryBroadcast = true;  
                mHandler.post(new Runnable() {  
                    @Override  
                    public void run() {  
                        Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW);  
                        statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);  
                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);  
                    }  
                });  
            } else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= mLowBatteryCloseWarningLevel) {//電量超過20了。電池狀態OK了  
                mSentLowBatteryBroadcast = false;  
                mHandler.post(new Runnable() {  
                    @Override  
                    public void run() {  
                        Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY);  
                        statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);  
                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);  
                    }  
                });  
            }  
  
            // Update the battery LED  
            mLed.updateLightsLocked();  
  
            // This needs to be done after sendIntent() so that we get the lastest battery stats.  
            if (logOutlier && dischargeDuration != 0) {  
                logOutlierLocked(dischargeDuration);  
            }  
  
            mLastBatteryStatus = mBatteryProps.batteryStatus;  
            mLastBatteryHealth = mBatteryProps.batteryHealth;  
            mLastBatteryPresent = mBatteryProps.batteryPresent;  
            mLastBatteryLevel = mBatteryProps.batteryLevel;  
            mLastPlugType = mPlugType;  
            mLastBatteryVoltage = mBatteryProps.batteryVoltage;  
            mLastBatteryTemperature = mBatteryProps.batteryTemperature;  
            mLastBatteryLevelCritical = mBatteryLevelCritical;  
            mLastInvalidCharger = mInvalidCharger;  
        }  
    }  
//電池電量改變,把屬性發出去(系統低電量提醒接收的是這個廣播)  
    private void sendIntentLocked() {  
        //  Pack up the values and broadcast them to everyone  
        final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);  
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY  
                | Intent.FLAG_RECEIVER_REPLACE_PENDING);  
  
        int icon = getIconLocked(mBatteryProps.batteryLevel);  
  
        intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryProps.batteryStatus);  
        intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryProps.batteryHealth);  
        intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryProps.batteryPresent);  
        intent.putExtra(BatteryManager.EXTRA_LEVEL, mBatteryProps.batteryLevel);  
        intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);  
        intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);  
        intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);  
        intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryProps.batteryVoltage);  
        intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryProps.batteryTemperature);  
        intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryProps.batteryTechnology);  
        intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);  
  
        if (DEBUG) {  
            Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED.  level:" + mBatteryProps.batteryLevel +  
                    ", scale:" + BATTERY_SCALE + ", status:" + mBatteryProps.batteryStatus +  
                    ", health:" + mBatteryProps.batteryHealth +  ", present:" + mBatteryProps.batteryPresent +  
                    ", voltage: " + mBatteryProps.batteryVoltage +  
                    ", temperature: " + mBatteryProps.batteryTemperature +  
                    ", technology: " + mBatteryProps.batteryTechnology +  
                    ", AC powered:" + mBatteryProps.chargerAcOnline + ", USB powered:" + mBatteryProps.chargerUsbOnline +  
                    ", Wireless powered:" + mBatteryProps.chargerWirelessOnline +  
                    ", icon:" + icon  + ", invalid charger:" + mInvalidCharger);  
        }  
  
        mHandler.post(new Runnable() {  
            @Override  
            public void run() {  
                ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);  
            }  
        });  
    }  
  
    private void logBatteryStatsLocked() {  
        IBinder batteryInfoService = ServiceManager.getService(BatteryStats.SERVICE_NAME);  
        if (batteryInfoService == null) return;  
  
        DropBoxManager db = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);  
        if (db == null || !db.isTagEnabled("BATTERY_DISCHARGE_INFO")) return;  
  
        File dumpFile = null;  
        FileOutputStream dumpStream = null;  
        try {  
            // dump the service to a file  
            dumpFile = new File(DUMPSYS_DATA_PATH + BatteryStats.SERVICE_NAME + ".dump");  
            dumpStream = new FileOutputStream(dumpFile);  
            batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS);  
            FileUtils.sync(dumpStream);  
  
            // add dump file to drop box  
            db.addFile("BATTERY_DISCHARGE_INFO", dumpFile, DropBoxManager.IS_TEXT);  
        } catch (RemoteException e) {  
            Slog.e(TAG, "failed to dump battery service", e);  
        } catch (IOException e) {  
            Slog.e(TAG, "failed to write dumpsys file", e);  
        } finally {  
            // make sure we clean up  
            if (dumpStream != null) {  
                try {  
                    dumpStream.close();  
                } catch (IOException e) {  
                    Slog.e(TAG, "failed to close dumpsys output stream");  
                }  
            }  
            if (dumpFile != null && !dumpFile.delete()) {  
                Slog.e(TAG, "failed to delete temporary dumpsys file: "  
                        + dumpFile.getAbsolutePath());  
            }  
        }  
    }  
  
    private void logOutlierLocked(long duration) {  
        ContentResolver cr = mContext.getContentResolver();  
        String dischargeThresholdString = Settings.Global.getString(cr,  
                Settings.Global.BATTERY_DISCHARGE_THRESHOLD);  
        String durationThresholdString = Settings.Global.getString(cr,  
                Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD);  
  
        if (dischargeThresholdString != null && durationThresholdString != null) {  
            try {  
                long durationThreshold = Long.parseLong(durationThresholdString);  
                int dischargeThreshold = Integer.parseInt(dischargeThresholdString);  
                if (duration <= durationThreshold &&  
                        mDischargeStartLevel - mBatteryProps.batteryLevel >= dischargeThreshold) {  
                    // If the discharge cycle is bad enough we want to know about it.  
                    logBatteryStatsLocked();  
                }  
                if (DEBUG) Slog.v(TAG, "duration threshold: " + durationThreshold +  
                        " discharge threshold: " + dischargeThreshold);  
                if (DEBUG) Slog.v(TAG, "duration: " + duration + " discharge: " +  
                        (mDischargeStartLevel - mBatteryProps.batteryLevel));  
            } catch (NumberFormatException e) {  
                Slog.e(TAG, "Invalid DischargeThresholds GService string: " +  
                        durationThresholdString + " or " + dischargeThresholdString);  
                return;  
            }  
        }  
    }  
  
    private int getIconLocked(int level) {  
        if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {  
            return com.android.internal.R.drawable.stat_sys_battery_charge;  
        } else if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {  
            return com.android.internal.R.drawable.stat_sys_battery;  
        } else if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING  
                || mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_FULL) {  
            if (isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)  
                    && mBatteryProps.batteryLevel >= 100) {  
                return com.android.internal.R.drawable.stat_sys_battery_charge;  
            } else {  
                return com.android.internal.R.drawable.stat_sys_battery;  
            }  
        } else {  
            return com.android.internal.R.drawable.stat_sys_battery_unknown;  
        }  
    }  
  
    @Override  
    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {  
        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)  
                != PackageManager.PERMISSION_GRANTED) {  
  
            pw.println("Permission Denial: can't dump Battery service from from pid="  
                    + Binder.getCallingPid()  
                    + ", uid=" + Binder.getCallingUid());  
            return;  
        }  
  
        synchronized (mLock) {  
            if (args == null || args.length == 0 || "-a".equals(args[0])) {  
                pw.println("Current Battery Service state:");  
                if (mUpdatesStopped) {  
                    pw.println("  (UPDATES STOPPED -- use 'reset' to restart)");  
                }  
                pw.println("  AC powered: " + mBatteryProps.chargerAcOnline);  
                pw.println("  USB powered: " + mBatteryProps.chargerUsbOnline);  
                pw.println("  Wireless powered: " + mBatteryProps.chargerWirelessOnline);  
                pw.println("  status: " + mBatteryProps.batteryStatus);  
                pw.println("  health: " + mBatteryProps.batteryHealth);  
                pw.println("  present: " + mBatteryProps.batteryPresent);  
                pw.println("  level: " + mBatteryProps.batteryLevel);  
                pw.println("  scale: " + BATTERY_SCALE);  
                pw.println("  voltage: " + mBatteryProps.batteryVoltage);  
  
                if (mBatteryProps.batteryCurrentNow != Integer.MIN_VALUE) {  
                    pw.println("  current now: " + mBatteryProps.batteryCurrentNow);  
                }  
  
                if (mBatteryProps.batteryChargeCounter != Integer.MIN_VALUE) {  
                    pw.println("  charge counter: " + mBatteryProps.batteryChargeCounter);  
                }  
  
                pw.println("  temperature: " + mBatteryProps.batteryTemperature);  
                pw.println("  technology: " + mBatteryProps.batteryTechnology);  
            } else if (args.length == 3 && "set".equals(args[0])) {  
                String key = args[1];  
                String value = args[2];  
                try {  
                    boolean update = true;  
                    if ("ac".equals(key)) {  
                        mBatteryProps.chargerAcOnline = Integer.parseInt(value) != 0;  
                    } else if ("usb".equals(key)) {  
                        mBatteryProps.chargerUsbOnline = Integer.parseInt(value) != 0;  
                    } else if ("wireless".equals(key)) {  
                        mBatteryProps.chargerWirelessOnline = Integer.parseInt(value) != 0;  
                    } else if ("status".equals(key)) {  
                        mBatteryProps.batteryStatus = Integer.parseInt(value);  
                    } else if ("level".equals(key)) {  
                        mBatteryProps.batteryLevel = Integer.parseInt(value);  
                    } else if ("invalid".equals(key)) {  
                        mInvalidCharger = Integer.parseInt(value);  
                    } else {  
                        pw.println("Unknown set option: " + key);  
                        update = false;  
                    }  
                    if (update) {  
                        long ident = Binder.clearCallingIdentity();  
                        try {  
                            mUpdatesStopped = true;  
                            processValuesLocked();  
                        } finally {  
                            Binder.restoreCallingIdentity(ident);  
                        }  
                    }  
                } catch (NumberFormatException ex) {  
                    pw.println("Bad value: " + value);  
                }  
            } else if (args.length == 1 && "reset".equals(args[0])) {  
                long ident = Binder.clearCallingIdentity();  
                try {  
                    mUpdatesStopped = false;  
                } finally {  
                    Binder.restoreCallingIdentity(ident);  
                }  
            } else {  
                pw.println("Dump current battery state, or:");  
                pw.println("  set ac|usb|wireless|status|level|invalid <value>");  
                pw.println("  reset");  
            }  
        }  
    }  
  
    private final UEventObserver mInvalidChargerObserver = new UEventObserver() {  
        @Override  
        public void onUEvent(UEventObserver.UEvent event) {  
            final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;  
            synchronized (mLock) {  
                if (mInvalidCharger != invalidCharger) {  
                    mInvalidCharger = invalidCharger;  
                }  
            }  
        }  
    };  
  
    private final class Led {  
        private final LightsService.Light mBatteryLight;  
  
        private final int mBatteryLowARGB;  
        private final int mBatteryMediumARGB;  
        private final int mBatteryFullARGB;  
        private final int mBatteryLedOn;  
        private final int mBatteryLedOff;  
  
        public Led(Context context, LightsService lights) {  
            mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);  
  
            mBatteryLowARGB = context.getResources().getInteger(  
                    com.android.internal.R.integer.config_notificationsBatteryLowARGB);  
            mBatteryMediumARGB = context.getResources().getInteger(  
                    com.android.internal.R.integer.config_notificationsBatteryMediumARGB);  
            mBatteryFullARGB = context.getResources().getInteger(  
                    com.android.internal.R.integer.config_notificationsBatteryFullARGB);  
            mBatteryLedOn = context.getResources().getInteger(  
                    com.android.internal.R.integer.config_notificationsBatteryLedOn);  
            mBatteryLedOff = context.getResources().getInteger(  
                    com.android.internal.R.integer.config_notificationsBatteryLedOff);  
        }  
  
        /** 
         * Synchronize on BatteryService. 
         */  
        public void updateLightsLocked() {  
            final int level = mBatteryProps.batteryLevel;  
            final int status = mBatteryProps.batteryStatus;  
            if (level < mLowBatteryWarningLevel) {  
                if (status == BatteryManager.BATTERY_STATUS_CHARGING) {  
                    // Solid red when battery is charging  
                    mBatteryLight.setColor(mBatteryLowARGB);  
                } else {  
                    // Flash red when battery is low and not charging  
                    mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED,  
                            mBatteryLedOn, mBatteryLedOff);  
                }  
            } else if (status == BatteryManager.BATTERY_STATUS_CHARGING  
                    || status == BatteryManager.BATTERY_STATUS_FULL) {  
                if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {  
                    // Solid green when full or charging and nearly full  
                    mBatteryLight.setColor(mBatteryFullARGB);  
                } else {  
                    // Solid orange when charging and halfway full  
                    mBatteryLight.setColor(mBatteryMediumARGB);  
                }  
            } else {  
                // No lights if not charging and not low  
                mBatteryLight.turnOff();  
            }  
        }  
    }  
  
    private final class BatteryListener extends IBatteryPropertiesListener.Stub {  
        public void batteryPropertiesChanged(BatteryProperties props) {  
            BatteryService.this.update(props);  
       }  
    }  
}  java

總結以下:此服務構造時會註冊監聽到系統JNI層。 當電池電量改變的時候會調用update(BatteryProperties props) -----》processValuesLocked() 。 而processValuesLocked() 函數會把電池狀態把廣播發送出去。其餘類再接收廣播進行處理
下一個類就是低電量提醒了,文件目錄:\frameworks\base\packages\SystemUI\src\com\android\systemui\power\PowerUI.java
[java] view plaincopy
/* 
 * Copyright (C) 2008 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.power;  
  
import android.app.AlertDialog;  
import android.content.BroadcastReceiver;  
import android.content.ContentResolver;  
import android.content.Context;  
import android.content.DialogInterface;  
import android.content.Intent;  
import android.content.IntentFilter;  
import android.media.AudioManager;  
import android.media.Ringtone;  
import android.media.RingtoneManager;  
import android.net.Uri;  
import android.os.BatteryManager;  
import android.os.Handler;  
import android.os.PowerManager;  
import android.os.SystemClock;  
import android.os.UserHandle;  
import android.provider.Settings;  
import android.util.Slog;  
import android.view.View;  
import android.view.WindowManager;  
import android.widget.TextView;  
  
import com.android.systemui.R;  
import com.android.systemui.SystemUI;  
  
import java.io.FileDescriptor;  
import java.io.PrintWriter;  
import java.util.Arrays;  
  
public class PowerUI extends SystemUI { //整體說一下,這裏纔是處理低電量提醒的地方,他接收的廣播是Intent.ACTION_BATTERY_CHANGED  
    static final String TAG = "PowerUI";  
  
    static final boolean DEBUG = false;  
  
    Handler mHandler = new Handler();  
  
    int mBatteryLevel = 100;  
    int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;  
    int mPlugType = 0;  
    int mInvalidCharger = 0;  
  
    int mLowBatteryAlertCloseLevel;  
    int[] mLowBatteryReminderLevels = new int[2];  
  
    AlertDialog mInvalidChargerDialog;  
    AlertDialog mLowBatteryDialog;  
    TextView mBatteryLevelTextView;  
  
    private long mScreenOffTime = -1;  
  
    public void start() {  //這個類會在手機啓動後,在SystemUI 裏啓動(就是系統界面)。  
  
        mLowBatteryAlertCloseLevel = mContext.getResources().getInteger( //電量告警取消值,值20   
                com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);  
        mLowBatteryReminderLevels[0] = mContext.getResources().getInteger( //低電量提醒 15  
                com.android.internal.R.integer.config_lowBatteryWarningLevel);  
        mLowBatteryReminderLevels[1] = mContext.getResources().getInteger( //低電量臨界值 4  
                com.android.internal.R.integer.config_criticalBatteryWarningLevel);  
  
        final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);  
        mScreenOffTime = pm.isScreenOn() ? -1 : SystemClock.elapsedRealtime();  
  
        // Register for Intent broadcasts for...  
        IntentFilter filter = new IntentFilter();  
        filter.addAction(Intent.ACTION_BATTERY_CHANGED);  
        filter.addAction(Intent.ACTION_SCREEN_OFF);  
        filter.addAction(Intent.ACTION_SCREEN_ON);  
        mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);  
    }  
  
    /** 
     * Buckets the battery level. 
     * 
     * The code in this function is a little weird because I couldn't comprehend 
     * the bucket going up when the battery level was going down. --joeo 
     * 
     * 1 means that the battery is "ok" 
     * 0 means that the battery is between "ok" and what we should warn about. 
     * less than 0 means that the battery is low 
     */  
    private int findBatteryLevelBucket(int level) {   //這個方法是用來警告判斷用的。  
        if (level >= mLowBatteryAlertCloseLevel) {  
            return 1;  
        }  
        if (level >= mLowBatteryReminderLevels[0]) {  
            return 0;  
        }  
        final int N = mLowBatteryReminderLevels.length;  
        for (int i=N-1; i>=0; i--) {  
            if (level <= mLowBatteryReminderLevels[i]) {  
                return -1-i;  
            }  
        }  
        throw new RuntimeException("not possible!");  
    }  
  
    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { //重點,廣播接收處理  
        @Override  
        public void onReceive(Context context, Intent intent) {  
            String action = intent.getAction();  
            if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {  //電池電量改變  
                final int oldBatteryLevel = mBatteryLevel; //下邊就是根據Intent獲取BatteryService傳過來的電池屬性  
                mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100); /  
                final int oldBatteryStatus = mBatteryStatus;  
                mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,  
                        BatteryManager.BATTERY_STATUS_UNKNOWN);  
                final int oldPlugType = mPlugType;  
                mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);  
                final int oldInvalidCharger = mInvalidCharger;  
                mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);  
  
                final boolean plugged = mPlugType != 0;  
                final boolean oldPlugged = oldPlugType != 0;  
  
                int oldBucket = findBatteryLevelBucket(oldBatteryLevel); //這兩個值特別有意思,就是說記錄下老的電量,記錄一下新的電量,比較電量是增長了,仍是減少了  
                int bucket = findBatteryLevelBucket(mBatteryLevel);  
  
                if (DEBUG) {  
                    Slog.d(TAG, "buckets   ....." + mLowBatteryAlertCloseLevel  
                            + " .. " + mLowBatteryReminderLevels[0]  
                            + " .. " + mLowBatteryReminderLevels[1]);  
                    Slog.d(TAG, "level          " + oldBatteryLevel + " --> " + mBatteryLevel);  
                    Slog.d(TAG, "status         " + oldBatteryStatus + " --> " + mBatteryStatus);  
                    Slog.d(TAG, "plugType       " + oldPlugType + " --> " + mPlugType);  
                    Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);  
                    Slog.d(TAG, "bucket         " + oldBucket + " --> " + bucket);  
                    Slog.d(TAG, "plugged        " + oldPlugged + " --> " + plugged);  
                }  
  
                if (oldInvalidCharger == 0 && mInvalidCharger != 0) {  
                    Slog.d(TAG, "showing invalid charger warning");  
                    showInvalidChargerDialog(); //就是充電器不識別的彈窗  
                    return;  
                } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {  
                    dismissInvalidChargerDialog();  
                } else if (mInvalidChargerDialog != null) {  
                    // if invalid charger is showing, don't show low battery  
                    return;  
                }  
  
                if (!plugged  
                        && (bucket < oldBucket || oldPlugged)  
                        && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN  
                        && bucket < 0) {  
                    showLowBatteryWarning(); <span style="color:#FF0000;">//這裏哈,低電量提醒的彈窗</span>  
  
                    // only play SFX when the dialog comes up or the bucket changes  
                    if (bucket != oldBucket || oldPlugged) {  
                        playLowBatterySound();  
                    }  
                } else if (plugged || (bucket > oldBucket && bucket > 0)) { //插上充電器或者充電電池電量超過20取消彈窗  
                    dismissLowBatteryWarning();  
                } else if (mBatteryLevelTextView != null) {  
                    showLowBatteryWarning();  
                }  
            } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {  
                mScreenOffTime = SystemClock.elapsedRealtime();  
            } else if (Intent.ACTION_SCREEN_ON.equals(action)) {  
                mScreenOffTime = -1;  
            } else {  
                Slog.w(TAG, "unknown intent: " + intent);  
            }  
        }  
    };  
  
    void dismissLowBatteryWarning() {  
        if (mLowBatteryDialog != null) {  
            Slog.i(TAG, "closing low battery warning: level=" + mBatteryLevel);  
            mLowBatteryDialog.dismiss();  
        }  
    }  
  
    void showLowBatteryWarning() {  
        Slog.i(TAG,  
                ((mBatteryLevelTextView == null) ? "showing" : "updating")  
                + " low battery warning: level=" + mBatteryLevel  
                + " [" + findBatteryLevelBucket(mBatteryLevel) + "]");  
  
        CharSequence levelText = mContext.getString(  
                R.string.battery_low_percent_format, mBatteryLevel);  
  
        if (mBatteryLevelTextView != null) {  
            mBatteryLevelTextView.setText(levelText);  
        } else {  
            View v = View.inflate(mContext, R.layout.battery_low, null);  
            mBatteryLevelTextView = (TextView)v.findViewById(R.id.level_percent);  
  
            mBatteryLevelTextView.setText(levelText);  
  
            AlertDialog.Builder b = new AlertDialog.Builder(mContext);  
                b.setCancelable(true);  
                b.setTitle(R.string.battery_low_title);  
                b.setView(v);  
                b.setIconAttribute(android.R.attr.alertDialogIcon);  
                b.setPositiveButton(android.R.string.ok, null);  
  
            final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);  
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK  
                    | Intent.FLAG_ACTIVITY_MULTIPLE_TASK  
                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS  
                    | Intent.FLAG_ACTIVITY_NO_HISTORY);  
            if (intent.resolveActivity(mContext.getPackageManager()) != null) {  
                b.setNegativeButton(R.string.battery_low_why,  
                        new DialogInterface.OnClickListener() {  
                    @Override  
                    public void onClick(DialogInterface dialog, int which) {  
                        mContext.startActivityAsUser(intent, UserHandle.CURRENT);  
                        dismissLowBatteryWarning();  
                    }  
                });  
            }  
  
            AlertDialog d = b.create();  
            d.setOnDismissListener(new DialogInterface.OnDismissListener() {  
                    @Override  
                    public void onDismiss(DialogInterface dialog) {  
                        mLowBatteryDialog = null;  
                        mBatteryLevelTextView = null;  
                    }  
                });  
            d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);  
            d.getWindow().getAttributes().privateFlags |=  
                    WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;  
            d.show();  
            mLowBatteryDialog = d;  
        }  
    }  
  
    void playLowBatterySound() {  
        final ContentResolver cr = mContext.getContentResolver();  
  
        final int silenceAfter = Settings.Global.getInt(cr,  
                Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0);  
        final long offTime = SystemClock.elapsedRealtime() - mScreenOffTime;  
        if (silenceAfter > 0  
                && mScreenOffTime > 0  
                && offTime > silenceAfter) {  
            Slog.i(TAG, "screen off too long (" + offTime + "ms, limit " + silenceAfter  
                    + "ms): not waking up the user with low battery sound");  
            return;  
        }  
  
        if (DEBUG) {  
            Slog.d(TAG, "playing low battery sound. pick-a-doop!"); // WOMP-WOMP is deprecated  
        }  
  
        if (Settings.Global.getInt(cr, Settings.Global.POWER_SOUNDS_ENABLED, 1) == 1) {  
            final String soundPath = Settings.Global.getString(cr,  
                    Settings.Global.LOW_BATTERY_SOUND);  
            if (soundPath != null) {  
                final Uri soundUri = Uri.parse("file://" + soundPath);  
                if (soundUri != null) {  
                    final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);  
                    if (sfx != null) {  
                        sfx.setStreamType(AudioManager.STREAM_SYSTEM);  
                        sfx.play();  
                    }  
                }  
            }  
        }  
    }  
  
    void dismissInvalidChargerDialog() {  
        if (mInvalidChargerDialog != null) {  
            mInvalidChargerDialog.dismiss();  
        }  
    }  
  
    void showInvalidChargerDialog() {  
        Slog.d(TAG, "showing invalid charger dialog");  
  
        dismissLowBatteryWarning();  
  
        AlertDialog.Builder b = new AlertDialog.Builder(mContext);  
            b.setCancelable(true);  
            b.setMessage(R.string.invalid_charger);  
            b.setIconAttribute(android.R.attr.alertDialogIcon);  
            b.setPositiveButton(android.R.string.ok, null);  
  
        AlertDialog d = b.create();  
            d.setOnDismissListener(new DialogInterface.OnDismissListener() {  
                    public void onDismiss(DialogInterface dialog) {  
                        mInvalidChargerDialog = null;  
                        mBatteryLevelTextView = null;  
                    }  
                });  
  
        d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);  
        d.show();  
        mInvalidChargerDialog = d;  
    }  
  
    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {  
        pw.print("mLowBatteryAlertCloseLevel=");  
        pw.println(mLowBatteryAlertCloseLevel);  
        pw.print("mLowBatteryReminderLevels=");  
        pw.println(Arrays.toString(mLowBatteryReminderLevels));  
        pw.print("mInvalidChargerDialog=");  
        pw.println(mInvalidChargerDialog == null ? "null" : mInvalidChargerDialog.toString());  
        pw.print("mLowBatteryDialog=");  
        pw.println(mLowBatteryDialog == null ? "null" : mLowBatteryDialog.toString());  
        pw.print("mBatteryLevel=");  
        pw.println(Integer.toString(mBatteryLevel));  
        pw.print("mBatteryStatus=");  
        pw.println(Integer.toString(mBatteryStatus));  
        pw.print("mPlugType=");  
        pw.println(Integer.toString(mPlugType));  
        pw.print("mInvalidCharger=");  
        pw.println(Integer.toString(mInvalidCharger));  
        pw.print("mScreenOffTime=");  
        pw.print(mScreenOffTime);  
        if (mScreenOffTime >= 0) {  
            pw.print(" (");  
            pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);  
            pw.print(" ago)");  
        }  
        pw.println();  
        pw.print("soundTimeout=");  
        pw.println(Settings.Global.getInt(mContext.getContentResolver(),  
                Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));  
        pw.print("bucket: ");  
        pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));  
    }  
}  
[java] view plain copy
/* 
 * Copyright (C) 2008 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.power;  
  
import android.app.AlertDialog;  
import android.content.BroadcastReceiver;  
import android.content.ContentResolver;  
import android.content.Context;  
import android.content.DialogInterface;  
import android.content.Intent;  
import android.content.IntentFilter;  
import android.media.AudioManager;  
import android.media.Ringtone;  
import android.media.RingtoneManager;  
import android.net.Uri;  
import android.os.BatteryManager;  
import android.os.Handler;  
import android.os.PowerManager;  
import android.os.SystemClock;  
import android.os.UserHandle;  
import android.provider.Settings;  
import android.util.Slog;  
import android.view.View;  
import android.view.WindowManager;  
import android.widget.TextView;  
  
import com.android.systemui.R;  
import com.android.systemui.SystemUI;  
  
import java.io.FileDescriptor;  
import java.io.PrintWriter;  
import java.util.Arrays;  
  
public class PowerUI extends SystemUI { //整體說一下,這裏纔是處理低電量提醒的地方,他接收的廣播是Intent.ACTION_BATTERY_CHANGED  
    static final String TAG = "PowerUI";  
  
    static final boolean DEBUG = false;  
  
    Handler mHandler = new Handler();  
  
    int mBatteryLevel = 100;  
    int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;  
    int mPlugType = 0;  
    int mInvalidCharger = 0;  
  
    int mLowBatteryAlertCloseLevel;  
    int[] mLowBatteryReminderLevels = new int[2];  
  
    AlertDialog mInvalidChargerDialog;  
    AlertDialog mLowBatteryDialog;  
    TextView mBatteryLevelTextView;  
  
    private long mScreenOffTime = -1;  
  
    public void start() {  //這個類會在手機啓動後,在SystemUI 裏啓動(就是系統界面)。  
  
        mLowBatteryAlertCloseLevel = mContext.getResources().getInteger( //電量告警取消值,值20   
                com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);  
        mLowBatteryReminderLevels[0] = mContext.getResources().getInteger( //低電量提醒 15  
                com.android.internal.R.integer.config_lowBatteryWarningLevel);  
        mLowBatteryReminderLevels[1] = mContext.getResources().getInteger( //低電量臨界值 4  
                com.android.internal.R.integer.config_criticalBatteryWarningLevel);  
  
        final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);  
        mScreenOffTime = pm.isScreenOn() ? -1 : SystemClock.elapsedRealtime();  
  
        // Register for Intent broadcasts for...  
        IntentFilter filter = new IntentFilter();  
        filter.addAction(Intent.ACTION_BATTERY_CHANGED);  
        filter.addAction(Intent.ACTION_SCREEN_OFF);  
        filter.addAction(Intent.ACTION_SCREEN_ON);  
        mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);  
    }  
  
    /** 
     * Buckets the battery level. 
     * 
     * The code in this function is a little weird because I couldn't comprehend 
     * the bucket going up when the battery level was going down. --joeo 
     * 
     * 1 means that the battery is "ok" 
     * 0 means that the battery is between "ok" and what we should warn about. 
     * less than 0 means that the battery is low 
     */  
    private int findBatteryLevelBucket(int level) {   //這個方法是用來警告判斷用的。  
        if (level >= mLowBatteryAlertCloseLevel) {  
            return 1;  
        }  
        if (level >= mLowBatteryReminderLevels[0]) {  
            return 0;  
        }  
        final int N = mLowBatteryReminderLevels.length;  
        for (int i=N-1; i>=0; i--) {  
            if (level <= mLowBatteryReminderLevels[i]) {  
                return -1-i;  
            }  
        }  
        throw new RuntimeException("not possible!");  
    }  
  
    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { //重點,廣播接收處理  
        @Override  
        public void onReceive(Context context, Intent intent) {  
            String action = intent.getAction();  
            if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {  //電池電量改變  
                final int oldBatteryLevel = mBatteryLevel; //下邊就是根據Intent獲取BatteryService傳過來的電池屬性  
                mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100); /  
                final int oldBatteryStatus = mBatteryStatus;  
                mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,  
                        BatteryManager.BATTERY_STATUS_UNKNOWN);  
                final int oldPlugType = mPlugType;  
                mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);  
                final int oldInvalidCharger = mInvalidCharger;  
                mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);  
  
                final boolean plugged = mPlugType != 0;  
                final boolean oldPlugged = oldPlugType != 0;  
  
                int oldBucket = findBatteryLevelBucket(oldBatteryLevel); //這兩個值特別有意思,就是說記錄下老的電量,記錄一下新的電量,比較電量是增長了,仍是減少了  
                int bucket = findBatteryLevelBucket(mBatteryLevel);  
  
                if (DEBUG) {  
                    Slog.d(TAG, "buckets   ....." + mLowBatteryAlertCloseLevel  
                            + " .. " + mLowBatteryReminderLevels[0]  
                            + " .. " + mLowBatteryReminderLevels[1]);  
                    Slog.d(TAG, "level          " + oldBatteryLevel + " --> " + mBatteryLevel);  
                    Slog.d(TAG, "status         " + oldBatteryStatus + " --> " + mBatteryStatus);  
                    Slog.d(TAG, "plugType       " + oldPlugType + " --> " + mPlugType);  
                    Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);  
                    Slog.d(TAG, "bucket         " + oldBucket + " --> " + bucket);  
                    Slog.d(TAG, "plugged        " + oldPlugged + " --> " + plugged);  
                }  
  
                if (oldInvalidCharger == 0 && mInvalidCharger != 0) {  
                    Slog.d(TAG, "showing invalid charger warning");  
                    showInvalidChargerDialog(); //就是充電器不識別的彈窗  
                    return;  
                } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {  
                    dismissInvalidChargerDialog();  
                } else if (mInvalidChargerDialog != null) {  
                    // if invalid charger is showing, don't show low battery  
                    return;  
                }  
  
                if (!plugged  
                        && (bucket < oldBucket || oldPlugged)  
                        && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN  
                        && bucket < 0) {  
                    showLowBatteryWarning(); <span style="color:#FF0000;">//這裏哈,低電量提醒的彈窗</span>  
  
                    // only play SFX when the dialog comes up or the bucket changes  
                    if (bucket != oldBucket || oldPlugged) {  
                        playLowBatterySound();  
                    }  
                } else if (plugged || (bucket > oldBucket && bucket > 0)) { //插上充電器或者充電電池電量超過20取消彈窗  
                    dismissLowBatteryWarning();  
                } else if (mBatteryLevelTextView != null) {  
                    showLowBatteryWarning();  
                }  
            } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {  
                mScreenOffTime = SystemClock.elapsedRealtime();  
            } else if (Intent.ACTION_SCREEN_ON.equals(action)) {  
                mScreenOffTime = -1;  
            } else {  
                Slog.w(TAG, "unknown intent: " + intent);  
            }  
        }  
    };  
  
    void dismissLowBatteryWarning() {  
        if (mLowBatteryDialog != null) {  
            Slog.i(TAG, "closing low battery warning: level=" + mBatteryLevel);  
            mLowBatteryDialog.dismiss();  
        }  
    }  
  
    void showLowBatteryWarning() {  
        Slog.i(TAG,  
                ((mBatteryLevelTextView == null) ? "showing" : "updating")  
                + " low battery warning: level=" + mBatteryLevel  
                + " [" + findBatteryLevelBucket(mBatteryLevel) + "]");  
  
        CharSequence levelText = mContext.getString(  
                R.string.battery_low_percent_format, mBatteryLevel);  
  
        if (mBatteryLevelTextView != null) {  
            mBatteryLevelTextView.setText(levelText);  
        } else {  
            View v = View.inflate(mContext, R.layout.battery_low, null);  
            mBatteryLevelTextView = (TextView)v.findViewById(R.id.level_percent);  
  
            mBatteryLevelTextView.setText(levelText);  
  
            AlertDialog.Builder b = new AlertDialog.Builder(mContext);  
                b.setCancelable(true);  
                b.setTitle(R.string.battery_low_title);  
                b.setView(v);  
                b.setIconAttribute(android.R.attr.alertDialogIcon);  
                b.setPositiveButton(android.R.string.ok, null);  
  
            final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);  
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK  
                    | Intent.FLAG_ACTIVITY_MULTIPLE_TASK  
                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS  
                    | Intent.FLAG_ACTIVITY_NO_HISTORY);  
            if (intent.resolveActivity(mContext.getPackageManager()) != null) {  
                b.setNegativeButton(R.string.battery_low_why,  
                        new DialogInterface.OnClickListener() {  
                    @Override  
                    public void onClick(DialogInterface dialog, int which) {  
                        mContext.startActivityAsUser(intent, UserHandle.CURRENT);  
                        dismissLowBatteryWarning();  
                    }  
                });  
            }  
  
            AlertDialog d = b.create();  
            d.setOnDismissListener(new DialogInterface.OnDismissListener() {  
                    @Override  
                    public void onDismiss(DialogInterface dialog) {  
                        mLowBatteryDialog = null;  
                        mBatteryLevelTextView = null;  
                    }  
                });  
            d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);  
            d.getWindow().getAttributes().privateFlags |=  
                    WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;  
            d.show();  
            mLowBatteryDialog = d;  
        }  
    }  
  
    void playLowBatterySound() {  
        final ContentResolver cr = mContext.getContentResolver();  
  
        final int silenceAfter = Settings.Global.getInt(cr,  
                Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0);  
        final long offTime = SystemClock.elapsedRealtime() - mScreenOffTime;  
        if (silenceAfter > 0  
                && mScreenOffTime > 0  
                && offTime > silenceAfter) {  
            Slog.i(TAG, "screen off too long (" + offTime + "ms, limit " + silenceAfter  
                    + "ms): not waking up the user with low battery sound");  
            return;  
        }  
  
        if (DEBUG) {  
            Slog.d(TAG, "playing low battery sound. pick-a-doop!"); // WOMP-WOMP is deprecated  
        }  
  
        if (Settings.Global.getInt(cr, Settings.Global.POWER_SOUNDS_ENABLED, 1) == 1) {  
            final String soundPath = Settings.Global.getString(cr,  
                    Settings.Global.LOW_BATTERY_SOUND);  
            if (soundPath != null) {  
                final Uri soundUri = Uri.parse("file://" + soundPath);  
                if (soundUri != null) {  
                    final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);  
                    if (sfx != null) {  
                        sfx.setStreamType(AudioManager.STREAM_SYSTEM);  
                        sfx.play();  
                    }  
                }  
            }  
        }  
    }  
  
    void dismissInvalidChargerDialog() {  
        if (mInvalidChargerDialog != null) {  
            mInvalidChargerDialog.dismiss();  
        }  
    }  
  
    void showInvalidChargerDialog() {  
        Slog.d(TAG, "showing invalid charger dialog");  
  
        dismissLowBatteryWarning();  
  
        AlertDialog.Builder b = new AlertDialog.Builder(mContext);  
            b.setCancelable(true);  
            b.setMessage(R.string.invalid_charger);  
            b.setIconAttribute(android.R.attr.alertDialogIcon);  
            b.setPositiveButton(android.R.string.ok, null);  
  
        AlertDialog d = b.create();  
            d.setOnDismissListener(new DialogInterface.OnDismissListener() {  
                    public void onDismiss(DialogInterface dialog) {  
                        mInvalidChargerDialog = null;  
                        mBatteryLevelTextView = null;  
                    }  
                });  
  
        d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);  
        d.show();  
        mInvalidChargerDialog = d;  
    }  
  
    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {  
        pw.print("mLowBatteryAlertCloseLevel=");  
        pw.println(mLowBatteryAlertCloseLevel);  
        pw.print("mLowBatteryReminderLevels=");  
        pw.println(Arrays.toString(mLowBatteryReminderLevels));  
        pw.print("mInvalidChargerDialog=");  
        pw.println(mInvalidChargerDialog == null ? "null" : mInvalidChargerDialog.toString());  
        pw.print("mLowBatteryDialog=");  
        pw.println(mLowBatteryDialog == null ? "null" : mLowBatteryDialog.toString());  
        pw.print("mBatteryLevel=");  
        pw.println(Integer.toString(mBatteryLevel));  
        pw.print("mBatteryStatus=");  
        pw.println(Integer.toString(mBatteryStatus));  
        pw.print("mPlugType=");  
        pw.println(Integer.toString(mPlugType));  
        pw.print("mInvalidCharger=");  
        pw.println(Integer.toString(mInvalidCharger));  
        pw.print("mScreenOffTime=");  
        pw.print(mScreenOffTime);  
        if (mScreenOffTime >= 0) {  
            pw.print(" (");  
            pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);  
            pw.print(" ago)");  
        }  
        pw.println();  
        pw.print("soundTimeout=");  
        pw.println(Settings.Global.getInt(mContext.getContentResolver(),  
                Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));  
        pw.print("bucket: ");  
        pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));  
    }  
}  
總結一下:這個類就是接收電池電量改變的廣播,而後彈窗提醒。android

下一個類就是沒電關機界面了,文件目錄:\frameworks\base\services\java\com\android\server\ShutdownLowBatteryActivity.java
這個Activity的配置文件是這樣的(\frameworks\base\core\res\AndroidManifest.xml):
[html] view plaincopy
 <activity android:name="com.android.server.ShutdownLowBatteryActivity"  
    android:theme="@android:style/Theme.Translucent.NoTitleBar"  
    android:configChanges="orientation|keyboardHidden|screenSize"  
    android:windowSoftInputMode="stateHidden"  
    android:permission="android.permission.SHUTDOWN"  
    android:excludeFromRecents="true">  
    <intent-filter>  
        <action android:name="android.intent.action.ACTION_REQUEST_SHUTDOWN_LOWBATTERY" />  
        <category android:name="android.intent.category.DEFAULT" />  
    </intent-filter>  
</activity>  
[html] view plain copy
 <activity android:name="com.android.server.ShutdownLowBatteryActivity"  
    android:theme="@android:style/Theme.Translucent.NoTitleBar"  
    android:configChanges="orientation|keyboardHidden|screenSize"  
    android:windowSoftInputMode="stateHidden"  
    android:permission="android.permission.SHUTDOWN"  
    android:excludeFromRecents="true">  
    <intent-filter>  
        <action android:name="android.intent.action.ACTION_REQUEST_SHUTDOWN_LOWBATTERY" />  
        <category android:name="android.intent.category.DEFAULT" />  
    </intent-filter>  
</activity>  express

android:theme="@android:style/Theme.Translucent.NoTitleBar"是API小於11的透明主題。這樣彈出的dialog會是老版本的主題。(這個Activity是透明的)
對於Theme,由於是4.4,我更傾向於:android:theme="@android:style/Theme.Holo.Panel"apache

ShutdownLowBatteryActivity.java:
[java] view plaincopy
/* 
 * Copyright (C) 2009 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.server;  
  
import android.app.Activity;  
import android.app.AlertDialog;  
import android.app.KeyguardManager;  
import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.DialogInterface;  
import android.content.Intent;  
import android.content.IntentFilter;  
import android.os.Bundle;  
import android.os.Handler;  
import android.util.Slog;  
import android.view.Window;  
import android.view.WindowManager;  
import android.view.View;  
  
  
//import com.android.internal.app.ShutdownThread;  
import com.android.server.power.ShutdownThread;  
  
import android.telephony.TelephonyManager;  
import android.telephony.PhoneStateListener;  
import android.media.MediaPlayer;  
import android.media.MediaPlayer.OnCompletionListener;  
import android.content.ContentResolver;  
import android.provider.Settings;  
  
import java.io.IOException;  
  
public class ShutdownLowBatteryActivity extends Activity {  
  
    private static final String TAG = "ShutdownLowBatteryActivity";  
    private boolean mConfirm;  
    private int mSeconds = 15;//電池電量等於0 15秒內不插充電器就自動關機  
    private AlertDialog mDialog;  
    private Handler myHandler = new Handler();  
    private Runnable myRunnable = new Runnable() { //這裏數秒關機  
        @Override  
        public void run() {  
            mSeconds --;  
            if(mSeconds <1)  
                mSeconds=0;  
            mDialog.setMessage(getString(com.android.internal.R.string.low_battery_shutdown_after_seconds,mSeconds));  
  
            if(mSeconds <= 1){  
                myHandler.removeCallbacks(myRunnable);  
                Handler h = new Handler();  
                h.post(new Runnable() {  
                    public void run() {  
                        ShutdownThread.shutdown(ShutdownLowBatteryActivity.this, mConfirm);  
                    }  
                });  
            }  
            myHandler.postDelayed(myRunnable,1000);  
        }  
    };  
  
    private BroadcastReceiver mReceiver;  
    private MediaPlayer mplayer;  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
  
        mConfirm = getIntent().getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false);  
        Slog.i(TAG, "onCreate(): confirm=" + mConfirm);  
   
        //if(getIntent().getBooleanExtra("can_be_cancel", false)) { //這行註釋掉了: 而後當連上充電器後或者電量漲到20的時候,就取消倒計時關機  
                mReceiver = new BroadcastReceiver() {  
                @Override  
                public void onReceive(Context context, Intent intent) {  
                    if(Intent.ACTION_BATTERY_OKAY.equals(intent.getAction())|  
                        Intent.ACTION_POWER_CONNECTED.equals(intent.getAction())){  
                        ShutDownWakeLock.releaseCpuLock();  
                        myHandler.removeCallbacks(myRunnable);  
                        if(mReceiver != null)  
                            unregisterReceiver(mReceiver);  
                        finish();  
                    }  
                }  
            };  
  
         IntentFilter filter=new IntentFilter(Intent.ACTION_POWER_CONNECTED);  
         filter.addAction(Intent.ACTION_BATTERY_OKAY);  
         registerReceiver(mReceiver, filter);  
         //}  
   
  
        PhoneStateListener mPhoneStateListener = new PhoneStateListener() { //若是正數秒呢,電話呼入了。取消自動關機  
            @Override  
            public void onCallStateChanged(int state, String ignored) {  
                if (state == TelephonyManager.CALL_STATE_RINGING) {  
                    ShutDownWakeLock.releaseCpuLock();  
                    myHandler.removeCallbacks(myRunnable);  
                    finish();  
                }  
            }  
        };  
  
        TelephonyManager mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);  
        mTelephonyManager.listen(mPhoneStateListener,  
                PhoneStateListener.LISTEN_CALL_STATE);  
        requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);  
        Window win = getWindow();  
        win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED  
                | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);  
        win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON  
                    | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);  
  
        setContentView(new View(this));  
        mDialog=new AlertDialog.Builder(this).create();  
        mDialog.setTitle(com.android.internal.R.string.low_battery_shutdown_title);  
        mDialog.setMessage(getString(com.android.internal.R.string.low_battery_shutdown_after_seconds,mSeconds));  
        if(!getIntent().getBooleanExtra("cant_be_cancel_by_button", false)) {//讀取配置文件,是否運行取消,而後根據這個顯示取消自動關機按鈕  
            mDialog.setButton(DialogInterface.BUTTON_NEUTRAL,getText(com.android.internal.R.string.cancel), new DialogInterface.OnClickListener() {  
                    @Override  
                    public void onClick(DialogInterface dialog, int which) {  
                        myHandler.removeCallbacks(myRunnable);  
                        dialog.cancel();  
                        if(mReceiver != null)  
                            unregisterReceiver(mReceiver);  
                        finish();  
                    }});  
        }  
        mDialog.setCancelable(false);  
        //mDialog.getWindow().setType( WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);  
        mDialog.show();  
        if(mConfirm == false){  
            myHandler.postDelayed(myRunnable, 1000);  
        }  
        myHandler.post(new Runnable(){  
            public void run(){  
                final ContentResolver cr = getContentResolver();  
                String path=Settings.System.getString(cr,Settings.System.NOTIFICATION_SOUND);  
  
               mplayer=new MediaPlayer();  
                try{  
                    mplayer.reset();  
                    mplayer.setDataSource("system/media/audio/ui/LowBattery.ogg");                    
                    mplayer.prepare();  
                    mplayer.start();  
                    mplayer.setOnCompletionListener(new OnCompletionListener() {  
                        @Override  
                        public void onCompletion(MediaPlayer mp) {  
                            if(null != mplayer){  
                                mplayer.stop();  
                                mplayer.release();  
                                mplayer = null;  
                            }  
                        }  
                    });  
                }  
                catch(IOException e){  
                      
                }  
            }  
        });  
    }  
}  
[java] view plain copy
/* 
 * Copyright (C) 2009 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.server;  
  
import android.app.Activity;  
import android.app.AlertDialog;  
import android.app.KeyguardManager;  
import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.DialogInterface;  
import android.content.Intent;  
import android.content.IntentFilter;  
import android.os.Bundle;  
import android.os.Handler;  
import android.util.Slog;  
import android.view.Window;  
import android.view.WindowManager;  
import android.view.View;  
  
  
//import com.android.internal.app.ShutdownThread;  
import com.android.server.power.ShutdownThread;  
  
import android.telephony.TelephonyManager;  
import android.telephony.PhoneStateListener;  
import android.media.MediaPlayer;  
import android.media.MediaPlayer.OnCompletionListener;  
import android.content.ContentResolver;  
import android.provider.Settings;  
  
import java.io.IOException;  
  
public class ShutdownLowBatteryActivity extends Activity {  
  
    private static final String TAG = "ShutdownLowBatteryActivity";  
    private boolean mConfirm;  
    private int mSeconds = 15;//電池電量等於0 15秒內不插充電器就自動關機  
    private AlertDialog mDialog;  
    private Handler myHandler = new Handler();  
    private Runnable myRunnable = new Runnable() { //這裏數秒關機  
        @Override  
        public void run() {  
            mSeconds --;  
            if(mSeconds <1)  
                mSeconds=0;  
            mDialog.setMessage(getString(com.android.internal.R.string.low_battery_shutdown_after_seconds,mSeconds));  
  
            if(mSeconds <= 1){  
                myHandler.removeCallbacks(myRunnable);  
                Handler h = new Handler();  
                h.post(new Runnable() {  
                    public void run() {  
                        ShutdownThread.shutdown(ShutdownLowBatteryActivity.this, mConfirm);  
                    }  
                });  
            }  
            myHandler.postDelayed(myRunnable,1000);  
        }  
    };  
  
    private BroadcastReceiver mReceiver;  
    private MediaPlayer mplayer;  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
  
        mConfirm = getIntent().getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false);  
        Slog.i(TAG, "onCreate(): confirm=" + mConfirm);  
   
        //if(getIntent().getBooleanExtra("can_be_cancel", false)) { //這行註釋掉了: 而後當連上充電器後或者電量漲到20的時候,就取消倒計時關機  
                mReceiver = new BroadcastReceiver() {  
                @Override  
                public void onReceive(Context context, Intent intent) {  
                    if(Intent.ACTION_BATTERY_OKAY.equals(intent.getAction())|  
                        Intent.ACTION_POWER_CONNECTED.equals(intent.getAction())){  
                        ShutDownWakeLock.releaseCpuLock();  
                        myHandler.removeCallbacks(myRunnable);  
                        if(mReceiver != null)  
                            unregisterReceiver(mReceiver);  
                        finish();  
                    }  
                }  
            };  
  
         IntentFilter filter=new IntentFilter(Intent.ACTION_POWER_CONNECTED);  
         filter.addAction(Intent.ACTION_BATTERY_OKAY);  
         registerReceiver(mReceiver, filter);  
         //}  
   
  
        PhoneStateListener mPhoneStateListener = new PhoneStateListener() { //若是正數秒呢,電話呼入了。取消自動關機  
            @Override  
            public void onCallStateChanged(int state, String ignored) {  
                if (state == TelephonyManager.CALL_STATE_RINGING) {  
                    ShutDownWakeLock.releaseCpuLock();  
                    myHandler.removeCallbacks(myRunnable);  
                    finish();  
                }  
            }  
        };  
  
        TelephonyManager mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);  
        mTelephonyManager.listen(mPhoneStateListener,  
                PhoneStateListener.LISTEN_CALL_STATE);  
        requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);  
        Window win = getWindow();  
        win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED  
                | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);  
        win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON  
                    | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);  
  
        setContentView(new View(this));  
        mDialog=new AlertDialog.Builder(this).create();  
        mDialog.setTitle(com.android.internal.R.string.low_battery_shutdown_title);  
        mDialog.setMessage(getString(com.android.internal.R.string.low_battery_shutdown_after_seconds,mSeconds));  
        if(!getIntent().getBooleanExtra("cant_be_cancel_by_button", false)) {//讀取配置文件,是否運行取消,而後根據這個顯示取消自動關機按鈕  
            mDialog.setButton(DialogInterface.BUTTON_NEUTRAL,getText(com.android.internal.R.string.cancel), new DialogInterface.OnClickListener() {  
                    @Override  
                    public void onClick(DialogInterface dialog, int which) {  
                        myHandler.removeCallbacks(myRunnable);  
                        dialog.cancel();  
                        if(mReceiver != null)  
                            unregisterReceiver(mReceiver);  
                        finish();  
                    }});  
        }  
        mDialog.setCancelable(false);  
        //mDialog.getWindow().setType( WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);  
        mDialog.show();  
        if(mConfirm == false){  
            myHandler.postDelayed(myRunnable, 1000);  
        }  
        myHandler.post(new Runnable(){  
            public void run(){  
                final ContentResolver cr = getContentResolver();  
                String path=Settings.System.getString(cr,Settings.System.NOTIFICATION_SOUND);  
  
               mplayer=new MediaPlayer();  
                try{  
                    mplayer.reset();  
                    mplayer.setDataSource("system/media/audio/ui/LowBattery.ogg");                    
                    mplayer.prepare();  
                    mplayer.start();  
                    mplayer.setOnCompletionListener(new OnCompletionListener() {  
                        @Override  
                        public void onCompletion(MediaPlayer mp) {  
                            if(null != mplayer){  
                                mplayer.stop();  
                                mplayer.release();  
                                mplayer = null;  
                            }  
                        }  
                    });  
                }  
                catch(IOException e){  
                      
                }  
            }  
        });  
    }  
}  
總結以下:這個類就是彈窗計時,提醒不連充電器就要關機了。app

最後一個相關的類,文件路徑:\frameworks\base\packages\SystemUI\src\com\android\systemui\BatteryMeterView.java
這個類是自定義控件,定義在手機狀態欄裏:less

[java] view plaincopy
/* 
 * Copyright (C) 2013 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;  
  
import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  
import android.content.IntentFilter;  
import android.content.res.Resources;  
import android.content.res.TypedArray;  
import android.graphics.Canvas;  
import android.graphics.Paint;  
import android.graphics.Path;  
import android.graphics.PorterDuff;  
import android.graphics.PorterDuffXfermode;  
import android.graphics.Rect;  
import android.graphics.RectF;  
import android.graphics.Typeface;  
import android.os.BatteryManager;  
import android.os.Bundle;  
import android.provider.Settings;  
import android.util.AttributeSet;  
import android.view.View;  
  
public class BatteryMeterView extends View implements DemoMode {  
    public static final String TAG = BatteryMeterView.class.getSimpleName();  
    public static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST";  
  
    public static final boolean ENABLE_PERCENT = true;  
    public static final boolean SINGLE_DIGIT_PERCENT = false;  
    public static final boolean SHOW_100_PERCENT = false;  
  
    public static final int FULL = 96;  
    public static final int EMPTY = 4;  
  
    public static final float SUBPIXEL = 0.4f;  // inset rects for softer edges  
  
    int[] mColors;  
  
    boolean mShowPercent = true;  
    Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint;  
    int mButtonHeight;  
    private float mTextHeight, mWarningTextHeight;  
  
    private int mHeight;  
    private int mWidth;  
    private String mWarningString;  
    private final int mChargeColor;  
    private final float[] mBoltPoints;  
    private final Path mBoltPath = new Path();  
  
    private final RectF mFrame = new RectF();  
    private final RectF mButtonFrame = new RectF();  
    private final RectF mClipFrame = new RectF();  
    private final RectF mBoltFrame = new RectF();  
  
    private class BatteryTracker extends BroadcastReceiver {  
        public static final int UNKNOWN_LEVEL = -1;  
  
        // current battery status  
        int level = UNKNOWN_LEVEL;  
        String percentStr;  
        int plugType;  
        boolean plugged;  
        int health;  
        int status;  
        String technology;  
        int voltage;  
        int temperature;  
        boolean testmode = false;  
  
        @Override  
        public void onReceive(Context context, Intent intent) {  
            final String action = intent.getAction();  
            if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { //接收電量改變的廣播,取電池狀態的屬性  
                if (testmode && ! intent.getBooleanExtra("testmode", false)) return;  
  
                level = (int)(100f  
                        * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)  
                        / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));  
  
                plugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);  
                plugged = plugType != 0;  
                health = intent.getIntExtra(BatteryManager.EXTRA_HEALTH,  
                        BatteryManager.BATTERY_HEALTH_UNKNOWN);  
                status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,  
                        BatteryManager.BATTERY_STATUS_UNKNOWN);  
                technology = intent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY);  
                voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, 0);  
                temperature = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0);  
  
                setContentDescription(  
                        context.getString(R.string.accessibility_battery_level, level));  
                postInvalidate();  
            } else if (action.equals(ACTION_LEVEL_TEST)) {  
                testmode = true;  
                post(new Runnable() {  
                    int curLevel = 0;  
                    int incr = 1;  
                    int saveLevel = level;  
                    int savePlugged = plugType;  
                    Intent dummy = new Intent(Intent.ACTION_BATTERY_CHANGED);  
                    @Override  
                    public void run() {  
                        if (curLevel < 0) {  
                            testmode = false;  
                            dummy.putExtra("level", saveLevel);  
                            dummy.putExtra("plugged", savePlugged);  
                            dummy.putExtra("testmode", false);  
                        } else {  
                            dummy.putExtra("level", curLevel);  
                            dummy.putExtra("plugged", incr > 0 ? BatteryManager.BATTERY_PLUGGED_AC : 0);  
                            dummy.putExtra("testmode", true);  
                        }  
                        getContext().sendBroadcast(dummy);  
  
                        if (!testmode) return;  
  
                        curLevel += incr;  
                        if (curLevel == 100) {  
                            incr *= -1;  
                        }  
                        postDelayed(this, 200);  
                    }  
                });  
            }  
        }  
    }  
  
    BatteryTracker mTracker = new BatteryTracker();  
  
    @Override  
    public void onAttachedToWindow() {  
        super.onAttachedToWindow();  
  
        IntentFilter filter = new IntentFilter();  
        filter.addAction(Intent.ACTION_BATTERY_CHANGED);  
        filter.addAction(ACTION_LEVEL_TEST);  
        final Intent sticky = getContext().registerReceiver(mTracker, filter);  
        if (sticky != null) {  
            // preload the battery level  
            mTracker.onReceive(getContext(), sticky);  
        }  
    }  
  
    @Override  
    public void onDetachedFromWindow() {  
        super.onDetachedFromWindow();  
  
        getContext().unregisterReceiver(mTracker);  
    }  
  
    public BatteryMeterView(Context context) {  
        this(context, null, 0);  
    }  
  
    public BatteryMeterView(Context context, AttributeSet attrs) {  
        this(context, attrs, 0);  
    }  
  
    public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) {  
        super(context, attrs, defStyle);  
  
        final Resources res = context.getResources();  
        TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels);  
        TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values);  
  
        final int N = levels.length();  
        mColors = new int[2*N];  
        for (int i=0; i<N; i++) {  
            mColors[2*i] = levels.getInt(i, 0);  
            mColors[2*i+1] = colors.getColor(i, 0);  
        }  
        levels.recycle();  
        colors.recycle();  
        mShowPercent = ENABLE_PERCENT && 0 != Settings.System.getInt(  
                context.getContentResolver(), "status_bar_show_battery_percent", 0);  
  
        mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol);  
  
        mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
        mFramePaint.setColor(res.getColor(R.color.batterymeter_frame_color));  
        mFramePaint.setDither(true);  
        mFramePaint.setStrokeWidth(0);  
        mFramePaint.setStyle(Paint.Style.FILL_AND_STROKE);  
        mFramePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));  
  
        mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
        mBatteryPaint.setDither(true);  
        mBatteryPaint.setStrokeWidth(0);  
        mBatteryPaint.setStyle(Paint.Style.FILL_AND_STROKE);  
  
        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
        mTextPaint.setColor(0xFFFFFFFF);  
        Typeface font = Typeface.create("sans-serif-condensed", Typeface.NORMAL);  
        mTextPaint.setTypeface(font);  
        mTextPaint.setTextAlign(Paint.Align.CENTER);  
  
        mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
        mWarningTextPaint.setColor(mColors[1]);  
        font = Typeface.create("sans-serif", Typeface.BOLD);  
        mWarningTextPaint.setTypeface(font);  
        mWarningTextPaint.setTextAlign(Paint.Align.CENTER);  
  
        mChargeColor = getResources().getColor(R.color.batterymeter_charge_color);  
  
        mBoltPaint = new Paint();  
        mBoltPaint.setAntiAlias(true);  
        mBoltPaint.setColor(res.getColor(R.color.batterymeter_bolt_color));  
        mBoltPoints = loadBoltPoints(res);  
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);  
    }  
  
    private static float[] loadBoltPoints(Resources res) {  
        final int[] pts = res.getIntArray(R.array.batterymeter_bolt_points);  
        int maxX = 0, maxY = 0;  
        for (int i = 0; i < pts.length; i += 2) {  
            maxX = Math.max(maxX, pts[i]);  
            maxY = Math.max(maxY, pts[i + 1]);  
        }  
        final float[] ptsF = new float[pts.length];  
        for (int i = 0; i < pts.length; i += 2) {  
            ptsF[i] = (float)pts[i] / maxX;  
            ptsF[i + 1] = (float)pts[i + 1] / maxY;  
        }  
        return ptsF;  
    }  
  
    @Override  
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
        mHeight = h;  
        mWidth = w;  
        mWarningTextPaint.setTextSize(h * 0.75f);  
        mWarningTextHeight = -mWarningTextPaint.getFontMetrics().ascent;  
    }  
  
    private int getColorForLevel(int percent) {  
        int thresh, color = 0;  
        for (int i=0; i<mColors.length; i+=2) {  
            thresh = mColors[i];  
            color = mColors[i+1];  
            if (percent <= thresh) return color;  
        }  
        return color;  
    }  
  
    @Override  
    public void draw(Canvas c) {  //經過電池屬性繪製圖標  
        BatteryTracker tracker = mDemoMode ? mDemoTracker : mTracker;  
        final int level = tracker.level;  
  
        if (level == BatteryTracker.UNKNOWN_LEVEL) return;  
  
        float drawFrac = (float) level / 100f;  
        final int pt = getPaddingTop();  
        final int pl = getPaddingLeft();  
        final int pr = getPaddingRight();  
        final int pb = getPaddingBottom();  
        int height = mHeight - pt - pb;  
        int width = mWidth - pl - pr;  
  
        mButtonHeight = (int) (height * 0.12f);  
  
        mFrame.set(0, 0, width, height);  
        mFrame.offset(pl, pt);  
  
        mButtonFrame.set(  //這個就是電池圖標上邊的突起  
                mFrame.left + width * 0.25f,  
                mFrame.top,  
                mFrame.right - width * 0.25f,  
                mFrame.top + mButtonHeight + 5 /*cover frame border of intersecting area*/);  
  
        mButtonFrame.top += SUBPIXEL;  
        mButtonFrame.left += SUBPIXEL;  
        mButtonFrame.right -= SUBPIXEL;  
  
        mFrame.top += mButtonHeight; //電池圖標桶裝圖  
        mFrame.left += SUBPIXEL;  
        mFrame.top += SUBPIXEL;  
        mFrame.right -= SUBPIXEL;  
        mFrame.bottom -= SUBPIXEL;  
  
        // first, draw the battery shape  
        c.drawRect(mFrame, mFramePaint);  
  
        // fill 'er up  
        final int color = tracker.plugged ? mChargeColor : getColorForLevel(level); //根據等級獲取顏色,若是是15如下,就是紅色。15以上是白色  
        mBatteryPaint.setColor(color);  
  
        if (level >= FULL) { //96時,電池圖標裏面填充完整,滿電。  
            drawFrac = 1f;  
        } else if (level <= EMPTY) { //這個變量是說,當電量小於4的時候,電池圖標裏面沒有填充色,空電量  
            drawFrac = 0f;  
        }  
  
        c.drawRect(mButtonFrame, drawFrac == 1f ? mBatteryPaint : mFramePaint);  
  
        mClipFrame.set(mFrame);  
        mClipFrame.top += (mFrame.height() * (1f - drawFrac));  
  
        c.save(Canvas.CLIP_SAVE_FLAG);  
        c.clipRect(mClipFrame);//在mFrame區域中保持mClipFrame的區域不變,參考下句註釋  
        c.drawRect(mFrame, mBatteryPaint);//在mFrame中,把除去mClipFrame的區域繪製。(電量狀態,若是是15如下,就是紅色。15以上是白色)  
        c.restore();  
  
        if (tracker.plugged) { //這裏就犀利了。這是說插上充電器就畫一個閃電  
            // draw the bolt  
            final float bl = mFrame.left + mFrame.width() / 4.5f;  
            final float bt = mFrame.top + mFrame.height() / 6f;  
            final float br = mFrame.right - mFrame.width() / 7f;  
            final float bb = mFrame.bottom - mFrame.height() / 10f;  
            if (mBoltFrame.left != bl || mBoltFrame.top != bt  
                    || mBoltFrame.right != br || mBoltFrame.bottom != bb) {  
                mBoltFrame.set(bl, bt, br, bb);  
                mBoltPath.reset();  
                mBoltPath.moveTo(  
                        mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),  
                        mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());  
                for (int i = 2; i < mBoltPoints.length; i += 2) {  
                    mBoltPath.lineTo(  
                            mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(),  
                            mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height());  
                }  
                mBoltPath.lineTo(  
                        mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),  
                        mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());  
            }  
            c.drawPath(mBoltPath, mBoltPaint);  
        } else if (level <= EMPTY) { //這個是空電量的時候,畫一個紅色的感嘆號  
            final float x = mWidth * 0.5f;  
            final float y = (mHeight + mWarningTextHeight) * 0.48f;  
            c.drawText(mWarningString, x, y, mWarningTextPaint);  
        } else if (mShowPercent && !(tracker.level == 100 && !SHOW_100_PERCENT)) { //以文字顯示電量。不過這個沒有用到(除非mShowPercent變量獲取的配置屬性爲true)  
            mTextPaint.setTextSize(height *  
                    (SINGLE_DIGIT_PERCENT ? 0.75f  
                            : (tracker.level == 100 ? 0.38f : 0.5f)));  
            mTextHeight = -mTextPaint.getFontMetrics().ascent;  
  
            final String str = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level);  
            final float x = mWidth * 0.5f;  
            final float y = (mHeight + mTextHeight) * 0.47f;  
            c.drawText(str,  
                    x,  
                    y,  
                    mTextPaint);  
        }  
    }  
  
    private boolean mDemoMode;  
    private BatteryTracker mDemoTracker = new BatteryTracker();  
  
    @Override  
    public void dispatchDemoCommand(String command, Bundle args) {  
        if (!mDemoMode && command.equals(COMMAND_ENTER)) {  
            mDemoMode = true;  
            mDemoTracker.level = mTracker.level;  
            mDemoTracker.plugged = mTracker.plugged;  
        } else if (mDemoMode && command.equals(COMMAND_EXIT)) {  
            mDemoMode = false;  
            postInvalidate();  
        } else if (mDemoMode && command.equals(COMMAND_BATTERY)) {  
           String level = args.getString("level");  
           String plugged = args.getString("plugged");  
           if (level != null) {  
               mDemoTracker.level = Math.min(Math.max(Integer.parseInt(level), 0), 100);  
           }  
           if (plugged != null) {  
               mDemoTracker.plugged = Boolean.parseBoolean(plugged);  
           }  
           postInvalidate();  
        }  
    }  
}  
[java] view plain copy
/* 
 * Copyright (C) 2013 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;  
  
import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  
import android.content.IntentFilter;  
import android.content.res.Resources;  
import android.content.res.TypedArray;  
import android.graphics.Canvas;  
import android.graphics.Paint;  
import android.graphics.Path;  
import android.graphics.PorterDuff;  
import android.graphics.PorterDuffXfermode;  
import android.graphics.Rect;  
import android.graphics.RectF;  
import android.graphics.Typeface;  
import android.os.BatteryManager;  
import android.os.Bundle;  
import android.provider.Settings;  
import android.util.AttributeSet;  
import android.view.View;  
  
public class BatteryMeterView extends View implements DemoMode {  
    public static final String TAG = BatteryMeterView.class.getSimpleName();  
    public static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST";  
  
    public static final boolean ENABLE_PERCENT = true;  
    public static final boolean SINGLE_DIGIT_PERCENT = false;  
    public static final boolean SHOW_100_PERCENT = false;  
  
    public static final int FULL = 96;  
    public static final int EMPTY = 4;  
  
    public static final float SUBPIXEL = 0.4f;  // inset rects for softer edges  
  
    int[] mColors;  
  
    boolean mShowPercent = true;  
    Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint;  
    int mButtonHeight;  
    private float mTextHeight, mWarningTextHeight;  
  
    private int mHeight;  
    private int mWidth;  
    private String mWarningString;  
    private final int mChargeColor;  
    private final float[] mBoltPoints;  
    private final Path mBoltPath = new Path();  
  
    private final RectF mFrame = new RectF();  
    private final RectF mButtonFrame = new RectF();  
    private final RectF mClipFrame = new RectF();  
    private final RectF mBoltFrame = new RectF();  
  
    private class BatteryTracker extends BroadcastReceiver {  
        public static final int UNKNOWN_LEVEL = -1;  
  
        // current battery status  
        int level = UNKNOWN_LEVEL;  
        String percentStr;  
        int plugType;  
        boolean plugged;  
        int health;  
        int status;  
        String technology;  
        int voltage;  
        int temperature;  
        boolean testmode = false;  
  
        @Override  
        public void onReceive(Context context, Intent intent) {  
            final String action = intent.getAction();  
            if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { //接收電量改變的廣播,取電池狀態的屬性  
                if (testmode && ! intent.getBooleanExtra("testmode", false)) return;  
  
                level = (int)(100f  
                        * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)  
                        / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));  
  
                plugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);  
                plugged = plugType != 0;  
                health = intent.getIntExtra(BatteryManager.EXTRA_HEALTH,  
                        BatteryManager.BATTERY_HEALTH_UNKNOWN);  
                status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,  
                        BatteryManager.BATTERY_STATUS_UNKNOWN);  
                technology = intent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY);  
                voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, 0);  
                temperature = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0);  
  
                setContentDescription(  
                        context.getString(R.string.accessibility_battery_level, level));  
                postInvalidate();  
            } else if (action.equals(ACTION_LEVEL_TEST)) {  
                testmode = true;  
                post(new Runnable() {  
                    int curLevel = 0;  
                    int incr = 1;  
                    int saveLevel = level;  
                    int savePlugged = plugType;  
                    Intent dummy = new Intent(Intent.ACTION_BATTERY_CHANGED);  
                    @Override  
                    public void run() {  
                        if (curLevel < 0) {  
                            testmode = false;  
                            dummy.putExtra("level", saveLevel);  
                            dummy.putExtra("plugged", savePlugged);  
                            dummy.putExtra("testmode", false);  
                        } else {  
                            dummy.putExtra("level", curLevel);  
                            dummy.putExtra("plugged", incr > 0 ? BatteryManager.BATTERY_PLUGGED_AC : 0);  
                            dummy.putExtra("testmode", true);  
                        }  
                        getContext().sendBroadcast(dummy);  
  
                        if (!testmode) return;  
  
                        curLevel += incr;  
                        if (curLevel == 100) {  
                            incr *= -1;  
                        }  
                        postDelayed(this, 200);  
                    }  
                });  
            }  
        }  
    }  
  
    BatteryTracker mTracker = new BatteryTracker();  
  
    @Override  
    public void onAttachedToWindow() {  
        super.onAttachedToWindow();  
  
        IntentFilter filter = new IntentFilter();  
        filter.addAction(Intent.ACTION_BATTERY_CHANGED);  
        filter.addAction(ACTION_LEVEL_TEST);  
        final Intent sticky = getContext().registerReceiver(mTracker, filter);  
        if (sticky != null) {  
            // preload the battery level  
            mTracker.onReceive(getContext(), sticky);  
        }  
    }  
  
    @Override  
    public void onDetachedFromWindow() {  
        super.onDetachedFromWindow();  
  
        getContext().unregisterReceiver(mTracker);  
    }  
  
    public BatteryMeterView(Context context) {  
        this(context, null, 0);  
    }  
  
    public BatteryMeterView(Context context, AttributeSet attrs) {  
        this(context, attrs, 0);  
    }  
  
    public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) {  
        super(context, attrs, defStyle);  
  
        final Resources res = context.getResources();  
        TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels);  
        TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values);  
  
        final int N = levels.length();  
        mColors = new int[2*N];  
        for (int i=0; i<N; i++) {  
            mColors[2*i] = levels.getInt(i, 0);  
            mColors[2*i+1] = colors.getColor(i, 0);  
        }  
        levels.recycle();  
        colors.recycle();  
        mShowPercent = ENABLE_PERCENT && 0 != Settings.System.getInt(  
                context.getContentResolver(), "status_bar_show_battery_percent", 0);  
  
        mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol);  
  
        mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
        mFramePaint.setColor(res.getColor(R.color.batterymeter_frame_color));  
        mFramePaint.setDither(true);  
        mFramePaint.setStrokeWidth(0);  
        mFramePaint.setStyle(Paint.Style.FILL_AND_STROKE);  
        mFramePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));  
  
        mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
        mBatteryPaint.setDither(true);  
        mBatteryPaint.setStrokeWidth(0);  
        mBatteryPaint.setStyle(Paint.Style.FILL_AND_STROKE);  
  
        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
        mTextPaint.setColor(0xFFFFFFFF);  
        Typeface font = Typeface.create("sans-serif-condensed", Typeface.NORMAL);  
        mTextPaint.setTypeface(font);  
        mTextPaint.setTextAlign(Paint.Align.CENTER);  
  
        mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
        mWarningTextPaint.setColor(mColors[1]);  
        font = Typeface.create("sans-serif", Typeface.BOLD);  
        mWarningTextPaint.setTypeface(font);  
        mWarningTextPaint.setTextAlign(Paint.Align.CENTER);  
  
        mChargeColor = getResources().getColor(R.color.batterymeter_charge_color);  
  
        mBoltPaint = new Paint();  
        mBoltPaint.setAntiAlias(true);  
        mBoltPaint.setColor(res.getColor(R.color.batterymeter_bolt_color));  
        mBoltPoints = loadBoltPoints(res);  
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);  
    }  
  
    private static float[] loadBoltPoints(Resources res) {  
        final int[] pts = res.getIntArray(R.array.batterymeter_bolt_points);  
        int maxX = 0, maxY = 0;  
        for (int i = 0; i < pts.length; i += 2) {  
            maxX = Math.max(maxX, pts[i]);  
            maxY = Math.max(maxY, pts[i + 1]);  
        }  
        final float[] ptsF = new float[pts.length];  
        for (int i = 0; i < pts.length; i += 2) {  
            ptsF[i] = (float)pts[i] / maxX;  
            ptsF[i + 1] = (float)pts[i + 1] / maxY;  
        }  
        return ptsF;  
    }  
  
    @Override  
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
        mHeight = h;  
        mWidth = w;  
        mWarningTextPaint.setTextSize(h * 0.75f);  
        mWarningTextHeight = -mWarningTextPaint.getFontMetrics().ascent;  
    }  
  
    private int getColorForLevel(int percent) {  
        int thresh, color = 0;  
        for (int i=0; i<mColors.length; i+=2) {  
            thresh = mColors[i];  
            color = mColors[i+1];  
            if (percent <= thresh) return color;  
        }  
        return color;  
    }  
  
    @Override  
    public void draw(Canvas c) {  //經過電池屬性繪製圖標  
        BatteryTracker tracker = mDemoMode ? mDemoTracker : mTracker;  
        final int level = tracker.level;  
  
        if (level == BatteryTracker.UNKNOWN_LEVEL) return;  
  
        float drawFrac = (float) level / 100f;  
        final int pt = getPaddingTop();  
        final int pl = getPaddingLeft();  
        final int pr = getPaddingRight();  
        final int pb = getPaddingBottom();  
        int height = mHeight - pt - pb;  
        int width = mWidth - pl - pr;  
  
        mButtonHeight = (int) (height * 0.12f);  
  
        mFrame.set(0, 0, width, height);  
        mFrame.offset(pl, pt);  
  
        mButtonFrame.set(  //這個就是電池圖標上邊的突起  
                mFrame.left + width * 0.25f,  
                mFrame.top,  
                mFrame.right - width * 0.25f,  
                mFrame.top + mButtonHeight + 5 /*cover frame border of intersecting area*/);  
  
        mButtonFrame.top += SUBPIXEL;  
        mButtonFrame.left += SUBPIXEL;  
        mButtonFrame.right -= SUBPIXEL;  
  
        mFrame.top += mButtonHeight; //電池圖標桶裝圖  
        mFrame.left += SUBPIXEL;  
        mFrame.top += SUBPIXEL;  
        mFrame.right -= SUBPIXEL;  
        mFrame.bottom -= SUBPIXEL;  
  
        // first, draw the battery shape  
        c.drawRect(mFrame, mFramePaint);  
  
        // fill 'er up  
        final int color = tracker.plugged ? mChargeColor : getColorForLevel(level); //根據等級獲取顏色,若是是15如下,就是紅色。15以上是白色  
        mBatteryPaint.setColor(color);  
  
        if (level >= FULL) { //96時,電池圖標裏面填充完整,滿電。  
            drawFrac = 1f;  
        } else if (level <= EMPTY) { //這個變量是說,當電量小於4的時候,電池圖標裏面沒有填充色,空電量  
            drawFrac = 0f;  
        }  
  
        c.drawRect(mButtonFrame, drawFrac == 1f ? mBatteryPaint : mFramePaint);  
  
        mClipFrame.set(mFrame);  
        mClipFrame.top += (mFrame.height() * (1f - drawFrac));  
  
        c.save(Canvas.CLIP_SAVE_FLAG);  
        c.clipRect(mClipFrame);//在mFrame區域中保持mClipFrame的區域不變,參考下句註釋  
        c.drawRect(mFrame, mBatteryPaint);//在mFrame中,把除去mClipFrame的區域繪製。(電量狀態,若是是15如下,就是紅色。15以上是白色)  
        c.restore();  
  
        if (tracker.plugged) { //這裏就犀利了。這是說插上充電器就畫一個閃電  
            // draw the bolt  
            final float bl = mFrame.left + mFrame.width() / 4.5f;  
            final float bt = mFrame.top + mFrame.height() / 6f;  
            final float br = mFrame.right - mFrame.width() / 7f;  
            final float bb = mFrame.bottom - mFrame.height() / 10f;  
            if (mBoltFrame.left != bl || mBoltFrame.top != bt  
                    || mBoltFrame.right != br || mBoltFrame.bottom != bb) {  
                mBoltFrame.set(bl, bt, br, bb);  
                mBoltPath.reset();  
                mBoltPath.moveTo(  
                        mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),  
                        mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());  
                for (int i = 2; i < mBoltPoints.length; i += 2) {  
                    mBoltPath.lineTo(  
                            mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(),  
                            mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height());  
                }  
                mBoltPath.lineTo(  
                        mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),  
                        mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());  
            }  
            c.drawPath(mBoltPath, mBoltPaint);  
        } else if (level <= EMPTY) { //這個是空電量的時候,畫一個紅色的感嘆號  
            final float x = mWidth * 0.5f;  
            final float y = (mHeight + mWarningTextHeight) * 0.48f;  
            c.drawText(mWarningString, x, y, mWarningTextPaint);  
        } else if (mShowPercent && !(tracker.level == 100 && !SHOW_100_PERCENT)) { //以文字顯示電量。不過這個沒有用到(除非mShowPercent變量獲取的配置屬性爲true)  
            mTextPaint.setTextSize(height *  
                    (SINGLE_DIGIT_PERCENT ? 0.75f  
                            : (tracker.level == 100 ? 0.38f : 0.5f)));  
            mTextHeight = -mTextPaint.getFontMetrics().ascent;  
  
            final String str = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level);  
            final float x = mWidth * 0.5f;  
            final float y = (mHeight + mTextHeight) * 0.47f;  
            c.drawText(str,  
                    x,  
                    y,  
                    mTextPaint);  
        }  
    }  
  
    private boolean mDemoMode;  
    private BatteryTracker mDemoTracker = new BatteryTracker();  
  
    @Override  
    public void dispatchDemoCommand(String command, Bundle args) {  
        if (!mDemoMode && command.equals(COMMAND_ENTER)) {  
            mDemoMode = true;  
            mDemoTracker.level = mTracker.level;  
            mDemoTracker.plugged = mTracker.plugged;  
        } else if (mDemoMode && command.equals(COMMAND_EXIT)) {  
            mDemoMode = false;  
            postInvalidate();  
        } else if (mDemoMode && command.equals(COMMAND_BATTERY)) {  
           String level = args.getString("level");  
           String plugged = args.getString("plugged");  
           if (level != null) {  
               mDemoTracker.level = Math.min(Math.max(Integer.parseInt(level), 0), 100);  
           }  
           if (plugged != null) {  
               mDemoTracker.plugged = Boolean.parseBoolean(plugged);  
           }  
           postInvalidate();  
        }  
    }  
}  async

總結:這個類就是繪製狀態欄裏面電量圖標的自定義控件。ide

以上就是大概的邏輯,現總結,用於備忘。函數

相關文章
相關標籤/搜索