Files
android_bootable_recovery/gui/pages.cpp
gordon1337 0d9133d654 Do not render the GUI when the blanktimer turns off the display.
Normally, when the display has been turned off by blanktimer, the GUI actually still renders the GUI elements (eg. progressbar while doing backup/restore/etc.)
This patch will check whenever the display is turned off, and if it is turned off simply dont render. This avoids heating up the device and will save battery.
2013-06-08 14:17:07 +02:00

933 lines
24 KiB
C++

/*
Copyright 2013 bigbiff/Dees_Troy TeamWin
This file is part of TWRP/TeamWin Recovery Project.
TWRP is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
TWRP is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with TWRP. If not, see <http://www.gnu.org/licenses/>.
*/
// pages.cpp - Source to manage GUI base objects
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/reboot.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <string>
extern "C" {
#include "../twcommon.h"
#include "../minuitwrp/minui.h"
}
#include "rapidxml.hpp"
#include "objects.hpp"
#include "blanktimer.hpp"
extern int gGuiRunning;
extern blanktimer blankTimer;
std::map<std::string, PageSet*> PageManager::mPageSets;
PageSet* PageManager::mCurrentSet;
PageSet* PageManager::mBaseSet = NULL;
// Helper routine to convert a string to a color declaration
int ConvertStrToColor(std::string str, COLOR* color)
{
// Set the default, solid black
memset(color, 0, sizeof(COLOR));
color->alpha = 255;
// Translate variables
DataManager::GetValue(str, str);
// Look for some defaults
if (str == "black") return 0;
else if (str == "white") { color->red = color->green = color->blue = 255; return 0; }
else if (str == "red") { color->red = 255; return 0; }
else if (str == "green") { color->green = 255; return 0; }
else if (str == "blue") { color->blue = 255; return 0; }
// At this point, we require an RGB(A) color
if (str[0] != '#') return -1;
str.erase(0, 1);
int result;
if (str.size() >= 8) {
// We have alpha channel
string alpha = str.substr(6, 2);
result = strtol(alpha.c_str(), NULL, 16);
color->alpha = result & 0x000000FF;
str.resize(6);
result = strtol(str.c_str(), NULL, 16);
color->red = (result >> 16) & 0x000000FF;
color->green = (result >> 8) & 0x000000FF;
color->blue = result & 0x000000FF;
} else {
result = strtol(str.c_str(), NULL, 16);
color->red = (result >> 16) & 0x000000FF;
color->green = (result >> 8) & 0x000000FF;
color->blue = result & 0x000000FF;
}
return 0;
}
// Helper APIs
bool LoadPlacement(xml_node<>* node, int* x, int* y, int* w /* = NULL */, int* h /* = NULL */, RenderObject::Placement* placement /* = NULL */)
{
if (!node) return false;
std::string value;
if (node->first_attribute("x"))
{
value = node->first_attribute("x")->value();
DataManager::GetValue(value, value);
*x = atol(value.c_str());
}
if (node->first_attribute("y"))
{
value = node->first_attribute("y")->value();
DataManager::GetValue(value, value);
*y = atol(value.c_str());
}
if (w && node->first_attribute("w"))
{
value = node->first_attribute("w")->value();
DataManager::GetValue(value, value);
*w = atol(value.c_str());
}
if (h && node->first_attribute("h"))
{
value = node->first_attribute("h")->value();
DataManager::GetValue(value, value);
*h = atol(value.c_str());
}
if (placement && node->first_attribute("placement"))
{
value = node->first_attribute("placement")->value();
DataManager::GetValue(value, value);
*placement = (RenderObject::Placement) atol(value.c_str());
}
return true;
}
int ActionObject::SetActionPos(int x, int y, int w, int h)
{
if (x < 0 || y < 0) return -1;
mActionX = x;
mActionY = y;
if (w || h)
{
mActionW = w;
mActionH = h;
}
return 0;
}
Page::Page(xml_node<>* page, xml_node<>* templates /* = NULL */)
{
mTouchStart = NULL;
// We can memset the whole structure, because the alpha channel is ignored
memset(&mBackground, 0, sizeof(COLOR));
// With NULL, we make a console-only display
if (!page)
{
mName = "console";
GUIConsole* element = new GUIConsole(NULL);
mRenders.push_back(element);
mActions.push_back(element);
return;
}
if (page->first_attribute("name"))
mName = page->first_attribute("name")->value();
else
{
LOGERR("No page name attribute found!\n");
return;
}
LOGINFO("Loading page %s\n", mName.c_str());
// This is a recursive routine for template handling
ProcessNode(page, templates);
return;
}
bool Page::ProcessNode(xml_node<>* page, xml_node<>* templates /* = NULL */, int depth /* = 0 */)
{
if (depth == 10)
{
LOGERR("Page processing depth has exceeded 10. Failing out. This is likely a recursive template.\n");
return false;
}
// Let's retrieve the background value, if any
xml_node<>* bg = page->first_node("background");
if (bg)
{
xml_attribute<>* attr = bg->first_attribute("color");
if (attr)
{
std::string color = attr->value();
ConvertStrToColor(color, &mBackground);
}
}
xml_node<>* child;
child = page->first_node("object");
while (child)
{
if (!child->first_attribute("type"))
break;
std::string type = child->first_attribute("type")->value();
if (type == "text")
{
GUIText* element = new GUIText(child);
mRenders.push_back(element);
mActions.push_back(element);
}
else if (type == "image")
{
GUIImage* element = new GUIImage(child);
mRenders.push_back(element);
}
else if (type == "fill")
{
GUIFill* element = new GUIFill(child);
mRenders.push_back(element);
}
else if (type == "action")
{
GUIAction* element = new GUIAction(child);
mActions.push_back(element);
}
else if (type == "console")
{
GUIConsole* element = new GUIConsole(child);
mRenders.push_back(element);
mActions.push_back(element);
}
else if (type == "button")
{
GUIButton* element = new GUIButton(child);
mRenders.push_back(element);
mActions.push_back(element);
}
else if (type == "checkbox")
{
GUICheckbox* element = new GUICheckbox(child);
mRenders.push_back(element);
mActions.push_back(element);
}
else if (type == "fileselector")
{
GUIFileSelector* element = new GUIFileSelector(child);
mRenders.push_back(element);
mActions.push_back(element);
}
else if (type == "animation")
{
GUIAnimation* element = new GUIAnimation(child);
mRenders.push_back(element);
}
else if (type == "progressbar")
{
GUIProgressBar* element = new GUIProgressBar(child);
mRenders.push_back(element);
mActions.push_back(element);
}
else if (type == "slider")
{
GUISlider* element = new GUISlider(child);
mRenders.push_back(element);
mActions.push_back(element);
}
else if (type == "slidervalue")
{
GUISliderValue *element = new GUISliderValue(child);
mRenders.push_back(element);
mActions.push_back(element);
}
else if (type == "listbox")
{
GUIListBox* element = new GUIListBox(child);
mRenders.push_back(element);
mActions.push_back(element);
}
else if (type == "keyboard")
{
GUIKeyboard* element = new GUIKeyboard(child);
mRenders.push_back(element);
mActions.push_back(element);
}
else if (type == "input")
{
GUIInput* element = new GUIInput(child);
mRenders.push_back(element);
mActions.push_back(element);
mInputs.push_back(element);
}
else if (type == "partitionlist")
{
GUIPartitionList* element = new GUIPartitionList(child);
mRenders.push_back(element);
mActions.push_back(element);
}
else if (type == "template")
{
if (!templates || !child->first_attribute("name"))
{
LOGERR("Invalid template request.\n");
}
else
{
std::string name = child->first_attribute("name")->value();
// We need to find the correct template
xml_node<>* node;
node = templates->first_node("template");
while (node)
{
if (!node->first_attribute("name"))
continue;
if (name == node->first_attribute("name")->value())
{
if (!ProcessNode(node, templates, depth + 1))
return false;
else
break;
}
node = node->next_sibling("template");
}
}
}
else
{
LOGERR("Unknown object type.\n");
}
child = child->next_sibling("object");
}
return true;
}
int Page::Render(void)
{
// Render background
gr_color(mBackground.red, mBackground.green, mBackground.blue, mBackground.alpha);
gr_fill(0, 0, gr_fb_width(), gr_fb_height());
// Render remaining objects
std::vector<RenderObject*>::iterator iter;
for (iter = mRenders.begin(); iter != mRenders.end(); iter++)
{
if ((*iter)->Render())
LOGERR("A render request has failed.\n");
}
return 0;
}
int Page::Update(void)
{
int retCode = 0;
std::vector<RenderObject*>::iterator iter;
for (iter = mRenders.begin(); iter != mRenders.end(); iter++)
{
int ret = (*iter)->Update();
if (ret < 0)
LOGERR("An update request has failed.\n");
else if (ret > retCode)
retCode = ret;
}
return retCode;
}
int Page::NotifyTouch(TOUCH_STATE state, int x, int y)
{
// By default, return 1 to ignore further touches if nobody is listening
int ret = 1;
// Don't try to handle a lack of handlers
if (mActions.size() == 0) return ret;
// We record mTouchStart so we can pass all the touch stream to the same handler
if (state == TOUCH_START)
{
std::vector<ActionObject*>::reverse_iterator iter;
// We work backwards, from top-most element to bottom-most element
for (iter = mActions.rbegin(); iter != mActions.rend(); iter++)
{
if ((*iter)->IsInRegion(x, y))
{
mTouchStart = (*iter);
ret = mTouchStart->NotifyTouch(state, x, y);
if (ret >= 0) break;
mTouchStart = NULL;
}
}
}
else if (state == TOUCH_RELEASE && mTouchStart != NULL)
{
ret = mTouchStart->NotifyTouch(state, x, y);
mTouchStart = NULL;
}
else if ((state == TOUCH_DRAG || state == TOUCH_HOLD || state == TOUCH_REPEAT) && mTouchStart != NULL)
{
ret = mTouchStart->NotifyTouch(state, x, y);
}
return ret;
}
int Page::NotifyKey(int key)
{
std::vector<ActionObject*>::reverse_iterator iter;
// Don't try to handle a lack of handlers
if (mActions.size() == 0) return 1;
// We work backwards, from top-most element to bottom-most element
for (iter = mActions.rbegin(); iter != mActions.rend(); iter++)
{
int ret = (*iter)->NotifyKey(key);
if (ret == 0)
return 0;
else if (ret < 0)
LOGERR("An action handler has returned an error");
}
return 1;
}
int Page::NotifyKeyboard(int key)
{
std::vector<InputObject*>::reverse_iterator iter;
// Don't try to handle a lack of handlers
if (mInputs.size() == 0) return 1;
// We work backwards, from top-most element to bottom-most element
for (iter = mInputs.rbegin(); iter != mInputs.rend(); iter++)
{
int ret = (*iter)->NotifyKeyboard(key);
if (ret == 0)
return 0;
else if (ret < 0)
LOGERR("A keyboard handler has returned an error");
}
return 1;
}
int Page::SetKeyBoardFocus(int inFocus)
{
std::vector<InputObject*>::reverse_iterator iter;
// Don't try to handle a lack of handlers
if (mInputs.size() == 0) return 1;
// We work backwards, from top-most element to bottom-most element
for (iter = mInputs.rbegin(); iter != mInputs.rend(); iter++)
{
int ret = (*iter)->SetInputFocus(inFocus);
if (ret == 0)
return 0;
else if (ret < 0)
LOGERR("An input focus handler has returned an error");
}
return 1;
}
void Page::SetPageFocus(int inFocus)
{
// Render remaining objects
std::vector<RenderObject*>::iterator iter;
for (iter = mRenders.begin(); iter != mRenders.end(); iter++)
{
(*iter)->SetPageFocus(inFocus);
}
return;
}
int Page::NotifyVarChange(std::string varName, std::string value)
{
std::vector<ActionObject*>::iterator iter;
// Don't try to handle a lack of handlers
if (mActions.size() == 0) return 1;
for (iter = mActions.begin(); iter != mActions.end(); ++iter)
{
if ((*iter)->NotifyVarChange(varName, value))
LOGERR("An action handler errored on NotifyVarChange.\n");
}
return 0;
}
PageSet::PageSet(char* xmlFile)
{
mResources = NULL;
mCurrentPage = NULL;
mOverlayPage = NULL;
mXmlFile = xmlFile;
if (xmlFile)
mDoc.parse<0>(mXmlFile);
else
mCurrentPage = new Page(NULL);
}
PageSet::~PageSet()
{
delete mResources;
free(mXmlFile);
}
int PageSet::Load(ZipArchive* package)
{
xml_node<>* parent;
xml_node<>* child;
xml_node<>* templates;
parent = mDoc.first_node("recovery");
if (!parent)
parent = mDoc.first_node("install");
// Now, let's parse the XML
LOGINFO("Loading resources...\n");
child = parent->first_node("resources");
if (child)
mResources = new ResourceManager(child, package);
LOGINFO("Loading variables...\n");
child = parent->first_node("variables");
if (child)
LoadVariables(child);
LOGINFO("Loading pages...\n");
// This may be NULL if no templates are present
templates = parent->first_node("templates");
child = parent->first_node("pages");
if (!child)
return -1;
return LoadPages(child, templates);
}
int PageSet::SetPage(std::string page)
{
Page* tmp = FindPage(page);
if (tmp)
{
if (mCurrentPage) mCurrentPage->SetPageFocus(0);
mCurrentPage = tmp;
mCurrentPage->SetPageFocus(1);
mCurrentPage->NotifyVarChange("", "");
return 0;
}
else
{
LOGERR("Unable to locate page (%s)\n", page.c_str());
}
return -1;
}
int PageSet::SetOverlay(Page* page)
{
if (mOverlayPage) mOverlayPage->SetPageFocus(0);
mOverlayPage = page;
if (mOverlayPage)
{
mOverlayPage->SetPageFocus(1);
mOverlayPage->NotifyVarChange("", "");
}
return 0;
}
Resource* PageSet::FindResource(std::string name)
{
return mResources ? mResources->FindResource(name) : NULL;
}
Page* PageSet::FindPage(std::string name)
{
std::vector<Page*>::iterator iter;
for (iter = mPages.begin(); iter != mPages.end(); iter++)
{
if (name == (*iter)->GetName())
return (*iter);
}
return NULL;
}
int PageSet::LoadVariables(xml_node<>* vars)
{
xml_node<>* child;
child = vars->first_node("variable");
while (child)
{
if (!child->first_attribute("name"))
break;
if (!child->first_attribute("value"))
break;
DataManager::SetValue(child->first_attribute("name")->value(), child->first_attribute("value")->value());
child = child->next_sibling("variable");
}
return 0;
}
int PageSet::LoadPages(xml_node<>* pages, xml_node<>* templates /* = NULL */)
{
xml_node<>* child;
if (!pages) return -1;
child = pages->first_node("page");
while (child != NULL)
{
Page* page = new Page(child, templates);
if (page->GetName().empty())
{
LOGERR("Unable to process load page\n");
delete page;
}
else
{
mPages.push_back(page);
}
child = child->next_sibling("page");
}
if (mPages.size() > 0)
return 0;
return -1;
}
int PageSet::IsCurrentPage(Page* page)
{
return ((mCurrentPage && mCurrentPage == page) ? 1 : 0);
}
int PageSet::Render(void)
{
int ret;
ret = (mCurrentPage ? mCurrentPage->Render() : -1);
if (ret < 0) return ret;
ret = (mOverlayPage ? mOverlayPage->Render() : -1);
return ret;
}
int PageSet::Update(void)
{
int ret;
ret = (mCurrentPage ? mCurrentPage->Update() : -1);
if (ret < 0 || ret > 1) return ret;
ret = (mOverlayPage ? mOverlayPage->Update() : -1);
return ret;
}
int PageSet::NotifyTouch(TOUCH_STATE state, int x, int y)
{
if (mOverlayPage) return (mOverlayPage->NotifyTouch(state, x, y));
return (mCurrentPage ? mCurrentPage->NotifyTouch(state, x, y) : -1);
}
int PageSet::NotifyKey(int key)
{
if (mOverlayPage) return (mOverlayPage->NotifyKey(key));
return (mCurrentPage ? mCurrentPage->NotifyKey(key) : -1);
}
int PageSet::NotifyKeyboard(int key)
{
if (mOverlayPage) return (mOverlayPage->NotifyKeyboard(key));
return (mCurrentPage ? mCurrentPage->NotifyKeyboard(key) : -1);
}
int PageSet::SetKeyBoardFocus(int inFocus)
{
if (mOverlayPage) return (mOverlayPage->SetKeyBoardFocus(inFocus));
return (mCurrentPage ? mCurrentPage->SetKeyBoardFocus(inFocus) : -1);
}
int PageSet::NotifyVarChange(std::string varName, std::string value)
{
if (mOverlayPage) mOverlayPage->NotifyVarChange(varName, value);
return (mCurrentPage ? mCurrentPage->NotifyVarChange(varName, value) : -1);
}
int PageManager::LoadPackage(std::string name, std::string package, std::string startpage)
{
int fd;
ZipArchive zip, *pZip = NULL;
long len;
char* xmlFile = NULL;
PageSet* pageSet = NULL;
int ret;
// Open the XML file
LOGINFO("Loading package: %s (%s)\n", name.c_str(), package.c_str());
if (mzOpenZipArchive(package.c_str(), &zip))
{
// We can try to load the XML directly...
struct stat st;
if(stat(package.c_str(),&st) != 0)
return -1;
len = st.st_size;
xmlFile = (char*) malloc(len + 1);
if (!xmlFile) return -1;
fd = open(package.c_str(), O_RDONLY);
if (fd == -1) goto error;
read(fd, xmlFile, len);
close(fd);
}
else
{
pZip = &zip;
const ZipEntry* ui_xml = mzFindZipEntry(&zip, "ui.xml");
if (ui_xml == NULL)
{
LOGERR("Unable to locate ui.xml in zip file\n");
goto error;
}
// Allocate the buffer for the file
len = mzGetZipEntryUncompLen(ui_xml);
xmlFile = (char*) malloc(len + 1);
if (!xmlFile) goto error;
if (!mzExtractZipEntryToBuffer(&zip, ui_xml, (unsigned char*) xmlFile))
{
LOGERR("Unable to extract ui.xml\n");
goto error;
}
}
// NULL-terminate the string
xmlFile[len] = 0x00;
// Before loading, mCurrentSet must be the loading package so we can find resources
pageSet = mCurrentSet;
mCurrentSet = new PageSet(xmlFile);
ret = mCurrentSet->Load(pZip);
if (ret == 0)
{
mCurrentSet->SetPage(startpage);
mPageSets.insert(std::pair<std::string, PageSet*>(name, mCurrentSet));
}
else
{
LOGERR("Package %s failed to load.\n", name.c_str());
}
// The first successful package we loaded is the base
if (mBaseSet == NULL)
mBaseSet = mCurrentSet;
mCurrentSet = pageSet;
if (pZip) mzCloseZipArchive(pZip);
return ret;
error:
LOGERR("An internal error has occurred.\n");
if (pZip) mzCloseZipArchive(pZip);
if (xmlFile) free(xmlFile);
return -1;
}
PageSet* PageManager::FindPackage(std::string name)
{
std::map<std::string, PageSet*>::iterator iter;
iter = mPageSets.find(name);
if (iter != mPageSets.end())
{
return (*iter).second;
}
LOGERR("Unable to locate package %s\n", name.c_str());
return NULL;
}
PageSet* PageManager::SelectPackage(std::string name)
{
LOGINFO("Switching packages (%s)\n", name.c_str());
PageSet* tmp;
tmp = FindPackage(name);
if (tmp)
mCurrentSet = tmp;
else
LOGERR("Unable to find package.\n");
return mCurrentSet;
}
int PageManager::ReloadPackage(std::string name, std::string package)
{
std::map<std::string, PageSet*>::iterator iter;
iter = mPageSets.find(name);
if (iter == mPageSets.end())
return -1;
PageSet* set = (*iter).second;
mPageSets.erase(iter);
if (LoadPackage(name, package, "main") != 0)
{
LOGERR("Failed to load package.\n");
mPageSets.insert(std::pair<std::string, PageSet*>(name, set));
return -1;
}
if (mCurrentSet == set) SelectPackage(name);
delete set;
return 0;
}
void PageManager::ReleasePackage(std::string name)
{
std::map<std::string, PageSet*>::iterator iter;
iter = mPageSets.find(name);
if (iter == mPageSets.end())
return;
PageSet* set = (*iter).second;
mPageSets.erase(iter);
delete set;
return;
}
int PageManager::ChangePage(std::string name)
{
DataManager::SetValue("tw_operation_state", 0);
int ret = (mCurrentSet ? mCurrentSet->SetPage(name) : -1);
return ret;
}
int PageManager::ChangeOverlay(std::string name)
{
if (name.empty())
return mCurrentSet->SetOverlay(NULL);
else
{
Page* page = mBaseSet ? mBaseSet->FindPage(name) : NULL;
return mCurrentSet->SetOverlay(page);
}
}
Resource* PageManager::FindResource(std::string name)
{
return (mCurrentSet ? mCurrentSet->FindResource(name) : NULL);
}
Resource* PageManager::FindResource(std::string package, std::string name)
{
PageSet* tmp;
tmp = FindPackage(name);
return (tmp ? tmp->FindResource(name) : NULL);
}
int PageManager::SwitchToConsole(void)
{
PageSet* console = new PageSet(NULL);
mCurrentSet = console;
return 0;
}
int PageManager::IsCurrentPage(Page* page)
{
return (mCurrentSet ? mCurrentSet->IsCurrentPage(page) : 0);
}
int PageManager::Render(void)
{
return (mCurrentSet ? mCurrentSet->Render() : -1);
}
int PageManager::Update(void)
{
if(blankTimer.IsScreenOff())
return 0;
return (mCurrentSet ? mCurrentSet->Update() : -1);
}
int PageManager::NotifyTouch(TOUCH_STATE state, int x, int y)
{
return (mCurrentSet ? mCurrentSet->NotifyTouch(state, x, y) : -1);
}
int PageManager::NotifyKey(int key)
{
return (mCurrentSet ? mCurrentSet->NotifyKey(key) : -1);
}
int PageManager::NotifyKeyboard(int key)
{
return (mCurrentSet ? mCurrentSet->NotifyKeyboard(key) : -1);
}
int PageManager::SetKeyBoardFocus(int inFocus)
{
return (mCurrentSet ? mCurrentSet->SetKeyBoardFocus(inFocus) : -1);
}
int PageManager::NotifyVarChange(std::string varName, std::string value)
{
return (mCurrentSet ? mCurrentSet->NotifyVarChange(varName, value) : -1);
}
extern "C" void gui_notifyVarChange(const char *name, const char* value)
{
if (!gGuiRunning) return;
PageManager::NotifyVarChange(name, value);
}