blob: 864ad623bbc4df84b99f71a5c8f640b66d4567f7 [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
Doug Zongker211aebc2011-10-28 15:13:10 -070051// Return the current time as a double (including fractions of a second).
52static double now() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -070053 struct timeval tv;
54 gettimeofday(&tv, nullptr);
55 return tv.tv_sec + tv.tv_usec / 1000000.0;
Doug Zongker211aebc2011-10-28 15:13:10 -070056}
57
Tianjie Xu66dbf632018-10-11 16:54:50 -070058Menu::Menu(size_t initial_selection, const DrawInterface& draw_func)
59 : selection_(initial_selection), draw_funcs_(draw_func) {}
60
Alessandro Astonee8754752020-03-09 23:17:50 +010061int Menu::selection() const {
Tianjie Xu66dbf632018-10-11 16:54:50 -070062 return selection_;
63}
64
Alessandro Astone3525c4d2020-06-25 19:54:18 +020065TextMenu::TextMenu(bool wrappable, size_t max_length,
Tianjie Xu66dbf632018-10-11 16:54:50 -070066 const std::vector<std::string>& headers, const std::vector<std::string>& items,
67 size_t initial_selection, int char_height, const DrawInterface& draw_funcs)
68 : Menu(initial_selection, draw_funcs),
Alessandro Astonee8754752020-03-09 23:17:50 +010069 wrappable_(wrappable),
Alessandro Astone3525c4d2020-06-25 19:54:18 +020070 calibrated_height_(false),
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070071 max_item_length_(max_length),
Tao Baoe02a5b22018-05-02 15:46:11 -070072 text_headers_(headers),
Tianjie Xu66dbf632018-10-11 16:54:50 -070073 char_height_(char_height) {
Tao Baoe02a5b22018-05-02 15:46:11 -070074
Alessandro Astonee8754752020-03-09 23:17:50 +010075 size_t items_count = items.size();
Tao Bao1fe1afe2018-05-01 15:56:05 -070076 for (size_t i = 0; i < items_count; ++i) {
77 text_items_.emplace_back(items[i].substr(0, max_item_length_));
Tao Baoe02a5b22018-05-02 15:46:11 -070078 }
79
80 CHECK(!text_items_.empty());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070081}
82
Tianjie Xu66dbf632018-10-11 16:54:50 -070083const std::vector<std::string>& TextMenu::text_headers() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070084 return text_headers_;
85}
86
Tianjie Xu66dbf632018-10-11 16:54:50 -070087std::string TextMenu::TextItem(size_t index) const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070088 CHECK_LT(index, text_items_.size());
89
90 return text_items_[index];
91}
92
Tianjie Xu66dbf632018-10-11 16:54:50 -070093size_t TextMenu::MenuStart() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070094 return menu_start_;
95}
96
Tianjie Xu66dbf632018-10-11 16:54:50 -070097size_t TextMenu::MenuEnd() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070098 return std::min(ItemsCount(), menu_start_ + max_display_items_);
99}
100
Tianjie Xu66dbf632018-10-11 16:54:50 -0700101size_t TextMenu::ItemsCount() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700102 return text_items_.size();
103}
104
Tianjie Xu66dbf632018-10-11 16:54:50 -0700105bool TextMenu::ItemsOverflow(std::string* cur_selection_str) const {
Alessandro Astonee8754752020-03-09 23:17:50 +0100106 if (ItemsCount() <= max_display_items_) {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700107 return false;
108 }
109
110 *cur_selection_str =
Alessandro Astonee8754752020-03-09 23:17:50 +0100111 android::base::StringPrintf("Current item: %d/%zu", selection_ + 1, ItemsCount());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700112 return true;
113}
114
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700115// TODO(xunchang) modify the function parameters to button up & down.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700116int TextMenu::Select(int sel) {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700117 CHECK_LE(ItemsCount(), static_cast<size_t>(std::numeric_limits<int>::max()));
118 int count = ItemsCount();
119
Alessandro Astonee8754752020-03-09 23:17:50 +0100120 int min = IsMain() ? 0 : -1; // -1 is back arrow
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700121
Alessandro Astonee8754752020-03-09 23:17:50 +0100122 if (sel < min) {
123 selection_ = wrappable() ? count - 1 : min;
124 } else if (sel >= count) {
125 selection_ = wrappable() ? min : count - 1;
126 } else {
127 selection_ = sel;
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700128 }
129
Alessandro Astonee8754752020-03-09 23:17:50 +0100130 if (selection_ >= 0) {
131 if (selection_ < menu_start_) {
132 menu_start_ = selection_;
133 } else if (static_cast<size_t>(selection_) >= MenuEnd()) {
134 menu_start_ = selection_ - max_display_items_ + 1;
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700135 }
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700136 }
137
138 return selection_;
139}
140
Tom Marshall53176c72020-03-10 20:17:49 +0100141int TextMenu::SelectVisible(int relative_sel) {
142 int sel = relative_sel;
143 if (menu_start_ > 0) {
144 sel += menu_start_;
145 }
146
147 return Select(sel);
148}
149
150int TextMenu::Scroll(int updown) {
151 if ((updown > 0 && menu_start_ + max_display_items_ < ItemsCount()) ||
152 (updown < 0 && menu_start_ > 0)) {
153 menu_start_ += updown;
154
155 /* We can receive a kInvokeItem event from a different source than touch,
156 like from Power button. For this reason, selection should not get out of
157 the screen. Constrain it to the first or last visible item of the list */
158 if (selection_ < menu_start_) {
159 selection_ = menu_start_;
160 } else if (selection_ >= menu_start_ + max_display_items_) {
161 selection_ = menu_start_ + max_display_items_ - 1;
162 }
163 }
164 return selection_;
165}
166
Tianjie Xu66dbf632018-10-11 16:54:50 -0700167int TextMenu::DrawHeader(int x, int y) const {
168 int offset = 0;
169
170 draw_funcs_.SetColor(UIElement::HEADER);
Alessandro Astonee8754752020-03-09 23:17:50 +0100171 offset += draw_funcs_.DrawWrappedTextLines(x, y + offset, text_headers());
Tianjie Xu66dbf632018-10-11 16:54:50 -0700172
173 return offset;
174}
175
176int TextMenu::DrawItems(int x, int y, int screen_width, bool long_press) const {
177 int offset = 0;
Alessandro Astonee8754752020-03-09 23:17:50 +0100178 int padding = draw_funcs_.MenuItemPadding();
Tianjie Xu66dbf632018-10-11 16:54:50 -0700179
180 draw_funcs_.SetColor(UIElement::MENU);
Alessandro Astonee8754752020-03-09 23:17:50 +0100181 offset += draw_funcs_.DrawHorizontalRule(y + offset) + 4;
182
183 int item_container_offset = offset; // store it for drawing scrollbar on most top
184
Tianjie Xu66dbf632018-10-11 16:54:50 -0700185 for (size_t i = MenuStart(); i < MenuEnd(); ++i) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700186 if (i == selection()) {
187 // Draw the highlight bar.
188 draw_funcs_.SetColor(long_press ? UIElement::MENU_SEL_BG_ACTIVE : UIElement::MENU_SEL_BG);
189
Alessandro Astonee8754752020-03-09 23:17:50 +0100190 int bar_height = padding + char_height_ + padding;
191 draw_funcs_.DrawHighlightBar(0, y + offset, screen_width, bar_height);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700192
Jesse Chanf8084632020-04-28 14:49:13 +0000193 // Colored text for the selected item.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700194 draw_funcs_.SetColor(UIElement::MENU_SEL_FG);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700195 }
Jesse Chanf8084632020-04-28 14:49:13 +0000196 offset += draw_funcs_.DrawTextLine(x, y + offset, TextItem(i), false /* bold */);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700197
198 draw_funcs_.SetColor(UIElement::MENU);
199 }
200 offset += draw_funcs_.DrawHorizontalRule(y + offset);
201
Alessandro Astonee8754752020-03-09 23:17:50 +0100202 std::string unused;
203 if (ItemsOverflow(&unused)) {
204 int container_height = max_display_items_ * (2 * padding + char_height_);
205 int bar_height = container_height / (text_items_.size() - max_display_items_ + 1);
206 int start_y = y + item_container_offset + bar_height * menu_start_;
207 draw_funcs_.SetColor(UIElement::SCROLLBAR);
208 draw_funcs_.DrawScrollBar(start_y, bar_height);
209 }
210
Tianjie Xu66dbf632018-10-11 16:54:50 -0700211 return offset;
212}
213
Tao Baoda409fb2018-10-21 23:36:26 -0700214GraphicMenu::GraphicMenu(const GRSurface* graphic_headers,
215 const std::vector<const GRSurface*>& graphic_items,
Tianjie Xub99e6062018-10-16 15:13:09 -0700216 size_t initial_selection, const DrawInterface& draw_funcs)
Tao Baoda409fb2018-10-21 23:36:26 -0700217 : Menu(initial_selection, draw_funcs) {
218 graphic_headers_ = graphic_headers->Clone();
219 graphic_items_.reserve(graphic_items.size());
220 for (const auto& item : graphic_items) {
221 graphic_items_.emplace_back(item->Clone());
222 }
223}
Tianjie Xu66dbf632018-10-11 16:54:50 -0700224
225int GraphicMenu::Select(int sel) {
226 CHECK_LE(graphic_items_.size(), static_cast<size_t>(std::numeric_limits<int>::max()));
227 int count = graphic_items_.size();
228
229 // Wraps the selection at boundary if the menu is not scrollable.
230 if (sel < 0) {
231 selection_ = count - 1;
232 } else if (sel >= count) {
233 selection_ = 0;
234 } else {
235 selection_ = sel;
236 }
237
238 return selection_;
239}
240
241int GraphicMenu::DrawHeader(int x, int y) const {
Tianjie Xub99e6062018-10-16 15:13:09 -0700242 draw_funcs_.SetColor(UIElement::HEADER);
Tao Baoda409fb2018-10-21 23:36:26 -0700243 draw_funcs_.DrawTextIcon(x, y, graphic_headers_.get());
Tianjie Xu66dbf632018-10-11 16:54:50 -0700244 return graphic_headers_->height;
245}
246
247int GraphicMenu::DrawItems(int x, int y, int screen_width, bool long_press) const {
248 int offset = 0;
249
250 draw_funcs_.SetColor(UIElement::MENU);
251 offset += draw_funcs_.DrawHorizontalRule(y + offset) + 4;
252
253 for (size_t i = 0; i < graphic_items_.size(); i++) {
254 auto& item = graphic_items_[i];
255 if (i == selection_) {
256 draw_funcs_.SetColor(long_press ? UIElement::MENU_SEL_BG_ACTIVE : UIElement::MENU_SEL_BG);
257
258 int bar_height = item->height + 4;
259 draw_funcs_.DrawHighlightBar(0, y + offset - 2, screen_width, bar_height);
260
261 // Bold white text for the selected item.
262 draw_funcs_.SetColor(UIElement::MENU_SEL_FG);
263 }
Tao Baoda409fb2018-10-21 23:36:26 -0700264 draw_funcs_.DrawTextIcon(x, y + offset, item.get());
Tianjie Xu66dbf632018-10-11 16:54:50 -0700265 offset += item->height;
266
267 draw_funcs_.SetColor(UIElement::MENU);
268 }
xunchangc7dbc732018-12-20 11:31:18 -0800269 offset += draw_funcs_.DrawHorizontalRule(y + offset);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700270
271 return offset;
272}
273
Tom Marshall53176c72020-03-10 20:17:49 +0100274size_t GraphicMenu::ItemsCount() const {
275 return graphic_items_.size();
276}
277
Tao Baoda409fb2018-10-21 23:36:26 -0700278bool GraphicMenu::Validate(size_t max_width, size_t max_height, const GRSurface* graphic_headers,
279 const std::vector<const GRSurface*>& graphic_items) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700280 int offset = 0;
Tianjie Xub99e6062018-10-16 15:13:09 -0700281 if (!ValidateGraphicSurface(max_width, max_height, offset, graphic_headers)) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700282 return false;
283 }
Tianjie Xub99e6062018-10-16 15:13:09 -0700284 offset += graphic_headers->height;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700285
Tianjie Xub99e6062018-10-16 15:13:09 -0700286 for (const auto& item : graphic_items) {
287 if (!ValidateGraphicSurface(max_width, max_height, offset, item)) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700288 return false;
289 }
290 offset += item->height;
291 }
292
293 return true;
294}
295
Tianjie Xub99e6062018-10-16 15:13:09 -0700296bool GraphicMenu::ValidateGraphicSurface(size_t max_width, size_t max_height, int y,
297 const GRSurface* surface) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700298 if (!surface) {
Tao Baoa00b4492019-01-16 09:29:47 -0800299 fprintf(stderr, "Graphic surface can not be null\n");
Tianjie Xu66dbf632018-10-11 16:54:50 -0700300 return false;
301 }
302
303 if (surface->pixel_bytes != 1 || surface->width != surface->row_bytes) {
Tao Baoa00b4492019-01-16 09:29:47 -0800304 fprintf(stderr, "Invalid graphic surface, pixel bytes: %zu, width: %zu row_bytes: %zu\n",
Tianjie Xu66dbf632018-10-11 16:54:50 -0700305 surface->pixel_bytes, surface->width, surface->row_bytes);
306 return false;
307 }
308
Tianjie Xub99e6062018-10-16 15:13:09 -0700309 if (surface->width > max_width || surface->height > max_height - y) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700310 fprintf(stderr,
Tao Baodd789822018-11-26 16:28:07 -0800311 "Graphic surface doesn't fit into the screen. width: %zu, height: %zu, max_width: %zu,"
Tianjie Xu66dbf632018-10-11 16:54:50 -0700312 " max_height: %zu, vertical offset: %d\n",
Tianjie Xub99e6062018-10-16 15:13:09 -0700313 surface->width, surface->height, max_width, max_height, y);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700314 return false;
315 }
316
317 return true;
318}
319
Alessandro Astonee8754752020-03-09 23:17:50 +0100320MenuDrawFunctions::MenuDrawFunctions(const DrawInterface& wrappee)
321 : wrappee_(wrappee) {
322}
323
324int MenuDrawFunctions::DrawTextLine(int x, int y, const std::string& line, bool bold) const {
325 gr_text(gr_menu_font(), x, y + MenuItemPadding(), line.c_str(), bold);
326 return 2 * MenuItemPadding() + MenuCharHeight();
327}
328
329int MenuDrawFunctions::DrawTextLines(int x, int y, const std::vector<std::string>& lines) const {
330 int offset = 0;
331 for (const auto& line : lines) {
332 offset += DrawTextLine(x, y + offset, line, false);
333 }
334 return offset;
335}
336
337int MenuDrawFunctions::DrawWrappedTextLines(int x, int y, const std::vector<std::string>& lines) const {
Alessandro Astone049a8712020-10-01 16:45:53 +0200338 const int padding = MenuItemPadding() / 2;
339
Alessandro Astonee8754752020-03-09 23:17:50 +0100340 // Keep symmetrical margins based on the given offset (i.e. x).
341 size_t text_cols = (gr_fb_width() - x * 2) / MenuCharWidth();
342 int offset = 0;
343 for (const auto& line : lines) {
344 size_t next_start = 0;
345 while (next_start < line.size()) {
346 std::string sub = line.substr(next_start, text_cols + 1);
347 if (sub.size() <= text_cols) {
348 next_start += sub.size();
349 } else {
350 // Line too long and must be wrapped to text_cols columns.
351 size_t last_space = sub.find_last_of(" \t\n");
352 if (last_space == std::string::npos) {
353 // No space found, just draw as much as we can.
354 sub.resize(text_cols);
355 next_start += text_cols;
356 } else {
357 sub.resize(last_space);
358 next_start += last_space + 1;
359 }
360 }
Alessandro Astone049a8712020-10-01 16:45:53 +0200361 offset += DrawTextLine(x, y + offset, sub, false) - (2 * MenuItemPadding() - padding);
Alessandro Astonee8754752020-03-09 23:17:50 +0100362 }
363 }
Alessandro Astone049a8712020-10-01 16:45:53 +0200364 if (!lines.empty()) {
365 offset += 2 * MenuItemPadding() - padding;
366 }
Alessandro Astonee8754752020-03-09 23:17:50 +0100367 return offset;
368}
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700369
Tao Bao0bc88de2018-07-31 14:53:16 -0700370constexpr int kDefaultMarginHeight = 0;
371constexpr int kDefaultMarginWidth = 0;
372constexpr int kDefaultAnimationFps = 30;
373
Alessandro Astonee8754752020-03-09 23:17:50 +0100374ScreenRecoveryUI::ScreenRecoveryUI()
Tao Bao0bc88de2018-07-31 14:53:16 -0700375 : margin_width_(
376 android::base::GetIntProperty("ro.recovery.ui.margin_width", kDefaultMarginWidth)),
377 margin_height_(
378 android::base::GetIntProperty("ro.recovery.ui.margin_height", kDefaultMarginHeight)),
379 animation_fps_(
380 android::base::GetIntProperty("ro.recovery.ui.animation_fps", kDefaultAnimationFps)),
381 density_(static_cast<float>(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f),
Michael Bestas2e2d5d62019-03-23 17:28:22 +0200382 blank_unblank_on_init_(
383 android::base::GetBoolProperty("ro.recovery.ui.blank_unblank_on_init", false)),
Tao Baoda409fb2018-10-21 23:36:26 -0700384 current_icon_(NONE),
385 current_frame_(0),
386 intro_done_(false),
Tao Bao736d59c2017-01-03 10:15:33 -0800387 progressBarType(EMPTY),
388 progressScopeStart(0),
389 progressScopeSize(0),
390 progress(0),
391 pagesIdentical(false),
392 text_cols_(0),
393 text_rows_(0),
394 text_(nullptr),
395 text_col_(0),
396 text_row_(0),
Tao Bao736d59c2017-01-03 10:15:33 -0800397 show_text(false),
398 show_text_ever(false),
Tao Bao736d59c2017-01-03 10:15:33 -0800399 file_viewer_text_(nullptr),
Tao Bao736d59c2017-01-03 10:15:33 -0800400 stage(-1),
401 max_stage(-1),
Tao Baoefb49ad2017-01-31 23:03:10 -0800402 locale_(""),
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700403 rtl_locale_(false) {}
Doug Zongker211aebc2011-10-28 15:13:10 -0700404
Tao Bao26ea9592018-05-09 16:32:02 -0700405ScreenRecoveryUI::~ScreenRecoveryUI() {
406 progress_thread_stopped_ = true;
Tao Bao94371fd2018-06-06 07:38:54 -0700407 if (progress_thread_.joinable()) {
408 progress_thread_.join();
409 }
Tao Bao60ac6222018-06-13 14:33:51 -0700410 // No-op if gr_init() (via Init()) was not called or had failed.
411 gr_exit();
Tao Bao26ea9592018-05-09 16:32:02 -0700412}
413
Tao Baoda409fb2018-10-21 23:36:26 -0700414const GRSurface* ScreenRecoveryUI::GetCurrentFrame() const {
415 if (current_icon_ == INSTALLING_UPDATE || current_icon_ == ERASING) {
416 return intro_done_ ? loop_frames_[current_frame_].get() : intro_frames_[current_frame_].get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700417 }
Tao Baoda409fb2018-10-21 23:36:26 -0700418 return error_icon_.get();
Elliott Hughes498cda62016-04-14 16:49:04 -0700419}
420
Tao Baoda409fb2018-10-21 23:36:26 -0700421const GRSurface* ScreenRecoveryUI::GetCurrentText() const {
422 switch (current_icon_) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700423 case ERASING:
Tao Baoda409fb2018-10-21 23:36:26 -0700424 return erasing_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700425 case ERROR:
Tao Baoda409fb2018-10-21 23:36:26 -0700426 return error_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700427 case INSTALLING_UPDATE:
Tao Baoda409fb2018-10-21 23:36:26 -0700428 return installing_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700429 case NO_COMMAND:
Tao Baoda409fb2018-10-21 23:36:26 -0700430 return no_command_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700431 case NONE:
432 abort();
433 }
Elliott Hughes498cda62016-04-14 16:49:04 -0700434}
435
Mikhail Lappob49767c2017-03-23 21:44:26 +0100436int ScreenRecoveryUI::PixelsFromDp(int dp) const {
Tao Bao0bc88de2018-07-31 14:53:16 -0700437 return dp * density_;
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700438}
439
440// Here's the intended layout:
441
Elliott Hughes6d089a92016-07-08 17:23:41 -0700442// | portrait large landscape large
443// ---------+-------------------------------------------------
Tao Bao3250f722017-06-29 14:32:05 -0700444// gap |
Elliott Hughes6d089a92016-07-08 17:23:41 -0700445// icon | (200dp)
446// gap | 68dp 68dp 56dp 112dp
447// text | (14sp)
448// gap | 32dp 32dp 26dp 52dp
449// progress | (2dp)
Tao Bao3250f722017-06-29 14:32:05 -0700450// gap |
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700451
Tao Bao3250f722017-06-29 14:32:05 -0700452// Note that "baseline" is actually the *top* of each icon (because that's how our drawing routines
453// 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 -0700454
Elliott Hughes6d089a92016-07-08 17:23:41 -0700455enum Layout { PORTRAIT = 0, PORTRAIT_LARGE = 1, LANDSCAPE = 2, LANDSCAPE_LARGE = 3, LAYOUT_MAX };
Tao Bao3250f722017-06-29 14:32:05 -0700456enum Dimension { TEXT = 0, ICON = 1, DIMENSION_MAX };
Elliott Hughes6d089a92016-07-08 17:23:41 -0700457static constexpr int kLayouts[LAYOUT_MAX][DIMENSION_MAX] = {
Tianjie Xu8f397302018-08-20 13:40:47 -0700458 { 32, 68 }, // PORTRAIT
459 { 32, 68 }, // PORTRAIT_LARGE
460 { 26, 56 }, // LANDSCAPE
461 { 52, 112 }, // LANDSCAPE_LARGE
Elliott Hughes6d089a92016-07-08 17:23:41 -0700462};
463
Tao Bao99b2d772017-06-23 22:47:03 -0700464int ScreenRecoveryUI::GetAnimationBaseline() const {
Tao Baoda409fb2018-10-21 23:36:26 -0700465 return GetTextBaseline() - PixelsFromDp(kLayouts[layout_][ICON]) -
466 gr_get_height(loop_frames_[0].get());
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700467}
468
Tao Bao99b2d772017-06-23 22:47:03 -0700469int ScreenRecoveryUI::GetTextBaseline() const {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700470 return GetProgressBaseline() - PixelsFromDp(kLayouts[layout_][TEXT]) -
Tao Baoda409fb2018-10-21 23:36:26 -0700471 gr_get_height(installing_text_.get());
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700472}
473
Tao Bao99b2d772017-06-23 22:47:03 -0700474int ScreenRecoveryUI::GetProgressBaseline() const {
Tao Baoda409fb2018-10-21 23:36:26 -0700475 int elements_sum = gr_get_height(loop_frames_[0].get()) + PixelsFromDp(kLayouts[layout_][ICON]) +
476 gr_get_height(installing_text_.get()) + PixelsFromDp(kLayouts[layout_][TEXT]) +
477 gr_get_height(progress_bar_fill_.get());
Luke Song92eda4d2017-09-19 10:51:35 -0700478 int bottom_gap = (ScreenHeight() - elements_sum) / 2;
Tao Baoda409fb2018-10-21 23:36:26 -0700479 return ScreenHeight() - bottom_gap - gr_get_height(progress_bar_fill_.get());
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700480}
481
Doug Zongker211aebc2011-10-28 15:13:10 -0700482// Clear the screen and draw the currently selected background icon (if any).
483// Should only be called with updateMutex locked.
Elliott Hughes498cda62016-04-14 16:49:04 -0700484void ScreenRecoveryUI::draw_background_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700485 pagesIdentical = false;
486 gr_color(0, 0, 0, 255);
487 gr_clear();
Tao Baoda409fb2018-10-21 23:36:26 -0700488 if (current_icon_ != NONE) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700489 if (max_stage != -1) {
Tao Baoda409fb2018-10-21 23:36:26 -0700490 int stage_height = gr_get_height(stage_marker_empty_.get());
491 int stage_width = gr_get_width(stage_marker_empty_.get());
492 int x = (ScreenWidth() - max_stage * gr_get_width(stage_marker_empty_.get())) / 2;
Tao Bao0bc88de2018-07-31 14:53:16 -0700493 int y = ScreenHeight() - stage_height - margin_height_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700494 for (int i = 0; i < max_stage; ++i) {
Tao Baoda409fb2018-10-21 23:36:26 -0700495 const auto& stage_surface = (i < stage) ? stage_marker_fill_ : stage_marker_empty_;
496 DrawSurface(stage_surface.get(), 0, 0, stage_width, stage_height, x, y);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700497 x += stage_width;
498 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700499 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700500
Tao Baoda409fb2018-10-21 23:36:26 -0700501 const auto& text_surface = GetCurrentText();
Luke Song92eda4d2017-09-19 10:51:35 -0700502 int text_x = (ScreenWidth() - gr_get_width(text_surface)) / 2;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700503 int text_y = GetTextBaseline();
504 gr_color(255, 255, 255, 255);
Luke Song92eda4d2017-09-19 10:51:35 -0700505 DrawTextIcon(text_x, text_y, text_surface);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700506 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700507}
508
Tao Baoea78d862017-06-28 14:52:17 -0700509// Draws the animation and progress bar (if any) on the screen. Does not flip pages. Should only be
510// called with updateMutex locked.
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700511void ScreenRecoveryUI::draw_foreground_locked() {
Tao Baoda409fb2018-10-21 23:36:26 -0700512 if (current_icon_ != NONE) {
513 const auto& frame = GetCurrentFrame();
Tao Bao736d59c2017-01-03 10:15:33 -0800514 int frame_width = gr_get_width(frame);
515 int frame_height = gr_get_height(frame);
Luke Song92eda4d2017-09-19 10:51:35 -0700516 int frame_x = (ScreenWidth() - frame_width) / 2;
Tao Bao736d59c2017-01-03 10:15:33 -0800517 int frame_y = GetAnimationBaseline();
zhang sanshan0d81b892019-08-07 16:21:10 +0800518 if (frame_x >= 0 && frame_y >= 0 && (frame_x + frame_width) < ScreenWidth() &&
519 (frame_y + frame_height) < ScreenHeight())
520 DrawSurface(frame, 0, 0, frame_width, frame_height, frame_x, frame_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800521 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700522
Tao Bao736d59c2017-01-03 10:15:33 -0800523 if (progressBarType != EMPTY) {
Tao Baoda409fb2018-10-21 23:36:26 -0700524 int width = gr_get_width(progress_bar_empty_.get());
525 int height = gr_get_height(progress_bar_empty_.get());
Doug Zongker211aebc2011-10-28 15:13:10 -0700526
Luke Song92eda4d2017-09-19 10:51:35 -0700527 int progress_x = (ScreenWidth() - width) / 2;
Tao Bao736d59c2017-01-03 10:15:33 -0800528 int progress_y = GetProgressBaseline();
Doug Zongker211aebc2011-10-28 15:13:10 -0700529
Tao Bao736d59c2017-01-03 10:15:33 -0800530 // Erase behind the progress bar (in case this was a progress-only update)
531 gr_color(0, 0, 0, 255);
Luke Song92eda4d2017-09-19 10:51:35 -0700532 DrawFill(progress_x, progress_y, width, height);
Doug Zongker211aebc2011-10-28 15:13:10 -0700533
Tao Bao736d59c2017-01-03 10:15:33 -0800534 if (progressBarType == DETERMINATE) {
535 float p = progressScopeStart + progress * progressScopeSize;
536 int pos = static_cast<int>(p * width);
Doug Zongker211aebc2011-10-28 15:13:10 -0700537
Tao Bao736d59c2017-01-03 10:15:33 -0800538 if (rtl_locale_) {
539 // Fill the progress bar from right to left.
540 if (pos > 0) {
Tao Baoda409fb2018-10-21 23:36:26 -0700541 DrawSurface(progress_bar_fill_.get(), width - pos, 0, pos, height,
542 progress_x + width - pos, progress_y);
Doug Zongker211aebc2011-10-28 15:13:10 -0700543 }
Tao Bao736d59c2017-01-03 10:15:33 -0800544 if (pos < width - 1) {
Tao Baoda409fb2018-10-21 23:36:26 -0700545 DrawSurface(progress_bar_empty_.get(), 0, 0, width - pos, height, progress_x, progress_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800546 }
547 } else {
548 // Fill the progress bar from left to right.
549 if (pos > 0) {
Tao Baoda409fb2018-10-21 23:36:26 -0700550 DrawSurface(progress_bar_fill_.get(), 0, 0, pos, height, progress_x, progress_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800551 }
552 if (pos < width - 1) {
Tao Baoda409fb2018-10-21 23:36:26 -0700553 DrawSurface(progress_bar_empty_.get(), pos, 0, width - pos, height, progress_x + pos,
554 progress_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800555 }
556 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700557 }
Tao Bao736d59c2017-01-03 10:15:33 -0800558 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700559}
560
Jesse Chanf8084632020-04-28 14:49:13 +0000561/* recovery dark: #7C4DFF
562 recovery light: #F890FF
563 fastbootd dark: #E65100
564 fastboot light: #FDD835 */
Tao Bao99b2d772017-06-23 22:47:03 -0700565void ScreenRecoveryUI::SetColor(UIElement e) const {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700566 switch (e) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700567 case UIElement::INFO:
Richard Hansen8b867d02020-05-04 15:58:49 -0400568 if (fastbootd_logo_enabled_)
569 gr_color(0xfd, 0xd8, 0x35, 255);
570 else
Alexander Martinze06e7452022-12-20 13:15:28 +0100571 gr_color(0, 0x66, 0x77, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700572 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700573 case UIElement::HEADER:
Jesse Chanf8084632020-04-28 14:49:13 +0000574 if (fastbootd_logo_enabled_)
575 gr_color(0xfd, 0xd8,0x35, 255);
576 else
Alexander Martinze06e7452022-12-20 13:15:28 +0100577 gr_color(0, 0x66, 0x77, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700578 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700579 case UIElement::MENU:
Alessandro Astonee8754752020-03-09 23:17:50 +0100580 gr_color(0xd8, 0xd8, 0xd8, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700581 break;
Jesse Chanf8084632020-04-28 14:49:13 +0000582 case UIElement::MENU_SEL_BG:
583 case UIElement::SCROLLBAR:
584 if (fastbootd_logo_enabled_)
585 gr_color(0xe6, 0x51, 0x00, 255);
586 else
Alexander Martinze06e7452022-12-20 13:15:28 +0100587 gr_color(0, 0x66, 0x77, 255);
Jesse Chanf8084632020-04-28 14:49:13 +0000588 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700589 case UIElement::MENU_SEL_BG_ACTIVE:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700590 gr_color(0, 156, 100, 255);
591 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700592 case UIElement::MENU_SEL_FG:
Jesse Chanf8084632020-04-28 14:49:13 +0000593 if (fastbootd_logo_enabled_)
594 gr_color(0, 0, 0, 255);
595 else
596 gr_color(0xd8, 0xd8, 0xd8, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700597 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700598 case UIElement::LOG:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700599 gr_color(196, 196, 196, 255);
600 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700601 case UIElement::TEXT_FILL:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700602 gr_color(0, 0, 0, 160);
603 break;
604 default:
605 gr_color(255, 255, 255, 255);
606 break;
607 }
Doug Zongkerc0441d12013-07-31 11:28:24 -0700608}
Doug Zongker211aebc2011-10-28 15:13:10 -0700609
Tianjie Xu29d55752017-09-20 17:53:46 -0700610void ScreenRecoveryUI::SelectAndShowBackgroundText(const std::vector<std::string>& locales_entries,
611 size_t sel) {
612 SetLocale(locales_entries[sel]);
613 std::vector<std::string> text_name = { "erasing_text", "error_text", "installing_text",
614 "installing_security_text", "no_command_text" };
Tao Baoda409fb2018-10-21 23:36:26 -0700615 std::unordered_map<std::string, std::unique_ptr<GRSurface>> surfaces;
Tianjie Xu29d55752017-09-20 17:53:46 -0700616 for (const auto& name : text_name) {
Tao Baoda409fb2018-10-21 23:36:26 -0700617 auto text_image = LoadLocalizedBitmap(name);
Tianjie Xu29d55752017-09-20 17:53:46 -0700618 if (!text_image) {
619 Print("Failed to load %s\n", name.c_str());
620 return;
621 }
Tao Baoda409fb2018-10-21 23:36:26 -0700622 surfaces.emplace(name, std::move(text_image));
Tianjie Xu29d55752017-09-20 17:53:46 -0700623 }
624
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700625 std::lock_guard<std::mutex> lg(updateMutex);
Tianjie Xu29d55752017-09-20 17:53:46 -0700626 gr_color(0, 0, 0, 255);
627 gr_clear();
628
Tao Bao0bc88de2018-07-31 14:53:16 -0700629 int text_y = margin_height_;
630 int text_x = margin_width_;
Tianjie Xu29d55752017-09-20 17:53:46 -0700631 int line_spacing = gr_sys_font()->char_height; // Put some extra space between images.
632 // Write the header and descriptive texts.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700633 SetColor(UIElement::INFO);
Tianjie Xu29d55752017-09-20 17:53:46 -0700634 std::string header = "Show background text image";
Tao Bao93e46ad2018-05-02 14:57:21 -0700635 text_y += DrawTextLine(text_x, text_y, header, true);
Tianjie Xu29d55752017-09-20 17:53:46 -0700636 std::string locale_selection = android::base::StringPrintf(
Tao Bao39c49182018-05-07 22:50:33 -0700637 "Current locale: %s, %zu/%zu", locales_entries[sel].c_str(), sel + 1, locales_entries.size());
Tao Bao93e46ad2018-05-02 14:57:21 -0700638 // clang-format off
639 std::vector<std::string> instruction = {
640 locale_selection,
641 "Use volume up/down to switch locales and power to exit."
642 };
643 // clang-format on
Tianjie Xu29d55752017-09-20 17:53:46 -0700644 text_y += DrawWrappedTextLines(text_x, text_y, instruction);
645
646 // Iterate through the text images and display them in order for the current locale.
647 for (const auto& p : surfaces) {
648 text_y += line_spacing;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700649 SetColor(UIElement::LOG);
Tao Bao93e46ad2018-05-02 14:57:21 -0700650 text_y += DrawTextLine(text_x, text_y, p.first, false);
Tianjie Xu29d55752017-09-20 17:53:46 -0700651 gr_color(255, 255, 255, 255);
652 gr_texticon(text_x, text_y, p.second.get());
653 text_y += gr_get_height(p.second.get());
654 }
655 // Update the whole screen.
656 gr_flip();
Tianjie Xu29d55752017-09-20 17:53:46 -0700657}
658
Tao Bao39c49182018-05-07 22:50:33 -0700659void ScreenRecoveryUI::CheckBackgroundTextImages() {
Tianjie Xu29d55752017-09-20 17:53:46 -0700660 // Load a list of locales embedded in one of the resource files.
661 std::vector<std::string> locales_entries = get_locales_in_png("installing_text");
662 if (locales_entries.empty()) {
663 Print("Failed to load locales from the resource files\n");
664 return;
665 }
Tao Bao39c49182018-05-07 22:50:33 -0700666 std::string saved_locale = locale_;
Tianjie Xu29d55752017-09-20 17:53:46 -0700667 size_t selected = 0;
668 SelectAndShowBackgroundText(locales_entries, selected);
669
670 FlushKeys();
671 while (true) {
Tom Marshall53176c72020-03-10 20:17:49 +0100672 InputEvent evt = WaitInputEvent();
673 if (evt.type() == EventType::EXTRA) {
674 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) break;
675 }
676 if (evt.type() == EventType::KEY) {
677 if (evt.key() == KEY_POWER || evt.key() == KEY_ENTER) {
678 break;
679 } else if (evt.key() == KEY_UP || evt.key() == KEY_VOLUMEUP) {
680 selected = (selected == 0) ? locales_entries.size() - 1 : selected - 1;
681 SelectAndShowBackgroundText(locales_entries, selected);
682 } else if (evt.key() == KEY_DOWN || evt.key() == KEY_VOLUMEDOWN) {
683 selected = (selected == locales_entries.size() - 1) ? 0 : selected + 1;
684 SelectAndShowBackgroundText(locales_entries, selected);
685 }
Tianjie Xu29d55752017-09-20 17:53:46 -0700686 }
687 }
688
689 SetLocale(saved_locale);
690}
691
Luke Song92eda4d2017-09-19 10:51:35 -0700692int ScreenRecoveryUI::ScreenWidth() const {
693 return gr_fb_width();
694}
695
696int ScreenRecoveryUI::ScreenHeight() const {
697 return gr_fb_height();
698}
699
Tao Bao65815b62018-10-23 10:54:02 -0700700void ScreenRecoveryUI::DrawSurface(const GRSurface* surface, int sx, int sy, int w, int h, int dx,
Luke Song92eda4d2017-09-19 10:51:35 -0700701 int dy) const {
702 gr_blit(surface, sx, sy, w, h, dx, dy);
703}
704
Tao Baoea78d862017-06-28 14:52:17 -0700705int ScreenRecoveryUI::DrawHorizontalRule(int y) const {
Luke Song92eda4d2017-09-19 10:51:35 -0700706 gr_fill(0, y + 4, ScreenWidth(), y + 6);
Tao Baoea78d862017-06-28 14:52:17 -0700707 return 8;
Elliott Hughes95fc63e2015-04-10 19:12:01 -0700708}
709
Luke Songe2bd8762017-06-12 16:08:33 -0700710void ScreenRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const {
Alessandro Astonee8754752020-03-09 23:17:50 +0100711 if (y + height > ScreenHeight())
712 height = ScreenHeight() - y;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700713 gr_fill(x, y, x + width, y + height);
Luke Songe2bd8762017-06-12 16:08:33 -0700714}
715
Alessandro Astonee8754752020-03-09 23:17:50 +0100716void ScreenRecoveryUI::DrawScrollBar(int y, int height) const {
717 int x = ScreenWidth() - margin_width_;
718 int width = 8;
719 gr_fill(x - width, y, x, y + height);
720}
721
Luke Song92eda4d2017-09-19 10:51:35 -0700722void ScreenRecoveryUI::DrawFill(int x, int y, int w, int h) const {
723 gr_fill(x, y, w, h);
724}
725
Tao Bao65815b62018-10-23 10:54:02 -0700726void ScreenRecoveryUI::DrawTextIcon(int x, int y, const GRSurface* surface) const {
Luke Song92eda4d2017-09-19 10:51:35 -0700727 gr_texticon(x, y, surface);
728}
729
Tao Bao93e46ad2018-05-02 14:57:21 -0700730int ScreenRecoveryUI::DrawTextLine(int x, int y, const std::string& line, bool bold) const {
731 gr_text(gr_sys_font(), x, y, line.c_str(), bold);
Tao Baoea78d862017-06-28 14:52:17 -0700732 return char_height_ + 4;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700733}
734
Tao Bao93e46ad2018-05-02 14:57:21 -0700735int ScreenRecoveryUI::DrawTextLines(int x, int y, const std::vector<std::string>& lines) const {
Tao Baoea78d862017-06-28 14:52:17 -0700736 int offset = 0;
Tao Bao93e46ad2018-05-02 14:57:21 -0700737 for (const auto& line : lines) {
738 offset += DrawTextLine(x, y + offset, line, false);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700739 }
Tao Baoea78d862017-06-28 14:52:17 -0700740 return offset;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700741}
742
Tao Bao93e46ad2018-05-02 14:57:21 -0700743int ScreenRecoveryUI::DrawWrappedTextLines(int x, int y,
744 const std::vector<std::string>& lines) const {
Tao Bao452b4872018-05-09 11:52:09 -0700745 // Keep symmetrical margins based on the given offset (i.e. x).
746 size_t text_cols = (ScreenWidth() - x * 2) / char_width_;
Tao Bao2bbc6d62017-08-13 23:48:55 -0700747 int offset = 0;
Tao Bao93e46ad2018-05-02 14:57:21 -0700748 for (const auto& line : lines) {
Tao Bao2bbc6d62017-08-13 23:48:55 -0700749 size_t next_start = 0;
750 while (next_start < line.size()) {
Tao Bao452b4872018-05-09 11:52:09 -0700751 std::string sub = line.substr(next_start, text_cols + 1);
752 if (sub.size() <= text_cols) {
Tao Bao2bbc6d62017-08-13 23:48:55 -0700753 next_start += sub.size();
754 } else {
Tao Bao452b4872018-05-09 11:52:09 -0700755 // Line too long and must be wrapped to text_cols columns.
Tao Bao2bbc6d62017-08-13 23:48:55 -0700756 size_t last_space = sub.find_last_of(" \t\n");
757 if (last_space == std::string::npos) {
Tao Bao93e46ad2018-05-02 14:57:21 -0700758 // No space found, just draw as much as we can.
Tao Bao452b4872018-05-09 11:52:09 -0700759 sub.resize(text_cols);
760 next_start += text_cols;
Tao Bao2bbc6d62017-08-13 23:48:55 -0700761 } else {
762 sub.resize(last_space);
763 next_start += last_space + 1;
764 }
765 }
Tao Bao93e46ad2018-05-02 14:57:21 -0700766 offset += DrawTextLine(x, y + offset, sub, false);
Tao Bao2bbc6d62017-08-13 23:48:55 -0700767 }
768 }
769 return offset;
770}
771
Jerry Zhang0e577ee2018-05-07 11:21:10 -0700772void ScreenRecoveryUI::SetTitle(const std::vector<std::string>& lines) {
773 title_lines_ = lines;
774}
775
Tianjie Xue5032212019-07-23 13:23:29 -0700776std::vector<std::string> ScreenRecoveryUI::GetMenuHelpMessage() const {
777 // clang-format off
778 static std::vector<std::string> REGULAR_HELP{
Richard Hansen36cf08d2020-05-13 10:07:36 -0400779 "Use the volume up/down keys to navigate.",
780 "Use the power key to select.",
Tianjie Xue5032212019-07-23 13:23:29 -0700781 };
782 static std::vector<std::string> LONG_PRESS_HELP{
783 "Any button cycles highlight.",
784 "Long-press activates.",
785 };
Richard Hansen22b04102020-05-12 18:18:43 -0400786 static const std::vector<std::string> NO_HELP = {};
Tianjie Xue5032212019-07-23 13:23:29 -0700787 // clang-format on
Richard Hansen22b04102020-05-12 18:18:43 -0400788 return HasTouchScreen() ? NO_HELP : HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP;
Tianjie Xue5032212019-07-23 13:23:29 -0700789}
790
Tao Bao171b4c42017-06-19 23:10:44 -0700791// Redraws everything on the screen. Does not flip pages. Should only be called with updateMutex
792// locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700793void ScreenRecoveryUI::draw_screen_locked() {
Tao Bao171b4c42017-06-19 23:10:44 -0700794 if (!show_text) {
795 draw_background_locked();
796 draw_foreground_locked();
797 return;
798 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700799
Tao Bao171b4c42017-06-19 23:10:44 -0700800 gr_color(0, 0, 0, 255);
801 gr_clear();
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700802
Tianjie Xue5032212019-07-23 13:23:29 -0700803 draw_menu_and_text_buffer_locked(GetMenuHelpMessage());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700804}
805
806// Draws the menu and text buffer on the screen. Should only be called with updateMutex locked.
Tao Bao93e46ad2018-05-02 14:57:21 -0700807void ScreenRecoveryUI::draw_menu_and_text_buffer_locked(
808 const std::vector<std::string>& help_message) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700809 int y = margin_height_;
David Anderson983e2d52019-01-02 11:35:38 -0800810
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700811 if (menu_) {
Alessandro Astone5d1957d2020-03-29 15:08:09 +0200812 auto& logo = fastbootd_logo_enabled_ ? fastbootd_logo_ : lineage_logo_;
Richard Hansend72e0c32020-05-12 18:08:56 -0400813 auto logo_width = gr_get_width(logo.get());
814 auto logo_height = gr_get_height(logo.get());
815 auto centered_x = ScreenWidth() / 2 - logo_width / 2;
816 DrawSurface(logo.get(), 0, 0, logo_width, logo_height, centered_x, y);
817 y += logo_height;
Tao Bao171b4c42017-06-19 23:10:44 -0700818
Richard Hansend72e0c32020-05-12 18:08:56 -0400819 if (!menu_->IsMain()) {
820 auto icon_w = gr_get_width(back_icon_.get());
821 auto icon_h = gr_get_height(back_icon_.get());
822 auto icon_x = centered_x / 2 - icon_w / 2;
823 auto icon_y = y - logo_height / 2 - icon_h / 2;
824 gr_blit(back_icon_sel_ && menu_->selection() == -1 ? back_icon_sel_.get() : back_icon_.get(),
825 0, 0, icon_w, icon_h, icon_x, icon_y);
Alessandro Astonee8754752020-03-09 23:17:50 +0100826 }
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700827
Richard Hansend72e0c32020-05-12 18:08:56 -0400828 int x = margin_width_ + kMenuIndent;
829 if (!title_lines_.empty()) {
830 SetColor(UIElement::INFO);
831 y += DrawTextLines(x, y, title_lines_);
832 }
Tianjie Xu66dbf632018-10-11 16:54:50 -0700833 y += menu_->DrawHeader(x, y);
Tom Marshall53176c72020-03-10 20:17:49 +0100834 menu_start_y_ = y + 12; // Skip horizontal rule and some margin
Alessandro Astone3525c4d2020-06-25 19:54:18 +0200835 menu_->SetMenuHeight(std::max(0, ScreenHeight() - menu_start_y_));
Tianjie Xu66dbf632018-10-11 16:54:50 -0700836 y += menu_->DrawItems(x, y, ScreenWidth(), IsLongPress());
Richard Hansen22b04102020-05-12 18:18:43 -0400837 if (!help_message.empty()) {
838 y += MenuItemPadding();
839 SetColor(UIElement::INFO);
840 y += DrawTextLines(x, y, help_message);
841 }
Tao Bao171b4c42017-06-19 23:10:44 -0700842 }
843
844 // Display from the bottom up, until we hit the top of the screen, the bottom of the menu, or
845 // we've displayed the entire text buffer.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700846 SetColor(UIElement::LOG);
Tao Baocb5524c2017-09-08 21:25:32 -0700847 int row = text_row_;
Tao Bao171b4c42017-06-19 23:10:44 -0700848 size_t count = 0;
Tao Bao0bc88de2018-07-31 14:53:16 -0700849 for (int ty = ScreenHeight() - margin_height_ - char_height_; ty >= y && count < text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700850 ty -= char_height_, ++count) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700851 DrawTextLine(margin_width_, ty, text_[row], false);
Tao Bao171b4c42017-06-19 23:10:44 -0700852 --row;
853 if (row < 0) row = text_rows_ - 1;
854 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700855}
856
857// Redraw everything on the screen and flip the screen (make it visible).
858// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700859void ScreenRecoveryUI::update_screen_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700860 draw_screen_locked();
861 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700862}
863
864// Updates only the progress bar, if possible, otherwise redraws the screen.
865// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700866void ScreenRecoveryUI::update_progress_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700867 if (show_text || !pagesIdentical) {
868 draw_screen_locked(); // Must redraw the whole screen
869 pagesIdentical = true;
870 } else {
871 draw_foreground_locked(); // Draw only the progress bar and overlays
872 }
873 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700874}
875
Elliott Hughes985022a2015-04-13 13:04:32 -0700876void ScreenRecoveryUI::ProgressThreadLoop() {
Tao Bao0bc88de2018-07-31 14:53:16 -0700877 double interval = 1.0 / animation_fps_;
Tao Bao26ea9592018-05-09 16:32:02 -0700878 while (!progress_thread_stopped_) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700879 double start = now();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700880 bool redraw = false;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700881 {
882 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker211aebc2011-10-28 15:13:10 -0700883
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700884 // update the installation animation, if active
885 // skip this if we have a text overlay (too expensive to update)
Tao Baoda409fb2018-10-21 23:36:26 -0700886 if ((current_icon_ == INSTALLING_UPDATE || current_icon_ == ERASING) && !show_text) {
887 if (!intro_done_) {
888 if (current_frame_ == intro_frames_.size() - 1) {
889 intro_done_ = true;
890 current_frame_ = 0;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700891 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700892 ++current_frame_;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700893 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700894 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700895 current_frame_ = (current_frame_ + 1) % loop_frames_.size();
Doug Zongker211aebc2011-10-28 15:13:10 -0700896 }
897
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700898 redraw = true;
899 }
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700900
901 // move the progress bar forward on timed intervals, if configured
902 int duration = progressScopeDuration;
903 if (progressBarType == DETERMINATE && duration > 0) {
904 double elapsed = now() - progressScopeTime;
905 float p = 1.0 * elapsed / duration;
906 if (p > 1.0) p = 1.0;
907 if (p > progress) {
908 progress = p;
909 redraw = true;
910 }
911 }
912
913 if (redraw) update_progress_locked();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700914 }
915
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700916 double end = now();
917 // minimum of 20ms delay between frames
918 double delay = interval - (end - start);
919 if (delay < 0.02) delay = 0.02;
920 usleep(static_cast<useconds_t>(delay * 1000000));
921 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700922}
923
Tao Baoda409fb2018-10-21 23:36:26 -0700924std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadBitmap(const std::string& filename) {
925 GRSurface* surface;
926 if (auto result = res_create_display_surface(filename.c_str(), &surface); result < 0) {
927 LOG(ERROR) << "Failed to load bitmap " << filename << " (error " << result << ")";
928 return nullptr;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700929 }
Tao Baoda409fb2018-10-21 23:36:26 -0700930 return std::unique_ptr<GRSurface>(surface);
Doug Zongkereac881c2014-03-07 09:21:25 -0800931}
932
Tao Baoda409fb2018-10-21 23:36:26 -0700933std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadLocalizedBitmap(const std::string& filename) {
934 GRSurface* surface;
xunchang9d05c8a2019-04-16 12:07:42 -0700935 auto result = res_create_localized_alpha_surface(filename.c_str(), locale_.c_str(), &surface);
936 if (result == 0) {
937 return std::unique_ptr<GRSurface>(surface);
Tao Bao736d59c2017-01-03 10:15:33 -0800938 }
xunchang9d05c8a2019-04-16 12:07:42 -0700939
940 result = res_create_localized_alpha_surface(filename.c_str(), DEFAULT_LOCALE, &surface);
941 if (result == 0) {
942 return std::unique_ptr<GRSurface>(surface);
943 }
944
xunchang9d05c8a2019-04-16 12:07:42 -0700945 return nullptr;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700946}
947
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700948static char** Alloc2d(size_t rows, size_t cols) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700949 char** result = new char*[rows];
950 for (size_t i = 0; i < rows; ++i) {
951 result[i] = new char[cols];
952 memset(result[i], 0, cols);
953 }
954 return result;
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700955}
956
Tianjie Xu35926c42016-04-28 18:06:26 -0700957// Choose the right background string to display during update.
958void ScreenRecoveryUI::SetSystemUpdateText(bool security_update) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700959 if (security_update) {
Tao Baoda409fb2018-10-21 23:36:26 -0700960 installing_text_ = LoadLocalizedBitmap("installing_security_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700961 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700962 installing_text_ = LoadLocalizedBitmap("installing_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700963 }
964 Redraw();
Tianjie Xu35926c42016-04-28 18:06:26 -0700965}
966
Sen Jiangd5304492016-12-09 16:20:49 -0800967bool ScreenRecoveryUI::InitTextParams() {
Tao Baoba4edb32018-06-13 11:22:50 -0700968 // gr_init() would return successfully on font initialization failure.
969 if (gr_sys_font() == nullptr) {
Tao Bao171b4c42017-06-19 23:10:44 -0700970 return false;
971 }
Tao Bao171b4c42017-06-19 23:10:44 -0700972 gr_font_size(gr_sys_font(), &char_width_, &char_height_);
Alessandro Astonee8754752020-03-09 23:17:50 +0100973 gr_font_size(gr_menu_font(), &menu_char_width_, &menu_char_height_);
Tao Bao0bc88de2018-07-31 14:53:16 -0700974 text_rows_ = (ScreenHeight() - margin_height_ * 2) / char_height_;
975 text_cols_ = (ScreenWidth() - margin_width_ * 2) / char_width_;
Tao Bao171b4c42017-06-19 23:10:44 -0700976 return true;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -0700977}
978
Tianjie Xub99e6062018-10-16 15:13:09 -0700979bool ScreenRecoveryUI::LoadWipeDataMenuText() {
Tianjie Xu1a0a30a2018-10-25 15:22:07 -0700980 // Ignores the errors since the member variables will stay as nullptr.
981 cancel_wipe_data_text_ = LoadLocalizedBitmap("cancel_wipe_data_text");
982 factory_data_reset_text_ = LoadLocalizedBitmap("factory_data_reset_text");
983 try_again_text_ = LoadLocalizedBitmap("try_again_text");
984 wipe_data_confirmation_text_ = LoadLocalizedBitmap("wipe_data_confirmation_text");
985 wipe_data_menu_header_text_ = LoadLocalizedBitmap("wipe_data_menu_header_text");
Tianjie Xub99e6062018-10-16 15:13:09 -0700986 return true;
987}
988
Mark Salyzyn30017e72020-05-13 12:39:12 -0700989static bool InitGraphics() {
990 // Timeout is same as init wait for file default of 5 seconds and is arbitrary
991 const unsigned timeout = 500; // 10ms increments
992 for (auto retry = timeout; retry > 0; --retry) {
993 if (gr_init() == 0) {
994 if (retry < timeout) {
995 // Log message like init wait for file completion log for consistency.
996 LOG(WARNING) << "wait for 'graphics' took " << ((timeout - retry) * 10) << "ms";
997 }
998 return true;
999 }
1000 std::this_thread::sleep_for(10ms);
1001 }
1002 // Log message like init wait for file timeout log for consistency.
1003 LOG(ERROR) << "timeout wait for 'graphics' took " << (timeout * 10) << "ms";
1004 return false;
1005}
1006
Tao Bao736d59c2017-01-03 10:15:33 -08001007bool ScreenRecoveryUI::Init(const std::string& locale) {
1008 RecoveryUI::Init(locale);
Tao Baoefb49ad2017-01-31 23:03:10 -08001009
Mark Salyzyn30017e72020-05-13 12:39:12 -07001010 if (!InitGraphics()) {
Tao Baoba4edb32018-06-13 11:22:50 -07001011 return false;
1012 }
1013
Tao Bao736d59c2017-01-03 10:15:33 -08001014 if (!InitTextParams()) {
1015 return false;
1016 }
Alessandro Astonee8754752020-03-09 23:17:50 +01001017 menu_draw_funcs_ = std::make_unique<MenuDrawFunctions>(*this);
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001018
Michael Bestas2e2d5d62019-03-23 17:28:22 +02001019 if (blank_unblank_on_init_) {
1020 gr_fb_blank(true);
1021 gr_fb_blank(false);
1022 }
1023
Tao Bao736d59c2017-01-03 10:15:33 -08001024 // Are we portrait or landscape?
1025 layout_ = (gr_fb_width() > gr_fb_height()) ? LANDSCAPE : PORTRAIT;
1026 // Are we the large variant of our base layout?
1027 if (gr_fb_height() > PixelsFromDp(800)) ++layout_;
Elliott Hughesfaf36e02016-04-20 17:22:16 -07001028
Tao Bao736d59c2017-01-03 10:15:33 -08001029 text_ = Alloc2d(text_rows_, text_cols_ + 1);
1030 file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
Doug Zongker55a36ac2013-03-04 15:49:02 -08001031
Tao Bao736d59c2017-01-03 10:15:33 -08001032 text_col_ = text_row_ = 0;
Doug Zongker211aebc2011-10-28 15:13:10 -07001033
Tao Baoefb49ad2017-01-31 23:03:10 -08001034 // Set up the locale info.
1035 SetLocale(locale);
1036
Tao Baoda409fb2018-10-21 23:36:26 -07001037 error_icon_ = LoadBitmap("icon_error");
Doug Zongker02ec6b82012-08-22 17:26:40 -07001038
Tao Baoda409fb2018-10-21 23:36:26 -07001039 progress_bar_empty_ = LoadBitmap("progress_empty");
1040 progress_bar_fill_ = LoadBitmap("progress_fill");
1041 stage_marker_empty_ = LoadBitmap("stage_empty");
1042 stage_marker_fill_ = LoadBitmap("stage_fill");
Elliott Hughes498cda62016-04-14 16:49:04 -07001043
Tao Baoda409fb2018-10-21 23:36:26 -07001044 erasing_text_ = LoadLocalizedBitmap("erasing_text");
1045 no_command_text_ = LoadLocalizedBitmap("no_command_text");
1046 error_text_ = LoadLocalizedBitmap("error_text");
Doug Zongker211aebc2011-10-28 15:13:10 -07001047
Alessandro Astonee8754752020-03-09 23:17:50 +01001048 back_icon_ = LoadBitmap("ic_back");
1049 back_icon_sel_ = LoadBitmap("ic_back_sel");
Alessandro Astoneb9f974d2020-02-26 17:25:54 +01001050 if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) ||
1051 android::base::GetBoolProperty("ro.fastbootd.available", false)) {
Nicholas Lim70dbdc22020-06-21 02:35:05 +08001052 lineage_logo_ = LoadBitmap("logo_image_switch");
David Anderson983e2d52019-01-02 11:35:38 -08001053 fastbootd_logo_ = LoadBitmap("fastbootd");
Nicholas Lim70dbdc22020-06-21 02:35:05 +08001054 } else {
1055 lineage_logo_ = LoadBitmap("logo_image");
David Anderson983e2d52019-01-02 11:35:38 -08001056 }
1057
Tao Baoda409fb2018-10-21 23:36:26 -07001058 // Background text for "installing_update" could be "installing update" or
1059 // "installing security update". It will be set after Init() according to the commands in BCB.
1060 installing_text_.reset();
Elliott Hughes498cda62016-04-14 16:49:04 -07001061
Tianjie Xub99e6062018-10-16 15:13:09 -07001062 LoadWipeDataMenuText();
1063
Tao Bao736d59c2017-01-03 10:15:33 -08001064 LoadAnimation();
Doug Zongker02ec6b82012-08-22 17:26:40 -07001065
Tao Bao26ea9592018-05-09 16:32:02 -07001066 // Keep the progress bar updated, even when the process is otherwise busy.
1067 progress_thread_ = std::thread(&ScreenRecoveryUI::ProgressThreadLoop, this);
Sen Jiangd5304492016-12-09 16:20:49 -08001068
Tao Bao736d59c2017-01-03 10:15:33 -08001069 return true;
Doug Zongker211aebc2011-10-28 15:13:10 -07001070}
1071
Tao Bao551d2c32018-05-09 20:53:13 -07001072std::string ScreenRecoveryUI::GetLocale() const {
Jerry Zhang2dea53e2018-05-02 17:15:03 -07001073 return locale_;
1074}
1075
Elliott Hughes498cda62016-04-14 16:49:04 -07001076void ScreenRecoveryUI::LoadAnimation() {
Tao Bao6cd81682018-05-03 21:53:11 -07001077 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(Paths::Get().resource_dir().c_str()),
1078 closedir);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001079 dirent* de;
1080 std::vector<std::string> intro_frame_names;
Joshua Lambert94a83be2019-12-09 20:24:18 +00001081 std::vector<std::string> loop_frame_names;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001082
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001083 while ((de = readdir(dir.get())) != nullptr) {
1084 int value, num_chars;
1085 if (sscanf(de->d_name, "intro%d%n.png", &value, &num_chars) == 1) {
1086 intro_frame_names.emplace_back(de->d_name, num_chars);
1087 } else if (sscanf(de->d_name, "loop%d%n.png", &value, &num_chars) == 1) {
Joshua Lambert94a83be2019-12-09 20:24:18 +00001088 loop_frame_names.emplace_back(de->d_name, num_chars);
Elliott Hughes498cda62016-04-14 16:49:04 -07001089 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001090 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001091
Tao Baoda409fb2018-10-21 23:36:26 -07001092 size_t intro_frames = intro_frame_names.size();
1093 size_t loop_frames = loop_frame_names.size();
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001094
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001095 // It's okay to not have an intro.
Tao Baoda409fb2018-10-21 23:36:26 -07001096 if (intro_frames == 0) intro_done_ = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001097 // But you must have an animation.
1098 if (loop_frames == 0) abort();
Elliott Hughes498cda62016-04-14 16:49:04 -07001099
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001100 std::sort(intro_frame_names.begin(), intro_frame_names.end());
1101 std::sort(loop_frame_names.begin(), loop_frame_names.end());
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001102
Tao Baoda409fb2018-10-21 23:36:26 -07001103 intro_frames_.clear();
1104 intro_frames_.reserve(intro_frames);
1105 for (const auto& frame_name : intro_frame_names) {
1106 intro_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001107 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001108
Tao Baoda409fb2018-10-21 23:36:26 -07001109 loop_frames_.clear();
1110 loop_frames_.reserve(loop_frames);
1111 for (const auto& frame_name : loop_frame_names) {
1112 loop_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001113 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001114}
1115
Elliott Hughes8de52072015-04-08 20:06:50 -07001116void ScreenRecoveryUI::SetBackground(Icon icon) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001117 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker02ec6b82012-08-22 17:26:40 -07001118
Tao Baoda409fb2018-10-21 23:36:26 -07001119 current_icon_ = icon;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001120 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001121}
1122
Elliott Hughes8de52072015-04-08 20:06:50 -07001123void ScreenRecoveryUI::SetProgressType(ProgressType type) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001124 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001125 if (progressBarType != type) {
1126 progressBarType = type;
1127 }
1128 progressScopeStart = 0;
1129 progressScopeSize = 0;
1130 progress = 0;
1131 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001132}
1133
Elliott Hughes8de52072015-04-08 20:06:50 -07001134void ScreenRecoveryUI::ShowProgress(float portion, float seconds) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001135 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001136 progressBarType = DETERMINATE;
1137 progressScopeStart += progressScopeSize;
1138 progressScopeSize = portion;
1139 progressScopeTime = now();
1140 progressScopeDuration = seconds;
1141 progress = 0;
1142 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001143}
1144
Elliott Hughes8de52072015-04-08 20:06:50 -07001145void ScreenRecoveryUI::SetProgress(float fraction) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001146 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001147 if (fraction < 0.0) fraction = 0.0;
1148 if (fraction > 1.0) fraction = 1.0;
1149 if (progressBarType == DETERMINATE && fraction > progress) {
1150 // Skip updates that aren't visibly different.
Tao Baoda409fb2018-10-21 23:36:26 -07001151 int width = gr_get_width(progress_bar_empty_.get());
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001152 float scale = width * progressScopeSize;
1153 if ((int)(progress * scale) != (int)(fraction * scale)) {
1154 progress = fraction;
1155 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001156 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001157 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001158}
1159
Doug Zongkerc87bab12013-11-25 13:53:25 -08001160void ScreenRecoveryUI::SetStage(int current, int max) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001161 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001162 stage = current;
1163 max_stage = max;
Doug Zongkerc87bab12013-11-25 13:53:25 -08001164}
1165
Tao Baob6918c72015-05-19 17:02:16 -07001166void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001167 std::string str;
1168 android::base::StringAppendV(&str, fmt, ap);
Doug Zongker211aebc2011-10-28 15:13:10 -07001169
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001170 if (copy_to_stdout) {
1171 fputs(str.c_str(), stdout);
1172 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001173
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001174 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001175 if (text_rows_ > 0 && text_cols_ > 0) {
1176 for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
1177 if (*ptr == '\n' || text_col_ >= text_cols_) {
Elliott Hughesc0491632015-05-06 12:40:05 -07001178 text_[text_row_][text_col_] = '\0';
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001179 text_col_ = 0;
1180 text_row_ = (text_row_ + 1) % text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001181 }
1182 if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
Doug Zongker211aebc2011-10-28 15:13:10 -07001183 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001184 text_[text_row_][text_col_] = '\0';
1185 update_screen_locked();
1186 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001187}
1188
Tao Baob6918c72015-05-19 17:02:16 -07001189void ScreenRecoveryUI::Print(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001190 va_list ap;
1191 va_start(ap, fmt);
1192 PrintV(fmt, true, ap);
1193 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001194}
1195
Tianjie Xu8f397302018-08-20 13:40:47 -07001196void ScreenRecoveryUI::PrintOnScreenOnly(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001197 va_list ap;
1198 va_start(ap, fmt);
1199 PrintV(fmt, false, ap);
1200 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001201}
1202
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001203void ScreenRecoveryUI::PutChar(char ch) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001204 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001205 if (ch != '\n') text_[text_row_][text_col_++] = ch;
1206 if (ch == '\n' || text_col_ >= text_cols_) {
1207 text_col_ = 0;
1208 ++text_row_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001209 }
Elliott Hughes8de52072015-04-08 20:06:50 -07001210}
1211
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001212void ScreenRecoveryUI::ClearText() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001213 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001214 text_col_ = 0;
1215 text_row_ = 0;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001216 for (size_t i = 0; i < text_rows_; ++i) {
1217 memset(text_[i], 0, text_cols_ + 1);
1218 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001219}
1220
1221void ScreenRecoveryUI::ShowFile(FILE* fp) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001222 std::vector<off_t> offsets;
1223 offsets.push_back(ftello(fp));
1224 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001225
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001226 struct stat sb;
1227 fstat(fileno(fp), &sb);
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001228
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001229 bool show_prompt = false;
1230 while (true) {
1231 if (show_prompt) {
1232 PrintOnScreenOnly("--(%d%% of %d bytes)--",
1233 static_cast<int>(100 * (double(ftello(fp)) / double(sb.st_size))),
1234 static_cast<int>(sb.st_size));
1235 Redraw();
1236 while (show_prompt) {
1237 show_prompt = false;
Tom Marshall53176c72020-03-10 20:17:49 +01001238 InputEvent evt = WaitInputEvent();
1239 if (evt.type() == EventType::EXTRA) {
1240 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) {
1241 return;
1242 }
1243 }
1244 if (evt.type() != EventType::KEY) {
1245 show_prompt = true;
1246 continue;
1247 }
1248 if (evt.key() == KEY_POWER || evt.key() == KEY_ENTER || evt.key() == KEY_BACKSPACE ||
1249 evt.key() == KEY_BACK || evt.key() == KEY_HOME || evt.key() == KEY_HOMEPAGE) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001250 return;
Tom Marshall53176c72020-03-10 20:17:49 +01001251 } else if (evt.key() == KEY_UP || evt.key() == KEY_VOLUMEUP || evt.key() == KEY_SCROLLUP) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001252 if (offsets.size() <= 1) {
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001253 show_prompt = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001254 } else {
1255 offsets.pop_back();
1256 fseek(fp, offsets.back(), SEEK_SET);
1257 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001258 } else {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001259 if (feof(fp)) {
1260 return;
1261 }
1262 offsets.push_back(ftello(fp));
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001263 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001264 }
1265 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001266 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001267
1268 int ch = getc(fp);
1269 if (ch == EOF) {
1270 while (text_row_ < text_rows_ - 1) PutChar('\n');
1271 show_prompt = true;
1272 } else {
1273 PutChar(ch);
1274 if (text_col_ == 0 && text_row_ >= text_rows_ - 1) {
1275 show_prompt = true;
1276 }
1277 }
1278 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001279}
1280
Tao Bao1d156b92018-05-02 12:43:18 -07001281void ScreenRecoveryUI::ShowFile(const std::string& filename) {
1282 std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(filename.c_str(), "re"), fclose);
1283 if (!fp) {
1284 Print(" Unable to open %s: %s\n", filename.c_str(), strerror(errno));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001285 return;
1286 }
Elliott Hughesc0491632015-05-06 12:40:05 -07001287
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001288 char** old_text = text_;
1289 size_t old_text_col = text_col_;
1290 size_t old_text_row = text_row_;
Elliott Hughesc0491632015-05-06 12:40:05 -07001291
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001292 // Swap in the alternate screen and clear it.
1293 text_ = file_viewer_text_;
1294 ClearText();
Elliott Hughesc0491632015-05-06 12:40:05 -07001295
Tao Bao1d156b92018-05-02 12:43:18 -07001296 ShowFile(fp.get());
Elliott Hughesc0491632015-05-06 12:40:05 -07001297
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001298 text_ = old_text;
1299 text_col_ = old_text_col;
1300 text_row_ = old_text_row;
Elliott Hughes8de52072015-04-08 20:06:50 -07001301}
1302
Tao Baoda409fb2018-10-21 23:36:26 -07001303std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(
1304 const GRSurface* graphic_header, const std::vector<const GRSurface*>& graphic_items,
1305 const std::vector<std::string>& text_headers, const std::vector<std::string>& text_items,
1306 size_t initial_selection) const {
Tianjie Xub99e6062018-10-16 15:13:09 -07001307 // horizontal unusable area: margin width + menu indent
1308 size_t max_width = ScreenWidth() - margin_width_ - kMenuIndent;
1309 // vertical unusable area: margin height + title lines + helper message + high light bar.
1310 // It is safe to reserve more space.
1311 size_t max_height = ScreenHeight() - margin_height_ - char_height_ * (title_lines_.size() + 3);
1312 if (GraphicMenu::Validate(max_width, max_height, graphic_header, graphic_items)) {
1313 return std::make_unique<GraphicMenu>(graphic_header, graphic_items, initial_selection, *this);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001314 }
Tianjie Xub99e6062018-10-16 15:13:09 -07001315
1316 fprintf(stderr, "Failed to initialize graphic menu, falling back to use the text menu.\n");
1317
1318 return CreateMenu(text_headers, text_items, initial_selection);
1319}
1320
1321std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(const std::vector<std::string>& text_headers,
1322 const std::vector<std::string>& text_items,
1323 size_t initial_selection) const {
Alessandro Astonee8754752020-03-09 23:17:50 +01001324 int menu_char_width = MenuCharWidth();
1325 int menu_char_height = MenuCharHeight();
Alessandro Astonee8754752020-03-09 23:17:50 +01001326 int menu_cols = (ScreenWidth() - margin_width_*2 - kMenuIndent) / menu_char_width;
1327 bool wrap_selection = !HasThreeButtons() && !HasTouchScreen();
Alessandro Astone3525c4d2020-06-25 19:54:18 +02001328 return std::make_unique<TextMenu>(wrap_selection, menu_cols, text_headers, text_items,
Alessandro Astonee8754752020-03-09 23:17:50 +01001329 initial_selection, menu_char_height, *menu_draw_funcs_);
Doug Zongker211aebc2011-10-28 15:13:10 -07001330}
1331
1332int ScreenRecoveryUI::SelectMenu(int sel) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001333 std::lock_guard<std::mutex> lg(updateMutex);
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001334 if (menu_) {
1335 int old_sel = menu_->selection();
1336 sel = menu_->Select(sel);
Elliott Hughesfc06f872015-03-23 13:45:31 -07001337
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001338 if (sel != old_sel) {
1339 update_screen_locked();
1340 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001341 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001342 return sel;
Doug Zongker211aebc2011-10-28 15:13:10 -07001343}
1344
Alessandro Astone4a00e642022-03-21 17:14:47 +01001345int ScreenRecoveryUI::SelectMenu(const Point& p) {
1346 // Correct position for overscan
1347 const Point point(p.x() - gr_overscan_offset_x(), p.y() - gr_overscan_offset_y());
1348
Tom Marshall53176c72020-03-10 20:17:49 +01001349 int new_sel = Device::kNoAction;
1350 std::lock_guard<std::mutex> lg(updateMutex);
1351 if (menu_) {
1352 if (!menu_->IsMain()) {
1353 // Back arrow hitbox
1354 const static int logo_width = gr_get_width(lineage_logo_.get());
1355 const static int logo_height = gr_get_height(lineage_logo_.get());
1356 const static int icon_w = gr_get_width(back_icon_.get());
1357 const static int icon_h = gr_get_height(back_icon_.get());
1358 const static int centered_x = ScreenWidth() / 2 - logo_width / 2;
1359 const static int icon_x = centered_x / 2 - icon_w / 2;
1360 const static int icon_y = margin_height_ + logo_height / 2 - icon_h / 2;
1361
1362 if (point.x() >= icon_x && point.x() <= icon_x + icon_w &&
1363 point.y() >= icon_y && point.y() <= icon_y + icon_h) {
1364 return Device::kGoBack;
1365 }
1366 }
1367
1368 if (point.y() >= menu_start_y_ &&
1369 point.y() < menu_start_y_ + menu_->ItemsCount() * MenuItemHeight()) {
1370 int old_sel = menu_->selection();
1371 int relative_sel = (point.y() - menu_start_y_) / MenuItemHeight();
1372 new_sel = menu_->SelectVisible(relative_sel);
1373 if (new_sel != -1 && new_sel != old_sel) {
1374 update_screen_locked();
1375 }
1376 }
1377 }
1378 return new_sel;
1379}
1380
1381int ScreenRecoveryUI::ScrollMenu(int updown) {
1382 std::lock_guard<std::mutex> lg(updateMutex);
1383 int sel = Device::kNoAction;
1384 if (menu_) {
1385 sel = menu_->Scroll(updown);
1386 update_screen_locked();
1387 }
1388 return sel;
1389}
1390
Tianjie Xub99e6062018-10-16 15:13:09 -07001391size_t ScreenRecoveryUI::ShowMenu(std::unique_ptr<Menu>&& menu, bool menu_only,
Tom Marshall55220ba2019-01-04 14:37:31 -08001392 const std::function<int(int, bool)>& key_handler,
1393 bool refreshable) {
Tao Bao3aec6962018-04-20 09:24:58 -07001394 // Throw away keys pressed previously, so user doesn't accidentally trigger menu items.
1395 FlushKeys();
1396
Jerry Zhangb76af932018-05-22 12:08:35 -07001397 // If there is a key interrupt in progress, return KeyError::INTERRUPTED without starting the
1398 // menu.
1399 if (IsKeyInterrupted()) return static_cast<size_t>(KeyError::INTERRUPTED);
1400
Tianjie Xub99e6062018-10-16 15:13:09 -07001401 CHECK(menu != nullptr);
Tao Bao3aec6962018-04-20 09:24:58 -07001402
Tianjie Xub99e6062018-10-16 15:13:09 -07001403 // Starts and displays the menu
1404 menu_ = std::move(menu);
1405 Redraw();
1406
1407 int selected = menu_->selection();
Tao Bao3aec6962018-04-20 09:24:58 -07001408 int chosen_item = -1;
1409 while (chosen_item < 0) {
Tom Marshall53176c72020-03-10 20:17:49 +01001410 InputEvent evt = WaitInputEvent();
1411 if (evt.type() == EventType::EXTRA) {
1412 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) {
1413 // WaitKey() was interrupted.
1414 return static_cast<size_t>(KeyError::INTERRUPTED);
1415 }
1416 if (evt.key() == static_cast<int>(KeyError::TIMED_OUT)) { // WaitKey() timed out.
1417 if (WasTextEverVisible()) {
1418 continue;
1419 } else {
1420 LOG(INFO) << "Timed out waiting for key input; rebooting.";
1421 menu_.reset();
1422 Redraw();
1423 return static_cast<size_t>(KeyError::TIMED_OUT);
1424 }
Tao Bao3aec6962018-04-20 09:24:58 -07001425 }
1426 }
1427
Tom Marshall53176c72020-03-10 20:17:49 +01001428 int action = Device::kNoAction;
1429 if (evt.type() == EventType::TOUCH) {
1430 int touch_sel = SelectMenu(evt.pos());
1431 if (touch_sel < 0) {
1432 action = touch_sel;
1433 } else {
1434 action = Device::kInvokeItem;
1435 selected = touch_sel;
1436 }
1437 } else {
1438 bool visible = IsTextVisible();
1439 action = key_handler(evt.key(), visible);
1440 }
Tom Marshall55220ba2019-01-04 14:37:31 -08001441
Tao Bao3aec6962018-04-20 09:24:58 -07001442 if (action < 0) {
1443 switch (action) {
1444 case Device::kHighlightUp:
1445 selected = SelectMenu(--selected);
1446 break;
1447 case Device::kHighlightDown:
1448 selected = SelectMenu(++selected);
1449 break;
Tom Marshall53176c72020-03-10 20:17:49 +01001450 case Device::kScrollUp:
1451 selected = ScrollMenu(-1);
1452 break;
1453 case Device::kScrollDown:
1454 selected = ScrollMenu(1);
1455 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001456 case Device::kInvokeItem:
Alessandro Astonee8754752020-03-09 23:17:50 +01001457 if (selected < 0) {
1458 chosen_item = Device::kGoBack;
1459 } else {
1460 chosen_item = selected;
1461 }
Tao Bao3aec6962018-04-20 09:24:58 -07001462 break;
1463 case Device::kNoAction:
1464 break;
Tom Marshalld13a8c52017-08-24 13:50:01 +00001465 case Device::kGoBack:
1466 chosen_item = Device::kGoBack;
1467 break;
1468 case Device::kGoHome:
1469 chosen_item = Device::kGoHome;
1470 break;
Tom Marshall90e09f82018-12-17 15:57:44 -08001471 case Device::kDoSideload:
1472 chosen_item = Device::kDoSideload;
1473 break;
Tom Marshall55220ba2019-01-04 14:37:31 -08001474 case Device::kRefresh:
1475 if (refreshable) {
1476 chosen_item = Device::kRefresh;
1477 }
1478 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001479 }
1480 } else if (!menu_only) {
1481 chosen_item = action;
1482 }
Tom Marshall90e09f82018-12-17 15:57:44 -08001483
1484 if (chosen_item == Device::kGoBack || chosen_item == Device::kGoHome ||
Tom Marshall55220ba2019-01-04 14:37:31 -08001485 chosen_item == Device::kDoSideload || chosen_item == Device::kRefresh) {
Tom Marshalld13a8c52017-08-24 13:50:01 +00001486 break;
1487 }
Tao Bao3aec6962018-04-20 09:24:58 -07001488 }
1489
Tianjie Xub99e6062018-10-16 15:13:09 -07001490 menu_.reset();
Tianjie Xub99e6062018-10-16 15:13:09 -07001491
Tao Bao3aec6962018-04-20 09:24:58 -07001492 return chosen_item;
1493}
1494
Tianjie Xub99e6062018-10-16 15:13:09 -07001495size_t ScreenRecoveryUI::ShowMenu(const std::vector<std::string>& headers,
1496 const std::vector<std::string>& items, size_t initial_selection,
1497 bool menu_only,
Tom Marshall55220ba2019-01-04 14:37:31 -08001498 const std::function<int(int, bool)>& key_handler,
1499 bool refreshable) {
Tianjie Xub99e6062018-10-16 15:13:09 -07001500 auto menu = CreateMenu(headers, items, initial_selection);
1501 if (menu == nullptr) {
1502 return initial_selection;
1503 }
1504
Tom Marshall55220ba2019-01-04 14:37:31 -08001505 return ShowMenu(std::move(menu), menu_only, key_handler, refreshable);
Tianjie Xub99e6062018-10-16 15:13:09 -07001506}
1507
1508size_t ScreenRecoveryUI::ShowPromptWipeDataMenu(const std::vector<std::string>& backup_headers,
1509 const std::vector<std::string>& backup_items,
1510 const std::function<int(int, bool)>& key_handler) {
Alessandro Astone5ed22c82020-10-05 15:11:36 +02001511 auto wipe_data_menu = CreateMenu(backup_headers, backup_items, 0);
Tianjie Xub99e6062018-10-16 15:13:09 -07001512 if (wipe_data_menu == nullptr) {
1513 return 0;
1514 }
1515
1516 return ShowMenu(std::move(wipe_data_menu), true, key_handler);
1517}
1518
Tianjie Xu1a0a30a2018-10-25 15:22:07 -07001519size_t ScreenRecoveryUI::ShowPromptWipeDataConfirmationMenu(
1520 const std::vector<std::string>& backup_headers, const std::vector<std::string>& backup_items,
1521 const std::function<int(int, bool)>& key_handler) {
1522 auto confirmation_menu =
1523 CreateMenu(wipe_data_confirmation_text_.get(),
1524 { cancel_wipe_data_text_.get(), factory_data_reset_text_.get() }, backup_headers,
1525 backup_items, 0);
1526 if (confirmation_menu == nullptr) {
1527 return 0;
1528 }
1529
1530 return ShowMenu(std::move(confirmation_menu), true, key_handler);
1531}
1532
Elliott Hughes8de52072015-04-08 20:06:50 -07001533bool ScreenRecoveryUI::IsTextVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001534 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001535 int visible = show_text;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001536 return visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001537}
1538
Elliott Hughes8de52072015-04-08 20:06:50 -07001539bool ScreenRecoveryUI::WasTextEverVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001540 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001541 int ever_visible = show_text_ever;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001542 return ever_visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001543}
1544
Elliott Hughes8de52072015-04-08 20:06:50 -07001545void ScreenRecoveryUI::ShowText(bool visible) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001546 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001547 show_text = visible;
1548 if (show_text) show_text_ever = true;
1549 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001550}
Doug Zongkerc0441d12013-07-31 11:28:24 -07001551
Elliott Hughes8de52072015-04-08 20:06:50 -07001552void ScreenRecoveryUI::Redraw() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001553 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001554 update_screen_locked();
Doug Zongkerc0441d12013-07-31 11:28:24 -07001555}
Elliott Hughes642aaa72015-04-10 12:47:46 -07001556
1557void ScreenRecoveryUI::KeyLongPress(int) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001558 // Redraw so that if we're in the menu, the highlight
1559 // will change color to indicate a successful long press.
1560 Redraw();
Elliott Hughes642aaa72015-04-10 12:47:46 -07001561}
Tao Baoefb49ad2017-01-31 23:03:10 -08001562
1563void ScreenRecoveryUI::SetLocale(const std::string& new_locale) {
1564 locale_ = new_locale;
1565 rtl_locale_ = false;
1566
1567 if (!new_locale.empty()) {
Tao Bao347a6592018-05-08 15:58:29 -07001568 size_t separator = new_locale.find('-');
1569 // lang has the language prefix prior to the separator, or full string if none exists.
1570 std::string lang = new_locale.substr(0, separator);
Tao Baoefb49ad2017-01-31 23:03:10 -08001571
1572 // A bit cheesy: keep an explicit list of supported RTL languages.
1573 if (lang == "ar" || // Arabic
1574 lang == "fa" || // Persian (Farsi)
1575 lang == "he" || // Hebrew (new language code)
1576 lang == "iw" || // Hebrew (old language code)
1577 lang == "ur") { // Urdu
1578 rtl_locale_ = true;
1579 }
1580 }
1581}