blob: 668d47a4eaa1d088d5ed408a9b37ca0fa5f712c2 [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
571 gr_color(0xf8, 0x90, 0xff, 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
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::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
587 gr_color(0x7c, 0x4d, 0xff, 255);
588 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 // TODO(xunchang) create a error code enum to refine the retry condition.
940 LOG(WARNING) << "Failed to load bitmap " << filename << " for locale " << locale_ << " (error "
941 << result << "). Falling back to use default locale.";
942
943 result = res_create_localized_alpha_surface(filename.c_str(), DEFAULT_LOCALE, &surface);
944 if (result == 0) {
945 return std::unique_ptr<GRSurface>(surface);
946 }
947
948 LOG(ERROR) << "Failed to load bitmap " << filename << " for locale " << DEFAULT_LOCALE
949 << " (error " << result << ")";
950 return nullptr;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700951}
952
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700953static char** Alloc2d(size_t rows, size_t cols) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700954 char** result = new char*[rows];
955 for (size_t i = 0; i < rows; ++i) {
956 result[i] = new char[cols];
957 memset(result[i], 0, cols);
958 }
959 return result;
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700960}
961
Tianjie Xu35926c42016-04-28 18:06:26 -0700962// Choose the right background string to display during update.
963void ScreenRecoveryUI::SetSystemUpdateText(bool security_update) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700964 if (security_update) {
Tao Baoda409fb2018-10-21 23:36:26 -0700965 installing_text_ = LoadLocalizedBitmap("installing_security_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700966 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700967 installing_text_ = LoadLocalizedBitmap("installing_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700968 }
969 Redraw();
Tianjie Xu35926c42016-04-28 18:06:26 -0700970}
971
Sen Jiangd5304492016-12-09 16:20:49 -0800972bool ScreenRecoveryUI::InitTextParams() {
Tao Baoba4edb32018-06-13 11:22:50 -0700973 // gr_init() would return successfully on font initialization failure.
974 if (gr_sys_font() == nullptr) {
Tao Bao171b4c42017-06-19 23:10:44 -0700975 return false;
976 }
Tao Bao171b4c42017-06-19 23:10:44 -0700977 gr_font_size(gr_sys_font(), &char_width_, &char_height_);
Alessandro Astonee8754752020-03-09 23:17:50 +0100978 gr_font_size(gr_menu_font(), &menu_char_width_, &menu_char_height_);
Tao Bao0bc88de2018-07-31 14:53:16 -0700979 text_rows_ = (ScreenHeight() - margin_height_ * 2) / char_height_;
980 text_cols_ = (ScreenWidth() - margin_width_ * 2) / char_width_;
Tao Bao171b4c42017-06-19 23:10:44 -0700981 return true;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -0700982}
983
Tianjie Xub99e6062018-10-16 15:13:09 -0700984bool ScreenRecoveryUI::LoadWipeDataMenuText() {
Tianjie Xu1a0a30a2018-10-25 15:22:07 -0700985 // Ignores the errors since the member variables will stay as nullptr.
986 cancel_wipe_data_text_ = LoadLocalizedBitmap("cancel_wipe_data_text");
987 factory_data_reset_text_ = LoadLocalizedBitmap("factory_data_reset_text");
988 try_again_text_ = LoadLocalizedBitmap("try_again_text");
989 wipe_data_confirmation_text_ = LoadLocalizedBitmap("wipe_data_confirmation_text");
990 wipe_data_menu_header_text_ = LoadLocalizedBitmap("wipe_data_menu_header_text");
Tianjie Xub99e6062018-10-16 15:13:09 -0700991 return true;
992}
993
Mark Salyzyn30017e72020-05-13 12:39:12 -0700994static bool InitGraphics() {
995 // Timeout is same as init wait for file default of 5 seconds and is arbitrary
996 const unsigned timeout = 500; // 10ms increments
997 for (auto retry = timeout; retry > 0; --retry) {
998 if (gr_init() == 0) {
999 if (retry < timeout) {
1000 // Log message like init wait for file completion log for consistency.
1001 LOG(WARNING) << "wait for 'graphics' took " << ((timeout - retry) * 10) << "ms";
1002 }
1003 return true;
1004 }
1005 std::this_thread::sleep_for(10ms);
1006 }
1007 // Log message like init wait for file timeout log for consistency.
1008 LOG(ERROR) << "timeout wait for 'graphics' took " << (timeout * 10) << "ms";
1009 return false;
1010}
1011
Tao Bao736d59c2017-01-03 10:15:33 -08001012bool ScreenRecoveryUI::Init(const std::string& locale) {
1013 RecoveryUI::Init(locale);
Tao Baoefb49ad2017-01-31 23:03:10 -08001014
Mark Salyzyn30017e72020-05-13 12:39:12 -07001015 if (!InitGraphics()) {
Tao Baoba4edb32018-06-13 11:22:50 -07001016 return false;
1017 }
1018
Tao Bao736d59c2017-01-03 10:15:33 -08001019 if (!InitTextParams()) {
1020 return false;
1021 }
Alessandro Astonee8754752020-03-09 23:17:50 +01001022 menu_draw_funcs_ = std::make_unique<MenuDrawFunctions>(*this);
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001023
Michael Bestas2e2d5d62019-03-23 17:28:22 +02001024 if (blank_unblank_on_init_) {
1025 gr_fb_blank(true);
1026 gr_fb_blank(false);
1027 }
1028
Tao Bao736d59c2017-01-03 10:15:33 -08001029 // Are we portrait or landscape?
1030 layout_ = (gr_fb_width() > gr_fb_height()) ? LANDSCAPE : PORTRAIT;
1031 // Are we the large variant of our base layout?
1032 if (gr_fb_height() > PixelsFromDp(800)) ++layout_;
Elliott Hughesfaf36e02016-04-20 17:22:16 -07001033
Tao Bao736d59c2017-01-03 10:15:33 -08001034 text_ = Alloc2d(text_rows_, text_cols_ + 1);
1035 file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
Doug Zongker55a36ac2013-03-04 15:49:02 -08001036
Tao Bao736d59c2017-01-03 10:15:33 -08001037 text_col_ = text_row_ = 0;
Doug Zongker211aebc2011-10-28 15:13:10 -07001038
Tao Baoefb49ad2017-01-31 23:03:10 -08001039 // Set up the locale info.
1040 SetLocale(locale);
1041
Tao Baoda409fb2018-10-21 23:36:26 -07001042 error_icon_ = LoadBitmap("icon_error");
Doug Zongker02ec6b82012-08-22 17:26:40 -07001043
Tao Baoda409fb2018-10-21 23:36:26 -07001044 progress_bar_empty_ = LoadBitmap("progress_empty");
1045 progress_bar_fill_ = LoadBitmap("progress_fill");
1046 stage_marker_empty_ = LoadBitmap("stage_empty");
1047 stage_marker_fill_ = LoadBitmap("stage_fill");
Elliott Hughes498cda62016-04-14 16:49:04 -07001048
Tao Baoda409fb2018-10-21 23:36:26 -07001049 erasing_text_ = LoadLocalizedBitmap("erasing_text");
1050 no_command_text_ = LoadLocalizedBitmap("no_command_text");
1051 error_text_ = LoadLocalizedBitmap("error_text");
Doug Zongker211aebc2011-10-28 15:13:10 -07001052
Alessandro Astonee8754752020-03-09 23:17:50 +01001053 back_icon_ = LoadBitmap("ic_back");
1054 back_icon_sel_ = LoadBitmap("ic_back_sel");
Alessandro Astoneb9f974d2020-02-26 17:25:54 +01001055 if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) ||
1056 android::base::GetBoolProperty("ro.fastbootd.available", false)) {
Nicholas Lim70dbdc22020-06-21 02:35:05 +08001057 lineage_logo_ = LoadBitmap("logo_image_switch");
David Anderson983e2d52019-01-02 11:35:38 -08001058 fastbootd_logo_ = LoadBitmap("fastbootd");
Nicholas Lim70dbdc22020-06-21 02:35:05 +08001059 } else {
1060 lineage_logo_ = LoadBitmap("logo_image");
David Anderson983e2d52019-01-02 11:35:38 -08001061 }
1062
Tao Baoda409fb2018-10-21 23:36:26 -07001063 // Background text for "installing_update" could be "installing update" or
1064 // "installing security update". It will be set after Init() according to the commands in BCB.
1065 installing_text_.reset();
Elliott Hughes498cda62016-04-14 16:49:04 -07001066
Tianjie Xub99e6062018-10-16 15:13:09 -07001067 LoadWipeDataMenuText();
1068
Tao Bao736d59c2017-01-03 10:15:33 -08001069 LoadAnimation();
Doug Zongker02ec6b82012-08-22 17:26:40 -07001070
Tao Bao26ea9592018-05-09 16:32:02 -07001071 // Keep the progress bar updated, even when the process is otherwise busy.
1072 progress_thread_ = std::thread(&ScreenRecoveryUI::ProgressThreadLoop, this);
Sen Jiangd5304492016-12-09 16:20:49 -08001073
Tao Bao736d59c2017-01-03 10:15:33 -08001074 return true;
Doug Zongker211aebc2011-10-28 15:13:10 -07001075}
1076
Tao Bao551d2c32018-05-09 20:53:13 -07001077std::string ScreenRecoveryUI::GetLocale() const {
Jerry Zhang2dea53e2018-05-02 17:15:03 -07001078 return locale_;
1079}
1080
Elliott Hughes498cda62016-04-14 16:49:04 -07001081void ScreenRecoveryUI::LoadAnimation() {
Tao Bao6cd81682018-05-03 21:53:11 -07001082 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(Paths::Get().resource_dir().c_str()),
1083 closedir);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001084 dirent* de;
1085 std::vector<std::string> intro_frame_names;
Joshua Lambert94a83be2019-12-09 20:24:18 +00001086 std::vector<std::string> loop_frame_names;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001087
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001088 while ((de = readdir(dir.get())) != nullptr) {
1089 int value, num_chars;
1090 if (sscanf(de->d_name, "intro%d%n.png", &value, &num_chars) == 1) {
1091 intro_frame_names.emplace_back(de->d_name, num_chars);
1092 } else if (sscanf(de->d_name, "loop%d%n.png", &value, &num_chars) == 1) {
Joshua Lambert94a83be2019-12-09 20:24:18 +00001093 loop_frame_names.emplace_back(de->d_name, num_chars);
Elliott Hughes498cda62016-04-14 16:49:04 -07001094 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001095 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001096
Tao Baoda409fb2018-10-21 23:36:26 -07001097 size_t intro_frames = intro_frame_names.size();
1098 size_t loop_frames = loop_frame_names.size();
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001099
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001100 // It's okay to not have an intro.
Tao Baoda409fb2018-10-21 23:36:26 -07001101 if (intro_frames == 0) intro_done_ = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001102 // But you must have an animation.
1103 if (loop_frames == 0) abort();
Elliott Hughes498cda62016-04-14 16:49:04 -07001104
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001105 std::sort(intro_frame_names.begin(), intro_frame_names.end());
1106 std::sort(loop_frame_names.begin(), loop_frame_names.end());
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001107
Tao Baoda409fb2018-10-21 23:36:26 -07001108 intro_frames_.clear();
1109 intro_frames_.reserve(intro_frames);
1110 for (const auto& frame_name : intro_frame_names) {
1111 intro_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001112 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001113
Tao Baoda409fb2018-10-21 23:36:26 -07001114 loop_frames_.clear();
1115 loop_frames_.reserve(loop_frames);
1116 for (const auto& frame_name : loop_frame_names) {
1117 loop_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001118 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001119}
1120
Elliott Hughes8de52072015-04-08 20:06:50 -07001121void ScreenRecoveryUI::SetBackground(Icon icon) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001122 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker02ec6b82012-08-22 17:26:40 -07001123
Tao Baoda409fb2018-10-21 23:36:26 -07001124 current_icon_ = icon;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001125 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001126}
1127
Elliott Hughes8de52072015-04-08 20:06:50 -07001128void ScreenRecoveryUI::SetProgressType(ProgressType type) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001129 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001130 if (progressBarType != type) {
1131 progressBarType = type;
1132 }
1133 progressScopeStart = 0;
1134 progressScopeSize = 0;
1135 progress = 0;
1136 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001137}
1138
Elliott Hughes8de52072015-04-08 20:06:50 -07001139void ScreenRecoveryUI::ShowProgress(float portion, float seconds) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001140 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001141 progressBarType = DETERMINATE;
1142 progressScopeStart += progressScopeSize;
1143 progressScopeSize = portion;
1144 progressScopeTime = now();
1145 progressScopeDuration = seconds;
1146 progress = 0;
1147 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001148}
1149
Elliott Hughes8de52072015-04-08 20:06:50 -07001150void ScreenRecoveryUI::SetProgress(float fraction) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001151 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001152 if (fraction < 0.0) fraction = 0.0;
1153 if (fraction > 1.0) fraction = 1.0;
1154 if (progressBarType == DETERMINATE && fraction > progress) {
1155 // Skip updates that aren't visibly different.
Tao Baoda409fb2018-10-21 23:36:26 -07001156 int width = gr_get_width(progress_bar_empty_.get());
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001157 float scale = width * progressScopeSize;
1158 if ((int)(progress * scale) != (int)(fraction * scale)) {
1159 progress = fraction;
1160 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001161 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001162 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001163}
1164
Doug Zongkerc87bab12013-11-25 13:53:25 -08001165void ScreenRecoveryUI::SetStage(int current, int max) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001166 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001167 stage = current;
1168 max_stage = max;
Doug Zongkerc87bab12013-11-25 13:53:25 -08001169}
1170
Tao Baob6918c72015-05-19 17:02:16 -07001171void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001172 std::string str;
1173 android::base::StringAppendV(&str, fmt, ap);
Doug Zongker211aebc2011-10-28 15:13:10 -07001174
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001175 if (copy_to_stdout) {
1176 fputs(str.c_str(), stdout);
1177 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001178
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001179 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001180 if (text_rows_ > 0 && text_cols_ > 0) {
1181 for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
1182 if (*ptr == '\n' || text_col_ >= text_cols_) {
Elliott Hughesc0491632015-05-06 12:40:05 -07001183 text_[text_row_][text_col_] = '\0';
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001184 text_col_ = 0;
1185 text_row_ = (text_row_ + 1) % text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001186 }
1187 if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
Doug Zongker211aebc2011-10-28 15:13:10 -07001188 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001189 text_[text_row_][text_col_] = '\0';
1190 update_screen_locked();
1191 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001192}
1193
Tao Baob6918c72015-05-19 17:02:16 -07001194void ScreenRecoveryUI::Print(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001195 va_list ap;
1196 va_start(ap, fmt);
1197 PrintV(fmt, true, ap);
1198 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001199}
1200
Tianjie Xu8f397302018-08-20 13:40:47 -07001201void ScreenRecoveryUI::PrintOnScreenOnly(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001202 va_list ap;
1203 va_start(ap, fmt);
1204 PrintV(fmt, false, ap);
1205 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001206}
1207
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001208void ScreenRecoveryUI::PutChar(char ch) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001209 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001210 if (ch != '\n') text_[text_row_][text_col_++] = ch;
1211 if (ch == '\n' || text_col_ >= text_cols_) {
1212 text_col_ = 0;
1213 ++text_row_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001214 }
Elliott Hughes8de52072015-04-08 20:06:50 -07001215}
1216
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001217void ScreenRecoveryUI::ClearText() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001218 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001219 text_col_ = 0;
1220 text_row_ = 0;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001221 for (size_t i = 0; i < text_rows_; ++i) {
1222 memset(text_[i], 0, text_cols_ + 1);
1223 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001224}
1225
1226void ScreenRecoveryUI::ShowFile(FILE* fp) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001227 std::vector<off_t> offsets;
1228 offsets.push_back(ftello(fp));
1229 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001230
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001231 struct stat sb;
1232 fstat(fileno(fp), &sb);
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001233
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001234 bool show_prompt = false;
1235 while (true) {
1236 if (show_prompt) {
1237 PrintOnScreenOnly("--(%d%% of %d bytes)--",
1238 static_cast<int>(100 * (double(ftello(fp)) / double(sb.st_size))),
1239 static_cast<int>(sb.st_size));
1240 Redraw();
1241 while (show_prompt) {
1242 show_prompt = false;
Tom Marshall53176c72020-03-10 20:17:49 +01001243 InputEvent evt = WaitInputEvent();
1244 if (evt.type() == EventType::EXTRA) {
1245 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) {
1246 return;
1247 }
1248 }
1249 if (evt.type() != EventType::KEY) {
1250 show_prompt = true;
1251 continue;
1252 }
1253 if (evt.key() == KEY_POWER || evt.key() == KEY_ENTER || evt.key() == KEY_BACKSPACE ||
1254 evt.key() == KEY_BACK || evt.key() == KEY_HOME || evt.key() == KEY_HOMEPAGE) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001255 return;
Tom Marshall53176c72020-03-10 20:17:49 +01001256 } else if (evt.key() == KEY_UP || evt.key() == KEY_VOLUMEUP || evt.key() == KEY_SCROLLUP) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001257 if (offsets.size() <= 1) {
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001258 show_prompt = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001259 } else {
1260 offsets.pop_back();
1261 fseek(fp, offsets.back(), SEEK_SET);
1262 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001263 } else {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001264 if (feof(fp)) {
1265 return;
1266 }
1267 offsets.push_back(ftello(fp));
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001268 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001269 }
1270 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001271 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001272
1273 int ch = getc(fp);
1274 if (ch == EOF) {
1275 while (text_row_ < text_rows_ - 1) PutChar('\n');
1276 show_prompt = true;
1277 } else {
1278 PutChar(ch);
1279 if (text_col_ == 0 && text_row_ >= text_rows_ - 1) {
1280 show_prompt = true;
1281 }
1282 }
1283 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001284}
1285
Tao Bao1d156b92018-05-02 12:43:18 -07001286void ScreenRecoveryUI::ShowFile(const std::string& filename) {
1287 std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(filename.c_str(), "re"), fclose);
1288 if (!fp) {
1289 Print(" Unable to open %s: %s\n", filename.c_str(), strerror(errno));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001290 return;
1291 }
Elliott Hughesc0491632015-05-06 12:40:05 -07001292
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001293 char** old_text = text_;
1294 size_t old_text_col = text_col_;
1295 size_t old_text_row = text_row_;
Elliott Hughesc0491632015-05-06 12:40:05 -07001296
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001297 // Swap in the alternate screen and clear it.
1298 text_ = file_viewer_text_;
1299 ClearText();
Elliott Hughesc0491632015-05-06 12:40:05 -07001300
Tao Bao1d156b92018-05-02 12:43:18 -07001301 ShowFile(fp.get());
Elliott Hughesc0491632015-05-06 12:40:05 -07001302
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001303 text_ = old_text;
1304 text_col_ = old_text_col;
1305 text_row_ = old_text_row;
Elliott Hughes8de52072015-04-08 20:06:50 -07001306}
1307
Tao Baoda409fb2018-10-21 23:36:26 -07001308std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(
1309 const GRSurface* graphic_header, const std::vector<const GRSurface*>& graphic_items,
1310 const std::vector<std::string>& text_headers, const std::vector<std::string>& text_items,
1311 size_t initial_selection) const {
Tianjie Xub99e6062018-10-16 15:13:09 -07001312 // horizontal unusable area: margin width + menu indent
1313 size_t max_width = ScreenWidth() - margin_width_ - kMenuIndent;
1314 // vertical unusable area: margin height + title lines + helper message + high light bar.
1315 // It is safe to reserve more space.
1316 size_t max_height = ScreenHeight() - margin_height_ - char_height_ * (title_lines_.size() + 3);
1317 if (GraphicMenu::Validate(max_width, max_height, graphic_header, graphic_items)) {
1318 return std::make_unique<GraphicMenu>(graphic_header, graphic_items, initial_selection, *this);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001319 }
Tianjie Xub99e6062018-10-16 15:13:09 -07001320
1321 fprintf(stderr, "Failed to initialize graphic menu, falling back to use the text menu.\n");
1322
1323 return CreateMenu(text_headers, text_items, initial_selection);
1324}
1325
1326std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(const std::vector<std::string>& text_headers,
1327 const std::vector<std::string>& text_items,
1328 size_t initial_selection) const {
Alessandro Astonee8754752020-03-09 23:17:50 +01001329 int menu_char_width = MenuCharWidth();
1330 int menu_char_height = MenuCharHeight();
Alessandro Astonee8754752020-03-09 23:17:50 +01001331 int menu_cols = (ScreenWidth() - margin_width_*2 - kMenuIndent) / menu_char_width;
1332 bool wrap_selection = !HasThreeButtons() && !HasTouchScreen();
Alessandro Astone3525c4d2020-06-25 19:54:18 +02001333 return std::make_unique<TextMenu>(wrap_selection, menu_cols, text_headers, text_items,
Alessandro Astonee8754752020-03-09 23:17:50 +01001334 initial_selection, menu_char_height, *menu_draw_funcs_);
Doug Zongker211aebc2011-10-28 15:13:10 -07001335}
1336
1337int ScreenRecoveryUI::SelectMenu(int sel) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001338 std::lock_guard<std::mutex> lg(updateMutex);
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001339 if (menu_) {
1340 int old_sel = menu_->selection();
1341 sel = menu_->Select(sel);
Elliott Hughesfc06f872015-03-23 13:45:31 -07001342
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001343 if (sel != old_sel) {
1344 update_screen_locked();
1345 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001346 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001347 return sel;
Doug Zongker211aebc2011-10-28 15:13:10 -07001348}
1349
Alessandro Astone4a00e642022-03-21 17:14:47 +01001350int ScreenRecoveryUI::SelectMenu(const Point& p) {
1351 // Correct position for overscan
1352 const Point point(p.x() - gr_overscan_offset_x(), p.y() - gr_overscan_offset_y());
1353
Tom Marshall53176c72020-03-10 20:17:49 +01001354 int new_sel = Device::kNoAction;
1355 std::lock_guard<std::mutex> lg(updateMutex);
1356 if (menu_) {
1357 if (!menu_->IsMain()) {
1358 // Back arrow hitbox
1359 const static int logo_width = gr_get_width(lineage_logo_.get());
1360 const static int logo_height = gr_get_height(lineage_logo_.get());
1361 const static int icon_w = gr_get_width(back_icon_.get());
1362 const static int icon_h = gr_get_height(back_icon_.get());
1363 const static int centered_x = ScreenWidth() / 2 - logo_width / 2;
1364 const static int icon_x = centered_x / 2 - icon_w / 2;
1365 const static int icon_y = margin_height_ + logo_height / 2 - icon_h / 2;
1366
1367 if (point.x() >= icon_x && point.x() <= icon_x + icon_w &&
1368 point.y() >= icon_y && point.y() <= icon_y + icon_h) {
1369 return Device::kGoBack;
1370 }
1371 }
1372
1373 if (point.y() >= menu_start_y_ &&
1374 point.y() < menu_start_y_ + menu_->ItemsCount() * MenuItemHeight()) {
1375 int old_sel = menu_->selection();
1376 int relative_sel = (point.y() - menu_start_y_) / MenuItemHeight();
1377 new_sel = menu_->SelectVisible(relative_sel);
1378 if (new_sel != -1 && new_sel != old_sel) {
1379 update_screen_locked();
1380 }
1381 }
1382 }
1383 return new_sel;
1384}
1385
1386int ScreenRecoveryUI::ScrollMenu(int updown) {
1387 std::lock_guard<std::mutex> lg(updateMutex);
1388 int sel = Device::kNoAction;
1389 if (menu_) {
1390 sel = menu_->Scroll(updown);
1391 update_screen_locked();
1392 }
1393 return sel;
1394}
1395
Tianjie Xub99e6062018-10-16 15:13:09 -07001396size_t ScreenRecoveryUI::ShowMenu(std::unique_ptr<Menu>&& menu, bool menu_only,
Tom Marshall55220ba2019-01-04 14:37:31 -08001397 const std::function<int(int, bool)>& key_handler,
1398 bool refreshable) {
Tao Bao3aec6962018-04-20 09:24:58 -07001399 // Throw away keys pressed previously, so user doesn't accidentally trigger menu items.
1400 FlushKeys();
1401
Jerry Zhangb76af932018-05-22 12:08:35 -07001402 // If there is a key interrupt in progress, return KeyError::INTERRUPTED without starting the
1403 // menu.
1404 if (IsKeyInterrupted()) return static_cast<size_t>(KeyError::INTERRUPTED);
1405
Tianjie Xub99e6062018-10-16 15:13:09 -07001406 CHECK(menu != nullptr);
Tao Bao3aec6962018-04-20 09:24:58 -07001407
Tianjie Xub99e6062018-10-16 15:13:09 -07001408 // Starts and displays the menu
1409 menu_ = std::move(menu);
1410 Redraw();
1411
1412 int selected = menu_->selection();
Tao Bao3aec6962018-04-20 09:24:58 -07001413 int chosen_item = -1;
1414 while (chosen_item < 0) {
Tom Marshall53176c72020-03-10 20:17:49 +01001415 InputEvent evt = WaitInputEvent();
1416 if (evt.type() == EventType::EXTRA) {
1417 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) {
1418 // WaitKey() was interrupted.
1419 return static_cast<size_t>(KeyError::INTERRUPTED);
1420 }
1421 if (evt.key() == static_cast<int>(KeyError::TIMED_OUT)) { // WaitKey() timed out.
1422 if (WasTextEverVisible()) {
1423 continue;
1424 } else {
1425 LOG(INFO) << "Timed out waiting for key input; rebooting.";
1426 menu_.reset();
1427 Redraw();
1428 return static_cast<size_t>(KeyError::TIMED_OUT);
1429 }
Tao Bao3aec6962018-04-20 09:24:58 -07001430 }
1431 }
1432
Tom Marshall53176c72020-03-10 20:17:49 +01001433 int action = Device::kNoAction;
1434 if (evt.type() == EventType::TOUCH) {
1435 int touch_sel = SelectMenu(evt.pos());
1436 if (touch_sel < 0) {
1437 action = touch_sel;
1438 } else {
1439 action = Device::kInvokeItem;
1440 selected = touch_sel;
1441 }
1442 } else {
1443 bool visible = IsTextVisible();
1444 action = key_handler(evt.key(), visible);
1445 }
Tom Marshall55220ba2019-01-04 14:37:31 -08001446
Tao Bao3aec6962018-04-20 09:24:58 -07001447 if (action < 0) {
1448 switch (action) {
1449 case Device::kHighlightUp:
1450 selected = SelectMenu(--selected);
1451 break;
1452 case Device::kHighlightDown:
1453 selected = SelectMenu(++selected);
1454 break;
Tom Marshall53176c72020-03-10 20:17:49 +01001455 case Device::kScrollUp:
1456 selected = ScrollMenu(-1);
1457 break;
1458 case Device::kScrollDown:
1459 selected = ScrollMenu(1);
1460 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001461 case Device::kInvokeItem:
Alessandro Astonee8754752020-03-09 23:17:50 +01001462 if (selected < 0) {
1463 chosen_item = Device::kGoBack;
1464 } else {
1465 chosen_item = selected;
1466 }
Tao Bao3aec6962018-04-20 09:24:58 -07001467 break;
1468 case Device::kNoAction:
1469 break;
Tom Marshalld13a8c52017-08-24 13:50:01 +00001470 case Device::kGoBack:
1471 chosen_item = Device::kGoBack;
1472 break;
1473 case Device::kGoHome:
1474 chosen_item = Device::kGoHome;
1475 break;
Tom Marshall90e09f82018-12-17 15:57:44 -08001476 case Device::kDoSideload:
1477 chosen_item = Device::kDoSideload;
1478 break;
Tom Marshall55220ba2019-01-04 14:37:31 -08001479 case Device::kRefresh:
1480 if (refreshable) {
1481 chosen_item = Device::kRefresh;
1482 }
1483 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001484 }
1485 } else if (!menu_only) {
1486 chosen_item = action;
1487 }
Tom Marshall90e09f82018-12-17 15:57:44 -08001488
1489 if (chosen_item == Device::kGoBack || chosen_item == Device::kGoHome ||
Tom Marshall55220ba2019-01-04 14:37:31 -08001490 chosen_item == Device::kDoSideload || chosen_item == Device::kRefresh) {
Tom Marshalld13a8c52017-08-24 13:50:01 +00001491 break;
1492 }
Tao Bao3aec6962018-04-20 09:24:58 -07001493 }
1494
Tianjie Xub99e6062018-10-16 15:13:09 -07001495 menu_.reset();
Tianjie Xub99e6062018-10-16 15:13:09 -07001496
Tao Bao3aec6962018-04-20 09:24:58 -07001497 return chosen_item;
1498}
1499
Tianjie Xub99e6062018-10-16 15:13:09 -07001500size_t ScreenRecoveryUI::ShowMenu(const std::vector<std::string>& headers,
1501 const std::vector<std::string>& items, size_t initial_selection,
1502 bool menu_only,
Tom Marshall55220ba2019-01-04 14:37:31 -08001503 const std::function<int(int, bool)>& key_handler,
1504 bool refreshable) {
Tianjie Xub99e6062018-10-16 15:13:09 -07001505 auto menu = CreateMenu(headers, items, initial_selection);
1506 if (menu == nullptr) {
1507 return initial_selection;
1508 }
1509
Tom Marshall55220ba2019-01-04 14:37:31 -08001510 return ShowMenu(std::move(menu), menu_only, key_handler, refreshable);
Tianjie Xub99e6062018-10-16 15:13:09 -07001511}
1512
1513size_t ScreenRecoveryUI::ShowPromptWipeDataMenu(const std::vector<std::string>& backup_headers,
1514 const std::vector<std::string>& backup_items,
1515 const std::function<int(int, bool)>& key_handler) {
Alessandro Astone5ed22c82020-10-05 15:11:36 +02001516 auto wipe_data_menu = CreateMenu(backup_headers, backup_items, 0);
Tianjie Xub99e6062018-10-16 15:13:09 -07001517 if (wipe_data_menu == nullptr) {
1518 return 0;
1519 }
1520
1521 return ShowMenu(std::move(wipe_data_menu), true, key_handler);
1522}
1523
Tianjie Xu1a0a30a2018-10-25 15:22:07 -07001524size_t ScreenRecoveryUI::ShowPromptWipeDataConfirmationMenu(
1525 const std::vector<std::string>& backup_headers, const std::vector<std::string>& backup_items,
1526 const std::function<int(int, bool)>& key_handler) {
1527 auto confirmation_menu =
1528 CreateMenu(wipe_data_confirmation_text_.get(),
1529 { cancel_wipe_data_text_.get(), factory_data_reset_text_.get() }, backup_headers,
1530 backup_items, 0);
1531 if (confirmation_menu == nullptr) {
1532 return 0;
1533 }
1534
1535 return ShowMenu(std::move(confirmation_menu), true, key_handler);
1536}
1537
Elliott Hughes8de52072015-04-08 20:06:50 -07001538bool ScreenRecoveryUI::IsTextVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001539 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001540 int visible = show_text;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001541 return visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001542}
1543
Elliott Hughes8de52072015-04-08 20:06:50 -07001544bool ScreenRecoveryUI::WasTextEverVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001545 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001546 int ever_visible = show_text_ever;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001547 return ever_visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001548}
1549
Elliott Hughes8de52072015-04-08 20:06:50 -07001550void ScreenRecoveryUI::ShowText(bool visible) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001551 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001552 show_text = visible;
1553 if (show_text) show_text_ever = true;
1554 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001555}
Doug Zongkerc0441d12013-07-31 11:28:24 -07001556
Elliott Hughes8de52072015-04-08 20:06:50 -07001557void ScreenRecoveryUI::Redraw() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001558 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001559 update_screen_locked();
Doug Zongkerc0441d12013-07-31 11:28:24 -07001560}
Elliott Hughes642aaa72015-04-10 12:47:46 -07001561
1562void ScreenRecoveryUI::KeyLongPress(int) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001563 // Redraw so that if we're in the menu, the highlight
1564 // will change color to indicate a successful long press.
1565 Redraw();
Elliott Hughes642aaa72015-04-10 12:47:46 -07001566}
Tao Baoefb49ad2017-01-31 23:03:10 -08001567
1568void ScreenRecoveryUI::SetLocale(const std::string& new_locale) {
1569 locale_ = new_locale;
1570 rtl_locale_ = false;
1571
1572 if (!new_locale.empty()) {
Tao Bao347a6592018-05-08 15:58:29 -07001573 size_t separator = new_locale.find('-');
1574 // lang has the language prefix prior to the separator, or full string if none exists.
1575 std::string lang = new_locale.substr(0, separator);
Tao Baoefb49ad2017-01-31 23:03:10 -08001576
1577 // A bit cheesy: keep an explicit list of supported RTL languages.
1578 if (lang == "ar" || // Arabic
1579 lang == "fa" || // Persian (Farsi)
1580 lang == "he" || // Hebrew (new language code)
1581 lang == "iw" || // Hebrew (old language code)
1582 lang == "ur") { // Urdu
1583 rtl_locale_ = true;
1584 }
1585 }
1586}