Wear Recovery UI: Adjust for round screens

Change-Id: Ice4b8d4fcdc37ee1ca44ef195cb870b141862f08
This commit is contained in:
Abhishek Nigam 2023-06-13 22:28:43 +00:00
parent 1551e6a0a4
commit ebc02271ec
6 changed files with 228 additions and 28 deletions

View file

@ -36,10 +36,8 @@ static const std::vector<std::pair<std::string, Device::BuiltinAction>> kFastboo
{ "Power off", Device::SHUTDOWN_FROM_FASTBOOT },
};
Device::BuiltinAction StartFastboot(Device* device, const std::vector<std::string>& /* args */) {
RecoveryUI* ui = device->GetUI();
std::vector<std::string> title_lines = { "Android Fastboot" };
void FillDefaultFastbootLines(std::vector<std::string>& 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<std::strin
title_lines.push_back(std::string("Secure boot - ") +
((android::base::GetProperty("ro.secure", "") == "1") ? "yes" : "no"));
title_lines.push_back("HW version - " + android::base::GetProperty("ro.revision", ""));
}
void FillWearableFastbootLines(std::vector<std::string>& 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<std::string>& /* args */) {
RecoveryUI* ui = device->GetUI();
std::vector<std::string> title_lines;
if (ui->IsWearable()) {
FillWearableFastbootLines(title_lines);
} else {
FillDefaultFastbootLines(title_lines);
}
ui->ResetKeyInterruptStatus();
ui->SetTitle(title_lines);

View file

@ -309,7 +309,7 @@ class ScreenRecoveryUI : public RecoveryUI, public DrawInterface {
void PutChar(char);
void ClearText();
void LoadAnimation();
virtual void LoadAnimation();
std::unique_ptr<GRSurface> LoadBitmap(const std::string& filename);
std::unique_ptr<GRSurface> 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<std::string>& locales_entries, size_t sel);
};
#endif // RECOVERY_UI_H

View file

@ -177,6 +177,10 @@ class RecoveryUI {
const std::vector<std::string>& backup_headers, const std::vector<std::string>& backup_items,
const std::function<int(int, bool)>& 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;

View file

@ -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<std::unique_ptr<GRSurface>> progress_frames_;
std::vector<std::unique_ptr<GRSurface>> 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<Menu> CreateMenu(const std::vector<std::string>& text_headers,
const std::vector<std::string>& 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

View file

@ -14,6 +14,7 @@
* limitations under the License.
*/
#include "otautil/paths.h"
#include "recovery_ui/wear_ui.h"
#include <string.h>
@ -23,24 +24,25 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <minui/minui.h>
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<std::string> 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<std::string> 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, decltype(&closedir)> dir(opendir(Paths::Get().resource_dir().c_str()),
closedir);
dirent* de;
std::vector<std::string> progress_frame_names;
std::vector<std::string> 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<std::mutex> 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<size_t>(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<Menu> WearRecoveryUI::CreateMenu(const std::vector<std::string>& text_headers,

View file

@ -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