blob: 76e6d763ae37377c1409a55d455671b08194afc7 [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_) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700818 int x = margin_width_ + kMenuIndent;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700819
Tianjie Xu66dbf632018-10-11 16:54:50 -0700820 SetColor(UIElement::INFO);
Jerry Zhang0e577ee2018-05-07 11:21:10 -0700821
Alessandro Astone56b46e12020-03-29 15:08:09 +0200822 auto& logo = fastbootd_logo_enabled_ ? fastbootd_logo_ : lineage_logo_;
823 if (logo && back_icon_) {
824 auto logo_width = gr_get_width(logo.get());
825 auto logo_height = gr_get_height(logo.get());
Alessandro Astone5a57a942020-03-09 23:17:50 +0100826 auto centered_x = ScreenWidth() / 2 - logo_width / 2;
Alessandro Astone56b46e12020-03-29 15:08:09 +0200827 DrawSurface(logo.get(), 0, 0, logo_width, logo_height, centered_x, y);
Alessandro Astone5a57a942020-03-09 23:17:50 +0100828 y += logo_height;
Tao Bao171b4c42017-06-19 23:10:44 -0700829
Alessandro Astone5a57a942020-03-09 23:17:50 +0100830 if (!menu_->IsMain()) {
831 auto icon_w = gr_get_width(back_icon_.get());
832 auto icon_h = gr_get_height(back_icon_.get());
833 auto icon_x = centered_x / 2 - icon_w / 2;
834 auto icon_y = y - logo_height / 2 - icon_h / 2;
835 gr_blit(back_icon_sel_ && menu_->selection() == -1 ? back_icon_sel_.get() : back_icon_.get(),
836 0, 0, icon_w, icon_h, icon_x, icon_y);
837 }
Jesse Chan968a21a2020-04-28 14:49:13 +0000838 y += MenuItemPadding();
Alessandro Astone5a57a942020-03-09 23:17:50 +0100839 } else {
840 for (size_t i = 0; i < title_lines_.size(); i++) {
841 y += DrawTextLine(x, y, title_lines_[i], i == 0);
842 }
Alessandro Astone5a57a942020-03-09 23:17:50 +0100843 }
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700844
Tianjie Xu66dbf632018-10-11 16:54:50 -0700845 y += menu_->DrawHeader(x, y);
Alessandro Astone84042042020-10-04 18:11:40 +0200846 menu_start_y_ = y + 12; // Skip horizontal rule and some margin
Alessandro Astone5cec9d82020-06-25 19:54:18 +0200847 menu_->SetMenuHeight(std::max(0, ScreenHeight() - menu_start_y_));
Tianjie Xu66dbf632018-10-11 16:54:50 -0700848 y += menu_->DrawItems(x, y, ScreenWidth(), IsLongPress());
Richard Hansenef8b20c2020-05-12 18:18:43 -0400849 if (!help_message.empty()) {
850 y += MenuItemPadding();
851 SetColor(UIElement::INFO);
852 y += DrawTextLines(x, y, help_message);
853 }
Tao Bao171b4c42017-06-19 23:10:44 -0700854 }
855
856 // Display from the bottom up, until we hit the top of the screen, the bottom of the menu, or
857 // we've displayed the entire text buffer.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700858 SetColor(UIElement::LOG);
Tao Baocb5524c2017-09-08 21:25:32 -0700859 int row = text_row_;
Tao Bao171b4c42017-06-19 23:10:44 -0700860 size_t count = 0;
Tao Bao0bc88de2018-07-31 14:53:16 -0700861 for (int ty = ScreenHeight() - margin_height_ - char_height_; ty >= y && count < text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700862 ty -= char_height_, ++count) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700863 DrawTextLine(margin_width_, ty, text_[row], false);
Tao Bao171b4c42017-06-19 23:10:44 -0700864 --row;
865 if (row < 0) row = text_rows_ - 1;
866 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700867}
868
869// Redraw everything on the screen and flip the screen (make it visible).
870// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700871void ScreenRecoveryUI::update_screen_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700872 draw_screen_locked();
873 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700874}
875
876// Updates only the progress bar, if possible, otherwise redraws the screen.
877// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700878void ScreenRecoveryUI::update_progress_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700879 if (show_text || !pagesIdentical) {
880 draw_screen_locked(); // Must redraw the whole screen
881 pagesIdentical = true;
882 } else {
883 draw_foreground_locked(); // Draw only the progress bar and overlays
884 }
885 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700886}
887
Elliott Hughes985022a2015-04-13 13:04:32 -0700888void ScreenRecoveryUI::ProgressThreadLoop() {
Tao Bao0bc88de2018-07-31 14:53:16 -0700889 double interval = 1.0 / animation_fps_;
Tao Bao26ea9592018-05-09 16:32:02 -0700890 while (!progress_thread_stopped_) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700891 double start = now();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700892 bool redraw = false;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700893 {
894 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker211aebc2011-10-28 15:13:10 -0700895
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700896 // update the installation animation, if active
897 // skip this if we have a text overlay (too expensive to update)
Tao Baoda409fb2018-10-21 23:36:26 -0700898 if ((current_icon_ == INSTALLING_UPDATE || current_icon_ == ERASING) && !show_text) {
899 if (!intro_done_) {
900 if (current_frame_ == intro_frames_.size() - 1) {
901 intro_done_ = true;
902 current_frame_ = 0;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700903 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700904 ++current_frame_;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700905 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700906 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700907 current_frame_ = (current_frame_ + 1) % loop_frames_.size();
Doug Zongker211aebc2011-10-28 15:13:10 -0700908 }
909
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700910 redraw = true;
911 }
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700912
913 // move the progress bar forward on timed intervals, if configured
914 int duration = progressScopeDuration;
915 if (progressBarType == DETERMINATE && duration > 0) {
916 double elapsed = now() - progressScopeTime;
917 float p = 1.0 * elapsed / duration;
918 if (p > 1.0) p = 1.0;
919 if (p > progress) {
920 progress = p;
921 redraw = true;
922 }
923 }
924
925 if (redraw) update_progress_locked();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700926 }
927
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700928 double end = now();
929 // minimum of 20ms delay between frames
930 double delay = interval - (end - start);
931 if (delay < 0.02) delay = 0.02;
932 usleep(static_cast<useconds_t>(delay * 1000000));
933 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700934}
935
Tao Baoda409fb2018-10-21 23:36:26 -0700936std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadBitmap(const std::string& filename) {
937 GRSurface* surface;
938 if (auto result = res_create_display_surface(filename.c_str(), &surface); result < 0) {
939 LOG(ERROR) << "Failed to load bitmap " << filename << " (error " << result << ")";
940 return nullptr;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700941 }
Tao Baoda409fb2018-10-21 23:36:26 -0700942 return std::unique_ptr<GRSurface>(surface);
Doug Zongkereac881c2014-03-07 09:21:25 -0800943}
944
Tao Baoda409fb2018-10-21 23:36:26 -0700945std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadLocalizedBitmap(const std::string& filename) {
946 GRSurface* surface;
xunchang9d05c8a2019-04-16 12:07:42 -0700947 auto result = res_create_localized_alpha_surface(filename.c_str(), locale_.c_str(), &surface);
948 if (result == 0) {
949 return std::unique_ptr<GRSurface>(surface);
Tao Bao736d59c2017-01-03 10:15:33 -0800950 }
xunchang9d05c8a2019-04-16 12:07:42 -0700951 // TODO(xunchang) create a error code enum to refine the retry condition.
952 LOG(WARNING) << "Failed to load bitmap " << filename << " for locale " << locale_ << " (error "
953 << result << "). Falling back to use default locale.";
954
955 result = res_create_localized_alpha_surface(filename.c_str(), DEFAULT_LOCALE, &surface);
956 if (result == 0) {
957 return std::unique_ptr<GRSurface>(surface);
958 }
959
960 LOG(ERROR) << "Failed to load bitmap " << filename << " for locale " << DEFAULT_LOCALE
961 << " (error " << result << ")";
962 return nullptr;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700963}
964
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700965static char** Alloc2d(size_t rows, size_t cols) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700966 char** result = new char*[rows];
967 for (size_t i = 0; i < rows; ++i) {
968 result[i] = new char[cols];
969 memset(result[i], 0, cols);
970 }
971 return result;
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700972}
973
Tianjie Xu35926c42016-04-28 18:06:26 -0700974// Choose the right background string to display during update.
975void ScreenRecoveryUI::SetSystemUpdateText(bool security_update) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700976 if (security_update) {
Tao Baoda409fb2018-10-21 23:36:26 -0700977 installing_text_ = LoadLocalizedBitmap("installing_security_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700978 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700979 installing_text_ = LoadLocalizedBitmap("installing_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700980 }
981 Redraw();
Tianjie Xu35926c42016-04-28 18:06:26 -0700982}
983
Sen Jiangd5304492016-12-09 16:20:49 -0800984bool ScreenRecoveryUI::InitTextParams() {
Tao Baoba4edb32018-06-13 11:22:50 -0700985 // gr_init() would return successfully on font initialization failure.
986 if (gr_sys_font() == nullptr) {
Tao Bao171b4c42017-06-19 23:10:44 -0700987 return false;
988 }
Tao Bao171b4c42017-06-19 23:10:44 -0700989 gr_font_size(gr_sys_font(), &char_width_, &char_height_);
Alessandro Astone5a57a942020-03-09 23:17:50 +0100990 gr_font_size(gr_menu_font(), &menu_char_width_, &menu_char_height_);
Tao Bao0bc88de2018-07-31 14:53:16 -0700991 text_rows_ = (ScreenHeight() - margin_height_ * 2) / char_height_;
992 text_cols_ = (ScreenWidth() - margin_width_ * 2) / char_width_;
Tao Bao171b4c42017-06-19 23:10:44 -0700993 return true;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -0700994}
995
Tianjie Xub99e6062018-10-16 15:13:09 -0700996bool ScreenRecoveryUI::LoadWipeDataMenuText() {
Tianjie Xu1a0a30a2018-10-25 15:22:07 -0700997 // Ignores the errors since the member variables will stay as nullptr.
998 cancel_wipe_data_text_ = LoadLocalizedBitmap("cancel_wipe_data_text");
999 factory_data_reset_text_ = LoadLocalizedBitmap("factory_data_reset_text");
1000 try_again_text_ = LoadLocalizedBitmap("try_again_text");
1001 wipe_data_confirmation_text_ = LoadLocalizedBitmap("wipe_data_confirmation_text");
1002 wipe_data_menu_header_text_ = LoadLocalizedBitmap("wipe_data_menu_header_text");
Tianjie Xub99e6062018-10-16 15:13:09 -07001003 return true;
1004}
1005
Mark Salyzyn30017e72020-05-13 12:39:12 -07001006static bool InitGraphics() {
1007 // Timeout is same as init wait for file default of 5 seconds and is arbitrary
1008 const unsigned timeout = 500; // 10ms increments
1009 for (auto retry = timeout; retry > 0; --retry) {
1010 if (gr_init() == 0) {
1011 if (retry < timeout) {
1012 // Log message like init wait for file completion log for consistency.
1013 LOG(WARNING) << "wait for 'graphics' took " << ((timeout - retry) * 10) << "ms";
1014 }
1015 return true;
1016 }
1017 std::this_thread::sleep_for(10ms);
1018 }
1019 // Log message like init wait for file timeout log for consistency.
1020 LOG(ERROR) << "timeout wait for 'graphics' took " << (timeout * 10) << "ms";
1021 return false;
1022}
1023
Tao Bao736d59c2017-01-03 10:15:33 -08001024bool ScreenRecoveryUI::Init(const std::string& locale) {
1025 RecoveryUI::Init(locale);
Tao Baoefb49ad2017-01-31 23:03:10 -08001026
Mark Salyzyn30017e72020-05-13 12:39:12 -07001027 if (!InitGraphics()) {
Tao Baoba4edb32018-06-13 11:22:50 -07001028 return false;
1029 }
fredchiouf2f5aa22022-02-11 16:39:53 +08001030 is_graphics_available = true;
Tao Baoba4edb32018-06-13 11:22:50 -07001031
Tao Bao736d59c2017-01-03 10:15:33 -08001032 if (!InitTextParams()) {
1033 return false;
1034 }
Alessandro Astone5a57a942020-03-09 23:17:50 +01001035 menu_draw_funcs_ = std::make_unique<MenuDrawFunctions>(*this);
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001036
Michael Bestas19630862019-03-23 17:28:22 +02001037 if (blank_unblank_on_init_) {
1038 gr_fb_blank(true);
1039 gr_fb_blank(false);
1040 }
1041
Tao Bao736d59c2017-01-03 10:15:33 -08001042 // Are we portrait or landscape?
1043 layout_ = (gr_fb_width() > gr_fb_height()) ? LANDSCAPE : PORTRAIT;
1044 // Are we the large variant of our base layout?
1045 if (gr_fb_height() > PixelsFromDp(800)) ++layout_;
Elliott Hughesfaf36e02016-04-20 17:22:16 -07001046
Tao Bao736d59c2017-01-03 10:15:33 -08001047 text_ = Alloc2d(text_rows_, text_cols_ + 1);
1048 file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
Doug Zongker55a36ac2013-03-04 15:49:02 -08001049
Tao Bao736d59c2017-01-03 10:15:33 -08001050 text_col_ = text_row_ = 0;
Doug Zongker211aebc2011-10-28 15:13:10 -07001051
Tao Baoefb49ad2017-01-31 23:03:10 -08001052 // Set up the locale info.
1053 SetLocale(locale);
1054
Tao Baoda409fb2018-10-21 23:36:26 -07001055 error_icon_ = LoadBitmap("icon_error");
Doug Zongker02ec6b82012-08-22 17:26:40 -07001056
Tao Baoda409fb2018-10-21 23:36:26 -07001057 progress_bar_empty_ = LoadBitmap("progress_empty");
1058 progress_bar_fill_ = LoadBitmap("progress_fill");
1059 stage_marker_empty_ = LoadBitmap("stage_empty");
1060 stage_marker_fill_ = LoadBitmap("stage_fill");
Elliott Hughes498cda62016-04-14 16:49:04 -07001061
Tao Baoda409fb2018-10-21 23:36:26 -07001062 erasing_text_ = LoadLocalizedBitmap("erasing_text");
1063 no_command_text_ = LoadLocalizedBitmap("no_command_text");
1064 error_text_ = LoadLocalizedBitmap("error_text");
Doug Zongker211aebc2011-10-28 15:13:10 -07001065
Alessandro Astone5a57a942020-03-09 23:17:50 +01001066 back_icon_ = LoadBitmap("ic_back");
1067 back_icon_sel_ = LoadBitmap("ic_back_sel");
Alessandro Astone957d98e2020-02-26 17:25:54 +01001068 if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) ||
1069 android::base::GetBoolProperty("ro.fastbootd.available", false)) {
Alessandro Astone56b46e12020-03-29 15:08:09 +02001070 lineage_logo_ = LoadBitmap("logo_image_switch");
David Anderson983e2d52019-01-02 11:35:38 -08001071 fastbootd_logo_ = LoadBitmap("fastbootd");
Alessandro Astone56b46e12020-03-29 15:08:09 +02001072 } else {
1073 lineage_logo_ = LoadBitmap("logo_image");
David Anderson983e2d52019-01-02 11:35:38 -08001074 }
1075
Tao Baoda409fb2018-10-21 23:36:26 -07001076 // Background text for "installing_update" could be "installing update" or
1077 // "installing security update". It will be set after Init() according to the commands in BCB.
1078 installing_text_.reset();
Elliott Hughes498cda62016-04-14 16:49:04 -07001079
Tianjie Xub99e6062018-10-16 15:13:09 -07001080 LoadWipeDataMenuText();
1081
Tao Bao736d59c2017-01-03 10:15:33 -08001082 LoadAnimation();
Doug Zongker02ec6b82012-08-22 17:26:40 -07001083
Tao Bao26ea9592018-05-09 16:32:02 -07001084 // Keep the progress bar updated, even when the process is otherwise busy.
1085 progress_thread_ = std::thread(&ScreenRecoveryUI::ProgressThreadLoop, this);
Sen Jiangd5304492016-12-09 16:20:49 -08001086
fredchiouf2f5aa22022-02-11 16:39:53 +08001087 // set the callback for hall sensor event
1088 (void)ev_sync_sw_state([this](auto&& a, auto&& b) { return this->SetSwCallback(a, b);});
1089
Tao Bao736d59c2017-01-03 10:15:33 -08001090 return true;
Doug Zongker211aebc2011-10-28 15:13:10 -07001091}
1092
Tao Bao551d2c32018-05-09 20:53:13 -07001093std::string ScreenRecoveryUI::GetLocale() const {
Jerry Zhang2dea53e2018-05-02 17:15:03 -07001094 return locale_;
1095}
1096
Elliott Hughes498cda62016-04-14 16:49:04 -07001097void ScreenRecoveryUI::LoadAnimation() {
Tao Bao6cd81682018-05-03 21:53:11 -07001098 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(Paths::Get().resource_dir().c_str()),
1099 closedir);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001100 dirent* de;
1101 std::vector<std::string> intro_frame_names;
Joshua Lambert94a83be2019-12-09 20:24:18 +00001102 std::vector<std::string> loop_frame_names;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001103
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001104 while ((de = readdir(dir.get())) != nullptr) {
1105 int value, num_chars;
1106 if (sscanf(de->d_name, "intro%d%n.png", &value, &num_chars) == 1) {
1107 intro_frame_names.emplace_back(de->d_name, num_chars);
1108 } else if (sscanf(de->d_name, "loop%d%n.png", &value, &num_chars) == 1) {
Joshua Lambert94a83be2019-12-09 20:24:18 +00001109 loop_frame_names.emplace_back(de->d_name, num_chars);
Elliott Hughes498cda62016-04-14 16:49:04 -07001110 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001111 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001112
Tao Baoda409fb2018-10-21 23:36:26 -07001113 size_t intro_frames = intro_frame_names.size();
1114 size_t loop_frames = loop_frame_names.size();
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001115
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001116 // It's okay to not have an intro.
Tao Baoda409fb2018-10-21 23:36:26 -07001117 if (intro_frames == 0) intro_done_ = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001118 // But you must have an animation.
1119 if (loop_frames == 0) abort();
Elliott Hughes498cda62016-04-14 16:49:04 -07001120
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001121 std::sort(intro_frame_names.begin(), intro_frame_names.end());
1122 std::sort(loop_frame_names.begin(), loop_frame_names.end());
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001123
Tao Baoda409fb2018-10-21 23:36:26 -07001124 intro_frames_.clear();
1125 intro_frames_.reserve(intro_frames);
1126 for (const auto& frame_name : intro_frame_names) {
1127 intro_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001128 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001129
Tao Baoda409fb2018-10-21 23:36:26 -07001130 loop_frames_.clear();
1131 loop_frames_.reserve(loop_frames);
1132 for (const auto& frame_name : loop_frame_names) {
1133 loop_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001134 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001135}
1136
Elliott Hughes8de52072015-04-08 20:06:50 -07001137void ScreenRecoveryUI::SetBackground(Icon icon) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001138 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker02ec6b82012-08-22 17:26:40 -07001139
Tao Baoda409fb2018-10-21 23:36:26 -07001140 current_icon_ = icon;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001141 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001142}
1143
Elliott Hughes8de52072015-04-08 20:06:50 -07001144void ScreenRecoveryUI::SetProgressType(ProgressType type) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001145 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001146 if (progressBarType != type) {
1147 progressBarType = type;
1148 }
1149 progressScopeStart = 0;
1150 progressScopeSize = 0;
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::ShowProgress(float portion, float seconds) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001156 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001157 progressBarType = DETERMINATE;
1158 progressScopeStart += progressScopeSize;
1159 progressScopeSize = portion;
1160 progressScopeTime = now();
1161 progressScopeDuration = seconds;
1162 progress = 0;
1163 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001164}
1165
Elliott Hughes8de52072015-04-08 20:06:50 -07001166void ScreenRecoveryUI::SetProgress(float fraction) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001167 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001168 if (fraction < 0.0) fraction = 0.0;
1169 if (fraction > 1.0) fraction = 1.0;
1170 if (progressBarType == DETERMINATE && fraction > progress) {
1171 // Skip updates that aren't visibly different.
Tao Baoda409fb2018-10-21 23:36:26 -07001172 int width = gr_get_width(progress_bar_empty_.get());
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001173 float scale = width * progressScopeSize;
1174 if ((int)(progress * scale) != (int)(fraction * scale)) {
1175 progress = fraction;
1176 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001177 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001178 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001179}
1180
Doug Zongkerc87bab12013-11-25 13:53:25 -08001181void ScreenRecoveryUI::SetStage(int current, int max) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001182 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001183 stage = current;
1184 max_stage = max;
Doug Zongkerc87bab12013-11-25 13:53:25 -08001185}
1186
Tao Baob6918c72015-05-19 17:02:16 -07001187void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001188 std::string str;
1189 android::base::StringAppendV(&str, fmt, ap);
Doug Zongker211aebc2011-10-28 15:13:10 -07001190
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001191 if (copy_to_stdout) {
1192 fputs(str.c_str(), stdout);
1193 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001194
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001195 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001196 if (text_rows_ > 0 && text_cols_ > 0) {
1197 for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
1198 if (*ptr == '\n' || text_col_ >= text_cols_) {
Elliott Hughesc0491632015-05-06 12:40:05 -07001199 text_[text_row_][text_col_] = '\0';
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001200 text_col_ = 0;
1201 text_row_ = (text_row_ + 1) % text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001202 }
1203 if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
Doug Zongker211aebc2011-10-28 15:13:10 -07001204 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001205 text_[text_row_][text_col_] = '\0';
1206 update_screen_locked();
1207 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001208}
1209
Tao Baob6918c72015-05-19 17:02:16 -07001210void ScreenRecoveryUI::Print(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001211 va_list ap;
1212 va_start(ap, fmt);
1213 PrintV(fmt, true, ap);
1214 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001215}
1216
Tianjie Xu8f397302018-08-20 13:40:47 -07001217void ScreenRecoveryUI::PrintOnScreenOnly(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001218 va_list ap;
1219 va_start(ap, fmt);
1220 PrintV(fmt, false, ap);
1221 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001222}
1223
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001224void ScreenRecoveryUI::PutChar(char ch) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001225 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001226 if (ch != '\n') text_[text_row_][text_col_++] = ch;
1227 if (ch == '\n' || text_col_ >= text_cols_) {
1228 text_col_ = 0;
1229 ++text_row_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001230 }
Elliott Hughes8de52072015-04-08 20:06:50 -07001231}
1232
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001233void ScreenRecoveryUI::ClearText() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001234 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001235 text_col_ = 0;
1236 text_row_ = 0;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001237 for (size_t i = 0; i < text_rows_; ++i) {
1238 memset(text_[i], 0, text_cols_ + 1);
1239 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001240}
1241
1242void ScreenRecoveryUI::ShowFile(FILE* fp) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001243 std::vector<off_t> offsets;
1244 offsets.push_back(ftello(fp));
1245 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001246
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001247 struct stat sb;
1248 fstat(fileno(fp), &sb);
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001249
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001250 bool show_prompt = false;
1251 while (true) {
1252 if (show_prompt) {
1253 PrintOnScreenOnly("--(%d%% of %d bytes)--",
1254 static_cast<int>(100 * (double(ftello(fp)) / double(sb.st_size))),
1255 static_cast<int>(sb.st_size));
1256 Redraw();
1257 while (show_prompt) {
1258 show_prompt = false;
Alessandro Astone84042042020-10-04 18:11:40 +02001259 InputEvent evt = WaitInputEvent();
1260 if (evt.type() == EventType::EXTRA) {
1261 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) {
1262 return;
1263 }
1264 }
1265 if (evt.type() != EventType::KEY) {
1266 show_prompt = true;
1267 continue;
1268 }
1269 if (evt.key() == KEY_POWER || evt.key() == KEY_ENTER || evt.key() == KEY_BACKSPACE ||
1270 evt.key() == KEY_BACK || evt.key() == KEY_HOME || evt.key() == KEY_HOMEPAGE) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001271 return;
Alessandro Astone84042042020-10-04 18:11:40 +02001272 } else if (evt.key() == KEY_UP || evt.key() == KEY_VOLUMEUP || evt.key() == KEY_SCROLLUP) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001273 if (offsets.size() <= 1) {
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001274 show_prompt = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001275 } else {
1276 offsets.pop_back();
1277 fseek(fp, offsets.back(), SEEK_SET);
1278 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001279 } else {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001280 if (feof(fp)) {
1281 return;
1282 }
1283 offsets.push_back(ftello(fp));
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001284 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001285 }
1286 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001287 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001288
1289 int ch = getc(fp);
1290 if (ch == EOF) {
1291 while (text_row_ < text_rows_ - 1) PutChar('\n');
1292 show_prompt = true;
1293 } else {
1294 PutChar(ch);
1295 if (text_col_ == 0 && text_row_ >= text_rows_ - 1) {
1296 show_prompt = true;
1297 }
1298 }
1299 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001300}
1301
Tao Bao1d156b92018-05-02 12:43:18 -07001302void ScreenRecoveryUI::ShowFile(const std::string& filename) {
1303 std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(filename.c_str(), "re"), fclose);
1304 if (!fp) {
1305 Print(" Unable to open %s: %s\n", filename.c_str(), strerror(errno));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001306 return;
1307 }
Elliott Hughesc0491632015-05-06 12:40:05 -07001308
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001309 char** old_text = text_;
1310 size_t old_text_col = text_col_;
1311 size_t old_text_row = text_row_;
Elliott Hughesc0491632015-05-06 12:40:05 -07001312
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001313 // Swap in the alternate screen and clear it.
1314 text_ = file_viewer_text_;
1315 ClearText();
Elliott Hughesc0491632015-05-06 12:40:05 -07001316
Tao Bao1d156b92018-05-02 12:43:18 -07001317 ShowFile(fp.get());
Elliott Hughesc0491632015-05-06 12:40:05 -07001318
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001319 text_ = old_text;
1320 text_col_ = old_text_col;
1321 text_row_ = old_text_row;
Elliott Hughes8de52072015-04-08 20:06:50 -07001322}
1323
Tao Baoda409fb2018-10-21 23:36:26 -07001324std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(
1325 const GRSurface* graphic_header, const std::vector<const GRSurface*>& graphic_items,
1326 const std::vector<std::string>& text_headers, const std::vector<std::string>& text_items,
1327 size_t initial_selection) const {
Tianjie Xub99e6062018-10-16 15:13:09 -07001328 // horizontal unusable area: margin width + menu indent
1329 size_t max_width = ScreenWidth() - margin_width_ - kMenuIndent;
1330 // vertical unusable area: margin height + title lines + helper message + high light bar.
1331 // It is safe to reserve more space.
1332 size_t max_height = ScreenHeight() - margin_height_ - char_height_ * (title_lines_.size() + 3);
1333 if (GraphicMenu::Validate(max_width, max_height, graphic_header, graphic_items)) {
1334 return std::make_unique<GraphicMenu>(graphic_header, graphic_items, initial_selection, *this);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001335 }
Tianjie Xub99e6062018-10-16 15:13:09 -07001336
1337 fprintf(stderr, "Failed to initialize graphic menu, falling back to use the text menu.\n");
1338
1339 return CreateMenu(text_headers, text_items, initial_selection);
1340}
1341
1342std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(const std::vector<std::string>& text_headers,
1343 const std::vector<std::string>& text_items,
1344 size_t initial_selection) const {
Alessandro Astone5a57a942020-03-09 23:17:50 +01001345 int menu_char_width = MenuCharWidth();
1346 int menu_char_height = MenuCharHeight();
Alessandro Astone5a57a942020-03-09 23:17:50 +01001347 int menu_cols = (ScreenWidth() - margin_width_*2 - kMenuIndent) / menu_char_width;
1348 bool wrap_selection = !HasThreeButtons() && !HasTouchScreen();
Alessandro Astone5cec9d82020-06-25 19:54:18 +02001349 return std::make_unique<TextMenu>(wrap_selection, menu_cols, text_headers, text_items,
Alessandro Astone5a57a942020-03-09 23:17:50 +01001350 initial_selection, menu_char_height, *menu_draw_funcs_);
Doug Zongker211aebc2011-10-28 15:13:10 -07001351}
1352
1353int ScreenRecoveryUI::SelectMenu(int sel) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001354 std::lock_guard<std::mutex> lg(updateMutex);
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001355 if (menu_) {
1356 int old_sel = menu_->selection();
1357 sel = menu_->Select(sel);
Elliott Hughesfc06f872015-03-23 13:45:31 -07001358
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001359 if (sel != old_sel) {
1360 update_screen_locked();
1361 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001362 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001363 return sel;
Doug Zongker211aebc2011-10-28 15:13:10 -07001364}
1365
Alessandro Astone84042042020-10-04 18:11:40 +02001366int ScreenRecoveryUI::SelectMenu(const Point& point) {
1367 int new_sel = Device::kNoAction;
1368 std::lock_guard<std::mutex> lg(updateMutex);
1369 if (menu_) {
1370 if (!menu_->IsMain()) {
1371 // Back arrow hitbox
1372 const static int logo_width = gr_get_width(lineage_logo_.get());
1373 const static int logo_height = gr_get_height(lineage_logo_.get());
1374 const static int icon_w = gr_get_width(back_icon_.get());
1375 const static int icon_h = gr_get_height(back_icon_.get());
1376 const static int centered_x = ScreenWidth() / 2 - logo_width / 2;
1377 const static int icon_x = centered_x / 2 - icon_w / 2;
1378 const static int icon_y = margin_height_ + logo_height / 2 - icon_h / 2;
1379
1380 if (point.x() >= icon_x && point.x() <= icon_x + icon_w &&
1381 point.y() >= icon_y && point.y() <= icon_y + icon_h) {
1382 return Device::kGoBack;
1383 }
1384 }
1385
1386 if (point.y() >= menu_start_y_ &&
1387 point.y() < menu_start_y_ + menu_->ItemsCount() * MenuItemHeight()) {
1388 int old_sel = menu_->selection();
1389 int relative_sel = (point.y() - menu_start_y_) / MenuItemHeight();
1390 new_sel = menu_->SelectVisible(relative_sel);
1391 if (new_sel != -1 && new_sel != old_sel) {
1392 update_screen_locked();
1393 }
1394 }
1395 }
1396 return new_sel;
1397}
1398
1399int ScreenRecoveryUI::ScrollMenu(int updown) {
1400 std::lock_guard<std::mutex> lg(updateMutex);
1401 int sel = Device::kNoAction;
1402 if (menu_) {
1403 sel = menu_->Scroll(updown);
1404 update_screen_locked();
1405 }
1406 return sel;
1407}
1408
Tianjie Xub99e6062018-10-16 15:13:09 -07001409size_t ScreenRecoveryUI::ShowMenu(std::unique_ptr<Menu>&& menu, bool menu_only,
Tom Marshalldd9b45d2019-01-04 14:37:31 -08001410 const std::function<int(int, bool)>& key_handler,
1411 bool refreshable) {
Tao Bao3aec6962018-04-20 09:24:58 -07001412 // Throw away keys pressed previously, so user doesn't accidentally trigger menu items.
1413 FlushKeys();
1414
Jerry Zhangb76af932018-05-22 12:08:35 -07001415 // If there is a key interrupt in progress, return KeyError::INTERRUPTED without starting the
1416 // menu.
1417 if (IsKeyInterrupted()) return static_cast<size_t>(KeyError::INTERRUPTED);
1418
Tianjie Xub99e6062018-10-16 15:13:09 -07001419 CHECK(menu != nullptr);
Tao Bao3aec6962018-04-20 09:24:58 -07001420
Tianjie Xub99e6062018-10-16 15:13:09 -07001421 // Starts and displays the menu
1422 menu_ = std::move(menu);
1423 Redraw();
1424
1425 int selected = menu_->selection();
Tao Bao3aec6962018-04-20 09:24:58 -07001426 int chosen_item = -1;
1427 while (chosen_item < 0) {
Alessandro Astone84042042020-10-04 18:11:40 +02001428 InputEvent evt = WaitInputEvent();
1429 if (evt.type() == EventType::EXTRA) {
1430 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) {
1431 // WaitKey() was interrupted.
1432 return static_cast<size_t>(KeyError::INTERRUPTED);
1433 }
1434 if (evt.key() == static_cast<int>(KeyError::TIMED_OUT)) { // WaitKey() timed out.
1435 if (WasTextEverVisible()) {
1436 continue;
1437 } else {
1438 LOG(INFO) << "Timed out waiting for key input; rebooting.";
1439 menu_.reset();
1440 Redraw();
1441 return static_cast<size_t>(KeyError::TIMED_OUT);
1442 }
Tao Bao3aec6962018-04-20 09:24:58 -07001443 }
1444 }
1445
Alessandro Astone84042042020-10-04 18:11:40 +02001446 int action = Device::kNoAction;
1447 if (evt.type() == EventType::TOUCH) {
1448 int touch_sel = SelectMenu(evt.pos());
1449 if (touch_sel < 0) {
1450 action = touch_sel;
1451 } else {
1452 action = Device::kInvokeItem;
1453 selected = touch_sel;
1454 }
1455 } else {
1456 bool visible = IsTextVisible();
1457 action = key_handler(evt.key(), visible);
1458 }
Tom Marshalldd9b45d2019-01-04 14:37:31 -08001459
Tao Bao3aec6962018-04-20 09:24:58 -07001460 if (action < 0) {
1461 switch (action) {
1462 case Device::kHighlightUp:
1463 selected = SelectMenu(--selected);
1464 break;
1465 case Device::kHighlightDown:
1466 selected = SelectMenu(++selected);
1467 break;
Alessandro Astone84042042020-10-04 18:11:40 +02001468 case Device::kScrollUp:
1469 selected = ScrollMenu(-1);
1470 break;
1471 case Device::kScrollDown:
1472 selected = ScrollMenu(1);
1473 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001474 case Device::kInvokeItem:
Alessandro Astone5a57a942020-03-09 23:17:50 +01001475 if (selected < 0) {
1476 chosen_item = Device::kGoBack;
1477 } else {
1478 chosen_item = selected;
1479 }
Tao Bao3aec6962018-04-20 09:24:58 -07001480 break;
1481 case Device::kNoAction:
1482 break;
Tom Marshallf20b3e32017-08-24 13:50:01 +00001483 case Device::kGoBack:
1484 chosen_item = Device::kGoBack;
1485 break;
1486 case Device::kGoHome:
1487 chosen_item = Device::kGoHome;
1488 break;
Tom Marshall9f2f3282018-12-17 15:57:44 -08001489 case Device::kDoSideload:
1490 chosen_item = Device::kDoSideload;
1491 break;
Tom Marshalldd9b45d2019-01-04 14:37:31 -08001492 case Device::kRefresh:
1493 if (refreshable) {
1494 chosen_item = Device::kRefresh;
1495 }
1496 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001497 }
1498 } else if (!menu_only) {
1499 chosen_item = action;
1500 }
Tom Marshall9f2f3282018-12-17 15:57:44 -08001501
1502 if (chosen_item == Device::kGoBack || chosen_item == Device::kGoHome ||
Tom Marshalldd9b45d2019-01-04 14:37:31 -08001503 chosen_item == Device::kDoSideload || chosen_item == Device::kRefresh) {
Tom Marshallf20b3e32017-08-24 13:50:01 +00001504 break;
1505 }
Tao Bao3aec6962018-04-20 09:24:58 -07001506 }
1507
Tianjie Xub99e6062018-10-16 15:13:09 -07001508 menu_.reset();
Tianjie Xub99e6062018-10-16 15:13:09 -07001509
Tao Bao3aec6962018-04-20 09:24:58 -07001510 return chosen_item;
1511}
1512
Tianjie Xub99e6062018-10-16 15:13:09 -07001513size_t ScreenRecoveryUI::ShowMenu(const std::vector<std::string>& headers,
1514 const std::vector<std::string>& items, size_t initial_selection,
1515 bool menu_only,
Tom Marshalldd9b45d2019-01-04 14:37:31 -08001516 const std::function<int(int, bool)>& key_handler,
1517 bool refreshable) {
Tianjie Xub99e6062018-10-16 15:13:09 -07001518 auto menu = CreateMenu(headers, items, initial_selection);
1519 if (menu == nullptr) {
1520 return initial_selection;
1521 }
1522
Tom Marshalldd9b45d2019-01-04 14:37:31 -08001523 return ShowMenu(std::move(menu), menu_only, key_handler, refreshable);
Tianjie Xub99e6062018-10-16 15:13:09 -07001524}
1525
1526size_t ScreenRecoveryUI::ShowPromptWipeDataMenu(const std::vector<std::string>& backup_headers,
1527 const std::vector<std::string>& backup_items,
1528 const std::function<int(int, bool)>& key_handler) {
Alessandro Astone303854d2020-10-05 15:11:36 +02001529 auto wipe_data_menu = CreateMenu(backup_headers, backup_items, 0);
Tianjie Xub99e6062018-10-16 15:13:09 -07001530 if (wipe_data_menu == nullptr) {
1531 return 0;
1532 }
1533
1534 return ShowMenu(std::move(wipe_data_menu), true, key_handler);
1535}
1536
Tianjie Xu1a0a30a2018-10-25 15:22:07 -07001537size_t ScreenRecoveryUI::ShowPromptWipeDataConfirmationMenu(
1538 const std::vector<std::string>& backup_headers, const std::vector<std::string>& backup_items,
1539 const std::function<int(int, bool)>& key_handler) {
1540 auto confirmation_menu =
1541 CreateMenu(wipe_data_confirmation_text_.get(),
1542 { cancel_wipe_data_text_.get(), factory_data_reset_text_.get() }, backup_headers,
1543 backup_items, 0);
1544 if (confirmation_menu == nullptr) {
1545 return 0;
1546 }
1547
1548 return ShowMenu(std::move(confirmation_menu), true, key_handler);
1549}
1550
Elliott Hughes8de52072015-04-08 20:06:50 -07001551bool ScreenRecoveryUI::IsTextVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001552 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001553 int visible = show_text;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001554 return visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001555}
1556
Elliott Hughes8de52072015-04-08 20:06:50 -07001557bool ScreenRecoveryUI::WasTextEverVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001558 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001559 int ever_visible = show_text_ever;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001560 return ever_visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001561}
1562
Elliott Hughes8de52072015-04-08 20:06:50 -07001563void ScreenRecoveryUI::ShowText(bool visible) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001564 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001565 show_text = visible;
1566 if (show_text) show_text_ever = true;
1567 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001568}
Doug Zongkerc0441d12013-07-31 11:28:24 -07001569
Elliott Hughes8de52072015-04-08 20:06:50 -07001570void ScreenRecoveryUI::Redraw() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001571 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001572 update_screen_locked();
Doug Zongkerc0441d12013-07-31 11:28:24 -07001573}
Elliott Hughes642aaa72015-04-10 12:47:46 -07001574
1575void ScreenRecoveryUI::KeyLongPress(int) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001576 // Redraw so that if we're in the menu, the highlight
1577 // will change color to indicate a successful long press.
1578 Redraw();
Elliott Hughes642aaa72015-04-10 12:47:46 -07001579}
Tao Baoefb49ad2017-01-31 23:03:10 -08001580
1581void ScreenRecoveryUI::SetLocale(const std::string& new_locale) {
1582 locale_ = new_locale;
1583 rtl_locale_ = false;
1584
1585 if (!new_locale.empty()) {
Tao Bao347a6592018-05-08 15:58:29 -07001586 size_t separator = new_locale.find('-');
1587 // lang has the language prefix prior to the separator, or full string if none exists.
1588 std::string lang = new_locale.substr(0, separator);
Tao Baoefb49ad2017-01-31 23:03:10 -08001589
1590 // A bit cheesy: keep an explicit list of supported RTL languages.
1591 if (lang == "ar" || // Arabic
1592 lang == "fa" || // Persian (Farsi)
1593 lang == "he" || // Hebrew (new language code)
1594 lang == "iw" || // Hebrew (old language code)
1595 lang == "ur") { // Urdu
1596 rtl_locale_ = true;
1597 }
1598 }
1599}
fredchiouf2f5aa22022-02-11 16:39:53 +08001600
1601int ScreenRecoveryUI::SetSwCallback(int code, int value) {
1602 if (!is_graphics_available) { return -1; }
1603 if (code > SW_MAX) { return -1; }
1604 if (code != SW_LID) { return 0; }
1605
1606 /* detect dual display */
1607 if (!gr_has_multiple_connectors()) { return -1; }
1608
1609 /* turn off all screen */
1610 gr_fb_blank(true, DirectRenderManager::DRM_INNER);
1611 gr_fb_blank(true, DirectRenderManager::DRM_OUTER);
1612 gr_color(0, 0, 0, 255);
1613 gr_clear();
1614
1615 /* turn on the screen */
1616 gr_fb_blank(false, value);
1617 gr_flip();
1618
1619 /* set the retation */
1620 std::string rotation_str;
1621 if (value == DirectRenderManager::DRM_OUTER) {
1622 rotation_str =
1623 android::base::GetProperty("ro.minui.second_rotation", "ROTATION_NONE");
1624 } else {
1625 rotation_str =
1626 android::base::GetProperty("ro.minui.default_rotation", "ROTATION_NONE");
1627 }
1628
1629 if (rotation_str == "ROTATION_RIGHT") {
1630 gr_rotate(GRRotation::RIGHT);
1631 } else if (rotation_str == "ROTATION_DOWN") {
1632 gr_rotate(GRRotation::DOWN);
1633 } else if (rotation_str == "ROTATION_LEFT") {
1634 gr_rotate(GRRotation::LEFT);
1635 } else { // "ROTATION_NONE" or unknown string
1636 gr_rotate(GRRotation::NONE);
1637 }
1638 Redraw();
1639
1640 return 0;
1641}