在Recovery模式首页,Recovery系统通过调用GetMenuItems()函数向我们展示了一个选项列表,当有按键操作发生时,系统会通过HandleMenuKey()函数来处理按键操作。通常情况下我们可以通过操作VolumeUp和VolumeDown来切换选项。原生环境下,如果当前选项为首项或尾项,我们想要切换至尾项或首项时,不得不频繁操作VolumeUp和VolumeDown进行切换。影响用户体验。那么如果当前选项为首项或尾项时,我们如何通过一次操作进行首尾项的切换呢?
首先我们来看Recovery系统处理按键的函数HandleMenuKey()。
源码地址:https://android.googlesource.com/platform/bootable/recovery/+/android-5.1.1_r2/default_device.cpp
int HandleMenuKey(int key, int visible) {
if (visible) {
switch (key) {
case KEY_DOWN:
case KEY_VOLUMEDOWN:
return kHighlightDown;
case KEY_UP:
case KEY_VOLUMEUP:
return kHighlightUp;
case KEY_ENTER:
case KEY_POWER:
return kInvokeItem;
}
}
return kNoAction;
}
通过上面这段代码了解到,Recovery系统是通过操作VolumeUp来向上切换选项,通过VolumeDown向下切换选项,且通过Power按键进入选项。因此在Recovery.cpp的get_menu_selection()函数中,Recovery系统通过调用上面的HandleMenuKey()函数判断当前动作。然后再根据动作作出相应的处理。这里的处理也就是切换选项,主要是告诉系统用户选择的菜单项和对屏幕中的菜单项进行高亮显示。
源码地址:https://android.googlesource.com/platform/bootable/recovery/+/android-5.1.1_r2/recovery.cpp
static int
get_menu_selection(const char* const * headers, const char* const * items,
int menu_only, int initial_selection, Device* device) {
// throw away keys pressed previously, so user doesn't
// accidentally trigger menu items.
ui->FlushKeys();
ui->StartMenu(headers, items, initial_selection);
int selected = initial_selection;
int chosen_item = -1;
while (chosen_item < 0) {
int key = ui->WaitKey();
int visible = ui->IsTextVisible();
if (key == -1) { // ui_wait_key() timed out
if (ui->WasTextEverVisible()) {
continue;
} else {
LOGI("timed out waiting for key input; rebooting.\n");
ui->EndMenu();
return 0; // XXX fixme
}
}
int action = device->HandleMenuKey(key, visible);
if (action < 0) {
switch (action) {
case Device::kHighlightUp:
--selected;
selected = ui->SelectMenu(selected);
break;
case Device::kHighlightDown:
++selected;
selected = ui->SelectMenu(selected);
break;
case Device::kInvokeItem:
chosen_item = selected;
break;
case Device::kNoAction:
break;
}
} else if (!menu_only) {
chosen_item = action;
}
}
ui->EndMenu();
return chosen_item;
}
上面标注为红色的代码也就是Recovery系统相应按键作出的选项切换的动作。也就是说如果当前用户按下一次VolumeUp健,Recovery系统就会向上切换选项菜单。但是如果当前选项菜单处于首项或尾项时,Recovery系统就会调用screen_ui.cpp中的SelectMenu()函数进行进一步的处理。
源码地址:https://android.googlesource.com/platform/bootable/recovery/+/android-5.1.1_r2/screen_ui.cpp
int ScreenRecoveryUI::SelectMenu(int sel) {
int old_sel;
pthread_mutex_lock(&updateMutex);
if (show_menu > 0) {
old_sel = menu_sel;
menu_sel = sel;
if (menu_sel < 0) menu_sel = 0;//这里表示如果当前项为首项,按键操作为向上切换时,保持首项不变。
if (menu_sel >= menu_items) menu_sel = menu_items-1;//这里表示如果当前项为尾项,按键操作为向下切换时,保持尾项不变。
sel = menu_sel;
if (menu_sel != old_sel) update_screen_locked();//重绘screen刷新界面
}
pthread_mutex_unlock(&updateMutex);
return sel;
}
上面红色部分即原生对当前项为首项或尾项时,切换选项所作出的处理。对于处理的方法在上面的备注中已经有相应的注释。那么我们想要实现循环切换选项的突破口也就是这个函数了。下面是修改后的函数:
int ScreenRecoveryUI::SelectMenu(int sel) {
int old_sel;
pthread_mutex_lock(&updateMutex);
if (show_menu > 0) {
old_sel = menu_sel;
menu_sel = sel;
if (menu_sel < 0) {
menu_sel = menu_items-1;//这里表示如果当前项为首项,按键操作为向上切换时,切换至尾项
else if (menu_sel >= menu_items){
menu_sel = 0;//这里表示如果当前项为尾项,按键操作为向下切换时,切换至首项
}
sel = menu_sel;
if (menu_sel != old_sel) update_screen_locked();//重绘screen刷新界面
}
pthread_mutex_unlock(&updateMutex);
return sel;
}