From ebc02271ecbe0fd00a076c696bf27f78e7d31e6a Mon Sep 17 00:00:00 2001 From: Abhishek Nigam Date: Tue, 13 Jun 2023 22:28:43 +0000 Subject: [PATCH] Wear Recovery UI: Adjust for round screens Change-Id: Ice4b8d4fcdc37ee1ca44ef195cb870b141862f08 --- fastboot/fastboot.cpp | 32 +++- recovery_ui/include/recovery_ui/screen_ui.h | 3 +- recovery_ui/include/recovery_ui/ui.h | 4 + recovery_ui/include/recovery_ui/wear_ui.h | 17 ++ recovery_ui/wear_ui.cpp | 164 +++++++++++++++++--- tools/image_generator/draw-progress.sh | 36 +++++ 6 files changed, 228 insertions(+), 28 deletions(-) create mode 100644 tools/image_generator/draw-progress.sh diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp index a0930087..7806248c 100644 --- a/fastboot/fastboot.cpp +++ b/fastboot/fastboot.cpp @@ -36,10 +36,8 @@ static const std::vector> kFastboo { "Power off", Device::SHUTDOWN_FROM_FASTBOOT }, }; -Device::BuiltinAction StartFastboot(Device* device, const std::vector& /* args */) { - RecoveryUI* ui = device->GetUI(); - - std::vector title_lines = { "Android Fastboot" }; +void FillDefaultFastbootLines(std::vector& title_lines) { + title_lines.push_back("Android Fastboot"); title_lines.push_back("Product name - " + android::base::GetProperty("ro.product.device", "")); title_lines.push_back("Bootloader version - " + android::base::GetProperty("ro.bootloader", "")); title_lines.push_back("Baseband version - " + @@ -48,6 +46,32 @@ Device::BuiltinAction StartFastboot(Device* device, const std::vector& title_lines) { + title_lines.push_back("Android Fastboot"); + title_lines.push_back(android::base::GetProperty("ro.product.device", "") + " - " + + android::base::GetProperty("ro.revision", "")); + title_lines.push_back(android::base::GetProperty("ro.bootloader", "")); + + const size_t max_baseband_len = 24; + const std::string& baseband = android::base::GetProperty("ro.build.expect.baseband", ""); + title_lines.push_back(baseband.length() > max_baseband_len + ? baseband.substr(0, max_baseband_len - 3) + "..." + : baseband); + + title_lines.push_back("Serial #: " + android::base::GetProperty("ro.serialno", "")); +} + +Device::BuiltinAction StartFastboot(Device* device, const std::vector& /* args */) { + RecoveryUI* ui = device->GetUI(); + std::vector title_lines; + + if (ui->IsWearable()) { + FillWearableFastbootLines(title_lines); + } else { + FillDefaultFastbootLines(title_lines); + } ui->ResetKeyInterruptStatus(); ui->SetTitle(title_lines); diff --git a/recovery_ui/include/recovery_ui/screen_ui.h b/recovery_ui/include/recovery_ui/screen_ui.h index 99ad5342..2e0fcc24 100644 --- a/recovery_ui/include/recovery_ui/screen_ui.h +++ b/recovery_ui/include/recovery_ui/screen_ui.h @@ -309,7 +309,7 @@ class ScreenRecoveryUI : public RecoveryUI, public DrawInterface { void PutChar(char); void ClearText(); - void LoadAnimation(); + virtual void LoadAnimation(); std::unique_ptr LoadBitmap(const std::string& filename); std::unique_ptr LoadLocalizedBitmap(const std::string& filename); @@ -416,6 +416,7 @@ class ScreenRecoveryUI : public RecoveryUI, public DrawInterface { // Display the background texts for "erasing", "error", "no_command" and "installing" for the // selected locale. void SelectAndShowBackgroundText(const std::vector& locales_entries, size_t sel); + }; #endif // RECOVERY_UI_H diff --git a/recovery_ui/include/recovery_ui/ui.h b/recovery_ui/include/recovery_ui/ui.h index c3e3ee26..c3bb03f3 100644 --- a/recovery_ui/include/recovery_ui/ui.h +++ b/recovery_ui/include/recovery_ui/ui.h @@ -177,6 +177,10 @@ class RecoveryUI { const std::vector& backup_headers, const std::vector& backup_items, const std::function& key_handler) = 0; + virtual bool IsWearable() { + return false; + } + // Set whether or not the fastbootd logo is displayed. void SetEnableFastbootdLogo(bool enable) { fastbootd_logo_enabled_ = enable; diff --git a/recovery_ui/include/recovery_ui/wear_ui.h b/recovery_ui/include/recovery_ui/wear_ui.h index 429af69d..e27e9408 100644 --- a/recovery_ui/include/recovery_ui/wear_ui.h +++ b/recovery_ui/include/recovery_ui/wear_ui.h @@ -29,6 +29,10 @@ class WearRecoveryUI : public ScreenRecoveryUI { void SetStage(int current, int max) override; protected: + // curved progress bar frames for round screens + std::vector> progress_frames_; + std::vector> rtl_progress_frames_; + // progress bar vertical position, it's centered horizontally const int progress_bar_baseline_; @@ -36,17 +40,30 @@ class WearRecoveryUI : public ScreenRecoveryUI { // Recovery, build id and etc) and the bottom lines that may otherwise go out of the screen. const int menu_unusable_rows_; + const bool is_screen_circle_; + std::unique_ptr CreateMenu(const std::vector& text_headers, const std::vector& text_items, size_t initial_selection) const override; int GetProgressBaseline() const override; + int GetTextBaseline() const override; + void update_progress_locked() override; + void LoadAnimation() override; + + bool IsWearable() override; + + void SetProgress(float fraction) override; + private: void draw_background_locked() override; void draw_screen_locked() override; + void draw_circle_foreground_locked(); + size_t GetProgressFrameIndex(float fraction) const; + }; #endif // RECOVERY_WEAR_UI_H diff --git a/recovery_ui/wear_ui.cpp b/recovery_ui/wear_ui.cpp index 8d8108f1..309891cd 100644 --- a/recovery_ui/wear_ui.cpp +++ b/recovery_ui/wear_ui.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "otautil/paths.h" #include "recovery_ui/wear_ui.h" #include @@ -23,24 +24,25 @@ #include #include + #include constexpr int kDefaultProgressBarBaseline = 259; constexpr int kDefaultMenuUnusableRows = 9; +constexpr int kProgressBarVerticalOffsetDp = 72; +constexpr bool kDefaultIsScreenCircle = true; WearRecoveryUI::WearRecoveryUI() : ScreenRecoveryUI(true), progress_bar_baseline_(android::base::GetIntProperty("ro.recovery.ui.progress_bar_baseline", kDefaultProgressBarBaseline)), menu_unusable_rows_(android::base::GetIntProperty("ro.recovery.ui.menu_unusable_rows", - kDefaultMenuUnusableRows)) { + kDefaultMenuUnusableRows)), + is_screen_circle_(android::base::GetBoolProperty("ro.recovery.ui.is_screen_circle", + kDefaultIsScreenCircle)) { // TODO: menu_unusable_rows_ should be computed based on the lines in draw_screen_locked(). - touch_screen_allowed_ = true; -} - -int WearRecoveryUI::GetProgressBaseline() const { - return progress_bar_baseline_; + SetEnableFastbootdLogo(false); // logo not required on Wear } // Draw background frame on the screen. Does not flip pages. @@ -51,40 +53,152 @@ void WearRecoveryUI::draw_background_locked() { gr_color(0, 0, 0, 255); gr_fill(0, 0, gr_fb_width(), gr_fb_height()); - if (current_icon_ != NONE) { + if (current_icon_ == ERROR) { const auto& 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 = (gr_fb_height() - frame_height) / 2; gr_blit(frame, 0, 0, frame_width, frame_height, frame_x, frame_y); + } - // Draw recovery text on screen above progress bar. + if (current_icon_ != NONE) { + // Draw recovery text on screen centered const auto& text = GetCurrentText(); int text_x = (ScreenWidth() - gr_get_width(text)) / 2; - int text_y = GetProgressBaseline() - gr_get_height(text) - 10; + int text_y = (ScreenHeight() - gr_get_height(text)) / 2; gr_color(255, 255, 255, 255); gr_texticon(text_x, text_y, text); } } void WearRecoveryUI::draw_screen_locked() { - draw_background_locked(); if (!show_text) { - draw_foreground_locked(); - } else { - SetColor(UIElement::TEXT_FILL); - gr_fill(0, 0, gr_fb_width(), gr_fb_height()); - - // clang-format off - static std::vector SWIPE_HELP = { - "Swipe up/down to move.", - "Swipe left/right to select.", - "", - }; - // clang-format on - draw_menu_and_text_buffer_locked(SWIPE_HELP); + draw_background_locked(); + if (is_screen_circle_) { + draw_circle_foreground_locked(); + } else { + draw_foreground_locked(); + } + return; } + + SetColor(UIElement::TEXT_FILL); + gr_clear(); + + // clang-format off + static std::vector SWIPE_HELP = { + "Swipe up/down to move.", + "Swipe left/right to select.", + "", + }; + // clang-format on + draw_menu_and_text_buffer_locked(SWIPE_HELP); +} + +void WearRecoveryUI::draw_circle_foreground_locked() { + if (current_icon_ != NONE) { + const auto& frame = GetCurrentFrame(); + int frame_width = gr_get_width(frame); + int frame_height = gr_get_height(frame); + int frame_x = (ScreenWidth() - frame_width) / 2; + int frame_y = GetAnimationBaseline(); + DrawSurface(frame, 0, 0, frame_width, frame_height, frame_x, frame_y); + } + + if (progressBarType == DETERMINATE) { + const auto& first_progress_frame = rtl_locale_ ? rtl_progress_frames_[0].get() + :progress_frames_[0].get(); + int width = gr_get_width(first_progress_frame); + int height = gr_get_height(first_progress_frame); + + int progress_x = (ScreenWidth() - width) / 2; + int progress_y = GetProgressBaseline(); + + const auto index = GetProgressFrameIndex(progress); + const auto& frame = rtl_locale_ ? rtl_progress_frames_[index].get() + : progress_frames_[index].get(); + + DrawSurface(frame, 0, 0, width, height, progress_x, progress_y); + } +} + +void WearRecoveryUI::LoadAnimation() { + ScreenRecoveryUI::LoadAnimation(); + std::unique_ptr dir(opendir(Paths::Get().resource_dir().c_str()), + closedir); + dirent* de; + std::vector progress_frame_names; + std::vector rtl_progress_frame_names; + + if(dir.get() == nullptr) abort(); + + while ((de = readdir(dir.get())) != nullptr) { + int value, num_chars; + if (sscanf(de->d_name, "progress%d%n.png", &value, &num_chars) == 1) { + progress_frame_names.emplace_back(de->d_name, num_chars); + } else if (sscanf(de->d_name, "rtl_progress%d%n.png", &value, &num_chars) == 1) { + rtl_progress_frame_names.emplace_back(de->d_name, num_chars); + } + } + + size_t progress_frames = progress_frame_names.size(); + size_t rtl_progress_frames = rtl_progress_frame_names.size(); + + // You must have an animation. + if (progress_frames == 0 || rtl_progress_frames == 0) abort(); + + std::sort(progress_frame_names.begin(), progress_frame_names.end()); + std::sort(rtl_progress_frame_names.begin(), rtl_progress_frame_names.end()); + + progress_frames_.clear(); + progress_frames_.reserve(progress_frames); + for (const auto& frame_name : progress_frame_names) { + progress_frames_.emplace_back(LoadBitmap(frame_name)); + } + + rtl_progress_frames_.clear(); + rtl_progress_frames_.reserve(rtl_progress_frames); + for (const auto& frame_name : rtl_progress_frame_names) { + rtl_progress_frames_.emplace_back(LoadBitmap(frame_name)); + } +} + +void WearRecoveryUI::SetProgress(float fraction) { + if (is_screen_circle_) { + std::lock_guard lg(updateMutex); + if (fraction < 0.0) fraction = 0.0; + if (fraction > 1.0) fraction = 1.0; + if (progressBarType == DETERMINATE && fraction > progress) { + // Skip updates that aren't visibly different. + if (GetProgressFrameIndex(fraction) != GetProgressFrameIndex(progress)) { + // circular display + progress = fraction; + update_progress_locked(); + } + } + } else { + // rectangular display + ScreenRecoveryUI::SetProgress(fraction); + } +} + +int WearRecoveryUI::GetProgressBaseline() const { + int progress_height = gr_get_height(progress_frames_[0].get()); + return (ScreenHeight() - progress_height) / 2 + PixelsFromDp(kProgressBarVerticalOffsetDp); +} + +int WearRecoveryUI::GetTextBaseline() const { + if (is_screen_circle_) { + return GetProgressBaseline() - PixelsFromDp(kProgressBarVerticalOffsetDp) - + gr_get_height(installing_text_.get()); + } else { + return ScreenRecoveryUI::GetTextBaseline(); + } +} + +size_t WearRecoveryUI::GetProgressFrameIndex(float fraction) const { + return static_cast(fraction * (progress_frames_.size() - 1)); } // TODO merge drawing routines with screen_ui @@ -93,6 +207,10 @@ void WearRecoveryUI::update_progress_locked() { gr_flip(); } +bool WearRecoveryUI::IsWearable() { + return true; +} + void WearRecoveryUI::SetStage(int /* current */, int /* max */) {} std::unique_ptr WearRecoveryUI::CreateMenu(const std::vector& text_headers, diff --git a/tools/image_generator/draw-progress.sh b/tools/image_generator/draw-progress.sh new file mode 100644 index 00000000..7462c4e1 --- /dev/null +++ b/tools/image_generator/draw-progress.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# arc central angle in degrees +arc_size="64.5" + +arc_start=$(bc -l <<< "90 - $arc_size / 2") +arc_end=$(bc -l <<< "90 + $arc_size / 2") + +N=100 +for ((i=0; i < $N; i++)); do + progress=$(bc -l <<< "$i / ($N - 1)") + fg_arc_start=$(bc -l <<< "$arc_end - $progress * $arc_size") + + filename="progress$(printf "%02d" $i).png" + echo "-- Writing file: $filename" + + convert -size 400x400 xc:black \ + -draw "stroke-linecap round stroke-width 8 \ + stroke gray ellipse 200,200 100,100 $arc_start,$arc_end \ + stroke white ellipse 200,200 100,100 $fg_arc_start,$arc_end" "$filename" + + echo "-- Writing file: rtl_$filename" + convert -size 400x400 xc:black \ + -draw "stroke-linecap round stroke-width 8 \ + stroke gray ellipse 200,200 100,100 $arc_start,$arc_end \ + stroke white ellipse 200,200 100,100 $fg_arc_start,$arc_end" "rtl_$filename" + + mogrify -crop 120x30+140+280 "$filename" + mogrify -crop 120x30+140+280 "rtl_$filename" + + # Use color format recovery can use + mogrify -define png:format=png24 -type TrueColor "$filename" + mogrify -define png:format=png24 -type TrueColor "rtl_$filename" + + mogrify -flop "rtl_$filename" +done