全部博文(478)
分类: Android平台
2015-12-10 12:57:20
为了支持不同的硬件设备(物理按键、显示、LEDs等),我们可以定制recovery界面进行可视化显示,并进行相关的操作。那么我们可以通过继承bootable/recovery/defalust_device.cpp,并重写相关的函数来为我们的设备进行可视化定制。因此本篇博文旨在为大家介绍如何构建一个能够实现recovery界面定制的静态库。首先来了解下面这段头文件:
device/yoyodyne/tardis/recovery/recovery_ui.cpp(不同厂商或平台对recovery_ui.cpp文件的命名并不一致,例如在MTK平台所封装的名字是SystemUI.cpp,不同厂商或平台可自己选择一个合适的名字来命名)
#include
#include "common.h"
#include "device.h"
#include "screen_ui.h"
在Device类中可以找到recovery界面对于标题和选项表述。标题为我们描述了如何操作菜单,而items表示recovery的功能选项。
注!在修改标题文本或者功能选项时,请考虑屏幕宽度,尽量保持字符长度简洁,以免过长导致显示不完整。
关于RecoveryUI按键功能,我们可以继承ScreenRecoveryUIimplementation 并实现相关的方法,这里我们来了解一下处理案件的CheckKey()函数,参考如下:
按键常量一般定义在linux/input.h(CheckKey())中,无论recovery模式下在做何种操作(安装更新、擦除用户数据)都可以实现对CheckKey()函数的调用。这里有四种常量值我们可以选择。
TOGGLE:触发器,可以用来控制显示、log
REBOOT:立即重启设备
IGNORE:忽略按键
ENQUEUE:同步队列按键
CheckKey()函数会对所有按键做出响应,即便是相同的按键事件。那么如果按键队列中有以下按键事件(A—down、B-down,B-up,A-up,那么此时只有CheckKey(B)会被调用。)。而且CheckKey()可以调用IsKeyPressed()函数来识别当前是否有保持按下的动作。(那么在上面的案件队列中如果此时CheckKey(B)被调用,IsKeyPressed(A)则会返回true)。
CheckKey()可以保存状态,以便于检测键序列。下面展示的这个例子展现给大家的是一个略微复杂的设置:同时按下Power+Volume-Up健切换显示;按下电源键连续5秒钟重启手机。
通过设置下面一系列的属性来定制(图标、动画、进度条)Recovery显示系统,以便于帮助我们将更加完善Recovery显示系统展现给我们的用户。那么我们如何控制动画的帧的数量、叠加偏移量。那么我们可设置下面的这些变量。
Variable Name |
Purpose |
Release |
animation_fps |
speed (in frames per second) of animations动画播放速度 |
Android 5.x and earlier |
installing_frames |
number of frames in the installation animation |
Android 4.x and earlier |
install_overlay_offset_x, install_overlay_offset_y |
offset of the per-frame overlay (relative to the base image) for the installation animation |
Android 4.x and earlier |
设置上面的这些变量,我们需要在子类中重写ScreenRecoveryUI::Init()函数。设置值之后还需要调用父类的Init()函数来完成初始化。如下所示:
在我们实现了Recovery UI之后,我们可以定义我们自己的Device Class(也就是Device class的子类)。我们可以使用单例模式来创建这样一个子类,然后作为GetUI()的返回值返回,如下:
在recovery用户界面初始化完成,相关参数被解析之后,我们可以调用StartRecovery()函数来启动revoery。如果我们不需要在启动recovery时执行默写操作那么就不需要在我们创建的子类中去重写StartRecovery()函数,因为默认情况下这个函数只是一个拓展性的预留函数,并没有实际的操作,如下所示:
recovery系统通过调用下面这两个函数可以可到一个标题列表和一个选项列表。在下面的实现当中,会返回两个之前已经定义好的静态数组,如下:
接着我们来熟悉一个处理按键的函数,当菜单可见且我们进行按键操作时,我们可以针对不同的按键做出相应的响应。
上面函数的返回值都是整形,也就是下面这些预定义的常量,如下:
visible参数表示即便当前选项菜单不可见也可以调用HandleMenuKey()函数。这和CheckKey()函数不尽相同,CheckKey()函数只有在recovery处于空闲状态或者等待输入的时候才能够调用,而当recovery进行数据擦除、安装更新的时候是无法调用CheckKey()函数的。
我们在处理按键的时候可以使用IsKeyPressed()函数来定制一些按键功能,如下所示,:
在前面我们了解到,我们可以通过按键来高亮显示某个菜单选项,那么我们该如何能够执行菜单选项所对应的功能呢,那么recovery为我们提供了InvokeMenuItem()函数,来帮助我们完成菜单选项所对应的功能,如下:
而且我们也可以通过GetMenuItem()函数来查看当前高亮显示的菜单选项所对应的动作,如REBOOT(重启)等,如下:
WipeData()函数是一个可选的操作,只要数据擦除操作初始化完成,我们可以在任何时候执行该操作。如果我们在data和cache分区之外其他地方储存了用户数据,而我们又想在执行恢复出厂设置时进行擦除我们可以在下面的方法中执行擦除操作,如下:
上面的函数中,如果返回值是0表示擦除成功,其他值则表示擦除失败。
原文如下:
Recovery UI
To support devices with different available hardware (physical buttons, LEDs, screens, etc.), you can customize the recovery interface to display status and access the manually-operated hidden features for each device.
Your goal is to build a small static library with a couple of C++ objects to provide the device-specific functionality. The file bootable/recovery/default_device.cpp is used by default, and makes a good starting point to copy when writing a version of this file for your device.
device/yoyodyne/tardis/recovery/recovery_ui.cpp
#include#include "common.h" #include "device.h" #include "screen_ui.h"
The Device class requires functions for returning headers and items that appear in the hidden recovery menu. Headers describe how to operate the menu (i.e. controls to change/select the highlighted item).
static const char* HEADERS[] = { "Volume up/down to move highlight;", "power button to select.", "", NULL }; static const char* ITEMS[] = {"reboot system now", "apply update from ADB", "wipe data/factory reset", "wipe cache partition", NULL };
Note: Long lines are truncated (not wrapped), so keep the width of your device screen in mind.
Next, define your device's RecoveryUI implementation. This example assumes the tardis device has a screen, so you can inherit from the built-in ScreenRecoveryUIimplementation (see instructions for devices without a screen.) The only function to customize from ScreenRecoveryUI is CheckKey(), which does the initial asynchronous key handling:
class TardisUI : public ScreenRecoveryUI { public: virtual KeyAction CheckKey(int key) { if (key == KEY_HOME) { return TOGGLE; } return ENQUEUE; } };
The KEY_* constants are defined in linux/input.h. CheckKey() is called no matter what is going on in the rest of recovery: when the menu is toggled off, when it is on, during package installation, during userdata wiping, etc. It can return one of four constants:
CheckKey() is called each time a key-down event is followed by a key-up event for the same key. (The sequence of events A-down B-down B-up A-up results only in CheckKey(B) being called.) CheckKey() can call IsKeyPressed(), to find out if other keys are being held down. (In the above sequence of key events, if CheckKey(B) calledIsKeyPressed(A) it would have returned true.)
CheckKey() can maintain state in its class; this can be useful to detect sequences of keys. This example shows a slightly more complex setup: the display is toggled by holding down power and pressing volume-up, and the device can be rebooted immediately by pressing the power button five times in a row (with no other intervening keys):
class TardisUI : public ScreenRecoveryUI { private: int consecutive_power_keys; public: TardisUI() : consecutive_power_keys(0) {} virtual KeyAction CheckKey(int key) { if (IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) { return TOGGLE; } if (key == KEY_POWER) { ++consecutive_power_keys; if (consecutive_power_keys >= 5) { return REBOOT; } } else { consecutive_power_keys = 0; } return ENQUEUE; } };
When using your own images (error icon, installation animation, progress bars) with ScreenRecoveryUI, you might need to set some member variables to specify attributes such as the number of frames, speed, and overlay offsets. You can set the following variables:
Variable Name | Purpose | Release |
---|---|---|
animation_fps | speed (in frames per second) of animations | Android 5.x and earlier |
installing_frames | number of frames in the installation animation | Android 4.x and earlier |
install_overlay_offset_x, install_overlay_offset_y | offset of the per-frame overlay (relative to the base image) for the installation animation | Android 4.x and earlier |
To set variables, override the ScreenRecoveryUI::Init() function in your subclass. Set the values, then call theparent Init() function to complete initialization:
class TardisUI : public ScreenRecoveryUI { ... void Init() { // change the speed at which animations run animation_fps = 30; ScreenRecoveryUI::Init(); }
The default values correspond to the default recovery images; when using these images you don't need to provide an Init() function. For details on images, see Recovery UI Images.
After you have a RecoveryUI implementation, define your device class (subclassed from the built-in Device class). It should create a single instance of your UI class and return that from the GetUI() function:
class TardisDevice : public Device { private: TardisUI* ui; public: TardisDevice() : ui(new TardisUI) { } RecoveryUI* GetUI() { return ui; }
The StartRecovery() method is called at the start of recovery, after the UI has been initialized and after the arguments have been parsed, but before any action has been taken. The default implementation does nothing, so you do not need to provide this in your subclass if you have nothing to do:
void StartRecovery() { // ... do something tardis-specific here, if needed .... }
The system calls two methods to get the list of header lines and the list of items. In this implementation, it returns the static arrays defined at the top of the file:
const char* const* GetMenuHeaders() { return HEADERS; } const char* const* GetMenuItems() { return ITEMS; }
Next, provide a HandleMenuKey() function, which takes a keypress and the current menu visibility, and decides what action to take:
int HandleMenuKey(int key, int visible) { if (visible) { switch (key) { case KEY_VOLUMEDOWN: return kHighlightDown; case KEY_VOLUMEUP: return kHighlightUp; case KEY_POWER: return kInvokeItem; } } return kNoAction; }
The method takes a key code (which has previously been processed and enqueued by the CheckKey() method of the UI object), and the current state of the menu/text log visibility. The return value is an integer. If the value is 0 or higher, that is taken as the position of a menu item, which is invoked immediately (see the InvokeMenuItem()method below). Otherwise it can be one of the following predefined constants:
As implied by the the visible argument, HandleMenuKey() is called even if the menu is not visible. Unlike CheckKey(), it is not called while recovery is doing something such as wiping data or installing a package—it's called only when recovery is idle and waiting for input.
If your device has a trackball-like input mechanism (generates input events with type EV_REL and code REL_Y), recovery synthesizes KEY_UP and KEY_DOWN keypresses whenever the trackball-like input device reports motion in the Y axis. All you need to do is map KEY_UP and KEY_DOWN events onto menu actions. This mapping does nothappen for CheckKey(), so you can't use trackball motions as triggers for rebooting or toggling the display.
To check for keys being held down as modifiers, call the IsKeyPressed() method of your own UI object. For example, on some devices pressing Alt-W in recovery would start a data wipe whether the menu was visible or not. YOu could implement like this:
int HandleMenuKey(int key, int visible) { if (ui->IsKeyPressed(KEY_LEFTALT) && key == KEY_W) { return 2; // position of the "wipe data" item in the menu } ... }
Note: If visible is false, it doesn't make sense to return the special values that manipulate the menu (move highlight, invoke highlighted item) since the user can't see the highlight. However, you can return the values if desired.
Next, provide an InvokeMenuItem() method that maps integer positions in the array of items returned byGetMenuItems() to actions. For the array of items in the tardis example, use:
BuiltinAction InvokeMenuItem(int menu_position) { switch (menu_position) { case 0: return REBOOT; case 1: return APPLY_ADB_SIDELOAD; case 2: return WIPE_DATA; case 3: return WIPE_CACHE; default: return NO_ACTION; } }
This method can return any member of the BuiltinAction enum to tell the system to take that action (or the NO_ACTION member if you want the system to do nothing). This is the place to provide additional recovery functionality beyond what's in the system: Add an item for it in your menu, execute it here when that menu item is invoked, and return NO_ACTION so the system does nothing else.
BuiltinAction contains the following values:
The last method, WipeData(), is optional and is called whenever a data wipe operation is initiated (either from recovery via the menu or when the user has chosen to do a factory data reset from the main system). This method is called before the user data and cache partitions are wiped. If your device stores user data anywhere other than those two partitions, you should erase it here. You should return 0 to indicate success and another value for failure, though currently the return value is ignored. The user data and cache partitions are wiped whether you return success or failure.
int WipeData() { // ... do something tardis-specific here, if needed .... return 0; }