blob: f7a6ee1fec1d6435ac51ca02f7c3ad59fde7042b [file] [log] [blame]
Doug Zongker211aebc2011-10-28 15:13:10 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Tianjie Xu8f397302018-08-20 13:40:47 -070017#include "recovery_ui/screen_ui.h"
Tao Baoefb49ad2017-01-31 23:03:10 -080018
Elliott Hughes498cda62016-04-14 16:49:04 -070019#include <dirent.h>
Doug Zongker211aebc2011-10-28 15:13:10 -070020#include <errno.h>
21#include <fcntl.h>
Doug Zongker211aebc2011-10-28 15:13:10 -070022#include <stdarg.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/stat.h>
27#include <sys/time.h>
28#include <sys/types.h>
29#include <time.h>
30#include <unistd.h>
31
Tao Bao1fe1afe2018-05-01 15:56:05 -070032#include <algorithm>
Tao Bao26ea9592018-05-09 16:32:02 -070033#include <chrono>
Tianjie Xu29d55752017-09-20 17:53:46 -070034#include <memory>
Tao Bao736d59c2017-01-03 10:15:33 -080035#include <string>
Tao Bao26ea9592018-05-09 16:32:02 -070036#include <thread>
Tianjie Xu29d55752017-09-20 17:53:46 -070037#include <unordered_map>
Elliott Hughes95fc63e2015-04-10 19:12:01 -070038#include <vector>
39
Mark Salyzyn30017e72020-05-13 12:39:12 -070040#include <android-base/chrono_utils.h>
Tianjie Xuc21edd42016-08-05 18:00:04 -070041#include <android-base/logging.h>
Elliott Hughescb220402016-09-23 15:30:55 -070042#include <android-base/properties.h>
Elliott Hughes4b166f02015-12-04 15:30:20 -080043#include <android-base/stringprintf.h>
Tao Baocb5524c2017-09-08 21:25:32 -070044#include <android-base/strings.h>
Tao Baob6918c72015-05-19 17:02:16 -070045
Tao Bao6cd81682018-05-03 21:53:11 -070046#include "minui/minui.h"
47#include "otautil/paths.h"
Tianjie Xu8f397302018-08-20 13:40:47 -070048#include "recovery_ui/device.h"
49#include "recovery_ui/ui.h"
Doug Zongker211aebc2011-10-28 15:13:10 -070050
fredchiouf2f5aa22022-02-11 16:39:53 +080051enum DirectRenderManager {
52 DRM_INNER,
53 DRM_OUTER,
54};
55
Doug Zongker211aebc2011-10-28 15:13:10 -070056// Return the current time as a double (including fractions of a second).
57static double now() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -070058 struct timeval tv;
59 gettimeofday(&tv, nullptr);
60 return tv.tv_sec + tv.tv_usec / 1000000.0;
Doug Zongker211aebc2011-10-28 15:13:10 -070061}
62
Tianjie Xu66dbf632018-10-11 16:54:50 -070063Menu::Menu(size_t initial_selection, const DrawInterface& draw_func)
64 : selection_(initial_selection), draw_funcs_(draw_func) {}
65
Alessandro Astone5a57a942020-03-09 23:17:50 +010066int Menu::selection() const {
Tianjie Xu66dbf632018-10-11 16:54:50 -070067 return selection_;
68}
69
Alessandro Astone5cec9d82020-06-25 19:54:18 +020070TextMenu::TextMenu(bool wrappable, size_t max_length,
Tianjie Xu66dbf632018-10-11 16:54:50 -070071 const std::vector<std::string>& headers, const std::vector<std::string>& items,
72 size_t initial_selection, int char_height, const DrawInterface& draw_funcs)
73 : Menu(initial_selection, draw_funcs),
Alessandro Astone5a57a942020-03-09 23:17:50 +010074 wrappable_(wrappable),
Alessandro Astone5cec9d82020-06-25 19:54:18 +020075 calibrated_height_(false),
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070076 max_item_length_(max_length),
Tao Baoe02a5b22018-05-02 15:46:11 -070077 text_headers_(headers),
Tianjie Xu66dbf632018-10-11 16:54:50 -070078 char_height_(char_height) {
Tao Baoe02a5b22018-05-02 15:46:11 -070079
Alessandro Astone5a57a942020-03-09 23:17:50 +010080 size_t items_count = items.size();
Tao Bao1fe1afe2018-05-01 15:56:05 -070081 for (size_t i = 0; i < items_count; ++i) {
82 text_items_.emplace_back(items[i].substr(0, max_item_length_));
Tao Baoe02a5b22018-05-02 15:46:11 -070083 }
84
85 CHECK(!text_items_.empty());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070086}
87
Tianjie Xu66dbf632018-10-11 16:54:50 -070088const std::vector<std::string>& TextMenu::text_headers() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070089 return text_headers_;
90}
91
Tianjie Xu66dbf632018-10-11 16:54:50 -070092std::string TextMenu::TextItem(size_t index) const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070093 CHECK_LT(index, text_items_.size());
94
95 return text_items_[index];
96}
97
Tianjie Xu66dbf632018-10-11 16:54:50 -070098size_t TextMenu::MenuStart() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070099 return menu_start_;
100}
101
Tianjie Xu66dbf632018-10-11 16:54:50 -0700102size_t TextMenu::MenuEnd() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700103 return std::min(ItemsCount(), menu_start_ + max_display_items_);
104}
105
Tianjie Xu66dbf632018-10-11 16:54:50 -0700106size_t TextMenu::ItemsCount() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700107 return text_items_.size();
108}
109
Tianjie Xu66dbf632018-10-11 16:54:50 -0700110bool TextMenu::ItemsOverflow(std::string* cur_selection_str) const {
Alessandro Astone5a57a942020-03-09 23:17:50 +0100111 if (ItemsCount() <= max_display_items_) {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700112 return false;
113 }
114
115 *cur_selection_str =
Alessandro Astone5a57a942020-03-09 23:17:50 +0100116 android::base::StringPrintf("Current item: %d/%zu", selection_ + 1, ItemsCount());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700117 return true;
118}
119
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700120// TODO(xunchang) modify the function parameters to button up & down.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700121int TextMenu::Select(int sel) {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700122 CHECK_LE(ItemsCount(), static_cast<size_t>(std::numeric_limits<int>::max()));
123 int count = ItemsCount();
124
Alessandro Astone5a57a942020-03-09 23:17:50 +0100125 int min = IsMain() ? 0 : -1; // -1 is back arrow
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700126
Alessandro Astone5a57a942020-03-09 23:17:50 +0100127 if (sel < min) {
128 selection_ = wrappable() ? count - 1 : min;
129 } else if (sel >= count) {
130 selection_ = wrappable() ? min : count - 1;
131 } else {
132 selection_ = sel;
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700133 }
134
Alessandro Astone5a57a942020-03-09 23:17:50 +0100135 if (selection_ >= 0) {
136 if (selection_ < menu_start_) {
137 menu_start_ = selection_;
138 } else if (static_cast<size_t>(selection_) >= MenuEnd()) {
139 menu_start_ = selection_ - max_display_items_ + 1;
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700140 }
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700141 }
142
143 return selection_;
144}
145
Alessandro Astone84042042020-10-04 18:11:40 +0200146int TextMenu::SelectVisible(int relative_sel) {
147 int sel = relative_sel;
148 if (menu_start_ > 0) {
149 sel += menu_start_;
150 }
151
152 return Select(sel);
153}
154
155int TextMenu::Scroll(int updown) {
156 if ((updown > 0 && menu_start_ + max_display_items_ < ItemsCount()) ||
157 (updown < 0 && menu_start_ > 0)) {
158 menu_start_ += updown;
159
160 /* We can receive a kInvokeItem event from a different source than touch,
161 like from Power button. For this reason, selection should not get out of
162 the screen. Constrain it to the first or last visible item of the list */
163 if (selection_ < menu_start_) {
164 selection_ = menu_start_;
165 } else if (selection_ >= menu_start_ + max_display_items_) {
166 selection_ = menu_start_ + max_display_items_ - 1;
167 }
168 }
169 return selection_;
170}
171
Tianjie Xu66dbf632018-10-11 16:54:50 -0700172int TextMenu::DrawHeader(int x, int y) const {
173 int offset = 0;
174
175 draw_funcs_.SetColor(UIElement::HEADER);
Alessandro Astone5a57a942020-03-09 23:17:50 +0100176 offset += draw_funcs_.DrawWrappedTextLines(x, y + offset, text_headers());
Tianjie Xu66dbf632018-10-11 16:54:50 -0700177
178 return offset;
179}
180
181int TextMenu::DrawItems(int x, int y, int screen_width, bool long_press) const {
182 int offset = 0;
Alessandro Astone5a57a942020-03-09 23:17:50 +0100183 int padding = draw_funcs_.MenuItemPadding();
Tianjie Xu66dbf632018-10-11 16:54:50 -0700184
185 draw_funcs_.SetColor(UIElement::MENU);
Alessandro Astone5a57a942020-03-09 23:17:50 +0100186 offset += draw_funcs_.DrawHorizontalRule(y + offset) + 4;
187
188 int item_container_offset = offset; // store it for drawing scrollbar on most top
189
Tianjie Xu66dbf632018-10-11 16:54:50 -0700190 for (size_t i = MenuStart(); i < MenuEnd(); ++i) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700191 if (i == selection()) {
192 // Draw the highlight bar.
193 draw_funcs_.SetColor(long_press ? UIElement::MENU_SEL_BG_ACTIVE : UIElement::MENU_SEL_BG);
194
Alessandro Astone5a57a942020-03-09 23:17:50 +0100195 int bar_height = padding + char_height_ + padding;
196 draw_funcs_.DrawHighlightBar(0, y + offset, screen_width, bar_height);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700197
Jesse Chan968a21a2020-04-28 14:49:13 +0000198 // Colored text for the selected item.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700199 draw_funcs_.SetColor(UIElement::MENU_SEL_FG);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700200 }
Jesse Chan968a21a2020-04-28 14:49:13 +0000201 offset += draw_funcs_.DrawTextLine(x, y + offset, TextItem(i), false /* bold */);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700202
203 draw_funcs_.SetColor(UIElement::MENU);
204 }
205 offset += draw_funcs_.DrawHorizontalRule(y + offset);
206
Alessandro Astone5a57a942020-03-09 23:17:50 +0100207 std::string unused;
208 if (ItemsOverflow(&unused)) {
209 int container_height = max_display_items_ * (2 * padding + char_height_);
210 int bar_height = container_height / (text_items_.size() - max_display_items_ + 1);
211 int start_y = y + item_container_offset + bar_height * menu_start_;
212 draw_funcs_.SetColor(UIElement::SCROLLBAR);
213 draw_funcs_.DrawScrollBar(start_y, bar_height);
214 }
215
Tianjie Xu66dbf632018-10-11 16:54:50 -0700216 return offset;
217}
218
Tao Baoda409fb2018-10-21 23:36:26 -0700219GraphicMenu::GraphicMenu(const GRSurface* graphic_headers,
220 const std::vector<const GRSurface*>& graphic_items,
Tianjie Xub99e6062018-10-16 15:13:09 -0700221 size_t initial_selection, const DrawInterface& draw_funcs)
Tao Baoda409fb2018-10-21 23:36:26 -0700222 : Menu(initial_selection, draw_funcs) {
223 graphic_headers_ = graphic_headers->Clone();
224 graphic_items_.reserve(graphic_items.size());
225 for (const auto& item : graphic_items) {
226 graphic_items_.emplace_back(item->Clone());
227 }
228}
Tianjie Xu66dbf632018-10-11 16:54:50 -0700229
230int GraphicMenu::Select(int sel) {
231 CHECK_LE(graphic_items_.size(), static_cast<size_t>(std::numeric_limits<int>::max()));
232 int count = graphic_items_.size();
233
234 // Wraps the selection at boundary if the menu is not scrollable.
235 if (sel < 0) {
236 selection_ = count - 1;
237 } else if (sel >= count) {
238 selection_ = 0;
239 } else {
240 selection_ = sel;
241 }
242
243 return selection_;
244}
245
246int GraphicMenu::DrawHeader(int x, int y) const {
Tianjie Xub99e6062018-10-16 15:13:09 -0700247 draw_funcs_.SetColor(UIElement::HEADER);
Tao Baoda409fb2018-10-21 23:36:26 -0700248 draw_funcs_.DrawTextIcon(x, y, graphic_headers_.get());
Tianjie Xu66dbf632018-10-11 16:54:50 -0700249 return graphic_headers_->height;
250}
251
252int GraphicMenu::DrawItems(int x, int y, int screen_width, bool long_press) const {
253 int offset = 0;
254
255 draw_funcs_.SetColor(UIElement::MENU);
256 offset += draw_funcs_.DrawHorizontalRule(y + offset) + 4;
257
258 for (size_t i = 0; i < graphic_items_.size(); i++) {
259 auto& item = graphic_items_[i];
260 if (i == selection_) {
261 draw_funcs_.SetColor(long_press ? UIElement::MENU_SEL_BG_ACTIVE : UIElement::MENU_SEL_BG);
262
263 int bar_height = item->height + 4;
264 draw_funcs_.DrawHighlightBar(0, y + offset - 2, screen_width, bar_height);
265
266 // Bold white text for the selected item.
267 draw_funcs_.SetColor(UIElement::MENU_SEL_FG);
268 }
Tao Baoda409fb2018-10-21 23:36:26 -0700269 draw_funcs_.DrawTextIcon(x, y + offset, item.get());
Tianjie Xu66dbf632018-10-11 16:54:50 -0700270 offset += item->height;
271
272 draw_funcs_.SetColor(UIElement::MENU);
273 }
xunchangc7dbc732018-12-20 11:31:18 -0800274 offset += draw_funcs_.DrawHorizontalRule(y + offset);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700275
276 return offset;
277}
278
Alessandro Astone84042042020-10-04 18:11:40 +0200279size_t GraphicMenu::ItemsCount() const {
280 return graphic_items_.size();
281}
282
Tao Baoda409fb2018-10-21 23:36:26 -0700283bool GraphicMenu::Validate(size_t max_width, size_t max_height, const GRSurface* graphic_headers,
284 const std::vector<const GRSurface*>& graphic_items) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700285 int offset = 0;
Tianjie Xub99e6062018-10-16 15:13:09 -0700286 if (!ValidateGraphicSurface(max_width, max_height, offset, graphic_headers)) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700287 return false;
288 }
Tianjie Xub99e6062018-10-16 15:13:09 -0700289 offset += graphic_headers->height;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700290
Tianjie Xub99e6062018-10-16 15:13:09 -0700291 for (const auto& item : graphic_items) {
292 if (!ValidateGraphicSurface(max_width, max_height, offset, item)) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700293 return false;
294 }
295 offset += item->height;
296 }
297
298 return true;
299}
300
Tianjie Xub99e6062018-10-16 15:13:09 -0700301bool GraphicMenu::ValidateGraphicSurface(size_t max_width, size_t max_height, int y,
302 const GRSurface* surface) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700303 if (!surface) {
Tao Baoa00b4492019-01-16 09:29:47 -0800304 fprintf(stderr, "Graphic surface can not be null\n");
Tianjie Xu66dbf632018-10-11 16:54:50 -0700305 return false;
306 }
307
308 if (surface->pixel_bytes != 1 || surface->width != surface->row_bytes) {
Tao Baoa00b4492019-01-16 09:29:47 -0800309 fprintf(stderr, "Invalid graphic surface, pixel bytes: %zu, width: %zu row_bytes: %zu\n",
Tianjie Xu66dbf632018-10-11 16:54:50 -0700310 surface->pixel_bytes, surface->width, surface->row_bytes);
311 return false;
312 }
313
Tianjie Xub99e6062018-10-16 15:13:09 -0700314 if (surface->width > max_width || surface->height > max_height - y) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700315 fprintf(stderr,
Tao Baodd789822018-11-26 16:28:07 -0800316 "Graphic surface doesn't fit into the screen. width: %zu, height: %zu, max_width: %zu,"
Tianjie Xu66dbf632018-10-11 16:54:50 -0700317 " max_height: %zu, vertical offset: %d\n",
Tianjie Xub99e6062018-10-16 15:13:09 -0700318 surface->width, surface->height, max_width, max_height, y);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700319 return false;
320 }
321
322 return true;
323}
324
Alessandro Astone5a57a942020-03-09 23:17:50 +0100325MenuDrawFunctions::MenuDrawFunctions(const DrawInterface& wrappee)
326 : wrappee_(wrappee) {
327}
328
329int MenuDrawFunctions::DrawTextLine(int x, int y, const std::string& line, bool bold) const {
330 gr_text(gr_menu_font(), x, y + MenuItemPadding(), line.c_str(), bold);
331 return 2 * MenuItemPadding() + MenuCharHeight();
332}
333
334int MenuDrawFunctions::DrawTextLines(int x, int y, const std::vector<std::string>& lines) const {
335 int offset = 0;
336 for (const auto& line : lines) {
337 offset += DrawTextLine(x, y + offset, line, false);
338 }
339 return offset;
340}
341
342int MenuDrawFunctions::DrawWrappedTextLines(int x, int y, const std::vector<std::string>& lines) const {
Alessandro Astone92b8b432020-10-01 16:45:53 +0200343 const int padding = MenuItemPadding() / 2;
344
Alessandro Astone5a57a942020-03-09 23:17:50 +0100345 // Keep symmetrical margins based on the given offset (i.e. x).
346 size_t text_cols = (gr_fb_width() - x * 2) / MenuCharWidth();
347 int offset = 0;
348 for (const auto& line : lines) {
349 size_t next_start = 0;
350 while (next_start < line.size()) {
351 std::string sub = line.substr(next_start, text_cols + 1);
352 if (sub.size() <= text_cols) {
353 next_start += sub.size();
354 } else {
355 // Line too long and must be wrapped to text_cols columns.
356 size_t last_space = sub.find_last_of(" \t\n");
357 if (last_space == std::string::npos) {
358 // No space found, just draw as much as we can.
359 sub.resize(text_cols);
360 next_start += text_cols;
361 } else {
362 sub.resize(last_space);
363 next_start += last_space + 1;
364 }
365 }
Alessandro Astone92b8b432020-10-01 16:45:53 +0200366 offset += DrawTextLine(x, y + offset, sub, false) - (2 * MenuItemPadding() - padding);
Alessandro Astone5a57a942020-03-09 23:17:50 +0100367 }
368 }
Alessandro Astone92b8b432020-10-01 16:45:53 +0200369 if (!lines.empty()) {
370 offset += 2 * MenuItemPadding() - padding;
371 }
Alessandro Astone5a57a942020-03-09 23:17:50 +0100372 return offset;
373}
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700374
Tao Bao0bc88de2018-07-31 14:53:16 -0700375constexpr int kDefaultMarginHeight = 0;
376constexpr int kDefaultMarginWidth = 0;
377constexpr int kDefaultAnimationFps = 30;
378
Alessandro Astone5a57a942020-03-09 23:17:50 +0100379ScreenRecoveryUI::ScreenRecoveryUI()
Tao Bao0bc88de2018-07-31 14:53:16 -0700380 : margin_width_(
381 android::base::GetIntProperty("ro.recovery.ui.margin_width", kDefaultMarginWidth)),
382 margin_height_(
383 android::base::GetIntProperty("ro.recovery.ui.margin_height", kDefaultMarginHeight)),
384 animation_fps_(
385 android::base::GetIntProperty("ro.recovery.ui.animation_fps", kDefaultAnimationFps)),
386 density_(static_cast<float>(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f),
Michael Bestas19630862019-03-23 17:28:22 +0200387 blank_unblank_on_init_(
388 android::base::GetBoolProperty("ro.recovery.ui.blank_unblank_on_init", false)),
Tao Baoda409fb2018-10-21 23:36:26 -0700389 current_icon_(NONE),
390 current_frame_(0),
391 intro_done_(false),
Tao Bao736d59c2017-01-03 10:15:33 -0800392 progressBarType(EMPTY),
393 progressScopeStart(0),
394 progressScopeSize(0),
395 progress(0),
396 pagesIdentical(false),
397 text_cols_(0),
398 text_rows_(0),
399 text_(nullptr),
400 text_col_(0),
401 text_row_(0),
Tao Bao736d59c2017-01-03 10:15:33 -0800402 show_text(false),
403 show_text_ever(false),
Tao Bao736d59c2017-01-03 10:15:33 -0800404 file_viewer_text_(nullptr),
Tao Bao736d59c2017-01-03 10:15:33 -0800405 stage(-1),
406 max_stage(-1),
Tao Baoefb49ad2017-01-31 23:03:10 -0800407 locale_(""),
fredchiouf2f5aa22022-02-11 16:39:53 +0800408 rtl_locale_(false),
409 is_graphics_available(false) {}
Doug Zongker211aebc2011-10-28 15:13:10 -0700410
Tao Bao26ea9592018-05-09 16:32:02 -0700411ScreenRecoveryUI::~ScreenRecoveryUI() {
412 progress_thread_stopped_ = true;
Tao Bao94371fd2018-06-06 07:38:54 -0700413 if (progress_thread_.joinable()) {
414 progress_thread_.join();
415 }
Tao Bao60ac6222018-06-13 14:33:51 -0700416 // No-op if gr_init() (via Init()) was not called or had failed.
417 gr_exit();
Tao Bao26ea9592018-05-09 16:32:02 -0700418}
419
Tao Baoda409fb2018-10-21 23:36:26 -0700420const GRSurface* ScreenRecoveryUI::GetCurrentFrame() const {
421 if (current_icon_ == INSTALLING_UPDATE || current_icon_ == ERASING) {
422 return intro_done_ ? loop_frames_[current_frame_].get() : intro_frames_[current_frame_].get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700423 }
Tao Baoda409fb2018-10-21 23:36:26 -0700424 return error_icon_.get();
Elliott Hughes498cda62016-04-14 16:49:04 -0700425}
426
Tao Baoda409fb2018-10-21 23:36:26 -0700427const GRSurface* ScreenRecoveryUI::GetCurrentText() const {
428 switch (current_icon_) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700429 case ERASING:
Tao Baoda409fb2018-10-21 23:36:26 -0700430 return erasing_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700431 case ERROR:
Tao Baoda409fb2018-10-21 23:36:26 -0700432 return error_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700433 case INSTALLING_UPDATE:
Tao Baoda409fb2018-10-21 23:36:26 -0700434 return installing_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700435 case NO_COMMAND:
Tao Baoda409fb2018-10-21 23:36:26 -0700436 return no_command_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700437 case NONE:
438 abort();
439 }
Elliott Hughes498cda62016-04-14 16:49:04 -0700440}
441
Mikhail Lappob49767c2017-03-23 21:44:26 +0100442int ScreenRecoveryUI::PixelsFromDp(int dp) const {
Tao Bao0bc88de2018-07-31 14:53:16 -0700443 return dp * density_;
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700444}
445
446// Here's the intended layout:
447
Elliott Hughes6d089a92016-07-08 17:23:41 -0700448// | portrait large landscape large
449// ---------+-------------------------------------------------
Tao Bao3250f722017-06-29 14:32:05 -0700450// gap |
Elliott Hughes6d089a92016-07-08 17:23:41 -0700451// icon | (200dp)
452// gap | 68dp 68dp 56dp 112dp
453// text | (14sp)
454// gap | 32dp 32dp 26dp 52dp
455// progress | (2dp)
Tao Bao3250f722017-06-29 14:32:05 -0700456// gap |
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700457
Tao Bao3250f722017-06-29 14:32:05 -0700458// Note that "baseline" is actually the *top* of each icon (because that's how our drawing routines
459// work), so that's the more useful measurement for calling code. We use even top and bottom gaps.
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700460
Elliott Hughes6d089a92016-07-08 17:23:41 -0700461enum Layout { PORTRAIT = 0, PORTRAIT_LARGE = 1, LANDSCAPE = 2, LANDSCAPE_LARGE = 3, LAYOUT_MAX };
Tao Bao3250f722017-06-29 14:32:05 -0700462enum Dimension { TEXT = 0, ICON = 1, DIMENSION_MAX };
Elliott Hughes6d089a92016-07-08 17:23:41 -0700463static constexpr int kLayouts[LAYOUT_MAX][DIMENSION_MAX] = {
Tianjie Xu8f397302018-08-20 13:40:47 -0700464 { 32, 68 }, // PORTRAIT
465 { 32, 68 }, // PORTRAIT_LARGE
466 { 26, 56 }, // LANDSCAPE
467 { 52, 112 }, // LANDSCAPE_LARGE
Elliott Hughes6d089a92016-07-08 17:23:41 -0700468};
469
Tao Bao99b2d772017-06-23 22:47:03 -0700470int ScreenRecoveryUI::GetAnimationBaseline() const {
Tao Baoda409fb2018-10-21 23:36:26 -0700471 return GetTextBaseline() - PixelsFromDp(kLayouts[layout_][ICON]) -
472 gr_get_height(loop_frames_[0].get());
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700473}
474
Tao Bao99b2d772017-06-23 22:47:03 -0700475int ScreenRecoveryUI::GetTextBaseline() const {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700476 return GetProgressBaseline() - PixelsFromDp(kLayouts[layout_][TEXT]) -
Tao Baoda409fb2018-10-21 23:36:26 -0700477 gr_get_height(installing_text_.get());
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700478}
479
Tao Bao99b2d772017-06-23 22:47:03 -0700480int ScreenRecoveryUI::GetProgressBaseline() const {
Tao Baoda409fb2018-10-21 23:36:26 -0700481 int elements_sum = gr_get_height(loop_frames_[0].get()) + PixelsFromDp(kLayouts[layout_][ICON]) +
482 gr_get_height(installing_text_.get()) + PixelsFromDp(kLayouts[layout_][TEXT]) +
483 gr_get_height(progress_bar_fill_.get());
Luke Song92eda4d2017-09-19 10:51:35 -0700484 int bottom_gap = (ScreenHeight() - elements_sum) / 2;
Tao Baoda409fb2018-10-21 23:36:26 -0700485 return ScreenHeight() - bottom_gap - gr_get_height(progress_bar_fill_.get());
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700486}
487
Doug Zongker211aebc2011-10-28 15:13:10 -0700488// Clear the screen and draw the currently selected background icon (if any).
489// Should only be called with updateMutex locked.
Elliott Hughes498cda62016-04-14 16:49:04 -0700490void ScreenRecoveryUI::draw_background_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700491 pagesIdentical = false;
492 gr_color(0, 0, 0, 255);
493 gr_clear();
Tao Baoda409fb2018-10-21 23:36:26 -0700494 if (current_icon_ != NONE) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700495 if (max_stage != -1) {
Tao Baoda409fb2018-10-21 23:36:26 -0700496 int stage_height = gr_get_height(stage_marker_empty_.get());
497 int stage_width = gr_get_width(stage_marker_empty_.get());
498 int x = (ScreenWidth() - max_stage * gr_get_width(stage_marker_empty_.get())) / 2;
Tao Bao0bc88de2018-07-31 14:53:16 -0700499 int y = ScreenHeight() - stage_height - margin_height_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700500 for (int i = 0; i < max_stage; ++i) {
Tao Baoda409fb2018-10-21 23:36:26 -0700501 const auto& stage_surface = (i < stage) ? stage_marker_fill_ : stage_marker_empty_;
502 DrawSurface(stage_surface.get(), 0, 0, stage_width, stage_height, x, y);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700503 x += stage_width;
504 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700505 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700506
Tao Baoda409fb2018-10-21 23:36:26 -0700507 const auto& text_surface = GetCurrentText();
Luke Song92eda4d2017-09-19 10:51:35 -0700508 int text_x = (ScreenWidth() - gr_get_width(text_surface)) / 2;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700509 int text_y = GetTextBaseline();
510 gr_color(255, 255, 255, 255);
Luke Song92eda4d2017-09-19 10:51:35 -0700511 DrawTextIcon(text_x, text_y, text_surface);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700512 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700513}
514
Tao Baoea78d862017-06-28 14:52:17 -0700515// Draws the animation and progress bar (if any) on the screen. Does not flip pages. Should only be
516// called with updateMutex locked.
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700517void ScreenRecoveryUI::draw_foreground_locked() {
Tao Baoda409fb2018-10-21 23:36:26 -0700518 if (current_icon_ != NONE) {
519 const auto& frame = GetCurrentFrame();
Tao Bao736d59c2017-01-03 10:15:33 -0800520 int frame_width = gr_get_width(frame);
521 int frame_height = gr_get_height(frame);
Luke Song92eda4d2017-09-19 10:51:35 -0700522 int frame_x = (ScreenWidth() - frame_width) / 2;
Tao Bao736d59c2017-01-03 10:15:33 -0800523 int frame_y = GetAnimationBaseline();
zhang sanshan0d81b892019-08-07 16:21:10 +0800524 if (frame_x >= 0 && frame_y >= 0 && (frame_x + frame_width) < ScreenWidth() &&
525 (frame_y + frame_height) < ScreenHeight())
526 DrawSurface(frame, 0, 0, frame_width, frame_height, frame_x, frame_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800527 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700528
Tao Bao736d59c2017-01-03 10:15:33 -0800529 if (progressBarType != EMPTY) {
Tao Baoda409fb2018-10-21 23:36:26 -0700530 int width = gr_get_width(progress_bar_empty_.get());
531 int height = gr_get_height(progress_bar_empty_.get());
Doug Zongker211aebc2011-10-28 15:13:10 -0700532
Luke Song92eda4d2017-09-19 10:51:35 -0700533 int progress_x = (ScreenWidth() - width) / 2;
Tao Bao736d59c2017-01-03 10:15:33 -0800534 int progress_y = GetProgressBaseline();
Doug Zongker211aebc2011-10-28 15:13:10 -0700535
Tao Bao736d59c2017-01-03 10:15:33 -0800536 // Erase behind the progress bar (in case this was a progress-only update)
537 gr_color(0, 0, 0, 255);
Luke Song92eda4d2017-09-19 10:51:35 -0700538 DrawFill(progress_x, progress_y, width, height);
Doug Zongker211aebc2011-10-28 15:13:10 -0700539
Tao Bao736d59c2017-01-03 10:15:33 -0800540 if (progressBarType == DETERMINATE) {
541 float p = progressScopeStart + progress * progressScopeSize;
542 int pos = static_cast<int>(p * width);
Doug Zongker211aebc2011-10-28 15:13:10 -0700543
Tao Bao736d59c2017-01-03 10:15:33 -0800544 if (rtl_locale_) {
545 // Fill the progress bar from right to left.
546 if (pos > 0) {
Tao Baoda409fb2018-10-21 23:36:26 -0700547 DrawSurface(progress_bar_fill_.get(), width - pos, 0, pos, height,
548 progress_x + width - pos, progress_y);
Doug Zongker211aebc2011-10-28 15:13:10 -0700549 }
Tao Bao736d59c2017-01-03 10:15:33 -0800550 if (pos < width - 1) {
Tao Baoda409fb2018-10-21 23:36:26 -0700551 DrawSurface(progress_bar_empty_.get(), 0, 0, width - pos, height, progress_x, progress_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800552 }
553 } else {
554 // Fill the progress bar from left to right.
555 if (pos > 0) {
Tao Baoda409fb2018-10-21 23:36:26 -0700556 DrawSurface(progress_bar_fill_.get(), 0, 0, pos, height, progress_x, progress_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800557 }
558 if (pos < width - 1) {
Tao Baoda409fb2018-10-21 23:36:26 -0700559 DrawSurface(progress_bar_empty_.get(), pos, 0, width - pos, height, progress_x + pos,
560 progress_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800561 }
562 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700563 }
Tao Bao736d59c2017-01-03 10:15:33 -0800564 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700565}
566
Jesse Chan968a21a2020-04-28 14:49:13 +0000567/* recovery dark: #7C4DFF
568 recovery light: #F890FF
569 fastbootd dark: #E65100
570 fastboot light: #FDD835 */
Tao Bao99b2d772017-06-23 22:47:03 -0700571void ScreenRecoveryUI::SetColor(UIElement e) const {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700572 switch (e) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700573 case UIElement::INFO:
Richard Hansenc7be4452020-05-04 15:58:49 -0400574 if (fastbootd_logo_enabled_)
575 gr_color(0xfd, 0xd8, 0x35, 255);
576 else
577 gr_color(0xf8, 0x90, 0xff, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700578 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700579 case UIElement::HEADER:
Jesse Chan968a21a2020-04-28 14:49:13 +0000580 if (fastbootd_logo_enabled_)
581 gr_color(0xfd, 0xd8,0x35, 255);
582 else
583 gr_color(0xf8, 0x90, 0xff, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700584 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700585 case UIElement::MENU:
Alessandro Astone5a57a942020-03-09 23:17:50 +0100586 gr_color(0xd8, 0xd8, 0xd8, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700587 break;
Jesse Chan968a21a2020-04-28 14:49:13 +0000588 case UIElement::MENU_SEL_BG:
589 case UIElement::SCROLLBAR:
590 if (fastbootd_logo_enabled_)
591 gr_color(0xe6, 0x51, 0x00, 255);
592 else
593 gr_color(0x7c, 0x4d, 0xff, 255);
594 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700595 case UIElement::MENU_SEL_BG_ACTIVE:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700596 gr_color(0, 156, 100, 255);
597 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700598 case UIElement::MENU_SEL_FG:
Jesse Chan968a21a2020-04-28 14:49:13 +0000599 if (fastbootd_logo_enabled_)
600 gr_color(0, 0, 0, 255);
601 else
602 gr_color(0xd8, 0xd8, 0xd8, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700603 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700604 case UIElement::LOG:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700605 gr_color(196, 196, 196, 255);
606 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700607 case UIElement::TEXT_FILL:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700608 gr_color(0, 0, 0, 160);
609 break;
610 default:
611 gr_color(255, 255, 255, 255);
612 break;
613 }
Doug Zongkerc0441d12013-07-31 11:28:24 -0700614}
Doug Zongker211aebc2011-10-28 15:13:10 -0700615
Tianjie Xu29d55752017-09-20 17:53:46 -0700616void ScreenRecoveryUI::SelectAndShowBackgroundText(const std::vector<std::string>& locales_entries,
617 size_t sel) {
618 SetLocale(locales_entries[sel]);
619 std::vector<std::string> text_name = { "erasing_text", "error_text", "installing_text",
620 "installing_security_text", "no_command_text" };
Tao Baoda409fb2018-10-21 23:36:26 -0700621 std::unordered_map<std::string, std::unique_ptr<GRSurface>> surfaces;
Tianjie Xu29d55752017-09-20 17:53:46 -0700622 for (const auto& name : text_name) {
Tao Baoda409fb2018-10-21 23:36:26 -0700623 auto text_image = LoadLocalizedBitmap(name);
Tianjie Xu29d55752017-09-20 17:53:46 -0700624 if (!text_image) {
625 Print("Failed to load %s\n", name.c_str());
626 return;
627 }
Tao Baoda409fb2018-10-21 23:36:26 -0700628 surfaces.emplace(name, std::move(text_image));
Tianjie Xu29d55752017-09-20 17:53:46 -0700629 }
630
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700631 std::lock_guard<std::mutex> lg(updateMutex);
Tianjie Xu29d55752017-09-20 17:53:46 -0700632 gr_color(0, 0, 0, 255);
633 gr_clear();
634
Tao Bao0bc88de2018-07-31 14:53:16 -0700635 int text_y = margin_height_;
636 int text_x = margin_width_;
Tianjie Xu29d55752017-09-20 17:53:46 -0700637 int line_spacing = gr_sys_font()->char_height; // Put some extra space between images.
638 // Write the header and descriptive texts.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700639 SetColor(UIElement::INFO);
Tianjie Xu29d55752017-09-20 17:53:46 -0700640 std::string header = "Show background text image";
Tao Bao93e46ad2018-05-02 14:57:21 -0700641 text_y += DrawTextLine(text_x, text_y, header, true);
Tianjie Xu29d55752017-09-20 17:53:46 -0700642 std::string locale_selection = android::base::StringPrintf(
Tao Bao39c49182018-05-07 22:50:33 -0700643 "Current locale: %s, %zu/%zu", locales_entries[sel].c_str(), sel + 1, locales_entries.size());
Tao Bao93e46ad2018-05-02 14:57:21 -0700644 // clang-format off
645 std::vector<std::string> instruction = {
646 locale_selection,
647 "Use volume up/down to switch locales and power to exit."
648 };
649 // clang-format on
Tianjie Xu29d55752017-09-20 17:53:46 -0700650 text_y += DrawWrappedTextLines(text_x, text_y, instruction);
651
652 // Iterate through the text images and display them in order for the current locale.
653 for (const auto& p : surfaces) {
654 text_y += line_spacing;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700655 SetColor(UIElement::LOG);
Tao Bao93e46ad2018-05-02 14:57:21 -0700656 text_y += DrawTextLine(text_x, text_y, p.first, false);
Tianjie Xu29d55752017-09-20 17:53:46 -0700657 gr_color(255, 255, 255, 255);
658 gr_texticon(text_x, text_y, p.second.get());
659 text_y += gr_get_height(p.second.get());
660 }
661 // Update the whole screen.
662 gr_flip();
Tianjie Xu29d55752017-09-20 17:53:46 -0700663}
664
Tao Bao39c49182018-05-07 22:50:33 -0700665void ScreenRecoveryUI::CheckBackgroundTextImages() {
Tianjie Xu29d55752017-09-20 17:53:46 -0700666 // Load a list of locales embedded in one of the resource files.
667 std::vector<std::string> locales_entries = get_locales_in_png("installing_text");
668 if (locales_entries.empty()) {
669 Print("Failed to load locales from the resource files\n");
670 return;
671 }
Tao Bao39c49182018-05-07 22:50:33 -0700672 std::string saved_locale = locale_;
Tianjie Xu29d55752017-09-20 17:53:46 -0700673 size_t selected = 0;
674 SelectAndShowBackgroundText(locales_entries, selected);
675
676 FlushKeys();
677 while (true) {
Alessandro Astone84042042020-10-04 18:11:40 +0200678 InputEvent evt = WaitInputEvent();
679 if (evt.type() == EventType::EXTRA) {
680 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) break;
681 }
682 if (evt.type() == EventType::KEY) {
683 if (evt.key() == KEY_POWER || evt.key() == KEY_ENTER) {
684 break;
685 } else if (evt.key() == KEY_UP || evt.key() == KEY_VOLUMEUP) {
686 selected = (selected == 0) ? locales_entries.size() - 1 : selected - 1;
687 SelectAndShowBackgroundText(locales_entries, selected);
688 } else if (evt.key() == KEY_DOWN || evt.key() == KEY_VOLUMEDOWN) {
689 selected = (selected == locales_entries.size() - 1) ? 0 : selected + 1;
690 SelectAndShowBackgroundText(locales_entries, selected);
691 }
Tianjie Xu29d55752017-09-20 17:53:46 -0700692 }
693 }
694
695 SetLocale(saved_locale);
696}
697
Luke Song92eda4d2017-09-19 10:51:35 -0700698int ScreenRecoveryUI::ScreenWidth() const {
699 return gr_fb_width();
700}
701
702int ScreenRecoveryUI::ScreenHeight() const {
703 return gr_fb_height();
704}
705
Tao Bao65815b62018-10-23 10:54:02 -0700706void ScreenRecoveryUI::DrawSurface(const GRSurface* surface, int sx, int sy, int w, int h, int dx,
Luke Song92eda4d2017-09-19 10:51:35 -0700707 int dy) const {
708 gr_blit(surface, sx, sy, w, h, dx, dy);
709}
710
Tao Baoea78d862017-06-28 14:52:17 -0700711int ScreenRecoveryUI::DrawHorizontalRule(int y) const {
Luke Song92eda4d2017-09-19 10:51:35 -0700712 gr_fill(0, y + 4, ScreenWidth(), y + 6);
Tao Baoea78d862017-06-28 14:52:17 -0700713 return 8;
Elliott Hughes95fc63e2015-04-10 19:12:01 -0700714}
715
Luke Songe2bd8762017-06-12 16:08:33 -0700716void ScreenRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const {
Alessandro Astone5a57a942020-03-09 23:17:50 +0100717 if (y + height > ScreenHeight())
718 height = ScreenHeight() - y;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700719 gr_fill(x, y, x + width, y + height);
Luke Songe2bd8762017-06-12 16:08:33 -0700720}
721
Alessandro Astone5a57a942020-03-09 23:17:50 +0100722void ScreenRecoveryUI::DrawScrollBar(int y, int height) const {
723 int x = ScreenWidth() - margin_width_;
724 int width = 8;
725 gr_fill(x - width, y, x, y + height);
726}
727
Luke Song92eda4d2017-09-19 10:51:35 -0700728void ScreenRecoveryUI::DrawFill(int x, int y, int w, int h) const {
729 gr_fill(x, y, w, h);
730}
731
Tao Bao65815b62018-10-23 10:54:02 -0700732void ScreenRecoveryUI::DrawTextIcon(int x, int y, const GRSurface* surface) const {
Luke Song92eda4d2017-09-19 10:51:35 -0700733 gr_texticon(x, y, surface);
734}
735
Tao Bao93e46ad2018-05-02 14:57:21 -0700736int ScreenRecoveryUI::DrawTextLine(int x, int y, const std::string& line, bool bold) const {
737 gr_text(gr_sys_font(), x, y, line.c_str(), bold);
Tao Baoea78d862017-06-28 14:52:17 -0700738 return char_height_ + 4;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700739}
740
Tao Bao93e46ad2018-05-02 14:57:21 -0700741int ScreenRecoveryUI::DrawTextLines(int x, int y, const std::vector<std::string>& lines) const {
Tao Baoea78d862017-06-28 14:52:17 -0700742 int offset = 0;
Tao Bao93e46ad2018-05-02 14:57:21 -0700743 for (const auto& line : lines) {
744 offset += DrawTextLine(x, y + offset, line, false);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700745 }
Tao Baoea78d862017-06-28 14:52:17 -0700746 return offset;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700747}
748
Tao Bao93e46ad2018-05-02 14:57:21 -0700749int ScreenRecoveryUI::DrawWrappedTextLines(int x, int y,
750 const std::vector<std::string>& lines) const {
Tao Bao452b4872018-05-09 11:52:09 -0700751 // Keep symmetrical margins based on the given offset (i.e. x).
752 size_t text_cols = (ScreenWidth() - x * 2) / char_width_;
Tao Bao2bbc6d62017-08-13 23:48:55 -0700753 int offset = 0;
Tao Bao93e46ad2018-05-02 14:57:21 -0700754 for (const auto& line : lines) {
Tao Bao2bbc6d62017-08-13 23:48:55 -0700755 size_t next_start = 0;
756 while (next_start < line.size()) {
Tao Bao452b4872018-05-09 11:52:09 -0700757 std::string sub = line.substr(next_start, text_cols + 1);
758 if (sub.size() <= text_cols) {
Tao Bao2bbc6d62017-08-13 23:48:55 -0700759 next_start += sub.size();
760 } else {
Tao Bao452b4872018-05-09 11:52:09 -0700761 // Line too long and must be wrapped to text_cols columns.
Tao Bao2bbc6d62017-08-13 23:48:55 -0700762 size_t last_space = sub.find_last_of(" \t\n");
763 if (last_space == std::string::npos) {
Tao Bao93e46ad2018-05-02 14:57:21 -0700764 // No space found, just draw as much as we can.
Tao Bao452b4872018-05-09 11:52:09 -0700765 sub.resize(text_cols);
766 next_start += text_cols;
Tao Bao2bbc6d62017-08-13 23:48:55 -0700767 } else {
768 sub.resize(last_space);
769 next_start += last_space + 1;
770 }
771 }
Tao Bao93e46ad2018-05-02 14:57:21 -0700772 offset += DrawTextLine(x, y + offset, sub, false);
Tao Bao2bbc6d62017-08-13 23:48:55 -0700773 }
774 }
775 return offset;
776}
777
Jerry Zhang0e577ee2018-05-07 11:21:10 -0700778void ScreenRecoveryUI::SetTitle(const std::vector<std::string>& lines) {
779 title_lines_ = lines;
780}
781
Tianjie Xue5032212019-07-23 13:23:29 -0700782std::vector<std::string> ScreenRecoveryUI::GetMenuHelpMessage() const {
783 // clang-format off
784 static std::vector<std::string> REGULAR_HELP{
Richard Hansen5dd4f972020-05-13 10:07:36 -0400785 "Use the volume up/down keys to navigate.",
786 "Use the power key to select.",
Tianjie Xue5032212019-07-23 13:23:29 -0700787 };
788 static std::vector<std::string> LONG_PRESS_HELP{
789 "Any button cycles highlight.",
790 "Long-press activates.",
791 };
Richard Hansenef8b20c2020-05-12 18:18:43 -0400792 static const std::vector<std::string> NO_HELP = {};
Tianjie Xue5032212019-07-23 13:23:29 -0700793 // clang-format on
Richard Hansenef8b20c2020-05-12 18:18:43 -0400794 return HasTouchScreen() ? NO_HELP : HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP;
Tianjie Xue5032212019-07-23 13:23:29 -0700795}
796
Tao Bao171b4c42017-06-19 23:10:44 -0700797// Redraws everything on the screen. Does not flip pages. Should only be called with updateMutex
798// locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700799void ScreenRecoveryUI::draw_screen_locked() {
Tao Bao171b4c42017-06-19 23:10:44 -0700800 if (!show_text) {
801 draw_background_locked();
802 draw_foreground_locked();
803 return;
804 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700805
Tao Bao171b4c42017-06-19 23:10:44 -0700806 gr_color(0, 0, 0, 255);
807 gr_clear();
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700808
Tianjie Xue5032212019-07-23 13:23:29 -0700809 draw_menu_and_text_buffer_locked(GetMenuHelpMessage());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700810}
811
812// Draws the menu and text buffer on the screen. Should only be called with updateMutex locked.
Tao Bao93e46ad2018-05-02 14:57:21 -0700813void ScreenRecoveryUI::draw_menu_and_text_buffer_locked(
814 const std::vector<std::string>& help_message) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700815 int y = margin_height_;
David Anderson983e2d52019-01-02 11:35:38 -0800816
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700817 if (menu_) {
Alessandro Astone56b46e12020-03-29 15:08:09 +0200818 auto& logo = fastbootd_logo_enabled_ ? fastbootd_logo_ : lineage_logo_;
Richard Hansen07d73332020-05-12 18:08:56 -0400819 auto logo_width = gr_get_width(logo.get());
820 auto logo_height = gr_get_height(logo.get());
821 auto centered_x = ScreenWidth() / 2 - logo_width / 2;
822 DrawSurface(logo.get(), 0, 0, logo_width, logo_height, centered_x, y);
823 y += logo_height;
Tao Bao171b4c42017-06-19 23:10:44 -0700824
Richard Hansen07d73332020-05-12 18:08:56 -0400825 if (!menu_->IsMain()) {
826 auto icon_w = gr_get_width(back_icon_.get());
827 auto icon_h = gr_get_height(back_icon_.get());
828 auto icon_x = centered_x / 2 - icon_w / 2;
829 auto icon_y = y - logo_height / 2 - icon_h / 2;
830 gr_blit(back_icon_sel_ && menu_->selection() == -1 ? back_icon_sel_.get() : back_icon_.get(),
831 0, 0, icon_w, icon_h, icon_x, icon_y);
Alessandro Astone5a57a942020-03-09 23:17:50 +0100832 }
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700833
Richard Hansen07d73332020-05-12 18:08:56 -0400834 int x = margin_width_ + kMenuIndent;
835 if (!title_lines_.empty()) {
836 SetColor(UIElement::INFO);
837 y += DrawTextLines(x, y, title_lines_);
838 }
Tianjie Xu66dbf632018-10-11 16:54:50 -0700839 y += menu_->DrawHeader(x, y);
Alessandro Astone84042042020-10-04 18:11:40 +0200840 menu_start_y_ = y + 12; // Skip horizontal rule and some margin
Alessandro Astone5cec9d82020-06-25 19:54:18 +0200841 menu_->SetMenuHeight(std::max(0, ScreenHeight() - menu_start_y_));
Tianjie Xu66dbf632018-10-11 16:54:50 -0700842 y += menu_->DrawItems(x, y, ScreenWidth(), IsLongPress());
Richard Hansenef8b20c2020-05-12 18:18:43 -0400843 if (!help_message.empty()) {
844 y += MenuItemPadding();
845 SetColor(UIElement::INFO);
846 y += DrawTextLines(x, y, help_message);
847 }
Tao Bao171b4c42017-06-19 23:10:44 -0700848 }
849
850 // Display from the bottom up, until we hit the top of the screen, the bottom of the menu, or
851 // we've displayed the entire text buffer.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700852 SetColor(UIElement::LOG);
Tao Baocb5524c2017-09-08 21:25:32 -0700853 int row = text_row_;
Tao Bao171b4c42017-06-19 23:10:44 -0700854 size_t count = 0;
Tao Bao0bc88de2018-07-31 14:53:16 -0700855 for (int ty = ScreenHeight() - margin_height_ - char_height_; ty >= y && count < text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700856 ty -= char_height_, ++count) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700857 DrawTextLine(margin_width_, ty, text_[row], false);
Tao Bao171b4c42017-06-19 23:10:44 -0700858 --row;
859 if (row < 0) row = text_rows_ - 1;
860 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700861}
862
863// Redraw everything on the screen and flip the screen (make it visible).
864// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700865void ScreenRecoveryUI::update_screen_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700866 draw_screen_locked();
867 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700868}
869
870// Updates only the progress bar, if possible, otherwise redraws the screen.
871// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700872void ScreenRecoveryUI::update_progress_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700873 if (show_text || !pagesIdentical) {
874 draw_screen_locked(); // Must redraw the whole screen
875 pagesIdentical = true;
876 } else {
877 draw_foreground_locked(); // Draw only the progress bar and overlays
878 }
879 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700880}
881
Elliott Hughes985022a2015-04-13 13:04:32 -0700882void ScreenRecoveryUI::ProgressThreadLoop() {
Tao Bao0bc88de2018-07-31 14:53:16 -0700883 double interval = 1.0 / animation_fps_;
Tao Bao26ea9592018-05-09 16:32:02 -0700884 while (!progress_thread_stopped_) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700885 double start = now();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700886 bool redraw = false;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700887 {
888 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker211aebc2011-10-28 15:13:10 -0700889
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700890 // update the installation animation, if active
891 // skip this if we have a text overlay (too expensive to update)
Tao Baoda409fb2018-10-21 23:36:26 -0700892 if ((current_icon_ == INSTALLING_UPDATE || current_icon_ == ERASING) && !show_text) {
893 if (!intro_done_) {
894 if (current_frame_ == intro_frames_.size() - 1) {
895 intro_done_ = true;
896 current_frame_ = 0;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700897 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700898 ++current_frame_;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700899 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700900 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700901 current_frame_ = (current_frame_ + 1) % loop_frames_.size();
Doug Zongker211aebc2011-10-28 15:13:10 -0700902 }
903
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700904 redraw = true;
905 }
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700906
907 // move the progress bar forward on timed intervals, if configured
908 int duration = progressScopeDuration;
909 if (progressBarType == DETERMINATE && duration > 0) {
910 double elapsed = now() - progressScopeTime;
911 float p = 1.0 * elapsed / duration;
912 if (p > 1.0) p = 1.0;
913 if (p > progress) {
914 progress = p;
915 redraw = true;
916 }
917 }
918
919 if (redraw) update_progress_locked();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700920 }
921
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700922 double end = now();
923 // minimum of 20ms delay between frames
924 double delay = interval - (end - start);
925 if (delay < 0.02) delay = 0.02;
926 usleep(static_cast<useconds_t>(delay * 1000000));
927 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700928}
929
Tao Baoda409fb2018-10-21 23:36:26 -0700930std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadBitmap(const std::string& filename) {
931 GRSurface* surface;
932 if (auto result = res_create_display_surface(filename.c_str(), &surface); result < 0) {
933 LOG(ERROR) << "Failed to load bitmap " << filename << " (error " << result << ")";
934 return nullptr;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700935 }
Tao Baoda409fb2018-10-21 23:36:26 -0700936 return std::unique_ptr<GRSurface>(surface);
Doug Zongkereac881c2014-03-07 09:21:25 -0800937}
938
Tao Baoda409fb2018-10-21 23:36:26 -0700939std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadLocalizedBitmap(const std::string& filename) {
940 GRSurface* surface;
xunchang9d05c8a2019-04-16 12:07:42 -0700941 auto result = res_create_localized_alpha_surface(filename.c_str(), locale_.c_str(), &surface);
942 if (result == 0) {
943 return std::unique_ptr<GRSurface>(surface);
Tao Bao736d59c2017-01-03 10:15:33 -0800944 }
xunchang9d05c8a2019-04-16 12:07:42 -0700945
946 result = res_create_localized_alpha_surface(filename.c_str(), DEFAULT_LOCALE, &surface);
947 if (result == 0) {
948 return std::unique_ptr<GRSurface>(surface);
949 }
950
xunchang9d05c8a2019-04-16 12:07:42 -0700951 return nullptr;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700952}
953
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700954static char** Alloc2d(size_t rows, size_t cols) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700955 char** result = new char*[rows];
956 for (size_t i = 0; i < rows; ++i) {
957 result[i] = new char[cols];
958 memset(result[i], 0, cols);
959 }
960 return result;
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700961}
962
Tianjie Xu35926c42016-04-28 18:06:26 -0700963// Choose the right background string to display during update.
964void ScreenRecoveryUI::SetSystemUpdateText(bool security_update) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700965 if (security_update) {
Tao Baoda409fb2018-10-21 23:36:26 -0700966 installing_text_ = LoadLocalizedBitmap("installing_security_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700967 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700968 installing_text_ = LoadLocalizedBitmap("installing_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700969 }
970 Redraw();
Tianjie Xu35926c42016-04-28 18:06:26 -0700971}
972
Sen Jiangd5304492016-12-09 16:20:49 -0800973bool ScreenRecoveryUI::InitTextParams() {
Tao Baoba4edb32018-06-13 11:22:50 -0700974 // gr_init() would return successfully on font initialization failure.
975 if (gr_sys_font() == nullptr) {
Tao Bao171b4c42017-06-19 23:10:44 -0700976 return false;
977 }
Tao Bao171b4c42017-06-19 23:10:44 -0700978 gr_font_size(gr_sys_font(), &char_width_, &char_height_);
Alessandro Astone5a57a942020-03-09 23:17:50 +0100979 gr_font_size(gr_menu_font(), &menu_char_width_, &menu_char_height_);
Tao Bao0bc88de2018-07-31 14:53:16 -0700980 text_rows_ = (ScreenHeight() - margin_height_ * 2) / char_height_;
981 text_cols_ = (ScreenWidth() - margin_width_ * 2) / char_width_;
Tao Bao171b4c42017-06-19 23:10:44 -0700982 return true;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -0700983}
984
Tianjie Xub99e6062018-10-16 15:13:09 -0700985bool ScreenRecoveryUI::LoadWipeDataMenuText() {
Tianjie Xu1a0a30a2018-10-25 15:22:07 -0700986 // Ignores the errors since the member variables will stay as nullptr.
987 cancel_wipe_data_text_ = LoadLocalizedBitmap("cancel_wipe_data_text");
988 factory_data_reset_text_ = LoadLocalizedBitmap("factory_data_reset_text");
989 try_again_text_ = LoadLocalizedBitmap("try_again_text");
990 wipe_data_confirmation_text_ = LoadLocalizedBitmap("wipe_data_confirmation_text");
991 wipe_data_menu_header_text_ = LoadLocalizedBitmap("wipe_data_menu_header_text");
Tianjie Xub99e6062018-10-16 15:13:09 -0700992 return true;
993}
994
Mark Salyzyn30017e72020-05-13 12:39:12 -0700995static bool InitGraphics() {
996 // Timeout is same as init wait for file default of 5 seconds and is arbitrary
997 const unsigned timeout = 500; // 10ms increments
998 for (auto retry = timeout; retry > 0; --retry) {
999 if (gr_init() == 0) {
1000 if (retry < timeout) {
1001 // Log message like init wait for file completion log for consistency.
1002 LOG(WARNING) << "wait for 'graphics' took " << ((timeout - retry) * 10) << "ms";
1003 }
1004 return true;
1005 }
1006 std::this_thread::sleep_for(10ms);
1007 }
1008 // Log message like init wait for file timeout log for consistency.
1009 LOG(ERROR) << "timeout wait for 'graphics' took " << (timeout * 10) << "ms";
1010 return false;
1011}
1012
Tao Bao736d59c2017-01-03 10:15:33 -08001013bool ScreenRecoveryUI::Init(const std::string& locale) {
1014 RecoveryUI::Init(locale);
Tao Baoefb49ad2017-01-31 23:03:10 -08001015
Mark Salyzyn30017e72020-05-13 12:39:12 -07001016 if (!InitGraphics()) {
Tao Baoba4edb32018-06-13 11:22:50 -07001017 return false;
1018 }
fredchiouf2f5aa22022-02-11 16:39:53 +08001019 is_graphics_available = true;
Tao Baoba4edb32018-06-13 11:22:50 -07001020
Tao Bao736d59c2017-01-03 10:15:33 -08001021 if (!InitTextParams()) {
1022 return false;
1023 }
Alessandro Astone5a57a942020-03-09 23:17:50 +01001024 menu_draw_funcs_ = std::make_unique<MenuDrawFunctions>(*this);
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001025
Michael Bestas19630862019-03-23 17:28:22 +02001026 if (blank_unblank_on_init_) {
1027 gr_fb_blank(true);
1028 gr_fb_blank(false);
1029 }
1030
Tao Bao736d59c2017-01-03 10:15:33 -08001031 // Are we portrait or landscape?
1032 layout_ = (gr_fb_width() > gr_fb_height()) ? LANDSCAPE : PORTRAIT;
1033 // Are we the large variant of our base layout?
1034 if (gr_fb_height() > PixelsFromDp(800)) ++layout_;
Elliott Hughesfaf36e02016-04-20 17:22:16 -07001035
Tao Bao736d59c2017-01-03 10:15:33 -08001036 text_ = Alloc2d(text_rows_, text_cols_ + 1);
1037 file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
Doug Zongker55a36ac2013-03-04 15:49:02 -08001038
Tao Bao736d59c2017-01-03 10:15:33 -08001039 text_col_ = text_row_ = 0;
Doug Zongker211aebc2011-10-28 15:13:10 -07001040
Tao Baoefb49ad2017-01-31 23:03:10 -08001041 // Set up the locale info.
1042 SetLocale(locale);
1043
Tao Baoda409fb2018-10-21 23:36:26 -07001044 error_icon_ = LoadBitmap("icon_error");
Doug Zongker02ec6b82012-08-22 17:26:40 -07001045
Tao Baoda409fb2018-10-21 23:36:26 -07001046 progress_bar_empty_ = LoadBitmap("progress_empty");
1047 progress_bar_fill_ = LoadBitmap("progress_fill");
1048 stage_marker_empty_ = LoadBitmap("stage_empty");
1049 stage_marker_fill_ = LoadBitmap("stage_fill");
Elliott Hughes498cda62016-04-14 16:49:04 -07001050
Tao Baoda409fb2018-10-21 23:36:26 -07001051 erasing_text_ = LoadLocalizedBitmap("erasing_text");
1052 no_command_text_ = LoadLocalizedBitmap("no_command_text");
1053 error_text_ = LoadLocalizedBitmap("error_text");
Doug Zongker211aebc2011-10-28 15:13:10 -07001054
Alessandro Astone5a57a942020-03-09 23:17:50 +01001055 back_icon_ = LoadBitmap("ic_back");
1056 back_icon_sel_ = LoadBitmap("ic_back_sel");
Alessandro Astone957d98e2020-02-26 17:25:54 +01001057 if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) ||
1058 android::base::GetBoolProperty("ro.fastbootd.available", false)) {
Alessandro Astone56b46e12020-03-29 15:08:09 +02001059 lineage_logo_ = LoadBitmap("logo_image_switch");
David Anderson983e2d52019-01-02 11:35:38 -08001060 fastbootd_logo_ = LoadBitmap("fastbootd");
Alessandro Astone56b46e12020-03-29 15:08:09 +02001061 } else {
1062 lineage_logo_ = LoadBitmap("logo_image");
David Anderson983e2d52019-01-02 11:35:38 -08001063 }
1064
Tao Baoda409fb2018-10-21 23:36:26 -07001065 // Background text for "installing_update" could be "installing update" or
1066 // "installing security update". It will be set after Init() according to the commands in BCB.
1067 installing_text_.reset();
Elliott Hughes498cda62016-04-14 16:49:04 -07001068
Tianjie Xub99e6062018-10-16 15:13:09 -07001069 LoadWipeDataMenuText();
1070
Tao Bao736d59c2017-01-03 10:15:33 -08001071 LoadAnimation();
Doug Zongker02ec6b82012-08-22 17:26:40 -07001072
Tao Bao26ea9592018-05-09 16:32:02 -07001073 // Keep the progress bar updated, even when the process is otherwise busy.
1074 progress_thread_ = std::thread(&ScreenRecoveryUI::ProgressThreadLoop, this);
Sen Jiangd5304492016-12-09 16:20:49 -08001075
fredchiouf2f5aa22022-02-11 16:39:53 +08001076 // set the callback for hall sensor event
1077 (void)ev_sync_sw_state([this](auto&& a, auto&& b) { return this->SetSwCallback(a, b);});
1078
Tao Bao736d59c2017-01-03 10:15:33 -08001079 return true;
Doug Zongker211aebc2011-10-28 15:13:10 -07001080}
1081
Tao Bao551d2c32018-05-09 20:53:13 -07001082std::string ScreenRecoveryUI::GetLocale() const {
Jerry Zhang2dea53e2018-05-02 17:15:03 -07001083 return locale_;
1084}
1085
Elliott Hughes498cda62016-04-14 16:49:04 -07001086void ScreenRecoveryUI::LoadAnimation() {
Tao Bao6cd81682018-05-03 21:53:11 -07001087 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(Paths::Get().resource_dir().c_str()),
1088 closedir);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001089 dirent* de;
1090 std::vector<std::string> intro_frame_names;
Joshua Lambert94a83be2019-12-09 20:24:18 +00001091 std::vector<std::string> loop_frame_names;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001092
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001093 while ((de = readdir(dir.get())) != nullptr) {
1094 int value, num_chars;
1095 if (sscanf(de->d_name, "intro%d%n.png", &value, &num_chars) == 1) {
1096 intro_frame_names.emplace_back(de->d_name, num_chars);
1097 } else if (sscanf(de->d_name, "loop%d%n.png", &value, &num_chars) == 1) {
Joshua Lambert94a83be2019-12-09 20:24:18 +00001098 loop_frame_names.emplace_back(de->d_name, num_chars);
Elliott Hughes498cda62016-04-14 16:49:04 -07001099 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001100 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001101
Tao Baoda409fb2018-10-21 23:36:26 -07001102 size_t intro_frames = intro_frame_names.size();
1103 size_t loop_frames = loop_frame_names.size();
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001104
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001105 // It's okay to not have an intro.
Tao Baoda409fb2018-10-21 23:36:26 -07001106 if (intro_frames == 0) intro_done_ = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001107 // But you must have an animation.
1108 if (loop_frames == 0) abort();
Elliott Hughes498cda62016-04-14 16:49:04 -07001109
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001110 std::sort(intro_frame_names.begin(), intro_frame_names.end());
1111 std::sort(loop_frame_names.begin(), loop_frame_names.end());
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001112
Tao Baoda409fb2018-10-21 23:36:26 -07001113 intro_frames_.clear();
1114 intro_frames_.reserve(intro_frames);
1115 for (const auto& frame_name : intro_frame_names) {
1116 intro_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001117 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001118
Tao Baoda409fb2018-10-21 23:36:26 -07001119 loop_frames_.clear();
1120 loop_frames_.reserve(loop_frames);
1121 for (const auto& frame_name : loop_frame_names) {
1122 loop_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001123 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001124}
1125
Elliott Hughes8de52072015-04-08 20:06:50 -07001126void ScreenRecoveryUI::SetBackground(Icon icon) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001127 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker02ec6b82012-08-22 17:26:40 -07001128
Tao Baoda409fb2018-10-21 23:36:26 -07001129 current_icon_ = icon;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001130 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001131}
1132
Elliott Hughes8de52072015-04-08 20:06:50 -07001133void ScreenRecoveryUI::SetProgressType(ProgressType type) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001134 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001135 if (progressBarType != type) {
1136 progressBarType = type;
1137 }
1138 progressScopeStart = 0;
1139 progressScopeSize = 0;
1140 progress = 0;
1141 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001142}
1143
Elliott Hughes8de52072015-04-08 20:06:50 -07001144void ScreenRecoveryUI::ShowProgress(float portion, float seconds) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001145 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001146 progressBarType = DETERMINATE;
1147 progressScopeStart += progressScopeSize;
1148 progressScopeSize = portion;
1149 progressScopeTime = now();
1150 progressScopeDuration = seconds;
1151 progress = 0;
1152 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001153}
1154
Elliott Hughes8de52072015-04-08 20:06:50 -07001155void ScreenRecoveryUI::SetProgress(float fraction) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001156 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001157 if (fraction < 0.0) fraction = 0.0;
1158 if (fraction > 1.0) fraction = 1.0;
1159 if (progressBarType == DETERMINATE && fraction > progress) {
1160 // Skip updates that aren't visibly different.
Tao Baoda409fb2018-10-21 23:36:26 -07001161 int width = gr_get_width(progress_bar_empty_.get());
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001162 float scale = width * progressScopeSize;
1163 if ((int)(progress * scale) != (int)(fraction * scale)) {
1164 progress = fraction;
1165 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001166 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001167 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001168}
1169
Doug Zongkerc87bab12013-11-25 13:53:25 -08001170void ScreenRecoveryUI::SetStage(int current, int max) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001171 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001172 stage = current;
1173 max_stage = max;
Doug Zongkerc87bab12013-11-25 13:53:25 -08001174}
1175
Tao Baob6918c72015-05-19 17:02:16 -07001176void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001177 std::string str;
1178 android::base::StringAppendV(&str, fmt, ap);
Doug Zongker211aebc2011-10-28 15:13:10 -07001179
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001180 if (copy_to_stdout) {
1181 fputs(str.c_str(), stdout);
1182 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001183
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001184 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001185 if (text_rows_ > 0 && text_cols_ > 0) {
1186 for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
1187 if (*ptr == '\n' || text_col_ >= text_cols_) {
Elliott Hughesc0491632015-05-06 12:40:05 -07001188 text_[text_row_][text_col_] = '\0';
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001189 text_col_ = 0;
1190 text_row_ = (text_row_ + 1) % text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001191 }
1192 if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
Doug Zongker211aebc2011-10-28 15:13:10 -07001193 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001194 text_[text_row_][text_col_] = '\0';
1195 update_screen_locked();
1196 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001197}
1198
Tao Baob6918c72015-05-19 17:02:16 -07001199void ScreenRecoveryUI::Print(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001200 va_list ap;
1201 va_start(ap, fmt);
1202 PrintV(fmt, true, ap);
1203 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001204}
1205
Tianjie Xu8f397302018-08-20 13:40:47 -07001206void ScreenRecoveryUI::PrintOnScreenOnly(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001207 va_list ap;
1208 va_start(ap, fmt);
1209 PrintV(fmt, false, ap);
1210 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001211}
1212
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001213void ScreenRecoveryUI::PutChar(char ch) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001214 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001215 if (ch != '\n') text_[text_row_][text_col_++] = ch;
1216 if (ch == '\n' || text_col_ >= text_cols_) {
1217 text_col_ = 0;
1218 ++text_row_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001219 }
Elliott Hughes8de52072015-04-08 20:06:50 -07001220}
1221
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001222void ScreenRecoveryUI::ClearText() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001223 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001224 text_col_ = 0;
1225 text_row_ = 0;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001226 for (size_t i = 0; i < text_rows_; ++i) {
1227 memset(text_[i], 0, text_cols_ + 1);
1228 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001229}
1230
1231void ScreenRecoveryUI::ShowFile(FILE* fp) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001232 std::vector<off_t> offsets;
1233 offsets.push_back(ftello(fp));
1234 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001235
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001236 struct stat sb;
1237 fstat(fileno(fp), &sb);
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001238
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001239 bool show_prompt = false;
1240 while (true) {
1241 if (show_prompt) {
1242 PrintOnScreenOnly("--(%d%% of %d bytes)--",
1243 static_cast<int>(100 * (double(ftello(fp)) / double(sb.st_size))),
1244 static_cast<int>(sb.st_size));
1245 Redraw();
1246 while (show_prompt) {
1247 show_prompt = false;
Alessandro Astone84042042020-10-04 18:11:40 +02001248 InputEvent evt = WaitInputEvent();
1249 if (evt.type() == EventType::EXTRA) {
1250 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) {
1251 return;
1252 }
1253 }
1254 if (evt.type() != EventType::KEY) {
1255 show_prompt = true;
1256 continue;
1257 }
1258 if (evt.key() == KEY_POWER || evt.key() == KEY_ENTER || evt.key() == KEY_BACKSPACE ||
1259 evt.key() == KEY_BACK || evt.key() == KEY_HOME || evt.key() == KEY_HOMEPAGE) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001260 return;
Alessandro Astone84042042020-10-04 18:11:40 +02001261 } else if (evt.key() == KEY_UP || evt.key() == KEY_VOLUMEUP || evt.key() == KEY_SCROLLUP) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001262 if (offsets.size() <= 1) {
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001263 show_prompt = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001264 } else {
1265 offsets.pop_back();
1266 fseek(fp, offsets.back(), SEEK_SET);
1267 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001268 } else {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001269 if (feof(fp)) {
1270 return;
1271 }
1272 offsets.push_back(ftello(fp));
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001273 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001274 }
1275 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001276 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001277
1278 int ch = getc(fp);
1279 if (ch == EOF) {
1280 while (text_row_ < text_rows_ - 1) PutChar('\n');
1281 show_prompt = true;
1282 } else {
1283 PutChar(ch);
1284 if (text_col_ == 0 && text_row_ >= text_rows_ - 1) {
1285 show_prompt = true;
1286 }
1287 }
1288 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001289}
1290
Tao Bao1d156b92018-05-02 12:43:18 -07001291void ScreenRecoveryUI::ShowFile(const std::string& filename) {
1292 std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(filename.c_str(), "re"), fclose);
1293 if (!fp) {
1294 Print(" Unable to open %s: %s\n", filename.c_str(), strerror(errno));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001295 return;
1296 }
Elliott Hughesc0491632015-05-06 12:40:05 -07001297
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001298 char** old_text = text_;
1299 size_t old_text_col = text_col_;
1300 size_t old_text_row = text_row_;
Elliott Hughesc0491632015-05-06 12:40:05 -07001301
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001302 // Swap in the alternate screen and clear it.
1303 text_ = file_viewer_text_;
1304 ClearText();
Elliott Hughesc0491632015-05-06 12:40:05 -07001305
Tao Bao1d156b92018-05-02 12:43:18 -07001306 ShowFile(fp.get());
Elliott Hughesc0491632015-05-06 12:40:05 -07001307
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001308 text_ = old_text;
1309 text_col_ = old_text_col;
1310 text_row_ = old_text_row;
Elliott Hughes8de52072015-04-08 20:06:50 -07001311}
1312
Tao Baoda409fb2018-10-21 23:36:26 -07001313std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(
1314 const GRSurface* graphic_header, const std::vector<const GRSurface*>& graphic_items,
1315 const std::vector<std::string>& text_headers, const std::vector<std::string>& text_items,
1316 size_t initial_selection) const {
Tianjie Xub99e6062018-10-16 15:13:09 -07001317 // horizontal unusable area: margin width + menu indent
1318 size_t max_width = ScreenWidth() - margin_width_ - kMenuIndent;
1319 // vertical unusable area: margin height + title lines + helper message + high light bar.
1320 // It is safe to reserve more space.
1321 size_t max_height = ScreenHeight() - margin_height_ - char_height_ * (title_lines_.size() + 3);
1322 if (GraphicMenu::Validate(max_width, max_height, graphic_header, graphic_items)) {
1323 return std::make_unique<GraphicMenu>(graphic_header, graphic_items, initial_selection, *this);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001324 }
Tianjie Xub99e6062018-10-16 15:13:09 -07001325
1326 fprintf(stderr, "Failed to initialize graphic menu, falling back to use the text menu.\n");
1327
1328 return CreateMenu(text_headers, text_items, initial_selection);
1329}
1330
1331std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(const std::vector<std::string>& text_headers,
1332 const std::vector<std::string>& text_items,
1333 size_t initial_selection) const {
Alessandro Astone5a57a942020-03-09 23:17:50 +01001334 int menu_char_width = MenuCharWidth();
1335 int menu_char_height = MenuCharHeight();
Alessandro Astone5a57a942020-03-09 23:17:50 +01001336 int menu_cols = (ScreenWidth() - margin_width_*2 - kMenuIndent) / menu_char_width;
1337 bool wrap_selection = !HasThreeButtons() && !HasTouchScreen();
Alessandro Astone5cec9d82020-06-25 19:54:18 +02001338 return std::make_unique<TextMenu>(wrap_selection, menu_cols, text_headers, text_items,
Alessandro Astone5a57a942020-03-09 23:17:50 +01001339 initial_selection, menu_char_height, *menu_draw_funcs_);
Doug Zongker211aebc2011-10-28 15:13:10 -07001340}
1341
1342int ScreenRecoveryUI::SelectMenu(int sel) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001343 std::lock_guard<std::mutex> lg(updateMutex);
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001344 if (menu_) {
1345 int old_sel = menu_->selection();
1346 sel = menu_->Select(sel);
Elliott Hughesfc06f872015-03-23 13:45:31 -07001347
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001348 if (sel != old_sel) {
1349 update_screen_locked();
1350 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001351 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001352 return sel;
Doug Zongker211aebc2011-10-28 15:13:10 -07001353}
1354
Alessandro Astone36645f02022-03-21 17:14:47 +01001355int ScreenRecoveryUI::SelectMenu(const Point& p) {
1356 // Correct position for overscan
1357 const Point point(p.x() - gr_overscan_offset_x(), p.y() - gr_overscan_offset_y());
1358
Alessandro Astone84042042020-10-04 18:11:40 +02001359 int new_sel = Device::kNoAction;
1360 std::lock_guard<std::mutex> lg(updateMutex);
1361 if (menu_) {
1362 if (!menu_->IsMain()) {
1363 // Back arrow hitbox
1364 const static int logo_width = gr_get_width(lineage_logo_.get());
1365 const static int logo_height = gr_get_height(lineage_logo_.get());
1366 const static int icon_w = gr_get_width(back_icon_.get());
1367 const static int icon_h = gr_get_height(back_icon_.get());
1368 const static int centered_x = ScreenWidth() / 2 - logo_width / 2;
1369 const static int icon_x = centered_x / 2 - icon_w / 2;
1370 const static int icon_y = margin_height_ + logo_height / 2 - icon_h / 2;
1371
1372 if (point.x() >= icon_x && point.x() <= icon_x + icon_w &&
1373 point.y() >= icon_y && point.y() <= icon_y + icon_h) {
1374 return Device::kGoBack;
1375 }
1376 }
1377
1378 if (point.y() >= menu_start_y_ &&
1379 point.y() < menu_start_y_ + menu_->ItemsCount() * MenuItemHeight()) {
1380 int old_sel = menu_->selection();
1381 int relative_sel = (point.y() - menu_start_y_) / MenuItemHeight();
1382 new_sel = menu_->SelectVisible(relative_sel);
1383 if (new_sel != -1 && new_sel != old_sel) {
1384 update_screen_locked();
1385 }
1386 }
1387 }
1388 return new_sel;
1389}
1390
1391int ScreenRecoveryUI::ScrollMenu(int updown) {
1392 std::lock_guard<std::mutex> lg(updateMutex);
1393 int sel = Device::kNoAction;
1394 if (menu_) {
1395 sel = menu_->Scroll(updown);
1396 update_screen_locked();
1397 }
1398 return sel;
1399}
1400
Tianjie Xub99e6062018-10-16 15:13:09 -07001401size_t ScreenRecoveryUI::ShowMenu(std::unique_ptr<Menu>&& menu, bool menu_only,
Tom Marshalldd9b45d2019-01-04 14:37:31 -08001402 const std::function<int(int, bool)>& key_handler,
1403 bool refreshable) {
Tao Bao3aec6962018-04-20 09:24:58 -07001404 // Throw away keys pressed previously, so user doesn't accidentally trigger menu items.
1405 FlushKeys();
1406
Jerry Zhangb76af932018-05-22 12:08:35 -07001407 // If there is a key interrupt in progress, return KeyError::INTERRUPTED without starting the
1408 // menu.
1409 if (IsKeyInterrupted()) return static_cast<size_t>(KeyError::INTERRUPTED);
1410
Tianjie Xub99e6062018-10-16 15:13:09 -07001411 CHECK(menu != nullptr);
Tao Bao3aec6962018-04-20 09:24:58 -07001412
Tianjie Xub99e6062018-10-16 15:13:09 -07001413 // Starts and displays the menu
1414 menu_ = std::move(menu);
1415 Redraw();
1416
1417 int selected = menu_->selection();
Tao Bao3aec6962018-04-20 09:24:58 -07001418 int chosen_item = -1;
1419 while (chosen_item < 0) {
Alessandro Astone84042042020-10-04 18:11:40 +02001420 InputEvent evt = WaitInputEvent();
1421 if (evt.type() == EventType::EXTRA) {
1422 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) {
1423 // WaitKey() was interrupted.
1424 return static_cast<size_t>(KeyError::INTERRUPTED);
1425 }
1426 if (evt.key() == static_cast<int>(KeyError::TIMED_OUT)) { // WaitKey() timed out.
1427 if (WasTextEverVisible()) {
1428 continue;
1429 } else {
1430 LOG(INFO) << "Timed out waiting for key input; rebooting.";
1431 menu_.reset();
1432 Redraw();
1433 return static_cast<size_t>(KeyError::TIMED_OUT);
1434 }
Tao Bao3aec6962018-04-20 09:24:58 -07001435 }
1436 }
1437
Alessandro Astone84042042020-10-04 18:11:40 +02001438 int action = Device::kNoAction;
1439 if (evt.type() == EventType::TOUCH) {
1440 int touch_sel = SelectMenu(evt.pos());
1441 if (touch_sel < 0) {
1442 action = touch_sel;
1443 } else {
1444 action = Device::kInvokeItem;
1445 selected = touch_sel;
1446 }
1447 } else {
1448 bool visible = IsTextVisible();
1449 action = key_handler(evt.key(), visible);
1450 }
Tom Marshalldd9b45d2019-01-04 14:37:31 -08001451
Tao Bao3aec6962018-04-20 09:24:58 -07001452 if (action < 0) {
1453 switch (action) {
1454 case Device::kHighlightUp:
1455 selected = SelectMenu(--selected);
1456 break;
1457 case Device::kHighlightDown:
1458 selected = SelectMenu(++selected);
1459 break;
Alessandro Astone84042042020-10-04 18:11:40 +02001460 case Device::kScrollUp:
1461 selected = ScrollMenu(-1);
1462 break;
1463 case Device::kScrollDown:
1464 selected = ScrollMenu(1);
1465 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001466 case Device::kInvokeItem:
Alessandro Astone5a57a942020-03-09 23:17:50 +01001467 if (selected < 0) {
1468 chosen_item = Device::kGoBack;
1469 } else {
1470 chosen_item = selected;
1471 }
Tao Bao3aec6962018-04-20 09:24:58 -07001472 break;
1473 case Device::kNoAction:
1474 break;
Tom Marshallf20b3e32017-08-24 13:50:01 +00001475 case Device::kGoBack:
1476 chosen_item = Device::kGoBack;
1477 break;
1478 case Device::kGoHome:
1479 chosen_item = Device::kGoHome;
1480 break;
Tom Marshall9f2f3282018-12-17 15:57:44 -08001481 case Device::kDoSideload:
1482 chosen_item = Device::kDoSideload;
1483 break;
Tom Marshalldd9b45d2019-01-04 14:37:31 -08001484 case Device::kRefresh:
1485 if (refreshable) {
1486 chosen_item = Device::kRefresh;
1487 }
1488 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001489 }
1490 } else if (!menu_only) {
1491 chosen_item = action;
1492 }
Tom Marshall9f2f3282018-12-17 15:57:44 -08001493
1494 if (chosen_item == Device::kGoBack || chosen_item == Device::kGoHome ||
Tom Marshalldd9b45d2019-01-04 14:37:31 -08001495 chosen_item == Device::kDoSideload || chosen_item == Device::kRefresh) {
Tom Marshallf20b3e32017-08-24 13:50:01 +00001496 break;
1497 }
Tao Bao3aec6962018-04-20 09:24:58 -07001498 }
1499
Tianjie Xub99e6062018-10-16 15:13:09 -07001500 menu_.reset();
Tianjie Xub99e6062018-10-16 15:13:09 -07001501
Tao Bao3aec6962018-04-20 09:24:58 -07001502 return chosen_item;
1503}
1504
Tianjie Xub99e6062018-10-16 15:13:09 -07001505size_t ScreenRecoveryUI::ShowMenu(const std::vector<std::string>& headers,
1506 const std::vector<std::string>& items, size_t initial_selection,
1507 bool menu_only,
Tom Marshalldd9b45d2019-01-04 14:37:31 -08001508 const std::function<int(int, bool)>& key_handler,
1509 bool refreshable) {
Tianjie Xub99e6062018-10-16 15:13:09 -07001510 auto menu = CreateMenu(headers, items, initial_selection);
1511 if (menu == nullptr) {
1512 return initial_selection;
1513 }
1514
Tom Marshalldd9b45d2019-01-04 14:37:31 -08001515 return ShowMenu(std::move(menu), menu_only, key_handler, refreshable);
Tianjie Xub99e6062018-10-16 15:13:09 -07001516}
1517
1518size_t ScreenRecoveryUI::ShowPromptWipeDataMenu(const std::vector<std::string>& backup_headers,
1519 const std::vector<std::string>& backup_items,
1520 const std::function<int(int, bool)>& key_handler) {
Alessandro Astone303854d2020-10-05 15:11:36 +02001521 auto wipe_data_menu = CreateMenu(backup_headers, backup_items, 0);
Tianjie Xub99e6062018-10-16 15:13:09 -07001522 if (wipe_data_menu == nullptr) {
1523 return 0;
1524 }
1525
1526 return ShowMenu(std::move(wipe_data_menu), true, key_handler);
1527}
1528
Tianjie Xu1a0a30a2018-10-25 15:22:07 -07001529size_t ScreenRecoveryUI::ShowPromptWipeDataConfirmationMenu(
1530 const std::vector<std::string>& backup_headers, const std::vector<std::string>& backup_items,
1531 const std::function<int(int, bool)>& key_handler) {
1532 auto confirmation_menu =
1533 CreateMenu(wipe_data_confirmation_text_.get(),
1534 { cancel_wipe_data_text_.get(), factory_data_reset_text_.get() }, backup_headers,
1535 backup_items, 0);
1536 if (confirmation_menu == nullptr) {
1537 return 0;
1538 }
1539
1540 return ShowMenu(std::move(confirmation_menu), true, key_handler);
1541}
1542
Elliott Hughes8de52072015-04-08 20:06:50 -07001543bool ScreenRecoveryUI::IsTextVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001544 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001545 int visible = show_text;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001546 return visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001547}
1548
Elliott Hughes8de52072015-04-08 20:06:50 -07001549bool ScreenRecoveryUI::WasTextEverVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001550 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001551 int ever_visible = show_text_ever;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001552 return ever_visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001553}
1554
Elliott Hughes8de52072015-04-08 20:06:50 -07001555void ScreenRecoveryUI::ShowText(bool visible) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001556 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001557 show_text = visible;
1558 if (show_text) show_text_ever = true;
1559 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001560}
Doug Zongkerc0441d12013-07-31 11:28:24 -07001561
Elliott Hughes8de52072015-04-08 20:06:50 -07001562void ScreenRecoveryUI::Redraw() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001563 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001564 update_screen_locked();
Doug Zongkerc0441d12013-07-31 11:28:24 -07001565}
Elliott Hughes642aaa72015-04-10 12:47:46 -07001566
1567void ScreenRecoveryUI::KeyLongPress(int) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001568 // Redraw so that if we're in the menu, the highlight
1569 // will change color to indicate a successful long press.
1570 Redraw();
Elliott Hughes642aaa72015-04-10 12:47:46 -07001571}
Tao Baoefb49ad2017-01-31 23:03:10 -08001572
1573void ScreenRecoveryUI::SetLocale(const std::string& new_locale) {
1574 locale_ = new_locale;
1575 rtl_locale_ = false;
1576
1577 if (!new_locale.empty()) {
Tao Bao347a6592018-05-08 15:58:29 -07001578 size_t separator = new_locale.find('-');
1579 // lang has the language prefix prior to the separator, or full string if none exists.
1580 std::string lang = new_locale.substr(0, separator);
Tao Baoefb49ad2017-01-31 23:03:10 -08001581
1582 // A bit cheesy: keep an explicit list of supported RTL languages.
1583 if (lang == "ar" || // Arabic
1584 lang == "fa" || // Persian (Farsi)
1585 lang == "he" || // Hebrew (new language code)
1586 lang == "iw" || // Hebrew (old language code)
1587 lang == "ur") { // Urdu
1588 rtl_locale_ = true;
1589 }
1590 }
1591}
fredchiouf2f5aa22022-02-11 16:39:53 +08001592
1593int ScreenRecoveryUI::SetSwCallback(int code, int value) {
1594 if (!is_graphics_available) { return -1; }
1595 if (code > SW_MAX) { return -1; }
1596 if (code != SW_LID) { return 0; }
1597
1598 /* detect dual display */
1599 if (!gr_has_multiple_connectors()) { return -1; }
1600
1601 /* turn off all screen */
1602 gr_fb_blank(true, DirectRenderManager::DRM_INNER);
1603 gr_fb_blank(true, DirectRenderManager::DRM_OUTER);
1604 gr_color(0, 0, 0, 255);
1605 gr_clear();
1606
1607 /* turn on the screen */
1608 gr_fb_blank(false, value);
1609 gr_flip();
1610
1611 /* set the retation */
1612 std::string rotation_str;
1613 if (value == DirectRenderManager::DRM_OUTER) {
1614 rotation_str =
1615 android::base::GetProperty("ro.minui.second_rotation", "ROTATION_NONE");
1616 } else {
1617 rotation_str =
1618 android::base::GetProperty("ro.minui.default_rotation", "ROTATION_NONE");
1619 }
1620
1621 if (rotation_str == "ROTATION_RIGHT") {
1622 gr_rotate(GRRotation::RIGHT);
1623 } else if (rotation_str == "ROTATION_DOWN") {
1624 gr_rotate(GRRotation::DOWN);
1625 } else if (rotation_str == "ROTATION_LEFT") {
1626 gr_rotate(GRRotation::LEFT);
1627 } else { // "ROTATION_NONE" or unknown string
1628 gr_rotate(GRRotation::NONE);
1629 }
1630 Redraw();
1631
1632 return 0;
1633}