在Android中加入充电指示

http://www.linuxidc.com/Linux/2011-02/32329.htm

基于Andriod系统的平台在没有硬件充电指示的情况下只有进入andriod系统才能进行充电指示,而我们有时候仅仅需要充电而不需要进入系统,本文介绍了实现这种功能的方法。

具体过程如下:当内核启动后调用充电指示控制程序。充电指示程序发现如果外部供电就开始显示充电的画面,同时将设备的电源控制锁定为关闭状态(这样当拔掉电源时候系统自动关闭),如果在充电指示过程中又按了开机键,就重新开启电源按键。这个过程在单片机等简单系统上不值得一提,因为每个人都会想的比我好,这里重点介绍如何融合到andriod系统以及其中的我认为可以让别人参考的方法。

方法一  ui显示部分。显然我们不能让系统跑的太深,否则控制起来就过于麻烦,充电显示等老半天才启动别人也觉得奇怪。所以有必要仅仅使用微型系统,微型系统自然能用的功能就少,在图像显示上面只能使用c语言操作framebuff,我的方法是参考bootable/recovery/minui/里面的库,其支持png的图片也文本显示,对于充电指示足够用了。一个可能遇到的问题是andriod提供的minui显示使用驱动的双buffer机制,在有的系统上不具备该功能,你可以强制只使用一个,修改graphics.c的函数gr_flip,将gr_active_fb 赋值为0。

方法二 电池检测。 可以参考com_andriod_server_batteryService.cpp文件,基本上把里面的函数复制过来就可以使用。

完成的charge充电程序必须加入到init.rc中才能被调用,我把它当做一个只运行一次的服务运行。

service console /system/bin/busybox sh
    console
#shuaiwen added,init.c has been hacked to block to wait this process to finish
service charge  /system/bin/charge
   oneshot

在init启动服务的过程都是非阻塞型的(新开一个线程运行),这会导致虽然服务已经被加入,但是系统还会完整启动,显然不能满足要求,实际的需求为当charge运行起来不应该运行其他服务。要实现这个目的,必须对init.c进行一点修改。我的方法是让init对启动的服务名称做判断,如果为charge就等待到该程序结束才继续运行。具体修改为改动init.c的函数service_start:

if (pid == 0) {
……..
}
//shuaiwen added begin
       else{
       int status;
     if(strcmp(svc->name,"charge")==0)
       {
       wait(&status); //wait child to exist
       load_565rle_image(INIT_IMAGE_FILE);//redraw background
       }
      // else printf("shuaiwen skip process %s.",svc->name);
     }
   //shuaiwen added end
if (pid < 0) {
….}


这样系统就会被charge阻塞住。Init.rc中排在charge后面的服务就暂时不会被启动。我上面改动的init.rc把charge服务放在console服务后面是因为我还是需要控制台调试。经过这样的改动,基本的充电服务就可以实现。我这里仅仅是举了一个例子,文中的例子仅仅供参考,请酌情参考。

我写的一个简单的充电指示文件内容列在下面供大家参考。

/*
Copyright:GNU
charge monitor.
revision history:
2010/5/10 create     shuaiwen
icon path:
/res/images/%s.png
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 
#include <linux/input.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/reboot.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
 
#include "minui/minui.h"
#include "minzip/Zip.h"
#define KEY_PWR 116
 
#define AC_ONLINE_PATH "/sys/class/power_supply/ac/online"
#define USB_ONLINE_PATH "/sys/class/power_supply/usb/online"
#define BATTERY_STATUS_PATH "/sys/class/power_supply/battery/status"
#define BATTERY_HEALTH_PATH "/sys/class/power_supply/battery/health"
#define BATTERY_PRESENT_PATH "/sys/class/power_supply/battery/present"
#define BATTERY_CAPACITY_PATH "/sys/class/power_supply/battery/capacity"
#define BATTERY_VOLTAGE_PATH "/sys/class/power_supply/battery/batt_vol"
#define BATTERY_TEMPERATURE_PATH "/sys/class/power_supply/battery/batt_temp"
#define BATTERY_TECHNOLOGY_PATH "/sys/class/power_supply/battery/technology"
 
enum {
BACKGROUND_ICON_CHARGE0,
BACKGROUND_ICON_CHARGE1,
BACKGROUND_ICON_CHARGE2,
BACKGROUND_ICON_CHARGE3,
BACKGROUND_ICON_CHARGE4,
BACKGROUND_ICON_CHARGE5,
BACKGROUND_ICON_END,
};
 
enum {
STATUSCHARGING,
STATUSDISCHARGING,
STATUSFULL,
STATUSNOTCHARGING,
STATUSUNKNOWN
};
 
enum{
HEALTHDEAD,
HEALTHGOOD,
HEALTHOVERHEAT,
HEALTHOVERVOLTAGE,
HEALTHUNKNOWN,
HEALTHUNSPECIFIEDFAILURE,
};
 
static gr_surface gBackgroundIcon[BACKGROUND_ICON_END];
static const struct { gr_surface* surface; const char *name; } BITMAPS[] = {
    { &gBackgroundIcon[BACKGROUND_ICON_CHARGE0], "battery001" },
    { &gBackgroundIcon[BACKGROUND_ICON_CHARGE1], "battery002" },
    { &gBackgroundIcon[BACKGROUND_ICON_CHARGE2], "battery003" },
    { &gBackgroundIcon[BACKGROUND_ICON_CHARGE3], "battery004" },
    { &gBackgroundIcon[BACKGROUND_ICON_CHARGE4], "battery005" },
    { &gBackgroundIcon[BACKGROUND_ICON_CHARGE5], "battery006" },
    { NULL,                             NULL },
};
 
static int readFromFile(const char* path, char* buf, size_t size)
{
    int fd = open(path, O_RDONLY, 0);
    if (fd == -1) {
        //LOGE("Could not open '%s'", path);
        return -1;
    }
   
    size_t count = read(fd, buf, size);
    if (count > 0) {
        count = (count < size) ? count : size - 1;
        while (count > 0 && buf[count-1] == '\n') count--;
        buf[count] = '\0';
    } else {
        buf[0] = '\0';
    }
 
    close(fd);
    return count;
}
 
static int getBatteryStatus(const char* status)
{
    switch (status[0]) {
        case 'C': return STATUSCHARGING;         // Charging
        case 'D': return STATUSDISCHARGING;      // Discharging
        case 'F': return STATUSFULL;             // Not charging
        case 'N': return STATUSNOTCHARGING;      // Full
        case 'U': return STATUSUNKNOWN;          // Unknown
           
        default: {
             printf("Unknown battery status '%s'", status);
            return STATUSUNKNOWN;
        }
    }
}
 
static int getBatteryHealth(const char* status)
{
    switch (status[0]) {
        case 'D': return HEALTHDEAD;         // Dead
        case 'G': return HEALTHGOOD;         // Good
        case 'O': {
            if (strcmp(status, "Overheat") == 0) {
                return HEALTHOVERHEAT;
            } else if (strcmp(status, "Over voltage") == 0) {
                return HEALTHOVERVOLTAGE;
            }
            printf("Unknown battery health[1] '%s'", status);
            return HEALTHUNKNOWN;
        }
       
        case 'U': {
            if (strcmp(status, "Unspecified failure") == 0) {
                return HEALTHUNSPECIFIEDFAILURE;
            } else if (strcmp(status, "Unknown") == 0) {
                return HEALTHUNKNOWN;
            }
            // fall through
        }
           
        default: {
            printf("Unknown battery health[2] '%s'", status);
            return HEALTHUNKNOWN;
        }
    }
}
 
static void ui_set_background(int icon)
{
   gr_surface gIcon;
     gIcon=gBackgroundIcon[icon];
    gr_color(0, 0, 0, 255);
    gr_fill(0, 0, gr_fb_width(), gr_fb_height());
    if (gIcon) {
         //printf("set backgroupd:%s\n\r",gBackgroundIcon[icon].name);
        int iconWidth = gr_get_width(gIcon);
        int iconHeight = gr_get_height(gIcon);
        int iconX = (gr_fb_width() - iconWidth) / 2;
        int iconY = (gr_fb_height() - iconHeight) / 2;
        gr_blit(gIcon, 0, 0, iconWidth, iconHeight, iconX, iconY);
    }
   gr_flip();
}
 
static void ui_init()
{
  gr_init();
  ev_init();
  int i;
    for (i = 0; BITMAPS[i].name != NULL; ++i) {
        int result = res_create_surface(BITMAPS[i].name, BITMAPS[i].surface);
        if (result < 0) {
            printf("Missing bitmap %s\n(Code %d)\n", BITMAPS[i].name, result);
            *BITMAPS[i].surface = NULL;
               }
               //else printf("create:%s\n\r",BITMAPS[i].name);
    }
 
}
const int SIZE = 128;
 
int power_ac()
{
    char buf[SIZE];
      if (readFromFile(AC_ONLINE_PATH, buf, SIZE) > 0) {
        if (buf[0] == '1') {
            return 1;
        }
    }
return 0;
  }
 
/*
function to indicate if the battery is present.
*/
int battery_present()
{
char buf[SIZE];
      if (readFromFile(BATTERY_PRESENT_PATH, buf, SIZE) > 0) {
        if (buf[0] == '1') {
            return 1;
        }
    }
return 0;
}
 
int battery_capacity()
{
char buf[SIZE];
   
    if (readFromFile(BATTERY_CAPACITY_PATH, buf, SIZE) > 0) {
        return atoi(buf);
    }
  return 0;
}
 
int battery_voltage()
{
char buf[SIZE];
   
    if (readFromFile(BATTERY_VOLTAGE_PATH, buf, SIZE) > 0) {
        return atoi(buf);
    }
  return 0;
}
 
 
int battery_temperature()
{
char buf[SIZE];
   
    if (readFromFile(BATTERY_TEMPERATURE_PATH, buf, SIZE) > 0) {
        return atoi(buf);
    }
  return 0;
}
 
int capacity_full()
{
  char buf[SIZE];
 if (readFromFile(BATTERY_STATUS_PATH, buf, SIZE) > 0)
    {
       return getBatteryStatus(buf);
    }
 
}
 
void do_nothing()    //don't commnet it before fix zip error.  shuaiwen
{
 ZipArchive zip;
  mzOpenZipArchive(NULL, &zip);
 
}
int main(int argc, char **argv)
{
   int index=0;
#define CIRCLE   100   //1000x1000 microsecond
    int circle=0;//interval to update battery status.
struct input_event ev;
 
//is AC power charging?
if(!power_ac())
{
printf("battery supply,don't enter charge mode.\n\r");
return 0; //continue runing.
}
printf("entern charge mode\n\r");
//powerio_low();  //fixme,set power io low to shutdown system when unplug the power.
   ui_init();
   while(1)
     {
       circle=0;
     ui_set_background(capacity_full()==STATUSCHARGING?index++%BACKGROUND_ICON_END:BACKGROUND_ICON_END-1);//todo:more condition must be considered.
     // ui_set_background(index++%BACKGROUND_ICON_END);
        while(circle++<CIRCLE)
        {
          usleep(1000);
         ev_get(&ev, 10);
         if((ev.type == EV_KEY)&&(ev.code==62))
             {
                 //  powerio_high();  //fixme
                 ev_exit();
                 gr_exit();
              return 0;//the system will continue running
               }
         //debug
            if(ev.type==EV_KEY)printf("key:%d pressed\n",ev.code);
           //
        }
 
    }
ev_exit();
gr_exit();
return 0;
}
 
 
对应的andriod.mk文件如下:
LOCAL_PATH :=$(call my-dir)
 
include $(CLEAR_VARS)
 
charge_local_path := $(LOCAL_PATH)
LOCAL_SRC_FILES :=main.c
LOCAL_MODULE := charge
LOCAL_FORCE_STATIC_EXECUTABLE := true
# This binary is in the recovery ramdisk, which is otherwise a copy of root.
# It gets copied there in config/Makefile.  LOCAL_MODULE_TAGS suppresses
# a (redundant) copy of the binary in /system/bin for user builds.
# TODO: Build the ramdisk image in a more principled way.
 
LOCAL_MODULE_TAGS := eng
 
LOCAL_STATIC_LIBRARIES := libminzip libunz libminui libpixelflinger_static libpng libcutils libstdc++ libc
 
include $(BUILD_EXECUTABLE)
 
include $(charge_local_path)/minui/Android.mk
include $(charge_local_path)/minzip/Android.mk
 
PRODUCT_COPY_FILES += \
       $(charge_local_path)/res/images/battery001.PNG:system/res/images/battery001.png \
       $(charge_local_path)/res/images/battery002.PNG:system/res/images/battery002.png \
       $(charge_local_path)/res/images/battery003.PNG:system/res/images/battery003.png \
       $(charge_local_path)/res/images/battery004.PNG:system/res/images/battery004.png \
       $(charge_local_path)/res/images/battery005.PNG:system/res/images/battery005.png \
       $(charge_local_path)/res/images/battery006.PNG:system/res/images/battery006.png



Android 12 ,锁屏界面添加充电图标的实现方式与之前的版本略有不同,需要通过在 KeyguardStatusViewManager 注册一个 StatusBarPlugin 并在其回调方法更新充电图标。 首先,在 KeyguardStatusViewManager 注册一个 StatusBarPlugin: ```java private StatusBarPlugin mStatusBarPlugin; private void registerStatusBarPlugin() { mStatusBarPlugin = (StatusBarPlugin) Dependency.get(StatusBarPlugin.class); mStatusBarPlugin.addKeyguardStateCallback(mKeyguardStateCallback); } ``` 注册后,需要实现一个 KeyguardStateCallback,该接口包含了锁屏状态的回调方法。 ```java private final StatusBarPlugin.KeyguardStateCallback mKeyguardStateCallback = new StatusBarPlugin.KeyguardStateCallback() { @Override public void onShowingStateChanged(boolean showing) { updateChargingIndicator(); } @Override public void onKeyguardFadingAwayChanged() { updateChargingIndicator(); } }; ``` 在回调方法,通过 updateChargingIndicator() 方法来更新充电图标的显示状态。 ```java private void updateChargingIndicator() { if (mStatusBarPlugin == null) { return; } boolean isCharging = mStatusBarPlugin.isKeyguardShowingChargingAnimation(); if (isCharging) { // 显示充电图标 mChargingIndicator.setVisibility(View.VISIBLE); } else { // 隐藏充电图标 mChargingIndicator.setVisibility(View.GONE); } } ``` 在 updateChargingIndicator() 方法,通过调用 mStatusBarPlugin.isKeyguardShowingChargingAnimation() 方法获取当前是否正在显示充电动画,如果正在充电,则显示充电图标,否则隐藏充电图标。 最后,在布局文件添加充电图标的 View: ```xml <ImageView android:id="@+id/charging_indicator" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_battery_charging" android:visibility="gone" /> ``` 通过上述步骤,就可以在 Android 12 的锁屏界面添加充电图标了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值