This patch is to refactor twrpDigest using polymorphism and inheritance to use the same call patterns for creating and reading a digest. Now a library. Use SHA2 from libcrypto. SHA2 is default if device has libcrypto. Change string MD5 everywhere to use digest or Digest instead. Updated string tags to digest. Translation will be required. Switch out digest code into a driver class from partitionmanager. SHA2 is better for digest creation due to decreased collision space compared to MD5 and SHA1. See https://en.wikipedia.org/wiki/SHA-2 Change-Id: I74b5546789990b12aa4ce2e389d25f80a3fe213f
1698 lines
43 KiB
C++
1698 lines
43 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 <dirent.h>
|
|
#include "../twrp-functions.hpp"
|
|
#include "../partitions.hpp"
|
|
|
|
#include <string>
|
|
#include <algorithm>
|
|
|
|
extern "C" {
|
|
#include "../twcommon.h"
|
|
#include "../minzip/SysUtil.h"
|
|
#include "../minzip/Zip.h"
|
|
#include "gui.h"
|
|
}
|
|
#include "../minuitwrp/minui.h"
|
|
|
|
#include "rapidxml.hpp"
|
|
#include "objects.hpp"
|
|
#include "blanktimer.hpp"
|
|
|
|
// version 2 requires theme to handle power button as action togglebacklight
|
|
#define TW_THEME_VERSION 3
|
|
|
|
#define TW_THEME_VER_ERR -2
|
|
|
|
extern int gGuiRunning;
|
|
|
|
std::map<std::string, PageSet*> PageManager::mPageSets;
|
|
PageSet* PageManager::mCurrentSet;
|
|
MouseCursor *PageManager::mMouseCursor = NULL;
|
|
HardwareKeyboard *PageManager::mHardwareKeyboard = NULL;
|
|
bool PageManager::mReloadTheme = false;
|
|
std::string PageManager::mStartPage = "main";
|
|
std::vector<language_struct> Language_List;
|
|
|
|
int tw_x_offset = 0;
|
|
int tw_y_offset = 0;
|
|
int tw_w_offset = 0;
|
|
int tw_h_offset = 0;
|
|
|
|
// 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
|
|
xml_node<>* FindNode(xml_node<>* parent, const char* nodename, int depth /* = 0 */)
|
|
{
|
|
if (!parent)
|
|
return NULL;
|
|
|
|
xml_node<>* child = parent->first_node(nodename);
|
|
if (child)
|
|
return child;
|
|
|
|
if (depth == 10) {
|
|
LOGERR("Too many style loops detected.\n");
|
|
return NULL;
|
|
}
|
|
|
|
xml_node<>* style = parent->first_node("style");
|
|
if (style) {
|
|
while (style) {
|
|
if (!style->first_attribute("name")) {
|
|
LOGERR("No name given for style.\n");
|
|
continue;
|
|
} else {
|
|
std::string name = style->first_attribute("name")->value();
|
|
xml_node<>* node = PageManager::FindStyle(name);
|
|
|
|
if (node) {
|
|
// We found the style that was named
|
|
xml_node<>* stylenode = FindNode(node, nodename, depth + 1);
|
|
if (stylenode)
|
|
return stylenode;
|
|
}
|
|
}
|
|
style = style->next_sibling("style");
|
|
}
|
|
} else {
|
|
// Search for stylename in the parent node <object type="foo" style="foo2">
|
|
xml_attribute<>* attr = parent->first_attribute("style");
|
|
// If no style is found anywhere else and the node wasn't found in the object itself
|
|
// as a special case we will search for a style that uses the same style name as the
|
|
// object type, so <object type="button"> would search for a style named "button"
|
|
if (!attr)
|
|
attr = parent->first_attribute("type");
|
|
// if there's no attribute type, the object type must be the element name
|
|
std::string stylename = attr ? attr->value() : parent->name();
|
|
xml_node<>* node = PageManager::FindStyle(stylename);
|
|
if (node) {
|
|
xml_node<>* stylenode = FindNode(node, nodename, depth + 1);
|
|
if (stylenode)
|
|
return stylenode;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
std::string LoadAttrString(xml_node<>* element, const char* attrname, const char* defaultvalue)
|
|
{
|
|
if (!element)
|
|
return defaultvalue;
|
|
|
|
xml_attribute<>* attr = element->first_attribute(attrname);
|
|
return attr ? attr->value() : defaultvalue;
|
|
}
|
|
|
|
int LoadAttrInt(xml_node<>* element, const char* attrname, int defaultvalue)
|
|
{
|
|
string value = LoadAttrString(element, attrname);
|
|
// resolve variables
|
|
DataManager::GetValue(value, value);
|
|
return value.empty() ? defaultvalue : atoi(value.c_str());
|
|
}
|
|
|
|
int LoadAttrIntScaleX(xml_node<>* element, const char* attrname, int defaultvalue)
|
|
{
|
|
return scale_theme_x(LoadAttrInt(element, attrname, defaultvalue));
|
|
}
|
|
|
|
int LoadAttrIntScaleY(xml_node<>* element, const char* attrname, int defaultvalue)
|
|
{
|
|
return scale_theme_y(LoadAttrInt(element, attrname, defaultvalue));
|
|
}
|
|
|
|
COLOR LoadAttrColor(xml_node<>* element, const char* attrname, bool* found_color, COLOR defaultvalue)
|
|
{
|
|
string value = LoadAttrString(element, attrname);
|
|
*found_color = !value.empty();
|
|
// resolve variables
|
|
DataManager::GetValue(value, value);
|
|
COLOR ret = defaultvalue;
|
|
if (ConvertStrToColor(value, &ret) == 0)
|
|
return ret;
|
|
else
|
|
return defaultvalue;
|
|
}
|
|
|
|
COLOR LoadAttrColor(xml_node<>* element, const char* attrname, COLOR defaultvalue)
|
|
{
|
|
bool found_color = false;
|
|
return LoadAttrColor(element, attrname, &found_color, defaultvalue);
|
|
}
|
|
|
|
FontResource* LoadAttrFont(xml_node<>* element, const char* attrname)
|
|
{
|
|
std::string name = LoadAttrString(element, attrname, "");
|
|
if (name.empty())
|
|
return NULL;
|
|
else
|
|
return PageManager::GetResources()->FindFont(name);
|
|
}
|
|
|
|
ImageResource* LoadAttrImage(xml_node<>* element, const char* attrname)
|
|
{
|
|
std::string name = LoadAttrString(element, attrname, "");
|
|
if (name.empty())
|
|
return NULL;
|
|
else
|
|
return PageManager::GetResources()->FindImage(name);
|
|
}
|
|
|
|
AnimationResource* LoadAttrAnimation(xml_node<>* element, const char* attrname)
|
|
{
|
|
std::string name = LoadAttrString(element, attrname, "");
|
|
if (name.empty())
|
|
return NULL;
|
|
else
|
|
return PageManager::GetResources()->FindAnimation(name);
|
|
}
|
|
|
|
bool LoadPlacement(xml_node<>* node, int* x, int* y, int* w /* = NULL */, int* h /* = NULL */, Placement* placement /* = NULL */)
|
|
{
|
|
if (!node)
|
|
return false;
|
|
|
|
if (node->first_attribute("x"))
|
|
*x = LoadAttrIntScaleX(node, "x") + tw_x_offset;
|
|
|
|
if (node->first_attribute("y"))
|
|
*y = LoadAttrIntScaleY(node, "y") + tw_y_offset;
|
|
|
|
if (w && node->first_attribute("w"))
|
|
*w = LoadAttrIntScaleX(node, "w");
|
|
|
|
if (h && node->first_attribute("h"))
|
|
*h = LoadAttrIntScaleY(node, "h");
|
|
|
|
if (placement && node->first_attribute("placement"))
|
|
*placement = (Placement) LoadAttrInt(node, "placement");
|
|
|
|
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, std::vector<xml_node<>*> *templates)
|
|
{
|
|
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, 0);
|
|
}
|
|
|
|
Page::~Page()
|
|
{
|
|
for (std::vector<GUIObject*>::iterator itr = mObjects.begin(); itr != mObjects.end(); ++itr)
|
|
delete *itr;
|
|
}
|
|
|
|
bool Page::ProcessNode(xml_node<>* page, std::vector<xml_node<>*> *templates, int depth)
|
|
{
|
|
if (depth == 10)
|
|
{
|
|
LOGERR("Page processing depth has exceeded 10. Failing out. This is likely a recursive template.\n");
|
|
return false;
|
|
}
|
|
|
|
for (xml_node<>* child = page->first_node(); child; child = child->next_sibling())
|
|
{
|
|
std::string type = child->name();
|
|
|
|
if (type == "background") {
|
|
mBackground = LoadAttrColor(child, "color", COLOR(0,0,0,0));
|
|
continue;
|
|
}
|
|
|
|
if (type == "object") {
|
|
// legacy format : <object type="...">
|
|
xml_attribute<>* attr = child->first_attribute("type");
|
|
type = attr ? attr->value() : "*unspecified*";
|
|
}
|
|
|
|
if (type == "text")
|
|
{
|
|
GUIText* element = new GUIText(child);
|
|
mObjects.push_back(element);
|
|
mRenders.push_back(element);
|
|
mActions.push_back(element);
|
|
}
|
|
else if (type == "image")
|
|
{
|
|
GUIImage* element = new GUIImage(child);
|
|
mObjects.push_back(element);
|
|
mRenders.push_back(element);
|
|
}
|
|
else if (type == "fill")
|
|
{
|
|
GUIFill* element = new GUIFill(child);
|
|
mObjects.push_back(element);
|
|
mRenders.push_back(element);
|
|
}
|
|
else if (type == "action")
|
|
{
|
|
GUIAction* element = new GUIAction(child);
|
|
mObjects.push_back(element);
|
|
mActions.push_back(element);
|
|
}
|
|
else if (type == "console")
|
|
{
|
|
GUIConsole* element = new GUIConsole(child);
|
|
mObjects.push_back(element);
|
|
mRenders.push_back(element);
|
|
mActions.push_back(element);
|
|
}
|
|
else if (type == "terminal")
|
|
{
|
|
GUITerminal* element = new GUITerminal(child);
|
|
mObjects.push_back(element);
|
|
mRenders.push_back(element);
|
|
mActions.push_back(element);
|
|
mInputs.push_back(element);
|
|
}
|
|
else if (type == "button")
|
|
{
|
|
GUIButton* element = new GUIButton(child);
|
|
mObjects.push_back(element);
|
|
mRenders.push_back(element);
|
|
mActions.push_back(element);
|
|
}
|
|
else if (type == "checkbox")
|
|
{
|
|
GUICheckbox* element = new GUICheckbox(child);
|
|
mObjects.push_back(element);
|
|
mRenders.push_back(element);
|
|
mActions.push_back(element);
|
|
}
|
|
else if (type == "fileselector")
|
|
{
|
|
GUIFileSelector* element = new GUIFileSelector(child);
|
|
mObjects.push_back(element);
|
|
mRenders.push_back(element);
|
|
mActions.push_back(element);
|
|
}
|
|
else if (type == "animation")
|
|
{
|
|
GUIAnimation* element = new GUIAnimation(child);
|
|
mObjects.push_back(element);
|
|
mRenders.push_back(element);
|
|
}
|
|
else if (type == "progressbar")
|
|
{
|
|
GUIProgressBar* element = new GUIProgressBar(child);
|
|
mObjects.push_back(element);
|
|
mRenders.push_back(element);
|
|
mActions.push_back(element);
|
|
}
|
|
else if (type == "slider")
|
|
{
|
|
GUISlider* element = new GUISlider(child);
|
|
mObjects.push_back(element);
|
|
mRenders.push_back(element);
|
|
mActions.push_back(element);
|
|
}
|
|
else if (type == "slidervalue")
|
|
{
|
|
GUISliderValue *element = new GUISliderValue(child);
|
|
mObjects.push_back(element);
|
|
mRenders.push_back(element);
|
|
mActions.push_back(element);
|
|
}
|
|
else if (type == "listbox")
|
|
{
|
|
GUIListBox* element = new GUIListBox(child);
|
|
mObjects.push_back(element);
|
|
mRenders.push_back(element);
|
|
mActions.push_back(element);
|
|
}
|
|
else if (type == "keyboard")
|
|
{
|
|
GUIKeyboard* element = new GUIKeyboard(child);
|
|
mObjects.push_back(element);
|
|
mRenders.push_back(element);
|
|
mActions.push_back(element);
|
|
}
|
|
else if (type == "input")
|
|
{
|
|
GUIInput* element = new GUIInput(child);
|
|
mObjects.push_back(element);
|
|
mRenders.push_back(element);
|
|
mActions.push_back(element);
|
|
mInputs.push_back(element);
|
|
}
|
|
else if (type == "partitionlist")
|
|
{
|
|
GUIPartitionList* element = new GUIPartitionList(child);
|
|
mObjects.push_back(element);
|
|
mRenders.push_back(element);
|
|
mActions.push_back(element);
|
|
}
|
|
else if (type == "patternpassword")
|
|
{
|
|
GUIPatternPassword* element = new GUIPatternPassword(child);
|
|
mObjects.push_back(element);
|
|
mRenders.push_back(element);
|
|
mActions.push_back(element);
|
|
}
|
|
else if (type == "textbox")
|
|
{
|
|
GUITextBox* element = new GUITextBox(child);
|
|
mObjects.push_back(element);
|
|
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();
|
|
xml_node<>* node;
|
|
bool node_found = false;
|
|
|
|
// We need to find the correct template
|
|
for (std::vector<xml_node<>*>::iterator itr = templates->begin(); itr != templates->end(); itr++) {
|
|
node = (*itr)->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 {
|
|
node_found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (node_found)
|
|
break;
|
|
node = node->next_sibling("template");
|
|
}
|
|
// [check] why is there no if (node_found) here too?
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOGERR("Unknown object type: %s.\n", type.c_str());
|
|
}
|
|
}
|
|
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, bool down)
|
|
{
|
|
std::vector<ActionObject*>::reverse_iterator iter;
|
|
|
|
int ret = 1;
|
|
// We work backwards, from top-most element to bottom-most element
|
|
for (iter = mActions.rbegin(); iter != mActions.rend(); iter++)
|
|
{
|
|
ret = (*iter)->NotifyKey(key, down);
|
|
if (ret == 0)
|
|
return 0;
|
|
if (ret < 0) {
|
|
LOGERR("An action handler has returned an error\n");
|
|
ret = 1;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int Page::NotifyCharInput(int ch)
|
|
{
|
|
std::vector<InputObject*>::reverse_iterator iter;
|
|
|
|
// We work backwards, from top-most element to bottom-most element
|
|
for (iter = mInputs.rbegin(); iter != mInputs.rend(); iter++)
|
|
{
|
|
int ret = (*iter)->NotifyCharInput(ch);
|
|
if (ret == 0)
|
|
return 0;
|
|
else if (ret < 0)
|
|
LOGERR("A char input handler has returned an error\n");
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int Page::SetKeyBoardFocus(int inFocus)
|
|
{
|
|
std::vector<InputObject*>::reverse_iterator iter;
|
|
|
|
// 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\n");
|
|
}
|
|
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<GUIObject*>::iterator iter;
|
|
for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
|
|
{
|
|
if ((*iter)->NotifyVarChange(varName, value))
|
|
LOGERR("An action handler errored on NotifyVarChange.\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
// transient data for loading themes
|
|
struct LoadingContext
|
|
{
|
|
ZipArchive* zip; // zip to load theme from, or NULL for the stock theme
|
|
std::set<std::string> filenames; // to detect cyclic includes
|
|
std::string basepath; // if zip is NULL, base path to load includes from with trailing slash, otherwise empty
|
|
std::vector<xml_document<>*> xmldocs; // all loaded xml docs
|
|
std::vector<char*> xmlbuffers; // text buffers with xml content
|
|
std::vector<xml_node<>*> styles; // refer to <styles> nodes inside xmldocs
|
|
std::vector<xml_node<>*> templates; // refer to <templates> nodes inside xmldocs
|
|
|
|
LoadingContext()
|
|
{
|
|
zip = NULL;
|
|
}
|
|
|
|
~LoadingContext()
|
|
{
|
|
// free all xml buffers
|
|
for (std::vector<char*>::iterator it = xmlbuffers.begin(); it != xmlbuffers.end(); ++it)
|
|
free(*it);
|
|
}
|
|
|
|
};
|
|
|
|
// for FindStyle
|
|
LoadingContext* PageManager::currentLoadingContext = NULL;
|
|
|
|
|
|
PageSet::PageSet()
|
|
{
|
|
mResources = new ResourceManager;
|
|
mCurrentPage = NULL;
|
|
|
|
set_scale_values(1, 1); // Reset any previous scaling values
|
|
}
|
|
|
|
PageSet::~PageSet()
|
|
{
|
|
mOverlays.clear();
|
|
for (std::vector<Page*>::iterator itr = mPages.begin(); itr != mPages.end(); ++itr)
|
|
delete *itr;
|
|
|
|
delete mResources;
|
|
}
|
|
|
|
int PageSet::Load(LoadingContext& ctx, const std::string& filename)
|
|
{
|
|
bool isMain = ctx.xmlbuffers.empty(); // if we have no files yet, remember that this is the main XML file
|
|
|
|
if (!ctx.filenames.insert(filename).second)
|
|
// ignore already loaded files to prevent crash with cyclic includes
|
|
return 0;
|
|
|
|
// load XML into buffer
|
|
char* xmlbuffer = PageManager::LoadFileToBuffer(filename, ctx.zip);
|
|
if (!xmlbuffer)
|
|
return -1; // error already displayed by LoadFileToBuffer
|
|
ctx.xmlbuffers.push_back(xmlbuffer);
|
|
|
|
// parse XML
|
|
xml_document<>* doc = new xml_document<>();
|
|
doc->parse<0>(xmlbuffer);
|
|
ctx.xmldocs.push_back(doc);
|
|
|
|
xml_node<>* root = doc->first_node("recovery");
|
|
if (!root)
|
|
root = doc->first_node("install");
|
|
if (!root) {
|
|
LOGERR("Unknown root element in %s\n", filename.c_str());
|
|
return -1;
|
|
}
|
|
|
|
if (isMain) {
|
|
int rc = LoadDetails(ctx, root);
|
|
if (rc != 0)
|
|
return rc;
|
|
}
|
|
|
|
LOGINFO("Loading resources...\n");
|
|
xml_node<>* child = root->first_node("resources");
|
|
if (child)
|
|
mResources->LoadResources(child, ctx.zip, "theme");
|
|
|
|
LOGINFO("Loading variables...\n");
|
|
child = root->first_node("variables");
|
|
if (child)
|
|
LoadVariables(child);
|
|
|
|
LOGINFO("Loading mouse cursor...\n");
|
|
child = root->first_node("mousecursor");
|
|
if (child)
|
|
PageManager::LoadCursorData(child);
|
|
|
|
LOGINFO("Loading pages...\n");
|
|
child = root->first_node("templates");
|
|
if (child)
|
|
ctx.templates.push_back(child);
|
|
|
|
child = root->first_node("styles");
|
|
if (child)
|
|
ctx.styles.push_back(child);
|
|
|
|
// Load pages
|
|
child = root->first_node("pages");
|
|
if (child) {
|
|
if (LoadPages(ctx, child)) {
|
|
LOGERR("PageSet::Load returning -1\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// process includes recursively
|
|
child = root->first_node("include");
|
|
if (child) {
|
|
xml_node<>* include = child->first_node("xmlfile");
|
|
while (include != NULL) {
|
|
xml_attribute<>* attr = include->first_attribute("name");
|
|
if (!attr) {
|
|
LOGERR("Skipping include/xmlfile with no name\n");
|
|
continue;
|
|
}
|
|
|
|
string filename = ctx.basepath + attr->value();
|
|
LOGINFO("Including file: %s...\n", filename.c_str());
|
|
int rc = Load(ctx, filename);
|
|
if (rc != 0)
|
|
return rc;
|
|
|
|
include = include->next_sibling("xmlfile");
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void PageSet::MakeEmergencyConsoleIfNeeded()
|
|
{
|
|
if (mPages.empty()) {
|
|
mCurrentPage = new Page(NULL, NULL); // fallback console page
|
|
// TODO: since removal of non-TTF fonts, the emergency console doesn't work without a font, which might be missing too
|
|
mPages.push_back(mCurrentPage);
|
|
}
|
|
}
|
|
|
|
int PageSet::LoadLanguage(char* languageFile, ZipArchive* package)
|
|
{
|
|
xml_document<> lang;
|
|
xml_node<>* parent;
|
|
xml_node<>* child;
|
|
std::string resource_source;
|
|
int ret = 0;
|
|
|
|
if (languageFile) {
|
|
printf("parsing languageFile\n");
|
|
lang.parse<0>(languageFile);
|
|
printf("parsing languageFile done\n");
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
parent = lang.first_node("language");
|
|
if (!parent) {
|
|
LOGERR("Unable to locate language node in language file.\n");
|
|
lang.clear();
|
|
return -1;
|
|
}
|
|
|
|
child = parent->first_node("display");
|
|
if (child) {
|
|
DataManager::SetValue("tw_language_display", child->value());
|
|
resource_source = child->value();
|
|
} else {
|
|
LOGERR("language file does not have a display value set\n");
|
|
DataManager::SetValue("tw_language_display", "Not Set");
|
|
resource_source = languageFile;
|
|
}
|
|
|
|
child = parent->first_node("resources");
|
|
if (child)
|
|
mResources->LoadResources(child, package, resource_source);
|
|
else
|
|
ret = -1;
|
|
DataManager::SetValue("tw_backup_name", gui_lookup("auto_generate", "(Auto Generate)"));
|
|
lang.clear();
|
|
return ret;
|
|
}
|
|
|
|
int PageSet::LoadDetails(LoadingContext& ctx, xml_node<>* root)
|
|
{
|
|
xml_node<>* child = root->first_node("details");
|
|
if (child) {
|
|
int theme_ver = 0;
|
|
xml_node<>* themeversion = child->first_node("themeversion");
|
|
if (themeversion && themeversion->value()) {
|
|
theme_ver = atoi(themeversion->value());
|
|
} else {
|
|
LOGINFO("No themeversion in theme.\n");
|
|
}
|
|
if (theme_ver != TW_THEME_VERSION) {
|
|
LOGINFO("theme version from xml: %i, expected %i\n", theme_ver, TW_THEME_VERSION);
|
|
if (ctx.zip) {
|
|
gui_err("theme_ver_err=Custom theme version does not match TWRP version. Using stock theme.");
|
|
return TW_THEME_VER_ERR;
|
|
} else {
|
|
gui_print_color("warning", "Stock theme version does not match TWRP version.\n");
|
|
}
|
|
}
|
|
xml_node<>* resolution = child->first_node("resolution");
|
|
if (resolution) {
|
|
LOGINFO("Checking resolution...\n");
|
|
xml_attribute<>* width_attr = resolution->first_attribute("width");
|
|
xml_attribute<>* height_attr = resolution->first_attribute("height");
|
|
xml_attribute<>* noscale_attr = resolution->first_attribute("noscaling");
|
|
if (width_attr && height_attr && !noscale_attr) {
|
|
int width = atoi(width_attr->value());
|
|
int height = atoi(height_attr->value());
|
|
int offx = 0, offy = 0;
|
|
#ifdef TW_ROUND_SCREEN
|
|
xml_node<>* roundscreen = child->first_node("roundscreen");
|
|
if (roundscreen) {
|
|
LOGINFO("TW_ROUND_SCREEN := true, using round screen XML settings.\n");
|
|
xml_attribute<>* offx_attr = roundscreen->first_attribute("offset_x");
|
|
xml_attribute<>* offy_attr = roundscreen->first_attribute("offset_y");
|
|
if (offx_attr) {
|
|
offx = atoi(offx_attr->value());
|
|
}
|
|
if (offy_attr) {
|
|
offy = atoi(offy_attr->value());
|
|
}
|
|
}
|
|
#endif
|
|
if (width != 0 && height != 0) {
|
|
float scale_w = (((float)gr_fb_width() + (float)tw_w_offset) - ((float)offx * 2.0)) / (float)width;
|
|
float scale_h = (((float)gr_fb_height() + (float)tw_h_offset) - ((float)offy * 2.0)) / (float)height;
|
|
#ifdef TW_ROUND_SCREEN
|
|
float scale_off_w = ((float)gr_fb_width() + (float)tw_w_offset) / (float)width;
|
|
float scale_off_h = ((float)gr_fb_height() + (float)tw_h_offset) / (float)height;
|
|
tw_x_offset = offx * scale_off_w;
|
|
tw_y_offset = offy * scale_off_h;
|
|
#endif
|
|
if (scale_w != 1 || scale_h != 1) {
|
|
LOGINFO("Scaling theme width %fx and height %fx, offsets x: %i y: %i w: %i h: %i\n",
|
|
scale_w, scale_h, tw_x_offset, tw_y_offset, tw_w_offset, tw_h_offset);
|
|
set_scale_values(scale_w, scale_h);
|
|
}
|
|
}
|
|
} else {
|
|
LOGINFO("XML does not contain width and height, no scaling will be applied\n");
|
|
}
|
|
} else {
|
|
LOGINFO("XML contains no resolution tag, no scaling will be applied.\n");
|
|
}
|
|
} else {
|
|
LOGINFO("XML contains no details tag, no scaling will be applied.\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
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 (page) {
|
|
if (mOverlays.size() >= 10) {
|
|
LOGERR("Too many overlays requested, max is 10.\n");
|
|
return -1;
|
|
}
|
|
|
|
std::vector<Page*>::iterator iter;
|
|
for (iter = mOverlays.begin(); iter != mOverlays.end(); iter++) {
|
|
if ((*iter)->GetName() == page->GetName()) {
|
|
mOverlays.erase(iter);
|
|
// SetOverlay() is (and should stay) the only function which
|
|
// adds to mOverlays. Then, each page can appear at most once.
|
|
break;
|
|
}
|
|
}
|
|
|
|
page->SetPageFocus(1);
|
|
page->NotifyVarChange("", "");
|
|
|
|
if (!mOverlays.empty())
|
|
mOverlays.back()->SetPageFocus(0);
|
|
|
|
mOverlays.push_back(page);
|
|
} else {
|
|
if (!mOverlays.empty()) {
|
|
mOverlays.back()->SetPageFocus(0);
|
|
mOverlays.pop_back();
|
|
if (!mOverlays.empty())
|
|
mOverlays.back()->SetPageFocus(1);
|
|
else if (mCurrentPage)
|
|
mCurrentPage->SetPageFocus(1); // Just in case somehow the regular page lost focus, we'll set it again
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const ResourceManager* PageSet::GetResources()
|
|
{
|
|
return mResources;
|
|
}
|
|
|
|
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;
|
|
xml_attribute<> *name, *value, *persist;
|
|
int p;
|
|
|
|
child = vars->first_node("variable");
|
|
while (child)
|
|
{
|
|
name = child->first_attribute("name");
|
|
value = child->first_attribute("value");
|
|
persist = child->first_attribute("persist");
|
|
if (name && value)
|
|
{
|
|
if (strcmp(name->value(), "tw_x_offset") == 0) {
|
|
tw_x_offset = atoi(value->value());
|
|
child = child->next_sibling("variable");
|
|
continue;
|
|
}
|
|
if (strcmp(name->value(), "tw_y_offset") == 0) {
|
|
tw_y_offset = atoi(value->value());
|
|
child = child->next_sibling("variable");
|
|
continue;
|
|
}
|
|
if (strcmp(name->value(), "tw_w_offset") == 0) {
|
|
tw_w_offset = atoi(value->value());
|
|
child = child->next_sibling("variable");
|
|
continue;
|
|
}
|
|
if (strcmp(name->value(), "tw_h_offset") == 0) {
|
|
tw_h_offset = atoi(value->value());
|
|
child = child->next_sibling("variable");
|
|
continue;
|
|
}
|
|
p = persist ? atoi(persist->value()) : 0;
|
|
string temp = value->value();
|
|
string valstr = gui_parse_text(temp);
|
|
|
|
if (valstr.find("+") != string::npos) {
|
|
string val1str = valstr;
|
|
val1str = val1str.substr(0, val1str.find('+'));
|
|
string val2str = valstr;
|
|
val2str = val2str.substr(val2str.find('+') + 1, string::npos);
|
|
int val1 = atoi(val1str.c_str());
|
|
int val2 = atoi(val2str.c_str());
|
|
int val = val1 + val2;
|
|
|
|
DataManager::SetValue(name->value(), val, p);
|
|
} else if (valstr.find("-") != string::npos) {
|
|
string val1str = valstr;
|
|
val1str = val1str.substr(0, val1str.find('-'));
|
|
string val2str = valstr;
|
|
val2str = val2str.substr(val2str.find('-') + 1, string::npos);
|
|
int val1 = atoi(val1str.c_str());
|
|
int val2 = atoi(val2str.c_str());
|
|
int val = val1 - val2;
|
|
|
|
DataManager::SetValue(name->value(), val, p);
|
|
} else {
|
|
DataManager::SetValue(name->value(), valstr, p);
|
|
}
|
|
}
|
|
|
|
child = child->next_sibling("variable");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int PageSet::LoadPages(LoadingContext& ctx, xml_node<>* pages)
|
|
{
|
|
xml_node<>* child;
|
|
|
|
if (!pages)
|
|
return -1;
|
|
|
|
child = pages->first_node("page");
|
|
while (child != NULL)
|
|
{
|
|
Page* page = new Page(child, &ctx.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);
|
|
}
|
|
|
|
std::string PageSet::GetCurrentPage() const
|
|
{
|
|
return mCurrentPage ? mCurrentPage->GetName() : "";
|
|
}
|
|
|
|
int PageSet::Render(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = (mCurrentPage ? mCurrentPage->Render() : -1);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
std::vector<Page*>::iterator iter;
|
|
|
|
for (iter = mOverlays.begin(); iter != mOverlays.end(); iter++) {
|
|
ret = ((*iter) ? (*iter)->Render() : -1);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int PageSet::Update(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = (mCurrentPage ? mCurrentPage->Update() : -1);
|
|
if (ret < 0 || ret > 1)
|
|
return ret;
|
|
|
|
std::vector<Page*>::iterator iter;
|
|
|
|
for (iter = mOverlays.begin(); iter != mOverlays.end(); iter++) {
|
|
ret = ((*iter) ? (*iter)->Update() : -1);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int PageSet::NotifyTouch(TOUCH_STATE state, int x, int y)
|
|
{
|
|
if (!mOverlays.empty())
|
|
return mOverlays.back()->NotifyTouch(state, x, y);
|
|
|
|
return (mCurrentPage ? mCurrentPage->NotifyTouch(state, x, y) : -1);
|
|
}
|
|
|
|
int PageSet::NotifyKey(int key, bool down)
|
|
{
|
|
if (!mOverlays.empty())
|
|
return mOverlays.back()->NotifyKey(key, down);
|
|
|
|
return (mCurrentPage ? mCurrentPage->NotifyKey(key, down) : -1);
|
|
}
|
|
|
|
int PageSet::NotifyCharInput(int ch)
|
|
{
|
|
if (!mOverlays.empty())
|
|
return mOverlays.back()->NotifyCharInput(ch);
|
|
|
|
return (mCurrentPage ? mCurrentPage->NotifyCharInput(ch) : -1);
|
|
}
|
|
|
|
int PageSet::SetKeyBoardFocus(int inFocus)
|
|
{
|
|
if (!mOverlays.empty())
|
|
return mOverlays.back()->SetKeyBoardFocus(inFocus);
|
|
|
|
return (mCurrentPage ? mCurrentPage->SetKeyBoardFocus(inFocus) : -1);
|
|
}
|
|
|
|
int PageSet::NotifyVarChange(std::string varName, std::string value)
|
|
{
|
|
std::vector<Page*>::iterator iter;
|
|
|
|
for (iter = mOverlays.begin(); iter != mOverlays.end(); iter++)
|
|
(*iter)->NotifyVarChange(varName, value);
|
|
|
|
return (mCurrentPage ? mCurrentPage->NotifyVarChange(varName, value) : -1);
|
|
}
|
|
|
|
void PageSet::AddStringResource(std::string resource_source, std::string resource_name, std::string value)
|
|
{
|
|
mResources->AddStringResource(resource_source, resource_name, value);
|
|
}
|
|
|
|
char* PageManager::LoadFileToBuffer(std::string filename, ZipArchive* package) {
|
|
size_t len;
|
|
char* buffer = NULL;
|
|
|
|
if (!package) {
|
|
// We can try to load the XML directly...
|
|
LOGINFO("PageManager::LoadFileToBuffer loading filename: '%s' directly\n", filename.c_str());
|
|
struct stat st;
|
|
if (stat(filename.c_str(),&st) != 0) {
|
|
// This isn't always an error, sometimes we request files that don't exist.
|
|
return NULL;
|
|
}
|
|
|
|
len = (size_t)st.st_size;
|
|
|
|
buffer = (char*) malloc(len + 1);
|
|
if (!buffer) {
|
|
LOGERR("PageManager::LoadFileToBuffer failed to malloc\n");
|
|
return NULL;
|
|
}
|
|
|
|
int fd = open(filename.c_str(), O_RDONLY);
|
|
if (fd == -1) {
|
|
LOGERR("PageManager::LoadFileToBuffer failed to open '%s' - (%s)\n", filename.c_str(), strerror(errno));
|
|
free(buffer);
|
|
return NULL;
|
|
}
|
|
|
|
if (read(fd, buffer, len) < 0) {
|
|
LOGERR("PageManager::LoadFileToBuffer failed to read '%s' - (%s)\n", filename.c_str(), strerror(errno));
|
|
free(buffer);
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
close(fd);
|
|
} else {
|
|
LOGINFO("PageManager::LoadFileToBuffer loading filename: '%s' from zip\n", filename.c_str());
|
|
const ZipEntry* zipentry = mzFindZipEntry(package, filename.c_str());
|
|
if (zipentry == NULL) {
|
|
LOGERR("Unable to locate '%s' in zip file\n", filename.c_str());
|
|
return NULL;
|
|
}
|
|
|
|
// Allocate the buffer for the file
|
|
len = mzGetZipEntryUncompLen(zipentry);
|
|
buffer = (char*) malloc(len + 1);
|
|
if (!buffer)
|
|
return NULL;
|
|
|
|
if (!mzExtractZipEntryToBuffer(package, zipentry, (unsigned char*) buffer)) {
|
|
LOGERR("Unable to extract '%s'\n", filename.c_str());
|
|
free(buffer);
|
|
return NULL;
|
|
}
|
|
}
|
|
// NULL-terminate the string
|
|
buffer[len] = 0x00;
|
|
return buffer;
|
|
}
|
|
|
|
void PageManager::LoadLanguageListDir(string dir) {
|
|
if (!TWFunc::Path_Exists(dir)) {
|
|
LOGERR("LoadLanguageListDir '%s' path not found\n", dir.c_str());
|
|
return;
|
|
}
|
|
|
|
DIR *d = opendir(dir.c_str());
|
|
struct dirent *p;
|
|
|
|
if (d == NULL) {
|
|
LOGERR("LoadLanguageListDir error opening dir: '%s', %s\n", dir.c_str(), strerror(errno));
|
|
return;
|
|
}
|
|
|
|
while ((p = readdir(d))) {
|
|
if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..") || strlen(p->d_name) < 5)
|
|
continue;
|
|
|
|
string file = p->d_name;
|
|
if (file.substr(strlen(p->d_name) - 4) != ".xml")
|
|
continue;
|
|
string path = dir + p->d_name;
|
|
string file_no_extn = file.substr(0, strlen(p->d_name) - 4);
|
|
struct language_struct language_entry;
|
|
language_entry.filename = file_no_extn;
|
|
char* xmlFile = PageManager::LoadFileToBuffer(dir + p->d_name, NULL);
|
|
if (xmlFile == NULL) {
|
|
LOGERR("LoadLanguageListDir unable to load '%s'\n", language_entry.filename.c_str());
|
|
continue;
|
|
}
|
|
xml_document<> *doc = new xml_document<>();
|
|
doc->parse<0>(xmlFile);
|
|
|
|
xml_node<>* parent = doc->first_node("language");
|
|
if (!parent) {
|
|
LOGERR("Invalid language XML file '%s'\n", language_entry.filename.c_str());
|
|
} else {
|
|
xml_node<>* child = parent->first_node("display");
|
|
if (child) {
|
|
language_entry.displayvalue = child->value();
|
|
} else {
|
|
LOGERR("No display value for '%s'\n", language_entry.filename.c_str());
|
|
language_entry.displayvalue = language_entry.filename;
|
|
}
|
|
Language_List.push_back(language_entry);
|
|
}
|
|
doc->clear();
|
|
delete doc;
|
|
free(xmlFile);
|
|
}
|
|
closedir(d);
|
|
}
|
|
|
|
void PageManager::LoadLanguageList(ZipArchive* package) {
|
|
Language_List.clear();
|
|
if (TWFunc::Path_Exists(TWRES "customlanguages"))
|
|
TWFunc::removeDir(TWRES "customlanguages", true);
|
|
if (package) {
|
|
TWFunc::Recursive_Mkdir(TWRES "customlanguages");
|
|
struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default
|
|
mzExtractRecursive(package, "languages", TWRES "customlanguages/", ×tamp, NULL, NULL, NULL);
|
|
LoadLanguageListDir(TWRES "customlanguages/");
|
|
} else {
|
|
LoadLanguageListDir(TWRES "languages/");
|
|
}
|
|
|
|
std::sort(Language_List.begin(), Language_List.end());
|
|
}
|
|
|
|
void PageManager::LoadLanguage(string filename) {
|
|
string actual_filename;
|
|
if (TWFunc::Path_Exists(TWRES "customlanguages/" + filename + ".xml"))
|
|
actual_filename = TWRES "customlanguages/" + filename + ".xml";
|
|
else
|
|
actual_filename = TWRES "languages/" + filename + ".xml";
|
|
char* xmlFile = PageManager::LoadFileToBuffer(actual_filename, NULL);
|
|
if (xmlFile == NULL)
|
|
LOGERR("Unable to load '%s'\n", actual_filename.c_str());
|
|
else {
|
|
mCurrentSet->LoadLanguage(xmlFile, NULL);
|
|
free(xmlFile);
|
|
}
|
|
PartitionManager.Translate_Partition_Display_Names();
|
|
}
|
|
|
|
int PageManager::LoadPackage(std::string name, std::string package, std::string startpage)
|
|
{
|
|
std::string mainxmlfilename = package;
|
|
ZipArchive zip;
|
|
char* languageFile = NULL;
|
|
char* baseLanguageFile = NULL;
|
|
PageSet* pageSet = NULL;
|
|
int ret;
|
|
MemMapping map;
|
|
|
|
mReloadTheme = false;
|
|
mStartPage = startpage;
|
|
|
|
// init the loading context
|
|
LoadingContext ctx;
|
|
|
|
// Open the XML file
|
|
LOGINFO("Loading package: %s (%s)\n", name.c_str(), package.c_str());
|
|
if (package.size() > 4 && package.substr(package.size() - 4) != ".zip")
|
|
{
|
|
LOGINFO("Load XML directly\n");
|
|
tw_x_offset = TW_X_OFFSET;
|
|
tw_y_offset = TW_Y_OFFSET;
|
|
tw_w_offset = TW_W_OFFSET;
|
|
tw_h_offset = TW_H_OFFSET;
|
|
if (name != "splash") {
|
|
LoadLanguageList(NULL);
|
|
languageFile = LoadFileToBuffer(TWRES "languages/en.xml", NULL);
|
|
}
|
|
ctx.basepath = TWRES;
|
|
}
|
|
else
|
|
{
|
|
LOGINFO("Loading zip theme\n");
|
|
tw_x_offset = 0;
|
|
tw_y_offset = 0;
|
|
tw_w_offset = 0;
|
|
tw_h_offset = 0;
|
|
if (!TWFunc::Path_Exists(package))
|
|
return -1;
|
|
if (sysMapFile(package.c_str(), &map) != 0) {
|
|
LOGERR("Failed to map '%s'\n", package.c_str());
|
|
goto error;
|
|
}
|
|
if (mzOpenZipArchive(map.addr, map.length, &zip)) {
|
|
LOGERR("Unable to open zip archive '%s'\n", package.c_str());
|
|
sysReleaseMap(&map);
|
|
goto error;
|
|
}
|
|
ctx.zip = &zip;
|
|
mainxmlfilename = "ui.xml";
|
|
LoadLanguageList(ctx.zip);
|
|
languageFile = LoadFileToBuffer("languages/en.xml", ctx.zip);
|
|
baseLanguageFile = LoadFileToBuffer(TWRES "languages/en.xml", NULL);
|
|
}
|
|
|
|
// Before loading, mCurrentSet must be the loading package so we can find resources
|
|
pageSet = mCurrentSet;
|
|
mCurrentSet = new PageSet();
|
|
|
|
if (baseLanguageFile) {
|
|
mCurrentSet->LoadLanguage(baseLanguageFile, NULL);
|
|
free(baseLanguageFile);
|
|
}
|
|
|
|
if (languageFile) {
|
|
mCurrentSet->LoadLanguage(languageFile, ctx.zip);
|
|
free(languageFile);
|
|
}
|
|
|
|
// Load and parse the XML and all includes
|
|
currentLoadingContext = &ctx; // required to find styles
|
|
ret = mCurrentSet->Load(ctx, mainxmlfilename);
|
|
currentLoadingContext = NULL;
|
|
|
|
if (ret == 0) {
|
|
mCurrentSet->SetPage(startpage);
|
|
mPageSets.insert(std::pair<std::string, PageSet*>(name, mCurrentSet));
|
|
} else {
|
|
if (ret != TW_THEME_VER_ERR)
|
|
LOGERR("Package %s failed to load.\n", name.c_str());
|
|
}
|
|
|
|
// reset to previous pageset
|
|
mCurrentSet = pageSet;
|
|
|
|
if (ctx.zip) {
|
|
mzCloseZipArchive(ctx.zip);
|
|
sysReleaseMap(&map);
|
|
}
|
|
return ret;
|
|
|
|
error:
|
|
// Sometimes we get here without a real error
|
|
if (ctx.zip) {
|
|
mzCloseZipArchive(ctx.zip);
|
|
sysReleaseMap(&map);
|
|
}
|
|
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;
|
|
mCurrentSet->MakeEmergencyConsoleIfNeeded();
|
|
mCurrentSet->NotifyVarChange("", "");
|
|
}
|
|
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;
|
|
|
|
mReloadTheme = false;
|
|
|
|
iter = mPageSets.find(name);
|
|
if (iter == mPageSets.end())
|
|
return -1;
|
|
|
|
if (mMouseCursor)
|
|
mMouseCursor->ResetData(gr_fb_width(), gr_fb_height());
|
|
|
|
PageSet* set = (*iter).second;
|
|
mPageSets.erase(iter);
|
|
|
|
if (LoadPackage(name, package, mStartPage) != 0)
|
|
{
|
|
LOGINFO("Failed to load package '%s'.\n", package.c_str());
|
|
mPageSets.insert(std::pair<std::string, PageSet*>(name, set));
|
|
return -1;
|
|
}
|
|
if (mCurrentSet == set)
|
|
SelectPackage(name);
|
|
delete set;
|
|
GUIConsole::Translate_Now();
|
|
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;
|
|
if (set == mCurrentSet)
|
|
mCurrentSet = NULL;
|
|
return;
|
|
}
|
|
|
|
int PageManager::RunReload() {
|
|
int ret_val = 0;
|
|
std::string theme_path;
|
|
|
|
if (!mReloadTheme)
|
|
return 0;
|
|
|
|
mReloadTheme = false;
|
|
theme_path = DataManager::GetSettingsStoragePath();
|
|
if (PartitionManager.Mount_By_Path(theme_path.c_str(), 1) < 0) {
|
|
LOGERR("Unable to mount %s during gui_reload_theme function.\n", theme_path.c_str());
|
|
ret_val = 1;
|
|
}
|
|
|
|
theme_path += "/TWRP/theme/ui.zip";
|
|
if (ret_val != 0 || ReloadPackage("TWRP", theme_path) != 0)
|
|
{
|
|
// Loading the custom theme failed - try loading the stock theme
|
|
LOGINFO("Attempting to reload stock theme...\n");
|
|
if (ReloadPackage("TWRP", TWRES "ui.xml"))
|
|
{
|
|
LOGERR("Failed to load base packages.\n");
|
|
ret_val = 1;
|
|
}
|
|
}
|
|
if (ret_val == 0) {
|
|
if (DataManager::GetStrValue("tw_language") != "en.xml") {
|
|
LOGINFO("Loading language '%s'\n", DataManager::GetStrValue("tw_language").c_str());
|
|
LoadLanguage(DataManager::GetStrValue("tw_language"));
|
|
}
|
|
}
|
|
|
|
// This makes the console re-translate
|
|
GUIConsole::Clear_For_Retranslation();
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
void PageManager::RequestReload() {
|
|
mReloadTheme = true;
|
|
}
|
|
|
|
void PageManager::SetStartPage(const std::string& page_name) {
|
|
mStartPage = page_name;
|
|
}
|
|
|
|
int PageManager::ChangePage(std::string name)
|
|
{
|
|
DataManager::SetValue("tw_operation_state", 0);
|
|
int ret = (mCurrentSet ? mCurrentSet->SetPage(name) : -1);
|
|
return ret;
|
|
}
|
|
|
|
std::string PageManager::GetCurrentPage()
|
|
{
|
|
return mCurrentSet ? mCurrentSet->GetCurrentPage() : "";
|
|
}
|
|
|
|
int PageManager::ChangeOverlay(std::string name)
|
|
{
|
|
if (name.empty())
|
|
return mCurrentSet->SetOverlay(NULL);
|
|
else
|
|
{
|
|
Page* page = mCurrentSet ? mCurrentSet->FindPage(name) : NULL;
|
|
return mCurrentSet->SetOverlay(page);
|
|
}
|
|
}
|
|
|
|
const ResourceManager* PageManager::GetResources()
|
|
{
|
|
return (mCurrentSet ? mCurrentSet->GetResources() : NULL);
|
|
}
|
|
|
|
int PageManager::IsCurrentPage(Page* page)
|
|
{
|
|
return (mCurrentSet ? mCurrentSet->IsCurrentPage(page) : 0);
|
|
}
|
|
|
|
int PageManager::Render(void)
|
|
{
|
|
if (blankTimer.isScreenOff())
|
|
return 0;
|
|
|
|
int res = (mCurrentSet ? mCurrentSet->Render() : -1);
|
|
if (mMouseCursor)
|
|
mMouseCursor->Render();
|
|
return res;
|
|
}
|
|
|
|
HardwareKeyboard *PageManager::GetHardwareKeyboard()
|
|
{
|
|
if (!mHardwareKeyboard)
|
|
mHardwareKeyboard = new HardwareKeyboard();
|
|
return mHardwareKeyboard;
|
|
}
|
|
|
|
xml_node<>* PageManager::FindStyle(std::string name)
|
|
{
|
|
if (!currentLoadingContext)
|
|
{
|
|
LOGERR("FindStyle works only while loading a theme.\n");
|
|
return NULL;
|
|
}
|
|
|
|
for (std::vector<xml_node<>*>::iterator itr = currentLoadingContext->styles.begin(); itr != currentLoadingContext->styles.end(); itr++) {
|
|
xml_node<>* node = (*itr)->first_node("style");
|
|
|
|
while (node) {
|
|
if (!node->first_attribute("name"))
|
|
continue;
|
|
|
|
if (name == node->first_attribute("name")->value())
|
|
return node;
|
|
node = node->next_sibling("style");
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
MouseCursor *PageManager::GetMouseCursor()
|
|
{
|
|
if (!mMouseCursor)
|
|
mMouseCursor = new MouseCursor(gr_fb_width(), gr_fb_height());
|
|
return mMouseCursor;
|
|
}
|
|
|
|
void PageManager::LoadCursorData(xml_node<>* node)
|
|
{
|
|
if (!mMouseCursor)
|
|
mMouseCursor = new MouseCursor(gr_fb_width(), gr_fb_height());
|
|
|
|
mMouseCursor->LoadData(node);
|
|
}
|
|
|
|
int PageManager::Update(void)
|
|
{
|
|
if (blankTimer.isScreenOff())
|
|
return 0;
|
|
|
|
if (RunReload())
|
|
return -2;
|
|
|
|
int res = (mCurrentSet ? mCurrentSet->Update() : -1);
|
|
|
|
if (mMouseCursor)
|
|
{
|
|
int c_res = mMouseCursor->Update();
|
|
if (c_res > res)
|
|
res = c_res;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int PageManager::NotifyTouch(TOUCH_STATE state, int x, int y)
|
|
{
|
|
return (mCurrentSet ? mCurrentSet->NotifyTouch(state, x, y) : -1);
|
|
}
|
|
|
|
int PageManager::NotifyKey(int key, bool down)
|
|
{
|
|
return (mCurrentSet ? mCurrentSet->NotifyKey(key, down) : -1);
|
|
}
|
|
|
|
int PageManager::NotifyCharInput(int ch)
|
|
{
|
|
return (mCurrentSet ? mCurrentSet->NotifyCharInput(ch) : -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);
|
|
}
|
|
|
|
void PageManager::AddStringResource(std::string resource_source, std::string resource_name, std::string value)
|
|
{
|
|
if (mCurrentSet)
|
|
mCurrentSet->AddStringResource(resource_source, resource_name, value);
|
|
}
|
|
|
|
extern "C" void gui_notifyVarChange(const char *name, const char* value)
|
|
{
|
|
if (!gGuiRunning)
|
|
return;
|
|
|
|
PageManager::NotifyVarChange(name, value);
|
|
}
|