diff options
Diffstat (limited to '')
-rw-r--r-- | applypatch/Android.mk | 1 | ||||
-rw-r--r-- | applypatch/imgdiff.cpp | 25 | ||||
-rw-r--r-- | recovery.cpp | 3 | ||||
-rw-r--r-- | screen_ui.cpp | 234 | ||||
-rw-r--r-- | screen_ui.h | 8 | ||||
-rw-r--r-- | stub_ui.h | 2 | ||||
-rw-r--r-- | tests/component/imgdiff_test.cpp | 80 | ||||
-rw-r--r-- | tests/component/verifier_test.cpp | 70 | ||||
-rw-r--r-- | ui.cpp | 67 | ||||
-rw-r--r-- | ui.h | 21 | ||||
-rw-r--r-- | wear_ui.cpp | 71 | ||||
-rw-r--r-- | wear_ui.h | 4 |
12 files changed, 335 insertions, 251 deletions
diff --git a/applypatch/Android.mk b/applypatch/Android.mk index 61e110617..ec3c6ee38 100644 --- a/applypatch/Android.mk +++ b/applypatch/Android.mk @@ -124,6 +124,7 @@ libimgdiff_cflags := \ libimgdiff_static_libraries := \ libbsdiff \ + libbase \ libz # libimgdiff (static library) diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp index 18a15a164..62de726a4 100644 --- a/applypatch/imgdiff.cpp +++ b/applypatch/imgdiff.cpp @@ -124,6 +124,7 @@ #include "applypatch/imgdiff.h" #include <errno.h> +#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -131,6 +132,9 @@ #include <sys/types.h> #include <unistd.h> +#include <android-base/file.h> +#include <android-base/unique_fd.h> + #include <bsdiff.h> #include <zlib.h> @@ -382,19 +386,12 @@ unsigned char* ReadImage(const char* filename, int* num_chunks, ImageChunk** chu } size_t sz = static_cast<size_t>(st.st_size); - unsigned char* img = static_cast<unsigned char*>(malloc(sz + 4)); - FILE* f = fopen(filename, "rb"); - if (fread(img, 1, sz, f) != sz) { + unsigned char* img = static_cast<unsigned char*>(malloc(sz)); + android::base::unique_fd fd(open(filename, O_RDONLY)); + if (!android::base::ReadFully(fd, img, sz)) { printf("failed to read \"%s\" %s\n", filename, strerror(errno)); - fclose(f); - return NULL; + return nullptr; } - fclose(f); - - // append 4 zero bytes to the data so we can always search for the - // four-byte string 1f8b0800 starting at any point in the actual - // file data, without special-casing the end of the data. - memset(img+sz, 0, 4); size_t pos = 0; @@ -518,10 +515,8 @@ unsigned char* ReadImage(const char* filename, int* num_chunks, ImageChunk** chu curr->data = p; for (curr->len = 0; curr->len < (sz - pos); ++curr->len) { - if (p[curr->len] == 0x1f && - p[curr->len+1] == 0x8b && - p[curr->len+2] == 0x08 && - p[curr->len+3] == 0x00) { + if (sz - pos >= 4 && p[curr->len] == 0x1f && p[curr->len + 1] == 0x8b && + p[curr->len + 2] == 0x08 && p[curr->len + 3] == 0x00) { break; } } diff --git a/recovery.cpp b/recovery.cpp index b7aeaee1f..5888c542a 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -1506,11 +1506,10 @@ int main(int argc, char **argv) { Device* device = make_device(); ui = device->GetUI(); - if (!ui->Init()) { + if (!ui->Init(locale)) { printf("Failed to initialize UI, use stub UI instead."); ui = new StubRecoveryUI(); } - ui->SetLocale(locale.c_str()); // Set background string to "installing security update" for security update, // otherwise set it to "installing system update". ui->SetSystemUpdateText(security_update); diff --git a/screen_ui.cpp b/screen_ui.cpp index 5b9e5a5a9..706877b4d 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -29,6 +29,7 @@ #include <time.h> #include <unistd.h> +#include <string> #include <vector> #include <android-base/logging.h> @@ -51,37 +52,34 @@ static double now() { return tv.tv_sec + tv.tv_usec / 1000000.0; } -ScreenRecoveryUI::ScreenRecoveryUI() : - currentIcon(NONE), - locale(nullptr), - progressBarType(EMPTY), - progressScopeStart(0), - progressScopeSize(0), - progress(0), - pagesIdentical(false), - text_cols_(0), - text_rows_(0), - text_(nullptr), - text_col_(0), - text_row_(0), - text_top_(0), - show_text(false), - show_text_ever(false), - menu_(nullptr), - show_menu(false), - menu_items(0), - menu_sel(0), - file_viewer_text_(nullptr), - intro_frames(0), - loop_frames(0), - current_frame(0), - intro_done(false), - animation_fps(30), // TODO: there's currently no way to infer this. - stage(-1), - max_stage(-1), - updateMutex(PTHREAD_MUTEX_INITIALIZER), - rtl_locale(false) { -} +ScreenRecoveryUI::ScreenRecoveryUI() + : currentIcon(NONE), + progressBarType(EMPTY), + progressScopeStart(0), + progressScopeSize(0), + progress(0), + pagesIdentical(false), + text_cols_(0), + text_rows_(0), + text_(nullptr), + text_col_(0), + text_row_(0), + text_top_(0), + show_text(false), + show_text_ever(false), + menu_(nullptr), + show_menu(false), + menu_items(0), + menu_sel(0), + file_viewer_text_(nullptr), + intro_frames(0), + loop_frames(0), + current_frame(0), + intro_done(false), + animation_fps(30), // TODO: there's currently no way to infer this. + stage(-1), + max_stage(-1), + updateMutex(PTHREAD_MUTEX_INITIALIZER) {} GRSurface* ScreenRecoveryUI::GetCurrentFrame() { if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) { @@ -175,51 +173,50 @@ void ScreenRecoveryUI::draw_background_locked() { // Does not flip pages. // Should only be called with updateMutex locked. void ScreenRecoveryUI::draw_foreground_locked() { - if (currentIcon != NONE) { - GRSurface* frame = GetCurrentFrame(); - int frame_width = gr_get_width(frame); - int frame_height = gr_get_height(frame); - int frame_x = (gr_fb_width() - frame_width) / 2; - int frame_y = GetAnimationBaseline(); - gr_blit(frame, 0, 0, frame_width, frame_height, frame_x, frame_y); - } - - if (progressBarType != EMPTY) { - int width = gr_get_width(progressBarEmpty); - int height = gr_get_height(progressBarEmpty); - - int progress_x = (gr_fb_width() - width)/2; - int progress_y = GetProgressBaseline(); - - // Erase behind the progress bar (in case this was a progress-only update) - gr_color(0, 0, 0, 255); - gr_fill(progress_x, progress_y, width, height); + if (currentIcon != NONE) { + GRSurface* frame = GetCurrentFrame(); + int frame_width = gr_get_width(frame); + int frame_height = gr_get_height(frame); + int frame_x = (gr_fb_width() - frame_width) / 2; + int frame_y = GetAnimationBaseline(); + gr_blit(frame, 0, 0, frame_width, frame_height, frame_x, frame_y); + } + + if (progressBarType != EMPTY) { + int width = gr_get_width(progressBarEmpty); + int height = gr_get_height(progressBarEmpty); + + int progress_x = (gr_fb_width() - width) / 2; + int progress_y = GetProgressBaseline(); + + // Erase behind the progress bar (in case this was a progress-only update) + gr_color(0, 0, 0, 255); + gr_fill(progress_x, progress_y, width, height); - if (progressBarType == DETERMINATE) { - float p = progressScopeStart + progress * progressScopeSize; - int pos = (int) (p * width); + if (progressBarType == DETERMINATE) { + float p = progressScopeStart + progress * progressScopeSize; + int pos = static_cast<int>(p * width); - if (rtl_locale) { - // Fill the progress bar from right to left. - if (pos > 0) { - gr_blit(progressBarFill, width-pos, 0, pos, height, - progress_x+width-pos, progress_y); - } - if (pos < width-1) { - gr_blit(progressBarEmpty, 0, 0, width-pos, height, progress_x, progress_y); - } - } else { - // Fill the progress bar from left to right. - if (pos > 0) { - gr_blit(progressBarFill, 0, 0, pos, height, progress_x, progress_y); - } - if (pos < width-1) { - gr_blit(progressBarEmpty, pos, 0, width-pos, height, - progress_x+pos, progress_y); - } - } + if (rtl_locale_) { + // Fill the progress bar from right to left. + if (pos > 0) { + gr_blit(progressBarFill, width - pos, 0, pos, height, progress_x + width - pos, + progress_y); + } + if (pos < width - 1) { + gr_blit(progressBarEmpty, 0, 0, width - pos, height, progress_x, progress_y); + } + } else { + // Fill the progress bar from left to right. + if (pos > 0) { + gr_blit(progressBarFill, 0, 0, pos, height, progress_x, progress_y); } + if (pos < width - 1) { + gr_blit(progressBarEmpty, pos, 0, width - pos, height, progress_x + pos, progress_y); + } + } } + } } void ScreenRecoveryUI::SetColor(UIElement e) { @@ -423,10 +420,10 @@ void ScreenRecoveryUI::LoadBitmap(const char* filename, GRSurface** surface) { } void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, GRSurface** surface) { - int result = res_create_localized_alpha_surface(filename, locale, surface); - if (result < 0) { - LOG(ERROR) << "couldn't load bitmap " << filename << " (error " << result << ")"; - } + int result = res_create_localized_alpha_surface(filename, locale_.c_str(), surface); + if (result < 0) { + LOG(ERROR) << "couldn't load bitmap " << filename << " (error " << result << ")"; + } } static char** Alloc2d(size_t rows, size_t cols) { @@ -459,47 +456,47 @@ bool ScreenRecoveryUI::InitTextParams() { return true; } -bool ScreenRecoveryUI::Init() { - RecoveryUI::Init(); - if (!InitTextParams()) { - return false; - } +bool ScreenRecoveryUI::Init(const std::string& locale) { + RecoveryUI::Init(locale); + if (!InitTextParams()) { + return false; + } - density_ = static_cast<float>(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f; + density_ = static_cast<float>(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f; - // Are we portrait or landscape? - layout_ = (gr_fb_width() > gr_fb_height()) ? LANDSCAPE : PORTRAIT; - // Are we the large variant of our base layout? - if (gr_fb_height() > PixelsFromDp(800)) ++layout_; + // Are we portrait or landscape? + layout_ = (gr_fb_width() > gr_fb_height()) ? LANDSCAPE : PORTRAIT; + // Are we the large variant of our base layout? + if (gr_fb_height() > PixelsFromDp(800)) ++layout_; - text_ = Alloc2d(text_rows_, text_cols_ + 1); - file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1); - menu_ = Alloc2d(text_rows_, text_cols_ + 1); + text_ = Alloc2d(text_rows_, text_cols_ + 1); + file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1); + menu_ = Alloc2d(text_rows_, text_cols_ + 1); - text_col_ = text_row_ = 0; - text_top_ = 1; + text_col_ = text_row_ = 0; + text_top_ = 1; - LoadBitmap("icon_error", &error_icon); + LoadBitmap("icon_error", &error_icon); - LoadBitmap("progress_empty", &progressBarEmpty); - LoadBitmap("progress_fill", &progressBarFill); + LoadBitmap("progress_empty", &progressBarEmpty); + LoadBitmap("progress_fill", &progressBarFill); - LoadBitmap("stage_empty", &stageMarkerEmpty); - LoadBitmap("stage_fill", &stageMarkerFill); + LoadBitmap("stage_empty", &stageMarkerEmpty); + LoadBitmap("stage_fill", &stageMarkerFill); - // Background text for "installing_update" could be "installing update" - // or "installing security update". It will be set after UI init according - // to commands in BCB. - installing_text = nullptr; - LoadLocalizedBitmap("erasing_text", &erasing_text); - LoadLocalizedBitmap("no_command_text", &no_command_text); - LoadLocalizedBitmap("error_text", &error_text); + // Background text for "installing_update" could be "installing update" + // or "installing security update". It will be set after UI init according + // to commands in BCB. + installing_text = nullptr; + LoadLocalizedBitmap("erasing_text", &erasing_text); + LoadLocalizedBitmap("no_command_text", &no_command_text); + LoadLocalizedBitmap("error_text", &error_text); - LoadAnimation(); + LoadAnimation(); - pthread_create(&progress_thread_, nullptr, ProgressThreadStartRoutine, this); + pthread_create(&progress_thread_, nullptr, ProgressThreadStartRoutine, this); - return true; + return true; } void ScreenRecoveryUI::LoadAnimation() { @@ -539,31 +536,6 @@ void ScreenRecoveryUI::LoadAnimation() { } } -void ScreenRecoveryUI::SetLocale(const char* new_locale) { - this->locale = new_locale; - this->rtl_locale = false; - - if (locale) { - char* lang = strdup(locale); - for (char* p = lang; *p; ++p) { - if (*p == '_') { - *p = '\0'; - break; - } - } - - // A bit cheesy: keep an explicit list of supported RTL languages. - if (strcmp(lang, "ar") == 0 || // Arabic - strcmp(lang, "fa") == 0 || // Persian (Farsi) - strcmp(lang, "he") == 0 || // Hebrew (new language code) - strcmp(lang, "iw") == 0 || // Hebrew (old language code) - strcmp(lang, "ur") == 0) { // Urdu - rtl_locale = true; - } - free(lang); - } -} - void ScreenRecoveryUI::SetBackground(Icon icon) { pthread_mutex_lock(&updateMutex); diff --git a/screen_ui.h b/screen_ui.h index 38e2f0723..3ad64907e 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -20,6 +20,8 @@ #include <pthread.h> #include <stdio.h> +#include <string> + #include "ui.h" #include "minui/minui.h" @@ -29,8 +31,7 @@ class ScreenRecoveryUI : public RecoveryUI { public: ScreenRecoveryUI(); - bool Init() override; - void SetLocale(const char* locale); + bool Init(const std::string& locale) override; // overall recovery state ("background image") void SetBackground(Icon icon); @@ -71,8 +72,6 @@ class ScreenRecoveryUI : public RecoveryUI { protected: Icon currentIcon; - const char* locale; - // The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi. float density_; // The layout to use. @@ -135,7 +134,6 @@ class ScreenRecoveryUI : public RecoveryUI { int char_width_; int char_height_; pthread_mutex_t updateMutex; - bool rtl_locale; virtual bool InitTextParams(); @@ -24,8 +24,6 @@ class StubRecoveryUI : public RecoveryUI { public: StubRecoveryUI() = default; - void SetLocale(const char* locale) override {} - void SetBackground(Icon icon) override {} void SetSystemUpdateText(bool security_update) override {} diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index 3711859de..7ad330783 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -404,6 +404,86 @@ TEST(ImgdiffTest, image_mode_spurious_magic) { ASSERT_EQ(tgt, patched); } +TEST(ImgdiffTest, image_mode_short_input1) { + // src: "abcdefgh" + '0x1f8b0b'. + const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', '\x1f', '\x8b', '\x08' }; + const std::string src(src_data.cbegin(), src_data.cend()); + TemporaryFile src_file; + ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path)); + + // tgt: "abcdefgxyz". + const std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' }; + const std::string tgt(tgt_data.cbegin(), tgt_data.cend()); + TemporaryFile tgt_file; + ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path)); + + TemporaryFile patch_file; + std::vector<const char*> args = { + "imgdiff", src_file.path, tgt_file.path, patch_file.path, + }; + ASSERT_EQ(0, imgdiff(args.size(), args.data())); + + // Verify. + std::string patch; + ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch)); + + // Expect one CHUNK_RAW (header) entry. + size_t num_normal; + size_t num_raw; + size_t num_deflate; + verify_patch_header(patch, &num_normal, &num_raw, &num_deflate); + ASSERT_EQ(0U, num_normal); + ASSERT_EQ(0U, num_deflate); + ASSERT_EQ(1U, num_raw); + + std::string patched; + ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(), + reinterpret_cast<const unsigned char*>(patch.data()), patch.size(), + MemorySink, &patched)); + ASSERT_EQ(tgt, patched); +} + +TEST(ImgdiffTest, image_mode_short_input2) { + // src: "abcdefgh" + '0x1f8b0b00'. + const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', '\x1f', '\x8b', '\x08', '\x00' }; + const std::string src(src_data.cbegin(), src_data.cend()); + TemporaryFile src_file; + ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path)); + + // tgt: "abcdefgxyz". + const std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' }; + const std::string tgt(tgt_data.cbegin(), tgt_data.cend()); + TemporaryFile tgt_file; + ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path)); + + TemporaryFile patch_file; + std::vector<const char*> args = { + "imgdiff", src_file.path, tgt_file.path, patch_file.path, + }; + ASSERT_EQ(0, imgdiff(args.size(), args.data())); + + // Verify. + std::string patch; + ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch)); + + // Expect one CHUNK_RAW (header) entry. + size_t num_normal; + size_t num_raw; + size_t num_deflate; + verify_patch_header(patch, &num_normal, &num_raw, &num_deflate); + ASSERT_EQ(0U, num_normal); + ASSERT_EQ(0U, num_deflate); + ASSERT_EQ(1U, num_raw); + + std::string patched; + ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(), + reinterpret_cast<const unsigned char*>(patch.data()), patch.size(), + MemorySink, &patched)); + ASSERT_EQ(tgt, patched); +} + TEST(ImgdiffTest, image_mode_single_entry_long) { // src: "abcdefgh" + '0x1f8b0b00' + some bytes. const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp index 8d8b461cb..4294d90eb 100644 --- a/tests/component/verifier_test.cpp +++ b/tests/component/verifier_test.cpp @@ -40,38 +40,44 @@ RecoveryUI* ui = NULL; class MockUI : public RecoveryUI { - bool Init() { return true; } - void SetStage(int, int) { } - void SetLocale(const char*) { } - void SetBackground(Icon /*icon*/) { } - void SetSystemUpdateText(bool /*security_update*/) { } - - void SetProgressType(ProgressType /*determinate*/) { } - void ShowProgress(float /*portion*/, float /*seconds*/) { } - void SetProgress(float /*fraction*/) { } - - void ShowText(bool /*visible*/) { } - bool IsTextVisible() { return false; } - bool WasTextEverVisible() { return false; } - void Print(const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - } - void PrintOnScreenOnly(const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - } - void ShowFile(const char*) { } - - void StartMenu(const char* const* /*headers*/, - const char* const* /*items*/, - int /*initial_selection*/) { } - int SelectMenu(int /*sel*/) { return 0; } - void EndMenu() { } + bool Init(const std::string&) override { + return true; + } + void SetStage(int, int) override {} + void SetBackground(Icon /*icon*/) override {} + void SetSystemUpdateText(bool /*security_update*/) override {} + + void SetProgressType(ProgressType /*determinate*/) override {} + void ShowProgress(float /*portion*/, float /*seconds*/) override {} + void SetProgress(float /*fraction*/) override {} + + void ShowText(bool /*visible*/) override {} + bool IsTextVisible() override { + return false; + } + bool WasTextEverVisible() override { + return false; + } + void Print(const char* fmt, ...) override { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + } + void PrintOnScreenOnly(const char* fmt, ...) override { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + } + void ShowFile(const char*) override {} + + void StartMenu(const char* const* /*headers*/, const char* const* /*items*/, + int /*initial_selection*/) override {} + int SelectMenu(int /*sel*/) override { + return 0; + } + void EndMenu() override {} }; void @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "ui.h" + #include <errno.h> #include <fcntl.h> #include <linux/input.h> @@ -28,6 +30,8 @@ #include <time.h> #include <unistd.h> +#include <string> + #include <android-base/properties.h> #include <cutils/android_reboot.h> @@ -35,25 +39,25 @@ #include "roots.h" #include "device.h" #include "minui/minui.h" -#include "screen_ui.h" -#include "ui.h" #define UI_WAIT_KEY_TIMEOUT_SEC 120 RecoveryUI::RecoveryUI() - : key_queue_len(0), - key_last_down(-1), - key_long_press(false), - key_down_count(0), - enable_reboot(true), - consecutive_power_keys(0), - last_key(-1), - has_power_key(false), - has_up_key(false), - has_down_key(false) { - pthread_mutex_init(&key_queue_mutex, nullptr); - pthread_cond_init(&key_queue_cond, nullptr); - memset(key_pressed, 0, sizeof(key_pressed)); + : locale_(""), + rtl_locale_(false), + key_queue_len(0), + key_last_down(-1), + key_long_press(false), + key_down_count(0), + enable_reboot(true), + consecutive_power_keys(0), + last_key(-1), + has_power_key(false), + has_up_key(false), + has_down_key(false) { + pthread_mutex_init(&key_queue_mutex, nullptr); + pthread_cond_init(&key_queue_cond, nullptr); + memset(key_pressed, 0, sizeof(key_pressed)); } void RecoveryUI::OnKeyDetected(int key_code) { @@ -80,13 +84,16 @@ static void* InputThreadLoop(void*) { return nullptr; } -bool RecoveryUI::Init() { - ev_init(InputCallback, this); +bool RecoveryUI::Init(const std::string& locale) { + // Set up the locale info. + SetLocale(locale); - ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1)); + ev_init(InputCallback, this); - pthread_create(&input_thread_, nullptr, InputThreadLoop, nullptr); - return true; + ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1)); + + pthread_create(&input_thread_, nullptr, InputThreadLoop, nullptr); + return true; } int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) { @@ -338,3 +345,23 @@ void RecoveryUI::SetEnableReboot(bool enabled) { enable_reboot = enabled; pthread_mutex_unlock(&key_queue_mutex); } + +void RecoveryUI::SetLocale(const std::string& new_locale) { + this->locale_ = new_locale; + this->rtl_locale_ = false; + + if (!new_locale.empty()) { + size_t underscore = new_locale.find('_'); + // lang has the language prefix prior to '_', or full string if '_' doesn't exist. + std::string lang = new_locale.substr(0, underscore); + + // A bit cheesy: keep an explicit list of supported RTL languages. + if (lang == "ar" || // Arabic + lang == "fa" || // Persian (Farsi) + lang == "he" || // Hebrew (new language code) + lang == "iw" || // Hebrew (old language code) + lang == "ur") { // Urdu + rtl_locale_ = true; + } + } +} @@ -21,6 +21,8 @@ #include <pthread.h> #include <time.h> +#include <string> + // Abstract class for controlling the user interface during recovery. class RecoveryUI { public: @@ -28,14 +30,13 @@ class RecoveryUI { virtual ~RecoveryUI() { } - // Initialize the object; called before anything else. Returns true on success. - virtual bool Init(); + // Initialize the object; called before anything else. UI texts will be + // initialized according to the given locale. Returns true on success. + virtual bool Init(const std::string& locale); + // Show a stage indicator. Call immediately after Init(). virtual void SetStage(int current, int max) = 0; - // After calling Init(), you can tell the UI what locale it is operating in. - virtual void SetLocale(const char* locale) = 0; - // Set the overall recovery state ("background image"). enum Icon { NONE, INSTALLING_UPDATE, ERASING, NO_COMMAND, ERROR }; virtual void SetBackground(Icon icon) = 0; @@ -122,10 +123,14 @@ class RecoveryUI { // statements will be displayed. virtual void EndMenu() = 0; -protected: + protected: void EnqueueKey(int key_code); -private: + // The locale that's used to show the rendered texts. + std::string locale_; + bool rtl_locale_; + + private: // Key event input queue pthread_mutex_t key_queue_mutex; pthread_cond_t key_queue_cond; @@ -162,6 +167,8 @@ private: static void* time_key_helper(void* cookie); void time_key(int key_code, int count); + + void SetLocale(const std::string&); }; #endif // RECOVERY_UI_H diff --git a/wear_ui.cpp b/wear_ui.cpp index bdb0ef009..b4c63a5ae 100644 --- a/wear_ui.cpp +++ b/wear_ui.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "wear_ui.h" + #include <errno.h> #include <fcntl.h> #include <stdarg.h> @@ -25,11 +27,11 @@ #include <time.h> #include <unistd.h> +#include <string> #include <vector> #include "common.h" #include "device.h" -#include "wear_ui.h" #include "android-base/properties.h" #include "android-base/strings.h" #include "android-base/stringprintf.h" @@ -204,51 +206,48 @@ bool WearRecoveryUI::InitTextParams() { return true; } -bool WearRecoveryUI::Init() { - if (!ScreenRecoveryUI::Init()) { - return false; - } +bool WearRecoveryUI::Init(const std::string& locale) { + if (!ScreenRecoveryUI::Init(locale)) { + return false; + } - LoadBitmap("icon_error", &backgroundIcon[ERROR]); - backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR]; + LoadBitmap("icon_error", &backgroundIcon[ERROR]); + backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR]; - // This leaves backgroundIcon[INSTALLING_UPDATE] and backgroundIcon[ERASING] - // as NULL which is fine since draw_background_locked() doesn't use them. + // This leaves backgroundIcon[INSTALLING_UPDATE] and backgroundIcon[ERASING] + // as NULL which is fine since draw_background_locked() doesn't use them. - return true; + return true; } -void WearRecoveryUI::SetStage(int current, int max) -{ -} +void WearRecoveryUI::SetStage(int current, int max) {} -void WearRecoveryUI::Print(const char *fmt, ...) -{ - char buf[256]; - va_list ap; - va_start(ap, fmt); - vsnprintf(buf, 256, fmt, ap); - va_end(ap); +void WearRecoveryUI::Print(const char* fmt, ...) { + char buf[256]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, 256, fmt, ap); + va_end(ap); - fputs(buf, stdout); + fputs(buf, stdout); - // This can get called before ui_init(), so be careful. - pthread_mutex_lock(&updateMutex); - if (text_rows_ > 0 && text_cols_ > 0) { - char *ptr; - for (ptr = buf; *ptr != '\0'; ++ptr) { - if (*ptr == '\n' || text_col_ >= text_cols_) { - text_[text_row_][text_col_] = '\0'; - text_col_ = 0; - text_row_ = (text_row_ + 1) % text_rows_; - if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_; - } - if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr; - } + // This can get called before ui_init(), so be careful. + pthread_mutex_lock(&updateMutex); + if (text_rows_ > 0 && text_cols_ > 0) { + char* ptr; + for (ptr = buf; *ptr != '\0'; ++ptr) { + if (*ptr == '\n' || text_col_ >= text_cols_) { text_[text_row_][text_col_] = '\0'; - update_screen_locked(); + text_col_ = 0; + text_row_ = (text_row_ + 1) % text_rows_; + if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_; + } + if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr; } - pthread_mutex_unlock(&updateMutex); + text_[text_row_][text_col_] = '\0'; + update_screen_locked(); + } + pthread_mutex_unlock(&updateMutex); } void WearRecoveryUI::StartMenu(const char* const * headers, const char* const * items, @@ -19,11 +19,13 @@ #include "screen_ui.h" +#include <string> + class WearRecoveryUI : public ScreenRecoveryUI { public: WearRecoveryUI(); - bool Init() override; + bool Init(const std::string& locale) override; void SetStage(int current, int max) override; |