From 74db157b9406594a549a70415668dd6cbe17d1d3 Mon Sep 17 00:00:00 2001 From: Ethan Yonker Date: Wed, 28 Oct 2015 12:44:49 -0500 Subject: Multiple Language Support This is similar to https://gerrit.omnirom.org/#/c/14014 A lot of the features built in the older patch set have been split out into separate patches, most of which have already been merged. The remaining functionality here should all be directly related to language selection and loading. We always load English as a base before loading other languages over the top of the base. The idea is that if another language is missing a translation, then we will still display the English. Maybe still to do: read the /cache/recovery/last_locale file and load a language based on that. For me, this file contains just: en_US We probably won't bother with region specific translations so we would have to look at either trimming off the _US or using some other method like perhaps a symlink or a combination of the two. Thanks to _that for twmsg.cpp class Change-Id: I9647a22e47883a3ddd2de1da51f64aab7c328f74 --- Android.mk | 5 + data.cpp | 15 +- find_file.cpp | 2 +- gui/Android.mk | 4 +- gui/action.cpp | 84 ++- gui/console.cpp | 113 +++- gui/devices/common/res/languages/en.xml | 226 +++++++ gui/devices/common/res/languages/es.xml | 13 + gui/devices/common/res/languages/fi.xml | 13 + gui/gui.cpp | 59 +- gui/gui.hpp | 33 + gui/listbox.cpp | 18 + gui/objects.hpp | 11 + gui/pages.cpp | 184 +++++- gui/pages.hpp | 21 +- gui/resources.cpp | 133 +++- gui/resources.hpp | 21 +- gui/theme/common/landscape.xml | 1062 ++++++++++++++++--------------- gui/theme/common/languages/en.xml | 666 +++++++++++++++++++ gui/theme/common/languages/es.xml | 13 + gui/theme/common/languages/fi.xml | 13 + gui/theme/common/portrait.xml | 1050 +++++++++++++++--------------- gui/theme/common/watch.xml | 1018 ++++++++++++++--------------- gui/theme/landscape_hdpi/ui.xml | 49 +- gui/theme/landscape_mdpi/ui.xml | 49 +- gui/theme/portrait_hdpi/ui.xml | 53 +- gui/theme/portrait_mdpi/ui.xml | 53 +- gui/theme/watch_mdpi/ui.xml | 6 +- gui/twmsg.cpp | 129 ++++ gui/twmsg.h | 97 +++ openrecoveryscript.cpp | 123 ++-- partition.cpp | 188 +++--- partitionmanager.cpp | 257 +++++--- partitions.hpp | 4 + twinstall.cpp | 18 +- twrp-functions.cpp | 45 +- twrp.cpp | 21 +- twrpDU.cpp | 7 +- twrpDigest.cpp | 10 +- twrpTar.cpp | 241 ++++--- 40 files changed, 4041 insertions(+), 2086 deletions(-) create mode 100644 gui/devices/common/res/languages/en.xml create mode 100644 gui/devices/common/res/languages/es.xml create mode 100644 gui/devices/common/res/languages/fi.xml create mode 100644 gui/gui.hpp mode change 100644 => 100755 gui/theme/common/landscape.xml create mode 100755 gui/theme/common/languages/en.xml create mode 100644 gui/theme/common/languages/es.xml create mode 100644 gui/theme/common/languages/fi.xml mode change 100644 => 100755 gui/theme/common/portrait.xml mode change 100644 => 100755 gui/theme/common/watch.xml create mode 100644 gui/twmsg.cpp create mode 100644 gui/twmsg.h diff --git a/Android.mk b/Android.mk index 33a43d8b4..61435c314 100644 --- a/Android.mk +++ b/Android.mk @@ -329,6 +329,11 @@ endif ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 22; echo $$?),0) LOCAL_CFLAGS += -DTW_USE_NEW_MINADBD endif +ifneq ($(TW_DEFAULT_LANGUAGE),) + LOCAL_CFLAGS += -DTW_DEFAULT_LANGUAGE=$(TW_DEFAULT_LANGUAGE) +else + LOCAL_CFLAGS += -DTW_DEFAULT_LANGUAGE=en +endif LOCAL_ADDITIONAL_DEPENDENCIES := \ dump_image \ diff --git a/data.cpp b/data.cpp index 9f2e6b5cb..6b0f97131 100644 --- a/data.cpp +++ b/data.cpp @@ -50,6 +50,7 @@ #include "find_file.hpp" #include "set_metadata.h" #include +#include "gui/gui.hpp" #define DEVID_MAX 64 #define HWID_MAX 32 @@ -229,7 +230,7 @@ void DataManager::get_device_id(void) { } strcpy(device_id, "serialno"); - LOGERR("=> device id not found, using '%s'\n", device_id); + LOGINFO("=> device id not found, using '%s'\n", device_id); mConstValues.insert(make_pair("device_id", device_id)); return; } @@ -616,8 +617,10 @@ void DataManager::SetBackupFolder() } } } else { - if (PartitionManager.Fstab_Processed() != 0) - LOGERR("Storage partition '%s' not found\n", str.c_str()); + if (PartitionManager.Fstab_Processed() != 0) { + LOGINFO("Storage partition '%s' not found\n", str.c_str()); + gui_err("unable_locate_storage=Unable to locate storage device."); + } } } @@ -890,6 +893,8 @@ void DataManager::SetDefaultValues() #endif mValues.insert(make_pair("tw_mount_system_ro", make_pair("2", 1))); mValues.insert(make_pair("tw_never_show_system_ro_page", make_pair("0", 1))); + mValues.insert(make_pair("tw_language", make_pair(EXPAND(TW_DEFAULT_LANGUAGE), 1))); + LOGINFO("LANG: %s\n", EXPAND(TW_DEFAULT_LANGUAGE)); pthread_mutex_unlock(&m_valuesLock); } @@ -1033,7 +1038,7 @@ void DataManager::Output_Version(void) } FILE *fp = fopen(Path.c_str(), "w"); if (fp == NULL) { - LOGERR("Unable to open '%s'.\n", Path.c_str()); + gui_msg(Msg(msg::kError, "error_opening_strerr=Error opening: '{1}' ({2})")(Path)(strerror(errno))); return; } strcpy(version, TW_VERSION_STR); @@ -1069,7 +1074,7 @@ void DataManager::ReadSettingsFile(void) { usleep(500000); if (!PartitionManager.Mount_Settings_Storage(false)) - LOGERR("Unable to mount %s when trying to read settings file.\n", settings_file); + gui_msg(Msg(msg::kError, "unable_to_mount=Unable to mount {1}")(settings_file)); } mkdir(mkdir_path, 0777); diff --git a/find_file.cpp b/find_file.cpp index 78db5343d..7f4191e4a 100644 --- a/find_file.cpp +++ b/find_file.cpp @@ -48,7 +48,7 @@ string Find_File::Find_Internal(const string& filename, const string& starting_p d = opendir(starting_path.c_str()); if (d == NULL) { - LOGERR("Find_File: Error opening '%s'\n", starting_path.c_str()); + LOGINFO("Find_File: Error opening '%s'\n", starting_path.c_str()); return ""; } diff --git a/gui/Android.mk b/gui/Android.mk index 66883a4fa..84f890ca7 100644 --- a/gui/Android.mk +++ b/gui/Android.mk @@ -28,7 +28,8 @@ LOCAL_SRC_FILES := \ mousecursor.cpp \ scrolllist.cpp \ patternpassword.cpp \ - textbox.cpp + textbox.cpp \ + twmsg.cpp ifneq ($(TWRP_CUSTOM_KEYBOARD),) LOCAL_SRC_FILES += $(TWRP_CUSTOM_KEYBOARD) @@ -107,6 +108,7 @@ ifeq ($(TW_CUSTOM_THEME),) ifeq ($(TWRP_NEW_THEME),true) TWRP_THEME_LOC := $(commands_recovery_local_path)/gui/theme/$(TW_THEME) TWRP_RES := $(commands_recovery_local_path)/gui/theme/common/fonts + TWRP_RES += $(commands_recovery_local_path)/gui/theme/common/languages TWRP_RES += $(commands_recovery_local_path)/gui/theme/common/$(word 1,$(subst _, ,$(TW_THEME))).xml # for future copying of used include xmls and fonts: # UI_XML := $(TWRP_THEME_LOC)/ui.xml diff --git a/gui/action.cpp b/gui/action.cpp index 46f4575d7..3e27dbb44 100644 --- a/gui/action.cpp +++ b/gui/action.cpp @@ -197,6 +197,7 @@ GUIAction::GUIAction(xml_node<>* node) ADD_ACTION(cancelbackup); ADD_ACTION(checkpartitionlifetimewrites); ADD_ACTION(mountsystemtoggle); + ADD_ACTION(setlanguage); // remember actions that run in the caller thread for (mapFunc::const_iterator it = mf.begin(); it != mf.end(); ++it) @@ -340,12 +341,12 @@ int GUIAction::NotifyVarChange(const std::string& varName, const std::string& va void GUIAction::simulate_progress_bar(void) { - gui_print("Simulating actions...\n"); + gui_msg("simulating=Simulating actions..."); for (int i = 0; i < 5; i++) { if (PartitionManager.stop_backup.get_value()) { DataManager::SetValue("tw_cancel_backup", 1); - gui_print("Backup Canceled.\n"); + gui_msg("backup_cancel=Backup Canceled."); DataManager::SetValue("ui_progress", 0); PartitionManager.stop_backup.set_value(0); return; @@ -381,10 +382,10 @@ int GUIAction::flash_zip(std::string filename, int* wipe_cache) { DataManager::SetValue("tw_operation", "Configuring TWRP"); DataManager::SetValue("tw_partition", ""); - gui_print("Configuring TWRP...\n"); + gui_msg("config_twrp=Configuring TWRP..."); if (TWFunc::Exec_Cmd("/sbin/installTwrp reinstall") < 0) { - gui_print("Unable to configure TWRP with this kernel.\n"); + gui_msg("config_twrp_err=Unable to configure TWRP with this kernel."); } } } @@ -587,12 +588,12 @@ int GUIAction::mount(std::string arg) if (!simulate) PartitionManager.usb_storage_enable(); else - gui_print("Simulating actions...\n"); + gui_msg("simulating=Simulating actions..."); } else if (!simulate) { PartitionManager.Mount_By_Path(arg, true); PartitionManager.Add_MTP_Storage(arg); } else - gui_print("Simulating actions...\n"); + gui_msg("simulating=Simulating actions..."); return 0; } @@ -602,12 +603,12 @@ int GUIAction::unmount(std::string arg) if (!simulate) PartitionManager.usb_storage_disable(); else - gui_print("Simulating actions...\n"); + gui_msg("simulating=Simulating actions..."); DataManager::SetValue(TW_ACTION_BUSY, 0); } else if (!simulate) { PartitionManager.UnMount_By_Path(arg, true); } else - gui_print("Simulating actions...\n"); + gui_msg("simulating=Simulating actions..."); return 0; } @@ -615,7 +616,7 @@ int GUIAction::restoredefaultsettings(std::string arg __unused) { operation_start("Restore Defaults"); if (simulate) // Simulated so that people don't accidently wipe out the "simulation is on" setting - gui_print("Simulating actions...\n"); + gui_msg("simulating=Simulating actions..."); else { DataManager::ResetDefaults(); PartitionManager.Update_System_Details(); @@ -636,7 +637,7 @@ int GUIAction::copylog(std::string arg __unused) TWFunc::copy_file("/tmp/recovery.log", dst.c_str(), 0755); tw_set_default_metadata(dst.c_str()); sync(); - gui_print("Copied recovery log to %s.\n", DataManager::GetCurrentStoragePath().c_str()); + gui_msg(Msg("copy_log=Copied recovery log to {1}.")(DataManager::GetCurrentStoragePath())); } else simulate_progress_bar(); operation_end(0); @@ -733,7 +734,7 @@ int GUIAction::overlay(std::string arg) int GUIAction::queuezip(std::string arg __unused) { if (zip_queue_index >= 10) { - gui_print("Maximum zip queue reached!\n"); + gui_msg("max_queue=Maximum zip queue reached!"); return 0; } DataManager::GetValue("tw_filename", zip_queue[zip_queue_index]); @@ -747,7 +748,7 @@ int GUIAction::queuezip(std::string arg __unused) int GUIAction::cancelzip(std::string arg __unused) { if (zip_queue_index <= 0) { - gui_print("Minimum zip queue reached!\n"); + gui_msg("min_queue=Minimum zip queue reached!"); return 0; } else { zip_queue_index--; @@ -925,7 +926,7 @@ int GUIAction::screenshot(std::string arg __unused) chmod(path, 0666); chown(path, uid, gid); - gui_print("Screenshot was saved to %s\n", path); + gui_msg(Msg("screenshot_saved=Screenshot was saved to %s")(path)); // blink to notify that the screenshow was taken gr_color(255, 255, 255, 255); @@ -933,7 +934,7 @@ int GUIAction::screenshot(std::string arg __unused) gr_flip(); gui_forceRender(); } else { - LOGERR("Failed to take a screenshot!\n"); + gui_err("screenshot_err=Failed to take a screenshot!"); } return 0; } @@ -959,7 +960,7 @@ int GUIAction::fileexists(std::string arg) void GUIAction::reinject_after_flash() { if (DataManager::GetIntValue(TW_HAS_INJECTTWRP) == 1 && DataManager::GetIntValue(TW_INJECT_AFTER_ZIP) == 1) { - gui_print("Injecting TWRP into boot image...\n"); + gui_msg("injecttwrp=Injecting TWRP into boot image..."); if (simulate) { simulate_progress_bar(); } else { @@ -970,7 +971,7 @@ void GUIAction::reinject_after_flash() string injectcmd = "injecttwrp --dump /tmp/backup_recovery_ramdisk.img /tmp/injected_boot.img --flash bd=" + Boot->Actual_Block_Device; TWFunc::Exec_Cmd(injectcmd); } - gui_print("TWRP injection complete.\n"); + gui_msg("done=Done."); } } } @@ -993,7 +994,7 @@ int GUIAction::flash(std::string arg) ret_val = flash_zip(zip_path, &wipe_cache); TWFunc::SetPerformanceMode(false); if (ret_val != 0) { - gui_print("Error flashing zip '%s'\n", zip_path.c_str()); + gui_msg(Msg(msg::kError, "zip_err=Error installing zip file '{1}'")(zip_path)); ret_val = 1; break; } @@ -1001,7 +1002,7 @@ int GUIAction::flash(std::string arg) zip_queue_index = 0; if (wipe_cache) { - gui_print("One or more zip requested a cache wipe\nWiping cache now.\n"); + gui_msg("zip_wipe_cache=One or more zip requested a cache wipe -- Wiping cache now."); PartitionManager.Wipe_By_Path("/cache"); } @@ -1063,7 +1064,7 @@ int GUIAction::wipe(std::string arg) LOGINFO("wipe_path '%s'\n", wipe_path.c_str()); if (wipe_path == "/and-sec") { if (!PartitionManager.Wipe_Android_Secure()) { - LOGERR("Unable to wipe android secure\n"); + gui_msg("and_sec_wipe_err=Unable to wipe android secure"); ret_val = false; break; } else { @@ -1071,7 +1072,7 @@ int GUIAction::wipe(std::string arg) } } else if (wipe_path == "DALVIK") { if (!PartitionManager.Wipe_Dalvik_Cache()) { - LOGERR("Failed to wipe dalvik\n"); + gui_err("dalvik_wipe_err=Failed to wipe dalvik"); ret_val = false; break; } else { @@ -1087,7 +1088,7 @@ int GUIAction::wipe(std::string arg) } if (!skip) { if (!PartitionManager.Wipe_By_Path(wipe_path)) { - LOGERR("Unable to wipe '%s'\n", wipe_path.c_str()); + gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(wipe_path)); ret_val = false; break; } else if (wipe_path == DataManager::GetSettingsStoragePath()) { @@ -1152,6 +1153,8 @@ int GUIAction::nandroid(std::string arg) if (arg == "backup") { string Backup_Name; DataManager::GetValue(TW_BACKUP_NAME, Backup_Name); + string auto_gen = gui_lookup("auto_gen", "(Auto Generate)"); + string curr_date = gui_lookup("curr_date", "(Current Date)"); if (Backup_Name == "(Auto Generate)" || Backup_Name == "(Current Date)" || Backup_Name == "0" || Backup_Name == "(" || PartitionManager.Check_Backup_Name(true) == 0) { ret = PartitionManager.Run_Backup(); } @@ -1159,7 +1162,7 @@ int GUIAction::nandroid(std::string arg) operation_end(1); return -1; } - DataManager::SetValue(TW_BACKUP_NAME, "(Auto Generate)"); + DataManager::SetValue(TW_BACKUP_NAME, auto_gen); } else if (arg == "restore") { string Restore_Name; DataManager::GetValue("tw_restore", Restore_Name); @@ -1178,7 +1181,7 @@ int GUIAction::nandroid(std::string arg) } else { DataManager::SetValue("tw_cancel_backup", 1); - gui_print("Backup Canceled.\n"); + gui_msg("backup_cancel=Backup Canceled."); ret = 0; } operation_end(ret); @@ -1242,7 +1245,7 @@ int GUIAction::partitionsd(std::string arg __unused) int allow_partition; DataManager::GetValue(TW_ALLOW_PARTITION_SDCARD, allow_partition); if (allow_partition == 0) { - gui_print("This device does not have a real SD Card!\nAborting!\n"); + gui_err("no_real_sdcard=This device does not have a real SD Card! Aborting!"); } else { if (!PartitionManager.Partition_SDCard()) ret_val = 1; // failed @@ -1332,7 +1335,7 @@ int GUIAction::terminalcommand(std::string arg) fp = popen(command.c_str(), "r"); if (fp == NULL) { - LOGERR("Error opening command to run.\n"); + LOGERR("Error opening command to run (%s).\n", strerror(errno)); } else { int fd = fileno(fp), has_data = 0, check = 0, keep_going = -1, bytes_read = 0; struct timeval timeout; @@ -1391,12 +1394,12 @@ int GUIAction::reinjecttwrp(std::string arg __unused) { int op_status = 0; operation_start("ReinjectTWRP"); - gui_print("Injecting TWRP into boot image...\n"); + gui_msg("injecttwrp=Injecting TWRP into boot image..."); if (simulate) { simulate_progress_bar(); } else { TWFunc::Exec_Cmd("injecttwrp --dump /tmp/backup_recovery_ramdisk.img /tmp/injected_boot.img --flash"); - gui_print("TWRP injection complete.\n"); + gui_msg("done=Done."); } operation_end(op_status); @@ -1462,7 +1465,7 @@ int GUIAction::adbsideload(std::string arg __unused) simulate_progress_bar(); operation_end(0); } else { - gui_print("Starting ADB sideload feature...\n"); + gui_msg("start_sideload=Starting ADB sideload feature..."); bool mtp_was_enabled = TWFunc::Toggle_MTP(false); // wait for the adb connection @@ -1471,7 +1474,7 @@ int GUIAction::adbsideload(std::string arg __unused) if (ret != 0) { if (ret == -2) - gui_print("You need adb 1.0.32 or newer to sideload to this device.\n"); + gui_msg("need_new_adb=You need adb 1.0.32 or newer to sideload to this device."); ret = 1; // failure } else { int wipe_cache = 0; @@ -1509,7 +1512,7 @@ int GUIAction::adbsideloadcancel(std::string arg __unused) { struct stat st; DataManager::SetValue("tw_has_cancel", 0); // Remove cancel button from gui - gui_print("Cancelling ADB sideload...\n"); + gui_msg("cancel_sideload=Cancelling ADB sideload..."); LOGINFO("Signaling child sideload process to exit.\n"); // Calling stat() on this magic filename signals the minadbd // subprocess to shut down. @@ -1543,7 +1546,7 @@ int GUIAction::openrecoveryscript(std::string arg __unused) // Run those first. int reboot = 0; if (TWFunc::Path_Exists(SCRIPT_FILE_TMP)) { - gui_print("Processing AOSP recovery commands...\n"); + gui_msg("running_recovery_commands=Running Recovery Commands"); if (OpenRecoveryScript::run_script_file() == 0) { reboot = 1; op_status = 0; @@ -1551,7 +1554,7 @@ int GUIAction::openrecoveryscript(std::string arg __unused) } // Check for the ORS file in /cache and attempt to run those commands. if (OpenRecoveryScript::check_for_script_file()) { - gui_print("Processing OpenRecoveryScript file...\n"); + gui_msg("running_ors=Running OpenRecoveryScript"); if (OpenRecoveryScript::run_script_file() == 0) { reboot = 1; op_status = 0; @@ -1640,7 +1643,6 @@ int GUIAction::repair(std::string arg __unused) if (PartitionManager.Repair_By_Path(part_path, true)) { op_status = 0; // success } else { - LOGERR("Error repairing file system.\n"); op_status = 1; // fail } } @@ -1662,7 +1664,6 @@ int GUIAction::resize(std::string arg __unused) if (PartitionManager.Resize_By_Path(part_path, true)) { op_status = 0; // success } else { - LOGERR("Error resizing file system.\n"); op_status = 1; // fail } } @@ -1685,7 +1686,7 @@ int GUIAction::changefilesystem(std::string arg __unused) if (PartitionManager.Wipe_By_Path(part_path, file_system)) { op_status = 0; // success } else { - LOGERR("Error changing file system.\n"); + gui_err("change_fs_err=Error changing file system."); op_status = 1; // fail } } @@ -1826,3 +1827,16 @@ int GUIAction::mountsystemtoggle(std::string arg) operation_end(op_status); return 0; } + +int GUIAction::setlanguage(std::string arg __unused) +{ + int op_status = 0; + + operation_start("Set Language"); + PageManager::LoadLanguage(DataManager::GetStrValue("tw_language")); + PageManager::RequestReload(); + op_status = 0; // success + + operation_end(op_status); + return 0; +} diff --git a/gui/console.cpp b/gui/console.cpp index 6b38d6137..b5204fb7c 100644 --- a/gui/console.cpp +++ b/gui/console.cpp @@ -1,3 +1,21 @@ +/* + Copyright 2015 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 . +*/ + // console.cpp - GUIConsole object #include @@ -24,10 +42,16 @@ extern "C" { #include "rapidxml.hpp" #include "objects.hpp" +#include "gui.hpp" +#include "twmsg.h" + +#define GUI_CONSOLE_BUFFER_SIZE 512 +size_t last_message_count = 0; +std::vector gMessages; -static std::vector gConsole; -static std::vector gConsoleColor; +std::vector gConsole; +std::vector gConsoleColor; static FILE* ors_file; extern "C" void __gui_print(const char *color, char *buf) @@ -58,22 +82,22 @@ extern "C" void __gui_print(const char *color, char *buf) gConsole.push_back(start); gConsoleColor.push_back(color); } - if (ors_file) { - fprintf(ors_file, "%s\n", buf); - fflush(ors_file); - } } extern "C" void gui_print(const char *fmt, ...) { - char buf[512]; // We're going to limit a single request to 512 bytes + char buf[GUI_CONSOLE_BUFFER_SIZE]; // We're going to limit a single request to 512 bytes va_list ap; va_start(ap, fmt); - vsnprintf(buf, 512, fmt, ap); + vsnprintf(buf, GUI_CONSOLE_BUFFER_SIZE, fmt, ap); va_end(ap); fputs(buf, stdout); + if (ors_file) { + fprintf(ors_file, "%s", buf); + fflush(ors_file); + } __gui_print("normal", buf); return; @@ -81,14 +105,18 @@ extern "C" void gui_print(const char *fmt, ...) extern "C" void gui_print_color(const char *color, const char *fmt, ...) { - char buf[512]; // We're going to limit a single request to 512 bytes + char buf[GUI_CONSOLE_BUFFER_SIZE]; // We're going to limit a single request to 512 bytes va_list ap; va_start(ap, fmt); - vsnprintf(buf, 512, fmt, ap); + vsnprintf(buf, GUI_CONSOLE_BUFFER_SIZE, fmt, ap); va_end(ap); fputs(buf, stdout); + if (ors_file) { + fprintf(ors_file, "%s", buf); + fflush(ors_file); + } __gui_print(color, buf); return; @@ -99,6 +127,70 @@ extern "C" void gui_set_FILE(FILE* f) ors_file = f; } +void gui_msg(const char* text) +{ + if (text) { + Message msg = Msg(text); + gui_msg(msg); + } +} + +void gui_warn(const char* text) +{ + if (text) { + Message msg = Msg(msg::kWarning, text); + gui_msg(msg); + } +} + +void gui_err(const char* text) +{ + if (text) { + Message msg = Msg(msg::kError, text); + gui_msg(msg); + } +} + +void gui_highlight(const char* text) +{ + if (text) { + Message msg = Msg(msg::kHighlight, text); + gui_msg(msg); + } +} + +void gui_msg(Message msg) +{ + std::string output = msg; + output += "\n"; + fputs(output.c_str(), stdout); + if (ors_file) { + fprintf(ors_file, "%s", output.c_str()); + fflush(ors_file); + } + gMessages.push_back(msg); +} + +void GUIConsole::Translate_Now() { + size_t message_count = gMessages.size(); + if (message_count <= last_message_count) + return; + + for (size_t m = last_message_count; m < message_count; m++) { + std::string message = gMessages[m]; + std::string color = "normal"; + if (gMessages[m].GetKind() == msg::kError) + color = "error"; + else if (gMessages[m].GetKind() == msg::kHighlight) + color = "highlight"; + else if (gMessages[m].GetKind() == msg::kWarning) + color = "warning"; + gConsole.push_back(message); + gConsoleColor.push_back(color); + } + last_message_count = message_count; +} + GUIConsole::GUIConsole(xml_node<>* node) : GUIScrollList(node) { xml_node<>* child; @@ -160,6 +252,7 @@ int GUIConsole::RenderSlideout(void) int GUIConsole::RenderConsole(void) { + Translate_Now(); AddLines(&gConsole, &gConsoleColor, &mLastCount, &rConsole, &rConsoleColor); GUIScrollList::Render(); diff --git a/gui/devices/common/res/languages/en.xml b/gui/devices/common/res/languages/en.xml new file mode 100644 index 000000000..c11176a71 --- /dev/null +++ b/gui/devices/common/res/languages/en.xml @@ -0,0 +1,226 @@ + + + + English + + + + + + + + + + System + System Image + Vendor + Vendor Image + Boot + Recovery + Cache + Data + SDCard + Internal Storage + Micro SDCard + USB OTG + Android Secure + Dalvik / ART Cache + + SD-EXT + + + Install + + + Kernel does not have support for reading SELinux contexts. + Full SELinux support is present. + No SELinux support (no libselinux). + MTP Enabled + MTP Crashed, not starting MTP on boot. + Rebooting... + Successfully decrypted with default password. + Unable to decrypt with default password. You may need to perform a Format Data. + Generating MD5 + + * Generating md5... + * MD5 Created. + * MD5 Error! + * Error computing MD5. + * MD5 Created. + (Current Date) + (Auto Generate) + Unable to locate '{1}' partition for backup calculations. + No partitions selected for backup. + * Total number of partitions to back up: {1} + * Total size of all data: {1}MB + * Available space: {1}MB + Unable to locate storage device. + Not enough free space on storage. + [BACKUP STARTED] + * Backup Folder: {1} + Failed to make backup folder. + Average backup rate for file systems: {1} MB/sec + Average backup rate for imaged drives: {1} MB/sec + [{1} MB TOTAL BACKED UP] + [BACKUP COMPLETED IN {1} SECONDS] + [RESTORE STARTED] + Restore folder: '%s' + + [{1} done ({2} seconds)] + Verifying MD5 + Skipping MD5 check based on user setting. + Calculating restore details... + Cannot restore %s -- mounted read only. + Unable to locate '{1}' partition for restoring. + No partitions selected for restore. + Restoring {1} partitions... + Total restore size is {1}MB + Updating System Details + [RESTORE COMPLETED IN {1} SECONDS] + + Error opening: '{1}' ({2}) + Unable to locate partition by backup name: '{1}' + Unable to find partition for path '{1}' + Updating partition details... + ...done + Wiping Dalvik Cache Directories... + Cleaned: {1}... + -- Dalvik Cache Directories Wipe Complete! + No android secure partitions found. + Unable to locate {1}. + Wiping internal storage -- /data/media... + Unable to mount {1} + Unable to mount internal_storage + Unable to mount storage + Failed to decrypt data. + No crypto support was compiled into this build. + Data successfully decrypted, new block device: '{1}' + Done. + Partitioning SD Card... + Unable to locate device to partition. + EXT + Swap size is larger than sdcard size. + Removing partition table... + Unable to remove partition table. + Creating {1} partition... + Unable to create {1} partition. + Formatting sd-ext as {1}... + Partitioning complete. + Unable to open '{1}'. + MTP already enabled + Failed to enable MTP + MTP support not included + [IMAGE FLASH STARTED] + Image to flash: '{1}' + Unable to locate '{1}' partition for flashing. + No partitions selected for flashing. + Too many partitions selected for flashing. + Invalid flash partition specified. + IMAGE FLASH COMPLETED] + Wiping {1} + {1} does not exist! Cannot repair! + Unable to repair {1}. + Could not mount /data and unable to find crypto footer. + + Can not create '{1}' folder ({2}). + + Failed to mount '{1}' ({2}) + + Failed to unmount '{1}' ({2}) + Cannot resize {1}. + Repairing {1} before resizing. + Unable to resize {1}. + No md5 file found for '{1}'. Please unselect Enable MD5 verification to restore. + MD5 failed to match on '{1}'. + Restoring + You may need to reboot recovery to be able to use /data again. + Unable to format to remove encryption. + Formatting {1} using {2}... + Unable to wipe {1}. + Removing all files under '{1}' + Wiping data without wiping /data/media ... + Backing up {1}... + Backing Up + Backup file size for '%s' is 0 bytes. + WARNING: This /data backup was made with {1} file system! The backup may not boot unless you change back to {1}. + Restoring {1}... + Restoring + Unable to recreate {1} folder. + Size of image is larger than target device + Flashing {1}... + Backup folder set to '{1}' + Unable to locate backup '{1}' + Setting restore options: '{1}': + MD5 check skip is on + Unable to use OpenRecoveryScript to restore an encrypted backup. + Mounting + Unmounting + Mounted '{1}' + Unmounted '{1}' + Setting '{1}' to '{2}' + Setting '{1}' to empty + Making Directory + Making directory: '{1}' + Running Command + ADB Sideload + Starting ADB sideload feature... + You need adb 1.0.32 or newer to sideload to this device. + No password provided. + Done processing script file + Injecting TWRP into boot image... + Error installing zip file '{1}' + Installing zip file '{1}' + Setting backup options: + Compression is on + MD5 Generation is off + Backup Failed + Backup complete! + Running Recovery Commands + Recovery Commands Complete + Running OpenRecoveryScript + OpenRecoveryScript Complete + Could not find '{1}' in the zip file. + Checking for MD5 file... + Failed to map file '{1}' + Verifying zip signature... + Zip signature verification failed! + Zip signature verified successfully. + Zip file is corrupt! + Skipping MD5 check: no MD5 file found + MD5 does not match + MD5 matched + {1} process ended with signal: {2} + {1} process ended with ERROR: {2} + Installing HTC Dumlock to system... + Restoring original boot... + Reflashing recovery to boot... + Running {1} script... + Renamed stock recovery file in /system to prevent the stock ROM from replacing TWRP. + Breaking backup file into multiple archives... + Error creating backup. + Error during restore process. + Splitting thread ID {1} into archive {2} + %llu of %llu files, %i%% + %lluMB of %lluMB, %i%% + Attempting to decrypt data partition via command line. + Failed to load base packages. + Simulating actions... + Backup Canceled. + Configuring TWRP... + Unable to configure TWRP with this kernel. + Copied recovery log to {1}. + Maximum zip queue reached! + Minimum zip queue reached! + Failed to take a screenshot! + One or more zip requested a cache wipe -- Wiping cache now. + Unable to wipe android secure + Failed to wipe dalvik + (Auto Generate) + (Current Date) + Backup name is too long. + Backup name '{1}' contains invalid character: '{1}' + A backup with this name already exists. + This device does not have a real SD Card! Aborting! + Cancelling ADB sideload... + Error changing file system. + + diff --git a/gui/devices/common/res/languages/es.xml b/gui/devices/common/res/languages/es.xml new file mode 100644 index 000000000..ad5c54a7b --- /dev/null +++ b/gui/devices/common/res/languages/es.xml @@ -0,0 +1,13 @@ + + + + EspaƱol + + + + + + + Instalar + + diff --git a/gui/devices/common/res/languages/fi.xml b/gui/devices/common/res/languages/fi.xml new file mode 100644 index 000000000..0c4f5bc87 --- /dev/null +++ b/gui/devices/common/res/languages/fi.xml @@ -0,0 +1,13 @@ + + + + Suomi + + + + + + + Asenna + + diff --git a/gui/gui.cpp b/gui/gui.cpp index 31b61be6d..78e8a7e0c 100644 --- a/gui/gui.cpp +++ b/gui/gui.cpp @@ -517,13 +517,19 @@ static void ors_command_read() fprintf(orsout, "%s", result); LOGINFO("Command cannot be performed, operation in progress.\n"); } else { - if (gui_console_only() == 0) { + if (strlen(command) == 11 && strncmp(command, "dumpstrings", 11) == 0) { + // This cannot be done safely with gui_console_only because gui_console_only updates mCurrentSet + // which makes the resources that we are trying to read unreachable. + gui_set_FILE(orsout); + PageManager::GetResources()->DumpStrings(); + gui_set_FILE(NULL); + } else if (gui_console_only() == 0) { LOGINFO("Console started successfully\n"); gui_set_FILE(orsout); if (strlen(command) > 11 && strncmp(command, "runscript", 9) == 0) { char* filename = command + 11; if (OpenRecoveryScript::copy_script_file(filename) == 0) { - LOGERR("Unable to copy script file\n"); + LOGINFO("Unable to copy script file\n"); } else { OpenRecoveryScript::run_script_file(); } @@ -534,7 +540,7 @@ static void ors_command_read() gui_print("%s = %s\n", varname, temp.c_str()); } else if (strlen(command) > 9 && strncmp(command, "decrypt", 7) == 0) { char* pass = command + 8; - gui_print("Attempting to decrypt data partition via command line.\n"); + gui_msg("decrypt_cmd=Attempting to decrypt data partition via command line."); if (PartitionManager.Decrypt_Device(pass) == 0) { set_page_done = 1; } @@ -753,15 +759,39 @@ std::string gui_parse_text(std::string str) { // This function parses text for DataManager values encompassed by %value% in the XML // and string resources (%@resource_name%) - size_t pos = 0; + size_t pos = 0, next, end; while (1) { - size_t next = str.find('%', pos); + next = str.find("{@", pos); + if (next == std::string::npos) + break; + + end = str.find('}', next + 1); + if (end == std::string::npos) + break; + + std::string var = str.substr(next + 2, (end - next) - 2); + str.erase(next, (end - next) + 1); + + size_t default_loc = var.find('=', 0); + std::string lookup; + if (default_loc == std::string::npos) { + str.insert(next, PageManager::GetResources()->FindString(var)); + } else { + lookup = var.substr(0, default_loc); + std::string default_string = var.substr(default_loc + 1, var.size() - default_loc - 1); + str.insert(next, PageManager::GetResources()->FindString(lookup, default_string)); + } + } + pos = 0; + while (1) + { + next = str.find('%', pos); if (next == std::string::npos) return str; - size_t end = str.find('%', next + 1); + end = str.find('%', next + 1); if (end == std::string::npos) return str; @@ -787,6 +817,10 @@ std::string gui_parse_text(std::string str) } } +std::string gui_lookup(const std::string& resource_name, const std::string& default_value) { + return PageManager::GetResources()->FindString(resource_name, default_value); +} + extern "C" int gui_init(void) { gr_init(); @@ -828,7 +862,7 @@ extern "C" int gui_loadResources(void) { if (PageManager::LoadPackage("TWRP", TWRES "ui.xml", "decrypt")) { - LOGERR("Failed to load base packages.\n"); + gui_err("base_pkg_err=Failed to load base packages."); goto error; } else @@ -849,10 +883,9 @@ extern "C" int gui_loadResources(void) retry_count--; } - if (!PartitionManager.Mount_Settings_Storage(false)) + if (!PartitionManager.Mount_Settings_Storage(true)) { - LOGERR("Unable to mount %s during GUI startup.\n", - theme_path.c_str()); + LOGINFO("Unable to mount %s during GUI startup.\n", theme_path.c_str()); check = 1; } } @@ -863,7 +896,7 @@ extern "C" int gui_loadResources(void) #endif // ifndef TW_OEM_BUILD if (PageManager::LoadPackage("TWRP", TWRES "ui.xml", "main")) { - LOGERR("Failed to load base packages.\n"); + gui_err("base_pkg_err=Failed to load base packages."); goto error; } #ifndef TW_OEM_BUILD @@ -886,7 +919,7 @@ extern "C" int gui_loadCustomResources(void) { #ifndef TW_OEM_BUILD if (!PartitionManager.Mount_Settings_Storage(false)) { - LOGERR("Unable to mount settings storage during GUI startup.\n"); + LOGINFO("Unable to mount settings storage during GUI startup.\n"); return -1; } @@ -898,7 +931,7 @@ extern "C" int gui_loadCustomResources(void) if (PageManager::ReloadPackage("TWRP", theme_path)) { // Custom theme failed to load, try to load stock theme if (PageManager::ReloadPackage("TWRP", TWRES "ui.xml")) { - LOGERR("Failed to load base packages.\n"); + gui_err("base_pkg_err=Failed to load base packages."); goto error; } } diff --git a/gui/gui.hpp b/gui/gui.hpp new file mode 100644 index 000000000..afcd9b0a9 --- /dev/null +++ b/gui/gui.hpp @@ -0,0 +1,33 @@ +/* + Copyright 2015 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 . +*/ + +#ifndef _GUI_HPP_HEADER +#define _GUI_HPP_HEADER + +#include "twmsg.h" + +void gui_msg(const char* text); +void gui_warn(const char* text); +void gui_err(const char* text); +void gui_highlight(const char* text); +void gui_msg(Message msg); + +std::string gui_parse_text(std::string inText); +std::string gui_lookup(const std::string& resource_name, const std::string& default_value); + +#endif //_GUI_HPP_HEADER diff --git a/gui/listbox.cpp b/gui/listbox.cpp index d2973bb6e..eeddff954 100644 --- a/gui/listbox.cpp +++ b/gui/listbox.cpp @@ -26,6 +26,9 @@ extern "C" { #include "rapidxml.hpp" #include "objects.hpp" #include "../data.hpp" +#include "pages.hpp" + +extern std::vector Language_List; GUIListBox::GUIListBox(xml_node<>* node) : GUIScrollList(node) { @@ -56,6 +59,21 @@ GUIListBox::GUIListBox(xml_node<>* node) : GUIScrollList(node) DataManager::SetValue(mVariable, attr->value()); // Get the currently selected value for the list DataManager::GetValue(mVariable, currentValue); + if (mVariable == "tw_language") { + std::vector::iterator iter; + for (iter = Language_List.begin(); iter != Language_List.end(); iter++) { + ListItem data; + data.displayName = (*iter).displayvalue; + data.variableValue = (*iter).filename; + data.action = NULL; + if (currentValue == (*iter).filename) { + data.selected = 1; + DataManager::SetValue("tw_language_display", (*iter).displayvalue); + } else + data.selected = 0; + mListItems.push_back(data); + } + } } else allowSelection = false; // allows using listbox as a read-only list or menu diff --git a/gui/objects.hpp b/gui/objects.hpp index 3d217c45f..438905b96 100644 --- a/gui/objects.hpp +++ b/gui/objects.hpp @@ -43,6 +43,15 @@ using namespace rapidxml; #define TW_Y_OFFSET 0 #endif +struct translate_later_struct { + std::string resource_name; // Name of the string resource for looking up + std::string default_value; // Default in case we don't find the string resource + std::string color; // Color for the console... normal, highlight, warning, error + std::string format; // Formatted extra variables like %i, %s + std::string text; // Final, translated, formatted text + bool inline_format; // Indicates if the final text includes an inlined format variable +}; + class RenderObject { public: @@ -356,6 +365,7 @@ protected: int cancelbackup(std::string arg); int checkpartitionlifetimewrites(std::string arg); int mountsystemtoggle(std::string arg); + int setlanguage(std::string arg); int simulate; }; @@ -734,6 +744,7 @@ public: virtual size_t GetItemCount(); virtual void RenderItem(size_t itemindex, int yPos, bool selected); virtual void NotifySelect(size_t item_selected); + static void Translate_Now(); protected: enum SlideoutState { diff --git a/gui/pages.cpp b/gui/pages.cpp index 0511b1ab5..13f3bd7b5 100644 --- a/gui/pages.cpp +++ b/gui/pages.cpp @@ -33,6 +33,7 @@ #include #include #include "../twrp-functions.hpp" +#include "../partitions.hpp" #include @@ -50,6 +51,14 @@ extern "C" { extern int gGuiRunning; +// From ../twrp.cpp +extern bool datamedia; + +// From console.cpp +extern size_t last_message_count; +extern std::vector gConsole; +extern std::vector gConsoleColor; + std::map PageManager::mPageSets; PageSet* PageManager::mCurrentSet; PageSet* PageManager::mBaseSet = NULL; @@ -57,6 +66,7 @@ MouseCursor *PageManager::mMouseCursor = NULL; HardwareKeyboard *PageManager::mHardwareKeyboard = NULL; bool PageManager::mReloadTheme = false; std::string PageManager::mStartPage = "main"; +std::vector Language_List; int tw_x_offset = 0; int tw_y_offset = 0; @@ -670,7 +680,48 @@ PageSet::~PageSet() delete mResources; } -int PageSet::Load(ZipArchive* package, char* xmlFile) +int PageSet::LoadLanguage(char* languageFile, ZipArchive* package) +{ + xml_document<> lang; + xml_node<>* parent; + xml_node<>* child; + std::string resource_source; + + 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 + return -1; + lang.clear(); + return 0; +} + +int PageSet::Load(ZipArchive* package, char* xmlFile, char* languageFile) { xml_document<> mDoc; xml_node<>* parent; @@ -735,10 +786,14 @@ int PageSet::Load(ZipArchive* package, char* xmlFile) } else { LOGINFO("XML contains no details tag, no scaling will be applied.\n"); } + + if (languageFile) + LoadLanguage(languageFile, package); + LOGINFO("Loading resources...\n"); child = parent->first_node("resources"); if (child) - mResources->LoadResources(child, package); + mResources->LoadResources(child, package, "theme"); LOGINFO("Loading variables...\n"); child = parent->first_node("variables"); @@ -831,7 +886,7 @@ int PageSet::CheckInclude(ZipArchive* package, xml_document<> *parentDoc) LOGINFO("Loading included resources...\n"); child = parent->first_node("resources"); if (child) - mResources->LoadResources(child, package); + mResources->LoadResources(child, package, "theme"); LOGINFO("Loading included variables...\n"); child = parent->first_node("variables"); @@ -1121,6 +1176,11 @@ int PageSet::NotifyVarChange(std::string varName, std::string 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; @@ -1181,12 +1241,96 @@ char* PageManager::LoadFileToBuffer(std::string filename, ZipArchive* package) { 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/"); + } +} + +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) { int fd; ZipArchive zip, *pZip = NULL; long len; char* xmlFile = NULL; + char* languageFile = NULL; PageSet* pageSet = NULL; int ret; MemMapping map; @@ -1201,6 +1345,8 @@ int PageManager::LoadPackage(std::string name, std::string package, std::string LOGINFO("Load XML directly\n"); tw_x_offset = TW_X_OFFSET; tw_y_offset = TW_Y_OFFSET; + LoadLanguageList(NULL); + languageFile = LoadFileToBuffer(TWRES "languages/en.xml", NULL); } else { @@ -1220,6 +1366,8 @@ int PageManager::LoadPackage(std::string name, std::string package, std::string } pZip = &zip; package = "ui.xml"; + LoadLanguageList(pZip); + languageFile = LoadFileToBuffer("languages/en.xml", pZip); } xmlFile = LoadFileToBuffer(package, pZip); @@ -1230,8 +1378,11 @@ int PageManager::LoadPackage(std::string name, std::string package, std::string // Before loading, mCurrentSet must be the loading package so we can find resources pageSet = mCurrentSet; mCurrentSet = new PageSet(xmlFile); - - ret = mCurrentSet->Load(pZip, xmlFile); + ret = mCurrentSet->Load(pZip, xmlFile, languageFile); + if (languageFile) { + free(languageFile); + languageFile = NULL; + } if (ret == 0) { mCurrentSet->SetPage(startpage); @@ -1253,6 +1404,8 @@ int PageManager::LoadPackage(std::string name, std::string package, std::string sysReleaseMap(&map); } free(xmlFile); + if (languageFile) + free(languageFile); return ret; error: @@ -1313,7 +1466,7 @@ int PageManager::ReloadPackage(std::string name, std::string package) if (LoadPackage(name, package, mStartPage) != 0) { - LOGERR("Failed to load package '%s'.\n", package.c_str()); + LOGINFO("Failed to load package '%s'.\n", package.c_str()); mPageSets.insert(std::pair(name, set)); return -1; } @@ -1322,6 +1475,7 @@ int PageManager::ReloadPackage(std::string name, std::string package) if (mBaseSet == set) mBaseSet = mCurrentSet; delete set; + GUIConsole::Translate_Now(); return 0; } @@ -1364,6 +1518,18 @@ int PageManager::RunReload() { 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 + last_message_count = 0; + gConsole.clear(); + gConsoleColor.clear(); + return ret_val; } @@ -1511,6 +1677,12 @@ 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) diff --git a/gui/pages.hpp b/gui/pages.hpp index 018c2cab2..5dcc9e0b5 100644 --- a/gui/pages.hpp +++ b/gui/pages.hpp @@ -6,7 +6,9 @@ #include "../minzip/Zip.h" #include #include +#include #include "rapidxml.hpp" +#include "gui.hpp" using namespace rapidxml; enum TOUCH_STATE { @@ -27,12 +29,18 @@ struct COLOR { : red(r), green(g), blue(b), alpha(a) {} }; +struct language_struct { + std::string filename; + std::string displayvalue; +}; + +extern std::vector Language_List; + // Utility Functions int ConvertStrToColor(std::string str, COLOR* color); int gui_forceRender(void); int gui_changePage(std::string newPage); int gui_changeOverlay(std::string newPage); -std::string gui_parse_text(string inText); class Resource; class ResourceManager; @@ -82,7 +90,8 @@ public: virtual ~PageSet(); public: - int Load(ZipArchive* package, char* xmlFile); + int LoadLanguage(char* languageFile, ZipArchive* package); + int Load(ZipArchive* package, char* xmlFile, char* languageFile); int CheckInclude(ZipArchive* package, xml_document<> *parentDoc); Page* FindPage(std::string name); @@ -103,6 +112,7 @@ public: int NotifyVarChange(std::string varName, std::string value); std::vector*> styles; + void AddStringResource(std::string resource_source, std::string resource_name, std::string value); protected: int LoadPages(xml_node<>* pages); @@ -121,6 +131,8 @@ class PageManager public: // Used by GUI static char* LoadFileToBuffer(std::string filename, ZipArchive* package); + static void LoadLanguageList(ZipArchive* package); + static void LoadLanguage(std::string filename); static int LoadPackage(std::string name, std::string package, std::string startpage); static PageSet* SelectPackage(std::string name); static int ReloadPackage(std::string name, std::string package); @@ -155,9 +167,14 @@ public: static HardwareKeyboard *GetHardwareKeyboard(); static xml_node<>* FindStyle(std::string name); + static void AddStringResource(std::string resource_source, std::string resource_name, std::string value); protected: static PageSet* FindPackage(std::string name); + static void LoadLanguageListDir(std::string dir); + static void Translate_Partition(const char* path, const char* resource_name, const char* default_value); + static void Translate_Partition(const char* path, const char* resource_name, const char* default_value, const char* storage_resource_name, const char* storage_default_value); + static void Translate_Partition_Display_Names(); protected: static std::map mPageSets; diff --git a/gui/resources.cpp b/gui/resources.cpp index 253d438f4..a8f41991b 100644 --- a/gui/resources.cpp +++ b/gui/resources.cpp @@ -99,6 +99,13 @@ void Resource::CheckAndScaleImage(gr_surface source, gr_surface* destination, in FontResource::FontResource(xml_node<>* node, ZipArchive* pZip) : Resource(node, pZip) +{ + origFontSize = 0; + origFont = NULL; + LoadFont(node, pZip); +} + +void FontResource::LoadFont(xml_node<>* node, ZipArchive* pZip) { std::string file; xml_attribute<>* attr; @@ -116,12 +123,21 @@ FontResource::FontResource(xml_node<>* node, ZipArchive* pZip) if(file.size() >= 4 && file.compare(file.size()-4, 4, ".ttf") == 0) { m_type = TYPE_TTF; + int font_size = 0; + + if (origFontSize != 0) { + attr = node->first_attribute("scale"); + if (attr == NULL) + return; + font_size = origFontSize * atoi(attr->value()) / 100; + } else { + attr = node->first_attribute("size"); + if (attr == NULL) + return; + font_size = scale_theme_min(atoi(attr->value())); + origFontSize = font_size; + } - attr = node->first_attribute("size"); - if(!attr) - return; - - int size = scale_theme_min(atoi(attr->value())); int dpi = 300; attr = node->first_attribute("dpi"); @@ -130,13 +146,13 @@ FontResource::FontResource(xml_node<>* node, ZipArchive* pZip) if (ExtractResource(pZip, "fonts", file, "", TMP_RESOURCE_NAME) == 0) { - mFont = gr_ttf_loadFont(TMP_RESOURCE_NAME, size, dpi); + mFont = gr_ttf_loadFont(TMP_RESOURCE_NAME, font_size, dpi); unlink(TMP_RESOURCE_NAME); } else { file = std::string(TWRES "fonts/") + file; - mFont = gr_ttf_loadFont(file.c_str(), size, dpi); + mFont = gr_ttf_loadFont(file.c_str(), font_size, dpi); } } else @@ -145,13 +161,28 @@ FontResource::FontResource(xml_node<>* node, ZipArchive* pZip) } } -FontResource::~FontResource() -{ +void FontResource::DeleteFont() { if(mFont) - { - if(m_type == TYPE_TTF) - gr_ttf_freeFont(mFont); + gr_ttf_freeFont(mFont); + mFont = NULL; + if(origFont) + gr_ttf_freeFont(origFont); + origFont = NULL; +} + +void FontResource::Override(xml_node<>* node, ZipArchive* pZip) { + if (!origFont) { + origFont = mFont; + } else if (mFont) { + gr_ttf_freeFont(mFont); + mFont = NULL; } + LoadFont(node, pZip); +} + +FontResource::~FontResource() +{ + DeleteFont(); } ImageResource::ImageResource(xml_node<>* node, ZipArchive* pZip) @@ -255,17 +286,58 @@ AnimationResource* ResourceManager::FindAnimation(const std::string& name) const std::string ResourceManager::FindString(const std::string& name) const { - std::map::const_iterator it = mStrings.find(name); - if (it != mStrings.end()) - return it->second; + if (this != NULL) { + std::map::const_iterator it = mStrings.find(name); + if (it != mStrings.end()) + return it->second.value; + LOGERR("String resource '%s' not found. No default value.\n", name.c_str()); + PageManager::AddStringResource("NO DEFAULT", name, "[" + name + ("]")); + } else { + LOGINFO("String resources not loaded when looking for '%s'. No default value.\n", name.c_str()); + } return "[" + name + ("]"); } +std::string ResourceManager::FindString(const std::string& name, const std::string& default_string) const +{ + if (this != NULL) { + std::map::const_iterator it = mStrings.find(name); + if (it != mStrings.end()) + return it->second.value; + LOGERR("String resource '%s' not found. Using default value.\n", name.c_str()); + PageManager::AddStringResource("DEFAULT", name, default_string); + } else { + LOGINFO("String resources not loaded when looking for '%s'. Using default value.\n", name.c_str()); + } + return default_string; +} + +void ResourceManager::DumpStrings() const +{ + if (this == NULL) { + gui_print("No string resources\n"); + return; + } + std::map::const_iterator it; + gui_print("Dumping all strings:\n"); + for (it = mStrings.begin(); it != mStrings.end(); it++) + gui_print("source: %s: '%s' = '%s'\n", it->second.source.c_str(), it->first.c_str(), it->second.value.c_str()); + gui_print("Done dumping strings\n"); +} + ResourceManager::ResourceManager() { } -void ResourceManager::LoadResources(xml_node<>* resList, ZipArchive* pZip) +void ResourceManager::AddStringResource(std::string resource_source, std::string resource_name, std::string value) +{ + string_resource_struct res; + res.source = resource_source; + res.value = value; + mStrings[resource_name] = res; +} + +void ResourceManager::LoadResources(xml_node<>* resList, ZipArchive* pZip, std::string resource_source) { if (!resList) return; @@ -290,6 +362,26 @@ void ResourceManager::LoadResources(xml_node<>* resList, ZipArchive* pZip) delete res; } } + else if (type == "fontoverride") + { + if (mFonts.size() != 0 && child && child->first_attribute("name")) { + string FontName = child->first_attribute("name")->value(); + size_t font_count = mFonts.size(), i; + bool found = false; + + for (i = 0; i < font_count; i++) { + if (mFonts[i]->GetName() == FontName) { + mFonts[i]->Override(child, pZip); + found = true; + break; + } + } + if (!found) { + LOGERR("Unable to locate font '%s' for override.\n", FontName.c_str()); + } + } else if (mFonts.size() != 0) + LOGERR("Unable to locate font name for type fontoverride.\n"); + } else if (type == "image") { ImageResource* res = new ImageResource(child, pZip); @@ -312,9 +404,12 @@ void ResourceManager::LoadResources(xml_node<>* resList, ZipArchive* pZip) } else if (type == "string") { - if (xml_attribute<>* attr = child->first_attribute("name")) - mStrings[attr->value()] = child->value(); - else + if (xml_attribute<>* attr = child->first_attribute("name")) { + string_resource_struct res; + res.source = resource_source; + res.value = child->value(); + mStrings[attr->value()] = res; + } else error = true; } else diff --git a/gui/resources.hpp b/gui/resources.hpp index 0eb32674d..ce06d9679 100644 --- a/gui/resources.hpp +++ b/gui/resources.hpp @@ -6,6 +6,7 @@ #include #include #include +#include "rapidxml.hpp" struct ZipArchive; @@ -49,10 +50,19 @@ public: public: void* GetResource() { return this ? mFont : NULL; } int GetHeight() { return gr_getMaxFontHeight(this ? mFont : NULL); } + void Override(xml_node<>* node, ZipArchive* pZip); protected: void* mFont; Type m_type; + +private: + void LoadFont(xml_node<>* node, ZipArchive* pZip); + void DeleteFont(); + +private: + int origFontSize; + void* origFont; }; class ImageResource : public Resource @@ -92,19 +102,26 @@ class ResourceManager public: ResourceManager(); virtual ~ResourceManager(); - void LoadResources(xml_node<>* resList, ZipArchive* pZip); + void AddStringResource(std::string resource_source, std::string resource_name, std::string value); + void LoadResources(xml_node<>* resList, ZipArchive* pZip, std::string resource_source); public: FontResource* FindFont(const std::string& name) const; ImageResource* FindImage(const std::string& name) const; AnimationResource* FindAnimation(const std::string& name) const; std::string FindString(const std::string& name) const; + std::string FindString(const std::string& name, const std::string& default_string) const; + void DumpStrings() const; private: + struct string_resource_struct { + std::string value; + std::string source; + }; std::vector mFonts; std::vector mImages; std::vector mAnimations; - std::map mStrings; + std::map mStrings; }; #endif // _RESOURCE_HEADER diff --git a/gui/theme/common/landscape.xml b/gui/theme/common/landscape.xml old mode 100644 new mode 100755 index a6b38a25f..938c8548d --- a/gui/theme/common/landscape.xml +++ b/gui/theme/common/landscape.xml @@ -193,7 +193,7 @@ - Team Win Recovery Project + {@twrp_header=Team Win Recovery Project} @@ -204,7 +204,7 @@ @@ -261,25 +261,25 @@ - Install Zip + {@install_zip_hdr=Install Zip} - Install Image + {@install_image_hdr=Install Image} - Select File from %tw_storage_display_name% (%tw_storage_free_size% MB) + {@select_file_from_storage=Select File from %tw_storage_display_name% (%tw_storage_free_size% MB)}