blob: e9a1fe03505e5248eb7588dcc750fbe41255898c [file] [log] [blame]
Doug Zongker211aebc2011-10-28 15:13:10 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Tianjie Xu8f397302018-08-20 13:40:47 -070017#include "recovery_ui/screen_ui.h"
Tao Baoefb49ad2017-01-31 23:03:10 -080018
Elliott Hughes498cda62016-04-14 16:49:04 -070019#include <dirent.h>
Doug Zongker211aebc2011-10-28 15:13:10 -070020#include <errno.h>
21#include <fcntl.h>
Doug Zongker211aebc2011-10-28 15:13:10 -070022#include <stdarg.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/stat.h>
27#include <sys/time.h>
28#include <sys/types.h>
29#include <time.h>
30#include <unistd.h>
31
Tao Bao1fe1afe2018-05-01 15:56:05 -070032#include <algorithm>
Tao Bao26ea9592018-05-09 16:32:02 -070033#include <chrono>
Tianjie Xu29d55752017-09-20 17:53:46 -070034#include <memory>
Tao Bao736d59c2017-01-03 10:15:33 -080035#include <string>
Tao Bao26ea9592018-05-09 16:32:02 -070036#include <thread>
Tianjie Xu29d55752017-09-20 17:53:46 -070037#include <unordered_map>
Elliott Hughes95fc63e2015-04-10 19:12:01 -070038#include <vector>
39
Mark Salyzyn30017e72020-05-13 12:39:12 -070040#include <android-base/chrono_utils.h>
Tianjie Xuc21edd42016-08-05 18:00:04 -070041#include <android-base/logging.h>
Elliott Hughescb220402016-09-23 15:30:55 -070042#include <android-base/properties.h>
Elliott Hughes4b166f02015-12-04 15:30:20 -080043#include <android-base/stringprintf.h>
Tao Baocb5524c2017-09-08 21:25:32 -070044#include <android-base/strings.h>
Tao Baob6918c72015-05-19 17:02:16 -070045
Tao Bao6cd81682018-05-03 21:53:11 -070046#include "minui/minui.h"
47#include "otautil/paths.h"
Tianjie Xu8f397302018-08-20 13:40:47 -070048#include "recovery_ui/device.h"
49#include "recovery_ui/ui.h"
Doug Zongker211aebc2011-10-28 15:13:10 -070050
fredchiouf2f5aa22022-02-11 16:39:53 +080051enum DirectRenderManager {
52 DRM_INNER,
53 DRM_OUTER,
54};
55
Doug Zongker211aebc2011-10-28 15:13:10 -070056// Return the current time as a double (including fractions of a second).
57static double now() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -070058 struct timeval tv;
59 gettimeofday(&tv, nullptr);
60 return tv.tv_sec + tv.tv_usec / 1000000.0;
Doug Zongker211aebc2011-10-28 15:13:10 -070061}
62
Tianjie Xu66dbf632018-10-11 16:54:50 -070063Menu::Menu(size_t initial_selection, const DrawInterface& draw_func)
64 : selection_(initial_selection), draw_funcs_(draw_func) {}
65
Alessandro Astone4a0de5a2020-03-09 23:17:50 +010066int Menu::selection() const {
Tianjie Xu66dbf632018-10-11 16:54:50 -070067 return selection_;
68}
69
Alessandro Astoneefc7a8e2020-06-25 19:54:18 +020070TextMenu::TextMenu(bool wrappable, size_t max_length,
Tianjie Xu66dbf632018-10-11 16:54:50 -070071 const std::vector<std::string>& headers, const std::vector<std::string>& items,
72 size_t initial_selection, int char_height, const DrawInterface& draw_funcs)
73 : Menu(initial_selection, draw_funcs),
Alessandro Astone4a0de5a2020-03-09 23:17:50 +010074 wrappable_(wrappable),
Alessandro Astoneefc7a8e2020-06-25 19:54:18 +020075 calibrated_height_(false),
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070076 max_item_length_(max_length),
Tao Baoe02a5b22018-05-02 15:46:11 -070077 text_headers_(headers),
Tianjie Xu66dbf632018-10-11 16:54:50 -070078 char_height_(char_height) {
Tao Baoe02a5b22018-05-02 15:46:11 -070079
Alessandro Astone4a0de5a2020-03-09 23:17:50 +010080 size_t items_count = items.size();
Tao Bao1fe1afe2018-05-01 15:56:05 -070081 for (size_t i = 0; i < items_count; ++i) {
82 text_items_.emplace_back(items[i].substr(0, max_item_length_));
Tao Baoe02a5b22018-05-02 15:46:11 -070083 }
84
85 CHECK(!text_items_.empty());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070086}
87
Tianjie Xu66dbf632018-10-11 16:54:50 -070088const std::vector<std::string>& TextMenu::text_headers() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070089 return text_headers_;
90}
91
Tianjie Xu66dbf632018-10-11 16:54:50 -070092std::string TextMenu::TextItem(size_t index) const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070093 CHECK_LT(index, text_items_.size());
94
95 return text_items_[index];
96}
97
Tianjie Xu66dbf632018-10-11 16:54:50 -070098size_t TextMenu::MenuStart() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070099 return menu_start_;
100}
101
Tianjie Xu66dbf632018-10-11 16:54:50 -0700102size_t TextMenu::MenuEnd() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700103 return std::min(ItemsCount(), menu_start_ + max_display_items_);
104}
105
Tianjie Xu66dbf632018-10-11 16:54:50 -0700106size_t TextMenu::ItemsCount() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700107 return text_items_.size();
108}
109
Tianjie Xu66dbf632018-10-11 16:54:50 -0700110bool TextMenu::ItemsOverflow(std::string* cur_selection_str) const {
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100111 if (ItemsCount() <= max_display_items_) {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700112 return false;
113 }
114
115 *cur_selection_str =
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100116 android::base::StringPrintf("Current item: %d/%zu", selection_ + 1, ItemsCount());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700117 return true;
118}
119
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700120// TODO(xunchang) modify the function parameters to button up & down.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700121int TextMenu::Select(int sel) {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700122 CHECK_LE(ItemsCount(), static_cast<size_t>(std::numeric_limits<int>::max()));
123 int count = ItemsCount();
124
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100125 int min = IsMain() ? 0 : -1; // -1 is back arrow
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700126
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100127 if (sel < min) {
128 selection_ = wrappable() ? count - 1 : min;
129 } else if (sel >= count) {
130 selection_ = wrappable() ? min : count - 1;
131 } else {
132 selection_ = sel;
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700133 }
134
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100135 if (selection_ >= 0) {
136 if (selection_ < menu_start_) {
137 menu_start_ = selection_;
138 } else if (static_cast<size_t>(selection_) >= MenuEnd()) {
139 menu_start_ = selection_ - max_display_items_ + 1;
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700140 }
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700141 }
142
143 return selection_;
144}
145
Alessandro Astone73961412020-10-04 18:11:40 +0200146int TextMenu::SelectVisible(int relative_sel) {
147 int sel = relative_sel;
148 if (menu_start_ > 0) {
149 sel += menu_start_;
150 }
151
152 return Select(sel);
153}
154
155int TextMenu::Scroll(int updown) {
156 if ((updown > 0 && menu_start_ + max_display_items_ < ItemsCount()) ||
157 (updown < 0 && menu_start_ > 0)) {
158 menu_start_ += updown;
159
160 /* We can receive a kInvokeItem event from a different source than touch,
161 like from Power button. For this reason, selection should not get out of
162 the screen. Constrain it to the first or last visible item of the list */
163 if (selection_ < menu_start_) {
164 selection_ = menu_start_;
165 } else if (selection_ >= menu_start_ + max_display_items_) {
166 selection_ = menu_start_ + max_display_items_ - 1;
167 }
168 }
169 return selection_;
170}
171
Tianjie Xu66dbf632018-10-11 16:54:50 -0700172int TextMenu::DrawHeader(int x, int y) const {
173 int offset = 0;
174
175 draw_funcs_.SetColor(UIElement::HEADER);
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100176 offset += draw_funcs_.DrawWrappedTextLines(x, y + offset, text_headers());
Tianjie Xu66dbf632018-10-11 16:54:50 -0700177
178 return offset;
179}
180
181int TextMenu::DrawItems(int x, int y, int screen_width, bool long_press) const {
182 int offset = 0;
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100183 int padding = draw_funcs_.MenuItemPadding();
Tianjie Xu66dbf632018-10-11 16:54:50 -0700184
185 draw_funcs_.SetColor(UIElement::MENU);
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100186 offset += draw_funcs_.DrawHorizontalRule(y + offset) + 4;
187
188 int item_container_offset = offset; // store it for drawing scrollbar on most top
189
Tianjie Xu66dbf632018-10-11 16:54:50 -0700190 for (size_t i = MenuStart(); i < MenuEnd(); ++i) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700191 if (i == selection()) {
192 // Draw the highlight bar.
193 draw_funcs_.SetColor(long_press ? UIElement::MENU_SEL_BG_ACTIVE : UIElement::MENU_SEL_BG);
194
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100195 int bar_height = padding + char_height_ + padding;
196 draw_funcs_.DrawHighlightBar(0, y + offset, screen_width, bar_height);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700197
Jesse Chan2f376422020-04-28 14:49:13 +0000198 // Colored text for the selected item.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700199 draw_funcs_.SetColor(UIElement::MENU_SEL_FG);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700200 }
Jesse Chan2f376422020-04-28 14:49:13 +0000201 offset += draw_funcs_.DrawTextLine(x, y + offset, TextItem(i), false /* bold */);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700202
203 draw_funcs_.SetColor(UIElement::MENU);
204 }
205 offset += draw_funcs_.DrawHorizontalRule(y + offset);
206
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100207 std::string unused;
208 if (ItemsOverflow(&unused)) {
209 int container_height = max_display_items_ * (2 * padding + char_height_);
210 int bar_height = container_height / (text_items_.size() - max_display_items_ + 1);
211 int start_y = y + item_container_offset + bar_height * menu_start_;
212 draw_funcs_.SetColor(UIElement::SCROLLBAR);
213 draw_funcs_.DrawScrollBar(start_y, bar_height);
214 }
215
Tianjie Xu66dbf632018-10-11 16:54:50 -0700216 return offset;
217}
218
Tao Baoda409fb2018-10-21 23:36:26 -0700219GraphicMenu::GraphicMenu(const GRSurface* graphic_headers,
220 const std::vector<const GRSurface*>& graphic_items,
Tianjie Xub99e6062018-10-16 15:13:09 -0700221 size_t initial_selection, const DrawInterface& draw_funcs)
Tao Baoda409fb2018-10-21 23:36:26 -0700222 : Menu(initial_selection, draw_funcs) {
223 graphic_headers_ = graphic_headers->Clone();
224 graphic_items_.reserve(graphic_items.size());
225 for (const auto& item : graphic_items) {
226 graphic_items_.emplace_back(item->Clone());
227 }
228}
Tianjie Xu66dbf632018-10-11 16:54:50 -0700229
230int GraphicMenu::Select(int sel) {
231 CHECK_LE(graphic_items_.size(), static_cast<size_t>(std::numeric_limits<int>::max()));
232 int count = graphic_items_.size();
233
234 // Wraps the selection at boundary if the menu is not scrollable.
235 if (sel < 0) {
236 selection_ = count - 1;
237 } else if (sel >= count) {
238 selection_ = 0;
239 } else {
240 selection_ = sel;
241 }
242
243 return selection_;
244}
245
246int GraphicMenu::DrawHeader(int x, int y) const {
Tianjie Xub99e6062018-10-16 15:13:09 -0700247 draw_funcs_.SetColor(UIElement::HEADER);
Tao Baoda409fb2018-10-21 23:36:26 -0700248 draw_funcs_.DrawTextIcon(x, y, graphic_headers_.get());
Tianjie Xu66dbf632018-10-11 16:54:50 -0700249 return graphic_headers_->height;
250}
251
252int GraphicMenu::DrawItems(int x, int y, int screen_width, bool long_press) const {
253 int offset = 0;
254
255 draw_funcs_.SetColor(UIElement::MENU);
256 offset += draw_funcs_.DrawHorizontalRule(y + offset) + 4;
257
258 for (size_t i = 0; i < graphic_items_.size(); i++) {
259 auto& item = graphic_items_[i];
260 if (i == selection_) {
261 draw_funcs_.SetColor(long_press ? UIElement::MENU_SEL_BG_ACTIVE : UIElement::MENU_SEL_BG);
262
263 int bar_height = item->height + 4;
264 draw_funcs_.DrawHighlightBar(0, y + offset - 2, screen_width, bar_height);
265
266 // Bold white text for the selected item.
267 draw_funcs_.SetColor(UIElement::MENU_SEL_FG);
268 }
Tao Baoda409fb2018-10-21 23:36:26 -0700269 draw_funcs_.DrawTextIcon(x, y + offset, item.get());
Tianjie Xu66dbf632018-10-11 16:54:50 -0700270 offset += item->height;
271
272 draw_funcs_.SetColor(UIElement::MENU);
273 }
xunchangc7dbc732018-12-20 11:31:18 -0800274 offset += draw_funcs_.DrawHorizontalRule(y + offset);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700275
276 return offset;
277}
278
Alessandro Astone73961412020-10-04 18:11:40 +0200279size_t GraphicMenu::ItemsCount() const {
280 return graphic_items_.size();
281}
282
Tao Baoda409fb2018-10-21 23:36:26 -0700283bool GraphicMenu::Validate(size_t max_width, size_t max_height, const GRSurface* graphic_headers,
284 const std::vector<const GRSurface*>& graphic_items) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700285 int offset = 0;
Tianjie Xub99e6062018-10-16 15:13:09 -0700286 if (!ValidateGraphicSurface(max_width, max_height, offset, graphic_headers)) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700287 return false;
288 }
Tianjie Xub99e6062018-10-16 15:13:09 -0700289 offset += graphic_headers->height;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700290
Tianjie Xub99e6062018-10-16 15:13:09 -0700291 for (const auto& item : graphic_items) {
292 if (!ValidateGraphicSurface(max_width, max_height, offset, item)) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700293 return false;
294 }
295 offset += item->height;
296 }
297
298 return true;
299}
300
Tianjie Xub99e6062018-10-16 15:13:09 -0700301bool GraphicMenu::ValidateGraphicSurface(size_t max_width, size_t max_height, int y,
302 const GRSurface* surface) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700303 if (!surface) {
Tao Baoa00b4492019-01-16 09:29:47 -0800304 fprintf(stderr, "Graphic surface can not be null\n");
Tianjie Xu66dbf632018-10-11 16:54:50 -0700305 return false;
306 }
307
308 if (surface->pixel_bytes != 1 || surface->width != surface->row_bytes) {
Tao Baoa00b4492019-01-16 09:29:47 -0800309 fprintf(stderr, "Invalid graphic surface, pixel bytes: %zu, width: %zu row_bytes: %zu\n",
Tianjie Xu66dbf632018-10-11 16:54:50 -0700310 surface->pixel_bytes, surface->width, surface->row_bytes);
311 return false;
312 }
313
Tianjie Xub99e6062018-10-16 15:13:09 -0700314 if (surface->width > max_width || surface->height > max_height - y) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700315 fprintf(stderr,
Tao Baodd789822018-11-26 16:28:07 -0800316 "Graphic surface doesn't fit into the screen. width: %zu, height: %zu, max_width: %zu,"
Tianjie Xu66dbf632018-10-11 16:54:50 -0700317 " max_height: %zu, vertical offset: %d\n",
Tianjie Xub99e6062018-10-16 15:13:09 -0700318 surface->width, surface->height, max_width, max_height, y);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700319 return false;
320 }
321
322 return true;
323}
324
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100325MenuDrawFunctions::MenuDrawFunctions(const DrawInterface& wrappee)
326 : wrappee_(wrappee) {
327}
328
329int MenuDrawFunctions::DrawTextLine(int x, int y, const std::string& line, bool bold) const {
330 gr_text(gr_menu_font(), x, y + MenuItemPadding(), line.c_str(), bold);
331 return 2 * MenuItemPadding() + MenuCharHeight();
332}
333
334int MenuDrawFunctions::DrawTextLines(int x, int y, const std::vector<std::string>& lines) const {
335 int offset = 0;
336 for (const auto& line : lines) {
337 offset += DrawTextLine(x, y + offset, line, false);
338 }
339 return offset;
340}
341
342int MenuDrawFunctions::DrawWrappedTextLines(int x, int y, const std::vector<std::string>& lines) const {
343 // Keep symmetrical margins based on the given offset (i.e. x).
344 size_t text_cols = (gr_fb_width() - x * 2) / MenuCharWidth();
345 int offset = 0;
346 for (const auto& line : lines) {
347 size_t next_start = 0;
348 while (next_start < line.size()) {
349 std::string sub = line.substr(next_start, text_cols + 1);
350 if (sub.size() <= text_cols) {
351 next_start += sub.size();
352 } else {
353 // Line too long and must be wrapped to text_cols columns.
354 size_t last_space = sub.find_last_of(" \t\n");
355 if (last_space == std::string::npos) {
356 // No space found, just draw as much as we can.
357 sub.resize(text_cols);
358 next_start += text_cols;
359 } else {
360 sub.resize(last_space);
361 next_start += last_space + 1;
362 }
363 }
364 offset += DrawTextLine(x, y + offset, sub, false);
365 }
366 }
367 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 Astone4a0de5a2020-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 Bestas43ff6792019-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_(""),
fredchiouf2f5aa22022-02-11 16:39:53 +0800403 rtl_locale_(false),
404 is_graphics_available(false) {}
Doug Zongker211aebc2011-10-28 15:13:10 -0700405
Tao Bao26ea9592018-05-09 16:32:02 -0700406ScreenRecoveryUI::~ScreenRecoveryUI() {
407 progress_thread_stopped_ = true;
Tao Bao94371fd2018-06-06 07:38:54 -0700408 if (progress_thread_.joinable()) {
409 progress_thread_.join();
410 }
Tao Bao60ac6222018-06-13 14:33:51 -0700411 // No-op if gr_init() (via Init()) was not called or had failed.
412 gr_exit();
Tao Bao26ea9592018-05-09 16:32:02 -0700413}
414
Tao Baoda409fb2018-10-21 23:36:26 -0700415const GRSurface* ScreenRecoveryUI::GetCurrentFrame() const {
416 if (current_icon_ == INSTALLING_UPDATE || current_icon_ == ERASING) {
417 return intro_done_ ? loop_frames_[current_frame_].get() : intro_frames_[current_frame_].get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700418 }
Tao Baoda409fb2018-10-21 23:36:26 -0700419 return error_icon_.get();
Elliott Hughes498cda62016-04-14 16:49:04 -0700420}
421
Tao Baoda409fb2018-10-21 23:36:26 -0700422const GRSurface* ScreenRecoveryUI::GetCurrentText() const {
423 switch (current_icon_) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700424 case ERASING:
Tao Baoda409fb2018-10-21 23:36:26 -0700425 return erasing_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700426 case ERROR:
Tao Baoda409fb2018-10-21 23:36:26 -0700427 return error_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700428 case INSTALLING_UPDATE:
Tao Baoda409fb2018-10-21 23:36:26 -0700429 return installing_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700430 case NO_COMMAND:
Tao Baoda409fb2018-10-21 23:36:26 -0700431 return no_command_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700432 case NONE:
433 abort();
434 }
Elliott Hughes498cda62016-04-14 16:49:04 -0700435}
436
Mikhail Lappob49767c2017-03-23 21:44:26 +0100437int ScreenRecoveryUI::PixelsFromDp(int dp) const {
Tao Bao0bc88de2018-07-31 14:53:16 -0700438 return dp * density_;
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700439}
440
441// Here's the intended layout:
442
Elliott Hughes6d089a92016-07-08 17:23:41 -0700443// | portrait large landscape large
444// ---------+-------------------------------------------------
Tao Bao3250f722017-06-29 14:32:05 -0700445// gap |
Elliott Hughes6d089a92016-07-08 17:23:41 -0700446// icon | (200dp)
447// gap | 68dp 68dp 56dp 112dp
448// text | (14sp)
449// gap | 32dp 32dp 26dp 52dp
450// progress | (2dp)
Tao Bao3250f722017-06-29 14:32:05 -0700451// gap |
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700452
Tao Bao3250f722017-06-29 14:32:05 -0700453// Note that "baseline" is actually the *top* of each icon (because that's how our drawing routines
454// 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 -0700455
Elliott Hughes6d089a92016-07-08 17:23:41 -0700456enum Layout { PORTRAIT = 0, PORTRAIT_LARGE = 1, LANDSCAPE = 2, LANDSCAPE_LARGE = 3, LAYOUT_MAX };
Tao Bao3250f722017-06-29 14:32:05 -0700457enum Dimension { TEXT = 0, ICON = 1, DIMENSION_MAX };
Elliott Hughes6d089a92016-07-08 17:23:41 -0700458static constexpr int kLayouts[LAYOUT_MAX][DIMENSION_MAX] = {
Tianjie Xu8f397302018-08-20 13:40:47 -0700459 { 32, 68 }, // PORTRAIT
460 { 32, 68 }, // PORTRAIT_LARGE
461 { 26, 56 }, // LANDSCAPE
462 { 52, 112 }, // LANDSCAPE_LARGE
Elliott Hughes6d089a92016-07-08 17:23:41 -0700463};
464
Tao Bao99b2d772017-06-23 22:47:03 -0700465int ScreenRecoveryUI::GetAnimationBaseline() const {
Tao Baoda409fb2018-10-21 23:36:26 -0700466 return GetTextBaseline() - PixelsFromDp(kLayouts[layout_][ICON]) -
467 gr_get_height(loop_frames_[0].get());
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700468}
469
Tao Bao99b2d772017-06-23 22:47:03 -0700470int ScreenRecoveryUI::GetTextBaseline() const {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700471 return GetProgressBaseline() - PixelsFromDp(kLayouts[layout_][TEXT]) -
Tao Baoda409fb2018-10-21 23:36:26 -0700472 gr_get_height(installing_text_.get());
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700473}
474
Tao Bao99b2d772017-06-23 22:47:03 -0700475int ScreenRecoveryUI::GetProgressBaseline() const {
Tao Baoda409fb2018-10-21 23:36:26 -0700476 int elements_sum = gr_get_height(loop_frames_[0].get()) + PixelsFromDp(kLayouts[layout_][ICON]) +
477 gr_get_height(installing_text_.get()) + PixelsFromDp(kLayouts[layout_][TEXT]) +
478 gr_get_height(progress_bar_fill_.get());
Luke Song92eda4d2017-09-19 10:51:35 -0700479 int bottom_gap = (ScreenHeight() - elements_sum) / 2;
Tao Baoda409fb2018-10-21 23:36:26 -0700480 return ScreenHeight() - bottom_gap - gr_get_height(progress_bar_fill_.get());
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700481}
482
Doug Zongker211aebc2011-10-28 15:13:10 -0700483// Clear the screen and draw the currently selected background icon (if any).
484// Should only be called with updateMutex locked.
Elliott Hughes498cda62016-04-14 16:49:04 -0700485void ScreenRecoveryUI::draw_background_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700486 pagesIdentical = false;
487 gr_color(0, 0, 0, 255);
488 gr_clear();
Tao Baoda409fb2018-10-21 23:36:26 -0700489 if (current_icon_ != NONE) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700490 if (max_stage != -1) {
Tao Baoda409fb2018-10-21 23:36:26 -0700491 int stage_height = gr_get_height(stage_marker_empty_.get());
492 int stage_width = gr_get_width(stage_marker_empty_.get());
493 int x = (ScreenWidth() - max_stage * gr_get_width(stage_marker_empty_.get())) / 2;
Tao Bao0bc88de2018-07-31 14:53:16 -0700494 int y = ScreenHeight() - stage_height - margin_height_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700495 for (int i = 0; i < max_stage; ++i) {
Tao Baoda409fb2018-10-21 23:36:26 -0700496 const auto& stage_surface = (i < stage) ? stage_marker_fill_ : stage_marker_empty_;
497 DrawSurface(stage_surface.get(), 0, 0, stage_width, stage_height, x, y);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700498 x += stage_width;
499 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700500 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700501
Tao Baoda409fb2018-10-21 23:36:26 -0700502 const auto& text_surface = GetCurrentText();
Luke Song92eda4d2017-09-19 10:51:35 -0700503 int text_x = (ScreenWidth() - gr_get_width(text_surface)) / 2;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700504 int text_y = GetTextBaseline();
505 gr_color(255, 255, 255, 255);
Luke Song92eda4d2017-09-19 10:51:35 -0700506 DrawTextIcon(text_x, text_y, text_surface);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700507 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700508}
509
Tao Baoea78d862017-06-28 14:52:17 -0700510// Draws the animation and progress bar (if any) on the screen. Does not flip pages. Should only be
511// called with updateMutex locked.
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700512void ScreenRecoveryUI::draw_foreground_locked() {
Tao Baoda409fb2018-10-21 23:36:26 -0700513 if (current_icon_ != NONE) {
514 const auto& frame = GetCurrentFrame();
Tao Bao736d59c2017-01-03 10:15:33 -0800515 int frame_width = gr_get_width(frame);
516 int frame_height = gr_get_height(frame);
Luke Song92eda4d2017-09-19 10:51:35 -0700517 int frame_x = (ScreenWidth() - frame_width) / 2;
Tao Bao736d59c2017-01-03 10:15:33 -0800518 int frame_y = GetAnimationBaseline();
zhang sanshan0d81b892019-08-07 16:21:10 +0800519 if (frame_x >= 0 && frame_y >= 0 && (frame_x + frame_width) < ScreenWidth() &&
520 (frame_y + frame_height) < ScreenHeight())
521 DrawSurface(frame, 0, 0, frame_width, frame_height, frame_x, frame_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800522 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700523
Tao Bao736d59c2017-01-03 10:15:33 -0800524 if (progressBarType != EMPTY) {
Tao Baoda409fb2018-10-21 23:36:26 -0700525 int width = gr_get_width(progress_bar_empty_.get());
526 int height = gr_get_height(progress_bar_empty_.get());
Doug Zongker211aebc2011-10-28 15:13:10 -0700527
Luke Song92eda4d2017-09-19 10:51:35 -0700528 int progress_x = (ScreenWidth() - width) / 2;
Tao Bao736d59c2017-01-03 10:15:33 -0800529 int progress_y = GetProgressBaseline();
Doug Zongker211aebc2011-10-28 15:13:10 -0700530
Tao Bao736d59c2017-01-03 10:15:33 -0800531 // Erase behind the progress bar (in case this was a progress-only update)
532 gr_color(0, 0, 0, 255);
Luke Song92eda4d2017-09-19 10:51:35 -0700533 DrawFill(progress_x, progress_y, width, height);
Doug Zongker211aebc2011-10-28 15:13:10 -0700534
Tao Bao736d59c2017-01-03 10:15:33 -0800535 if (progressBarType == DETERMINATE) {
536 float p = progressScopeStart + progress * progressScopeSize;
537 int pos = static_cast<int>(p * width);
Doug Zongker211aebc2011-10-28 15:13:10 -0700538
Tao Bao736d59c2017-01-03 10:15:33 -0800539 if (rtl_locale_) {
540 // Fill the progress bar from right to left.
541 if (pos > 0) {
Tao Baoda409fb2018-10-21 23:36:26 -0700542 DrawSurface(progress_bar_fill_.get(), width - pos, 0, pos, height,
543 progress_x + width - pos, progress_y);
Doug Zongker211aebc2011-10-28 15:13:10 -0700544 }
Tao Bao736d59c2017-01-03 10:15:33 -0800545 if (pos < width - 1) {
Tao Baoda409fb2018-10-21 23:36:26 -0700546 DrawSurface(progress_bar_empty_.get(), 0, 0, width - pos, height, progress_x, progress_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800547 }
548 } else {
549 // Fill the progress bar from left to right.
550 if (pos > 0) {
Tao Baoda409fb2018-10-21 23:36:26 -0700551 DrawSurface(progress_bar_fill_.get(), 0, 0, pos, height, progress_x, progress_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800552 }
553 if (pos < width - 1) {
Tao Baoda409fb2018-10-21 23:36:26 -0700554 DrawSurface(progress_bar_empty_.get(), pos, 0, width - pos, height, progress_x + pos,
555 progress_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800556 }
557 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700558 }
Tao Bao736d59c2017-01-03 10:15:33 -0800559 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700560}
561
Jesse Chan2f376422020-04-28 14:49:13 +0000562/* recovery dark: #7C4DFF
563 recovery light: #F890FF
564 fastbootd dark: #E65100
565 fastboot light: #FDD835 */
Tao Bao99b2d772017-06-23 22:47:03 -0700566void ScreenRecoveryUI::SetColor(UIElement e) const {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700567 switch (e) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700568 case UIElement::INFO:
Richard Hansenc14fd432020-05-04 15:58:49 -0400569 if (fastbootd_logo_enabled_)
570 gr_color(0xfd, 0xd8, 0x35, 255);
571 else
572 gr_color(0xf8, 0x90, 0xff, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700573 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700574 case UIElement::HEADER:
Jesse Chan2f376422020-04-28 14:49:13 +0000575 if (fastbootd_logo_enabled_)
576 gr_color(0xfd, 0xd8,0x35, 255);
577 else
578 gr_color(0xf8, 0x90, 0xff, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700579 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700580 case UIElement::MENU:
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100581 gr_color(0xd8, 0xd8, 0xd8, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700582 break;
Jesse Chan2f376422020-04-28 14:49:13 +0000583 case UIElement::MENU_SEL_BG:
584 case UIElement::SCROLLBAR:
585 if (fastbootd_logo_enabled_)
586 gr_color(0xe6, 0x51, 0x00, 255);
587 else
588 gr_color(0x7c, 0x4d, 0xff, 255);
589 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700590 case UIElement::MENU_SEL_BG_ACTIVE:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700591 gr_color(0, 156, 100, 255);
592 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700593 case UIElement::MENU_SEL_FG:
Jesse Chan2f376422020-04-28 14:49:13 +0000594 if (fastbootd_logo_enabled_)
595 gr_color(0, 0, 0, 255);
596 else
597 gr_color(0xd8, 0xd8, 0xd8, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700598 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700599 case UIElement::LOG:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700600 gr_color(196, 196, 196, 255);
601 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700602 case UIElement::TEXT_FILL:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700603 gr_color(0, 0, 0, 160);
604 break;
605 default:
606 gr_color(255, 255, 255, 255);
607 break;
608 }
Doug Zongkerc0441d12013-07-31 11:28:24 -0700609}
Doug Zongker211aebc2011-10-28 15:13:10 -0700610
Tianjie Xu29d55752017-09-20 17:53:46 -0700611void ScreenRecoveryUI::SelectAndShowBackgroundText(const std::vector<std::string>& locales_entries,
612 size_t sel) {
613 SetLocale(locales_entries[sel]);
614 std::vector<std::string> text_name = { "erasing_text", "error_text", "installing_text",
615 "installing_security_text", "no_command_text" };
Tao Baoda409fb2018-10-21 23:36:26 -0700616 std::unordered_map<std::string, std::unique_ptr<GRSurface>> surfaces;
Tianjie Xu29d55752017-09-20 17:53:46 -0700617 for (const auto& name : text_name) {
Tao Baoda409fb2018-10-21 23:36:26 -0700618 auto text_image = LoadLocalizedBitmap(name);
Tianjie Xu29d55752017-09-20 17:53:46 -0700619 if (!text_image) {
620 Print("Failed to load %s\n", name.c_str());
621 return;
622 }
Tao Baoda409fb2018-10-21 23:36:26 -0700623 surfaces.emplace(name, std::move(text_image));
Tianjie Xu29d55752017-09-20 17:53:46 -0700624 }
625
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700626 std::lock_guard<std::mutex> lg(updateMutex);
Tianjie Xu29d55752017-09-20 17:53:46 -0700627 gr_color(0, 0, 0, 255);
628 gr_clear();
629
Tao Bao0bc88de2018-07-31 14:53:16 -0700630 int text_y = margin_height_;
631 int text_x = margin_width_;
Tianjie Xu29d55752017-09-20 17:53:46 -0700632 int line_spacing = gr_sys_font()->char_height; // Put some extra space between images.
633 // Write the header and descriptive texts.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700634 SetColor(UIElement::INFO);
Tianjie Xu29d55752017-09-20 17:53:46 -0700635 std::string header = "Show background text image";
Tao Bao93e46ad2018-05-02 14:57:21 -0700636 text_y += DrawTextLine(text_x, text_y, header, true);
Tianjie Xu29d55752017-09-20 17:53:46 -0700637 std::string locale_selection = android::base::StringPrintf(
Tao Bao39c49182018-05-07 22:50:33 -0700638 "Current locale: %s, %zu/%zu", locales_entries[sel].c_str(), sel + 1, locales_entries.size());
Tao Bao93e46ad2018-05-02 14:57:21 -0700639 // clang-format off
640 std::vector<std::string> instruction = {
641 locale_selection,
642 "Use volume up/down to switch locales and power to exit."
643 };
644 // clang-format on
Tianjie Xu29d55752017-09-20 17:53:46 -0700645 text_y += DrawWrappedTextLines(text_x, text_y, instruction);
646
647 // Iterate through the text images and display them in order for the current locale.
648 for (const auto& p : surfaces) {
649 text_y += line_spacing;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700650 SetColor(UIElement::LOG);
Tao Bao93e46ad2018-05-02 14:57:21 -0700651 text_y += DrawTextLine(text_x, text_y, p.first, false);
Tianjie Xu29d55752017-09-20 17:53:46 -0700652 gr_color(255, 255, 255, 255);
653 gr_texticon(text_x, text_y, p.second.get());
654 text_y += gr_get_height(p.second.get());
655 }
656 // Update the whole screen.
657 gr_flip();
Tianjie Xu29d55752017-09-20 17:53:46 -0700658}
659
Tao Bao39c49182018-05-07 22:50:33 -0700660void ScreenRecoveryUI::CheckBackgroundTextImages() {
Tianjie Xu29d55752017-09-20 17:53:46 -0700661 // Load a list of locales embedded in one of the resource files.
662 std::vector<std::string> locales_entries = get_locales_in_png("installing_text");
663 if (locales_entries.empty()) {
664 Print("Failed to load locales from the resource files\n");
665 return;
666 }
Tao Bao39c49182018-05-07 22:50:33 -0700667 std::string saved_locale = locale_;
Tianjie Xu29d55752017-09-20 17:53:46 -0700668 size_t selected = 0;
669 SelectAndShowBackgroundText(locales_entries, selected);
670
671 FlushKeys();
672 while (true) {
Alessandro Astone73961412020-10-04 18:11:40 +0200673 InputEvent evt = WaitInputEvent();
674 if (evt.type() == EventType::EXTRA) {
675 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) break;
676 }
677 if (evt.type() == EventType::KEY) {
678 if (evt.key() == KEY_POWER || evt.key() == KEY_ENTER) {
679 break;
680 } else if (evt.key() == KEY_UP || evt.key() == KEY_VOLUMEUP) {
681 selected = (selected == 0) ? locales_entries.size() - 1 : selected - 1;
682 SelectAndShowBackgroundText(locales_entries, selected);
683 } else if (evt.key() == KEY_DOWN || evt.key() == KEY_VOLUMEDOWN) {
684 selected = (selected == locales_entries.size() - 1) ? 0 : selected + 1;
685 SelectAndShowBackgroundText(locales_entries, selected);
686 }
Tianjie Xu29d55752017-09-20 17:53:46 -0700687 }
688 }
689
690 SetLocale(saved_locale);
691}
692
Luke Song92eda4d2017-09-19 10:51:35 -0700693int ScreenRecoveryUI::ScreenWidth() const {
694 return gr_fb_width();
695}
696
697int ScreenRecoveryUI::ScreenHeight() const {
698 return gr_fb_height();
699}
700
Tao Bao65815b62018-10-23 10:54:02 -0700701void ScreenRecoveryUI::DrawSurface(const GRSurface* surface, int sx, int sy, int w, int h, int dx,
Luke Song92eda4d2017-09-19 10:51:35 -0700702 int dy) const {
703 gr_blit(surface, sx, sy, w, h, dx, dy);
704}
705
Tao Baoea78d862017-06-28 14:52:17 -0700706int ScreenRecoveryUI::DrawHorizontalRule(int y) const {
Luke Song92eda4d2017-09-19 10:51:35 -0700707 gr_fill(0, y + 4, ScreenWidth(), y + 6);
Tao Baoea78d862017-06-28 14:52:17 -0700708 return 8;
Elliott Hughes95fc63e2015-04-10 19:12:01 -0700709}
710
Luke Songe2bd8762017-06-12 16:08:33 -0700711void ScreenRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const {
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100712 if (y + height > ScreenHeight())
713 height = ScreenHeight() - y;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700714 gr_fill(x, y, x + width, y + height);
Luke Songe2bd8762017-06-12 16:08:33 -0700715}
716
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100717void ScreenRecoveryUI::DrawScrollBar(int y, int height) const {
718 int x = ScreenWidth() - margin_width_;
719 int width = 8;
720 gr_fill(x - width, y, x, y + height);
721}
722
Luke Song92eda4d2017-09-19 10:51:35 -0700723void ScreenRecoveryUI::DrawFill(int x, int y, int w, int h) const {
724 gr_fill(x, y, w, h);
725}
726
Tao Bao65815b62018-10-23 10:54:02 -0700727void ScreenRecoveryUI::DrawTextIcon(int x, int y, const GRSurface* surface) const {
Luke Song92eda4d2017-09-19 10:51:35 -0700728 gr_texticon(x, y, surface);
729}
730
Tao Bao93e46ad2018-05-02 14:57:21 -0700731int ScreenRecoveryUI::DrawTextLine(int x, int y, const std::string& line, bool bold) const {
732 gr_text(gr_sys_font(), x, y, line.c_str(), bold);
Tao Baoea78d862017-06-28 14:52:17 -0700733 return char_height_ + 4;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700734}
735
Tao Bao93e46ad2018-05-02 14:57:21 -0700736int ScreenRecoveryUI::DrawTextLines(int x, int y, const std::vector<std::string>& lines) const {
Tao Baoea78d862017-06-28 14:52:17 -0700737 int offset = 0;
Tao Bao93e46ad2018-05-02 14:57:21 -0700738 for (const auto& line : lines) {
739 offset += DrawTextLine(x, y + offset, line, false);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700740 }
Tao Baoea78d862017-06-28 14:52:17 -0700741 return offset;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700742}
743
Tao Bao93e46ad2018-05-02 14:57:21 -0700744int ScreenRecoveryUI::DrawWrappedTextLines(int x, int y,
745 const std::vector<std::string>& lines) const {
Tao Bao452b4872018-05-09 11:52:09 -0700746 // Keep symmetrical margins based on the given offset (i.e. x).
747 size_t text_cols = (ScreenWidth() - x * 2) / char_width_;
Tao Bao2bbc6d62017-08-13 23:48:55 -0700748 int offset = 0;
Tao Bao93e46ad2018-05-02 14:57:21 -0700749 for (const auto& line : lines) {
Tao Bao2bbc6d62017-08-13 23:48:55 -0700750 size_t next_start = 0;
751 while (next_start < line.size()) {
Tao Bao452b4872018-05-09 11:52:09 -0700752 std::string sub = line.substr(next_start, text_cols + 1);
753 if (sub.size() <= text_cols) {
Tao Bao2bbc6d62017-08-13 23:48:55 -0700754 next_start += sub.size();
755 } else {
Tao Bao452b4872018-05-09 11:52:09 -0700756 // Line too long and must be wrapped to text_cols columns.
Tao Bao2bbc6d62017-08-13 23:48:55 -0700757 size_t last_space = sub.find_last_of(" \t\n");
758 if (last_space == std::string::npos) {
Tao Bao93e46ad2018-05-02 14:57:21 -0700759 // No space found, just draw as much as we can.
Tao Bao452b4872018-05-09 11:52:09 -0700760 sub.resize(text_cols);
761 next_start += text_cols;
Tao Bao2bbc6d62017-08-13 23:48:55 -0700762 } else {
763 sub.resize(last_space);
764 next_start += last_space + 1;
765 }
766 }
Tao Bao93e46ad2018-05-02 14:57:21 -0700767 offset += DrawTextLine(x, y + offset, sub, false);
Tao Bao2bbc6d62017-08-13 23:48:55 -0700768 }
769 }
770 return offset;
771}
772
Jerry Zhang0e577ee2018-05-07 11:21:10 -0700773void ScreenRecoveryUI::SetTitle(const std::vector<std::string>& lines) {
774 title_lines_ = lines;
775}
776
Tianjie Xue5032212019-07-23 13:23:29 -0700777std::vector<std::string> ScreenRecoveryUI::GetMenuHelpMessage() const {
778 // clang-format off
779 static std::vector<std::string> REGULAR_HELP{
Richard Hansen1cfb0e52020-05-13 10:07:36 -0400780 "Use the volume up/down keys to navigate.",
781 "Use the power key to select.",
Tianjie Xue5032212019-07-23 13:23:29 -0700782 };
783 static std::vector<std::string> LONG_PRESS_HELP{
784 "Any button cycles highlight.",
785 "Long-press activates.",
786 };
Richard Hansenfdf0ca62020-05-12 18:18:43 -0400787 static const std::vector<std::string> NO_HELP = {};
Tianjie Xue5032212019-07-23 13:23:29 -0700788 // clang-format on
Richard Hansenfdf0ca62020-05-12 18:18:43 -0400789 return HasTouchScreen() ? NO_HELP : HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP;
Tianjie Xue5032212019-07-23 13:23:29 -0700790}
791
Tao Bao171b4c42017-06-19 23:10:44 -0700792// Redraws everything on the screen. Does not flip pages. Should only be called with updateMutex
793// locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700794void ScreenRecoveryUI::draw_screen_locked() {
Tao Bao171b4c42017-06-19 23:10:44 -0700795 if (!show_text) {
796 draw_background_locked();
797 draw_foreground_locked();
798 return;
799 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700800
Tao Bao171b4c42017-06-19 23:10:44 -0700801 gr_color(0, 0, 0, 255);
802 gr_clear();
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700803
Tianjie Xue5032212019-07-23 13:23:29 -0700804 draw_menu_and_text_buffer_locked(GetMenuHelpMessage());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700805}
806
807// Draws the menu and text buffer on the screen. Should only be called with updateMutex locked.
Tao Bao93e46ad2018-05-02 14:57:21 -0700808void ScreenRecoveryUI::draw_menu_and_text_buffer_locked(
809 const std::vector<std::string>& help_message) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700810 int y = margin_height_;
David Anderson983e2d52019-01-02 11:35:38 -0800811
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700812 if (menu_) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700813 int x = margin_width_ + kMenuIndent;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700814
Tianjie Xu66dbf632018-10-11 16:54:50 -0700815 SetColor(UIElement::INFO);
Jerry Zhang0e577ee2018-05-07 11:21:10 -0700816
Alessandro Astone82ffc162020-03-29 15:08:09 +0200817 auto& logo = fastbootd_logo_enabled_ ? fastbootd_logo_ : lineage_logo_;
818 if (logo && back_icon_) {
819 auto logo_width = gr_get_width(logo.get());
820 auto logo_height = gr_get_height(logo.get());
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100821 auto centered_x = ScreenWidth() / 2 - logo_width / 2;
Alessandro Astone82ffc162020-03-29 15:08:09 +0200822 DrawSurface(logo.get(), 0, 0, logo_width, logo_height, centered_x, y);
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100823 y += logo_height;
Tao Bao171b4c42017-06-19 23:10:44 -0700824
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100825 if (!menu_->IsMain()) {
826 auto icon_w = gr_get_width(back_icon_.get());
827 auto icon_h = gr_get_height(back_icon_.get());
828 auto icon_x = centered_x / 2 - icon_w / 2;
829 auto icon_y = y - logo_height / 2 - icon_h / 2;
830 gr_blit(back_icon_sel_ && menu_->selection() == -1 ? back_icon_sel_.get() : back_icon_.get(),
831 0, 0, icon_w, icon_h, icon_x, icon_y);
832 }
Jesse Chan2f376422020-04-28 14:49:13 +0000833 y += MenuItemPadding();
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100834 } else {
835 for (size_t i = 0; i < title_lines_.size(); i++) {
836 y += DrawTextLine(x, y, title_lines_[i], i == 0);
837 }
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100838 }
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700839
Tianjie Xu66dbf632018-10-11 16:54:50 -0700840 y += menu_->DrawHeader(x, y);
Alessandro Astone73961412020-10-04 18:11:40 +0200841 menu_start_y_ = y + 12; // Skip horizontal rule and some margin
Alessandro Astoneefc7a8e2020-06-25 19:54:18 +0200842 menu_->SetMenuHeight(std::max(0, ScreenHeight() - menu_start_y_));
Tianjie Xu66dbf632018-10-11 16:54:50 -0700843 y += menu_->DrawItems(x, y, ScreenWidth(), IsLongPress());
Richard Hansenfdf0ca62020-05-12 18:18:43 -0400844 if (!help_message.empty()) {
845 y += MenuItemPadding();
846 SetColor(UIElement::INFO);
847 y += DrawTextLines(x, y, help_message);
848 }
Tao Bao171b4c42017-06-19 23:10:44 -0700849 }
850
851 // Display from the bottom up, until we hit the top of the screen, the bottom of the menu, or
852 // we've displayed the entire text buffer.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700853 SetColor(UIElement::LOG);
Tao Baocb5524c2017-09-08 21:25:32 -0700854 int row = text_row_;
Tao Bao171b4c42017-06-19 23:10:44 -0700855 size_t count = 0;
Tao Bao0bc88de2018-07-31 14:53:16 -0700856 for (int ty = ScreenHeight() - margin_height_ - char_height_; ty >= y && count < text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700857 ty -= char_height_, ++count) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700858 DrawTextLine(margin_width_, ty, text_[row], false);
Tao Bao171b4c42017-06-19 23:10:44 -0700859 --row;
860 if (row < 0) row = text_rows_ - 1;
861 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700862}
863
864// Redraw everything on the screen and flip the screen (make it visible).
865// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700866void ScreenRecoveryUI::update_screen_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700867 draw_screen_locked();
868 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700869}
870
871// Updates only the progress bar, if possible, otherwise redraws the screen.
872// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700873void ScreenRecoveryUI::update_progress_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700874 if (show_text || !pagesIdentical) {
875 draw_screen_locked(); // Must redraw the whole screen
876 pagesIdentical = true;
877 } else {
878 draw_foreground_locked(); // Draw only the progress bar and overlays
879 }
880 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700881}
882
Elliott Hughes985022a2015-04-13 13:04:32 -0700883void ScreenRecoveryUI::ProgressThreadLoop() {
Tao Bao0bc88de2018-07-31 14:53:16 -0700884 double interval = 1.0 / animation_fps_;
Tao Bao26ea9592018-05-09 16:32:02 -0700885 while (!progress_thread_stopped_) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700886 double start = now();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700887 bool redraw = false;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700888 {
889 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker211aebc2011-10-28 15:13:10 -0700890
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700891 // update the installation animation, if active
892 // skip this if we have a text overlay (too expensive to update)
Tao Baoda409fb2018-10-21 23:36:26 -0700893 if ((current_icon_ == INSTALLING_UPDATE || current_icon_ == ERASING) && !show_text) {
894 if (!intro_done_) {
895 if (current_frame_ == intro_frames_.size() - 1) {
896 intro_done_ = true;
897 current_frame_ = 0;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700898 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700899 ++current_frame_;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700900 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700901 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700902 current_frame_ = (current_frame_ + 1) % loop_frames_.size();
Doug Zongker211aebc2011-10-28 15:13:10 -0700903 }
904
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700905 redraw = true;
906 }
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700907
908 // move the progress bar forward on timed intervals, if configured
909 int duration = progressScopeDuration;
910 if (progressBarType == DETERMINATE && duration > 0) {
911 double elapsed = now() - progressScopeTime;
912 float p = 1.0 * elapsed / duration;
913 if (p > 1.0) p = 1.0;
914 if (p > progress) {
915 progress = p;
916 redraw = true;
917 }
918 }
919
920 if (redraw) update_progress_locked();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700921 }
922
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700923 double end = now();
924 // minimum of 20ms delay between frames
925 double delay = interval - (end - start);
926 if (delay < 0.02) delay = 0.02;
927 usleep(static_cast<useconds_t>(delay * 1000000));
928 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700929}
930
Tao Baoda409fb2018-10-21 23:36:26 -0700931std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadBitmap(const std::string& filename) {
932 GRSurface* surface;
933 if (auto result = res_create_display_surface(filename.c_str(), &surface); result < 0) {
934 LOG(ERROR) << "Failed to load bitmap " << filename << " (error " << result << ")";
935 return nullptr;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700936 }
Tao Baoda409fb2018-10-21 23:36:26 -0700937 return std::unique_ptr<GRSurface>(surface);
Doug Zongkereac881c2014-03-07 09:21:25 -0800938}
939
Tao Baoda409fb2018-10-21 23:36:26 -0700940std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadLocalizedBitmap(const std::string& filename) {
941 GRSurface* surface;
xunchang9d05c8a2019-04-16 12:07:42 -0700942 auto result = res_create_localized_alpha_surface(filename.c_str(), locale_.c_str(), &surface);
943 if (result == 0) {
944 return std::unique_ptr<GRSurface>(surface);
Tao Bao736d59c2017-01-03 10:15:33 -0800945 }
xunchang9d05c8a2019-04-16 12:07:42 -0700946 // TODO(xunchang) create a error code enum to refine the retry condition.
947 LOG(WARNING) << "Failed to load bitmap " << filename << " for locale " << locale_ << " (error "
948 << result << "). Falling back to use default locale.";
949
950 result = res_create_localized_alpha_surface(filename.c_str(), DEFAULT_LOCALE, &surface);
951 if (result == 0) {
952 return std::unique_ptr<GRSurface>(surface);
953 }
954
955 LOG(ERROR) << "Failed to load bitmap " << filename << " for locale " << DEFAULT_LOCALE
956 << " (error " << result << ")";
957 return nullptr;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700958}
959
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700960static char** Alloc2d(size_t rows, size_t cols) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700961 char** result = new char*[rows];
962 for (size_t i = 0; i < rows; ++i) {
963 result[i] = new char[cols];
964 memset(result[i], 0, cols);
965 }
966 return result;
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700967}
968
Tianjie Xu35926c42016-04-28 18:06:26 -0700969// Choose the right background string to display during update.
970void ScreenRecoveryUI::SetSystemUpdateText(bool security_update) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700971 if (security_update) {
Tao Baoda409fb2018-10-21 23:36:26 -0700972 installing_text_ = LoadLocalizedBitmap("installing_security_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700973 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700974 installing_text_ = LoadLocalizedBitmap("installing_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700975 }
976 Redraw();
Tianjie Xu35926c42016-04-28 18:06:26 -0700977}
978
Sen Jiangd5304492016-12-09 16:20:49 -0800979bool ScreenRecoveryUI::InitTextParams() {
Tao Baoba4edb32018-06-13 11:22:50 -0700980 // gr_init() would return successfully on font initialization failure.
981 if (gr_sys_font() == nullptr) {
Tao Bao171b4c42017-06-19 23:10:44 -0700982 return false;
983 }
Tao Bao171b4c42017-06-19 23:10:44 -0700984 gr_font_size(gr_sys_font(), &char_width_, &char_height_);
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100985 gr_font_size(gr_menu_font(), &menu_char_width_, &menu_char_height_);
Tao Bao0bc88de2018-07-31 14:53:16 -0700986 text_rows_ = (ScreenHeight() - margin_height_ * 2) / char_height_;
987 text_cols_ = (ScreenWidth() - margin_width_ * 2) / char_width_;
Tao Bao171b4c42017-06-19 23:10:44 -0700988 return true;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -0700989}
990
Tianjie Xub99e6062018-10-16 15:13:09 -0700991bool ScreenRecoveryUI::LoadWipeDataMenuText() {
Tianjie Xu1a0a30a2018-10-25 15:22:07 -0700992 // Ignores the errors since the member variables will stay as nullptr.
993 cancel_wipe_data_text_ = LoadLocalizedBitmap("cancel_wipe_data_text");
994 factory_data_reset_text_ = LoadLocalizedBitmap("factory_data_reset_text");
995 try_again_text_ = LoadLocalizedBitmap("try_again_text");
996 wipe_data_confirmation_text_ = LoadLocalizedBitmap("wipe_data_confirmation_text");
997 wipe_data_menu_header_text_ = LoadLocalizedBitmap("wipe_data_menu_header_text");
Tianjie Xub99e6062018-10-16 15:13:09 -0700998 return true;
999}
1000
Mark Salyzyn30017e72020-05-13 12:39:12 -07001001static bool InitGraphics() {
1002 // Timeout is same as init wait for file default of 5 seconds and is arbitrary
1003 const unsigned timeout = 500; // 10ms increments
1004 for (auto retry = timeout; retry > 0; --retry) {
1005 if (gr_init() == 0) {
1006 if (retry < timeout) {
1007 // Log message like init wait for file completion log for consistency.
1008 LOG(WARNING) << "wait for 'graphics' took " << ((timeout - retry) * 10) << "ms";
1009 }
1010 return true;
1011 }
1012 std::this_thread::sleep_for(10ms);
1013 }
1014 // Log message like init wait for file timeout log for consistency.
1015 LOG(ERROR) << "timeout wait for 'graphics' took " << (timeout * 10) << "ms";
1016 return false;
1017}
1018
Tao Bao736d59c2017-01-03 10:15:33 -08001019bool ScreenRecoveryUI::Init(const std::string& locale) {
1020 RecoveryUI::Init(locale);
Tao Baoefb49ad2017-01-31 23:03:10 -08001021
Mark Salyzyn30017e72020-05-13 12:39:12 -07001022 if (!InitGraphics()) {
Tao Baoba4edb32018-06-13 11:22:50 -07001023 return false;
1024 }
fredchiouf2f5aa22022-02-11 16:39:53 +08001025 is_graphics_available = true;
Tao Baoba4edb32018-06-13 11:22:50 -07001026
Tao Bao736d59c2017-01-03 10:15:33 -08001027 if (!InitTextParams()) {
1028 return false;
1029 }
Alessandro Astone4a0de5a2020-03-09 23:17:50 +01001030 menu_draw_funcs_ = std::make_unique<MenuDrawFunctions>(*this);
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001031
Michael Bestas43ff6792019-03-23 17:28:22 +02001032 if (blank_unblank_on_init_) {
1033 gr_fb_blank(true);
1034 gr_fb_blank(false);
1035 }
1036
Tao Bao736d59c2017-01-03 10:15:33 -08001037 // Are we portrait or landscape?
1038 layout_ = (gr_fb_width() > gr_fb_height()) ? LANDSCAPE : PORTRAIT;
1039 // Are we the large variant of our base layout?
1040 if (gr_fb_height() > PixelsFromDp(800)) ++layout_;
Elliott Hughesfaf36e02016-04-20 17:22:16 -07001041
Tao Bao736d59c2017-01-03 10:15:33 -08001042 text_ = Alloc2d(text_rows_, text_cols_ + 1);
1043 file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
Doug Zongker55a36ac2013-03-04 15:49:02 -08001044
Tao Bao736d59c2017-01-03 10:15:33 -08001045 text_col_ = text_row_ = 0;
Doug Zongker211aebc2011-10-28 15:13:10 -07001046
Tao Baoefb49ad2017-01-31 23:03:10 -08001047 // Set up the locale info.
1048 SetLocale(locale);
1049
Tao Baoda409fb2018-10-21 23:36:26 -07001050 error_icon_ = LoadBitmap("icon_error");
Doug Zongker02ec6b82012-08-22 17:26:40 -07001051
Tao Baoda409fb2018-10-21 23:36:26 -07001052 progress_bar_empty_ = LoadBitmap("progress_empty");
1053 progress_bar_fill_ = LoadBitmap("progress_fill");
1054 stage_marker_empty_ = LoadBitmap("stage_empty");
1055 stage_marker_fill_ = LoadBitmap("stage_fill");
Elliott Hughes498cda62016-04-14 16:49:04 -07001056
Tao Baoda409fb2018-10-21 23:36:26 -07001057 erasing_text_ = LoadLocalizedBitmap("erasing_text");
1058 no_command_text_ = LoadLocalizedBitmap("no_command_text");
1059 error_text_ = LoadLocalizedBitmap("error_text");
Doug Zongker211aebc2011-10-28 15:13:10 -07001060
Alessandro Astone4a0de5a2020-03-09 23:17:50 +01001061 back_icon_ = LoadBitmap("ic_back");
1062 back_icon_sel_ = LoadBitmap("ic_back_sel");
Alessandro Astonecae99222020-02-26 17:25:54 +01001063 if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) ||
1064 android::base::GetBoolProperty("ro.fastbootd.available", false)) {
Alessandro Astone82ffc162020-03-29 15:08:09 +02001065 lineage_logo_ = LoadBitmap("logo_image_switch");
David Anderson983e2d52019-01-02 11:35:38 -08001066 fastbootd_logo_ = LoadBitmap("fastbootd");
Alessandro Astone82ffc162020-03-29 15:08:09 +02001067 } else {
1068 lineage_logo_ = LoadBitmap("logo_image");
David Anderson983e2d52019-01-02 11:35:38 -08001069 }
1070
Tao Baoda409fb2018-10-21 23:36:26 -07001071 // Background text for "installing_update" could be "installing update" or
1072 // "installing security update". It will be set after Init() according to the commands in BCB.
1073 installing_text_.reset();
Elliott Hughes498cda62016-04-14 16:49:04 -07001074
Tianjie Xub99e6062018-10-16 15:13:09 -07001075 LoadWipeDataMenuText();
1076
Tao Bao736d59c2017-01-03 10:15:33 -08001077 LoadAnimation();
Doug Zongker02ec6b82012-08-22 17:26:40 -07001078
Tao Bao26ea9592018-05-09 16:32:02 -07001079 // Keep the progress bar updated, even when the process is otherwise busy.
1080 progress_thread_ = std::thread(&ScreenRecoveryUI::ProgressThreadLoop, this);
Sen Jiangd5304492016-12-09 16:20:49 -08001081
fredchiouf2f5aa22022-02-11 16:39:53 +08001082 // set the callback for hall sensor event
1083 (void)ev_sync_sw_state([this](auto&& a, auto&& b) { return this->SetSwCallback(a, b);});
1084
Tao Bao736d59c2017-01-03 10:15:33 -08001085 return true;
Doug Zongker211aebc2011-10-28 15:13:10 -07001086}
1087
Tao Bao551d2c32018-05-09 20:53:13 -07001088std::string ScreenRecoveryUI::GetLocale() const {
Jerry Zhang2dea53e2018-05-02 17:15:03 -07001089 return locale_;
1090}
1091
Elliott Hughes498cda62016-04-14 16:49:04 -07001092void ScreenRecoveryUI::LoadAnimation() {
Tao Bao6cd81682018-05-03 21:53:11 -07001093 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(Paths::Get().resource_dir().c_str()),
1094 closedir);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001095 dirent* de;
1096 std::vector<std::string> intro_frame_names;
Joshua Lambert94a83be2019-12-09 20:24:18 +00001097 std::vector<std::string> loop_frame_names;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001098
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001099 while ((de = readdir(dir.get())) != nullptr) {
1100 int value, num_chars;
1101 if (sscanf(de->d_name, "intro%d%n.png", &value, &num_chars) == 1) {
1102 intro_frame_names.emplace_back(de->d_name, num_chars);
1103 } else if (sscanf(de->d_name, "loop%d%n.png", &value, &num_chars) == 1) {
Joshua Lambert94a83be2019-12-09 20:24:18 +00001104 loop_frame_names.emplace_back(de->d_name, num_chars);
Elliott Hughes498cda62016-04-14 16:49:04 -07001105 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001106 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001107
Tao Baoda409fb2018-10-21 23:36:26 -07001108 size_t intro_frames = intro_frame_names.size();
1109 size_t loop_frames = loop_frame_names.size();
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001110
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001111 // It's okay to not have an intro.
Tao Baoda409fb2018-10-21 23:36:26 -07001112 if (intro_frames == 0) intro_done_ = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001113 // But you must have an animation.
1114 if (loop_frames == 0) abort();
Elliott Hughes498cda62016-04-14 16:49:04 -07001115
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001116 std::sort(intro_frame_names.begin(), intro_frame_names.end());
1117 std::sort(loop_frame_names.begin(), loop_frame_names.end());
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001118
Tao Baoda409fb2018-10-21 23:36:26 -07001119 intro_frames_.clear();
1120 intro_frames_.reserve(intro_frames);
1121 for (const auto& frame_name : intro_frame_names) {
1122 intro_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001123 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001124
Tao Baoda409fb2018-10-21 23:36:26 -07001125 loop_frames_.clear();
1126 loop_frames_.reserve(loop_frames);
1127 for (const auto& frame_name : loop_frame_names) {
1128 loop_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001129 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001130}
1131
Elliott Hughes8de52072015-04-08 20:06:50 -07001132void ScreenRecoveryUI::SetBackground(Icon icon) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001133 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker02ec6b82012-08-22 17:26:40 -07001134
Tao Baoda409fb2018-10-21 23:36:26 -07001135 current_icon_ = icon;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001136 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001137}
1138
Elliott Hughes8de52072015-04-08 20:06:50 -07001139void ScreenRecoveryUI::SetProgressType(ProgressType type) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001140 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001141 if (progressBarType != type) {
1142 progressBarType = type;
1143 }
1144 progressScopeStart = 0;
1145 progressScopeSize = 0;
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::ShowProgress(float portion, float seconds) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001151 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001152 progressBarType = DETERMINATE;
1153 progressScopeStart += progressScopeSize;
1154 progressScopeSize = portion;
1155 progressScopeTime = now();
1156 progressScopeDuration = seconds;
1157 progress = 0;
1158 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001159}
1160
Elliott Hughes8de52072015-04-08 20:06:50 -07001161void ScreenRecoveryUI::SetProgress(float fraction) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001162 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001163 if (fraction < 0.0) fraction = 0.0;
1164 if (fraction > 1.0) fraction = 1.0;
1165 if (progressBarType == DETERMINATE && fraction > progress) {
1166 // Skip updates that aren't visibly different.
Tao Baoda409fb2018-10-21 23:36:26 -07001167 int width = gr_get_width(progress_bar_empty_.get());
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001168 float scale = width * progressScopeSize;
1169 if ((int)(progress * scale) != (int)(fraction * scale)) {
1170 progress = fraction;
1171 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001172 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001173 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001174}
1175
Doug Zongkerc87bab12013-11-25 13:53:25 -08001176void ScreenRecoveryUI::SetStage(int current, int max) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001177 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001178 stage = current;
1179 max_stage = max;
Doug Zongkerc87bab12013-11-25 13:53:25 -08001180}
1181
Tao Baob6918c72015-05-19 17:02:16 -07001182void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001183 std::string str;
1184 android::base::StringAppendV(&str, fmt, ap);
Doug Zongker211aebc2011-10-28 15:13:10 -07001185
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001186 if (copy_to_stdout) {
1187 fputs(str.c_str(), stdout);
1188 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001189
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001190 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001191 if (text_rows_ > 0 && text_cols_ > 0) {
1192 for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
1193 if (*ptr == '\n' || text_col_ >= text_cols_) {
Elliott Hughesc0491632015-05-06 12:40:05 -07001194 text_[text_row_][text_col_] = '\0';
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001195 text_col_ = 0;
1196 text_row_ = (text_row_ + 1) % text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001197 }
1198 if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
Doug Zongker211aebc2011-10-28 15:13:10 -07001199 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001200 text_[text_row_][text_col_] = '\0';
1201 update_screen_locked();
1202 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001203}
1204
Tao Baob6918c72015-05-19 17:02:16 -07001205void ScreenRecoveryUI::Print(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001206 va_list ap;
1207 va_start(ap, fmt);
1208 PrintV(fmt, true, ap);
1209 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001210}
1211
Tianjie Xu8f397302018-08-20 13:40:47 -07001212void ScreenRecoveryUI::PrintOnScreenOnly(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001213 va_list ap;
1214 va_start(ap, fmt);
1215 PrintV(fmt, false, ap);
1216 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001217}
1218
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001219void ScreenRecoveryUI::PutChar(char ch) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001220 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001221 if (ch != '\n') text_[text_row_][text_col_++] = ch;
1222 if (ch == '\n' || text_col_ >= text_cols_) {
1223 text_col_ = 0;
1224 ++text_row_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001225 }
Elliott Hughes8de52072015-04-08 20:06:50 -07001226}
1227
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001228void ScreenRecoveryUI::ClearText() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001229 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001230 text_col_ = 0;
1231 text_row_ = 0;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001232 for (size_t i = 0; i < text_rows_; ++i) {
1233 memset(text_[i], 0, text_cols_ + 1);
1234 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001235}
1236
1237void ScreenRecoveryUI::ShowFile(FILE* fp) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001238 std::vector<off_t> offsets;
1239 offsets.push_back(ftello(fp));
1240 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001241
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001242 struct stat sb;
1243 fstat(fileno(fp), &sb);
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001244
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001245 bool show_prompt = false;
1246 while (true) {
1247 if (show_prompt) {
1248 PrintOnScreenOnly("--(%d%% of %d bytes)--",
1249 static_cast<int>(100 * (double(ftello(fp)) / double(sb.st_size))),
1250 static_cast<int>(sb.st_size));
1251 Redraw();
1252 while (show_prompt) {
1253 show_prompt = false;
Alessandro Astone73961412020-10-04 18:11:40 +02001254 InputEvent evt = WaitInputEvent();
1255 if (evt.type() == EventType::EXTRA) {
1256 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) {
1257 return;
1258 }
1259 }
1260 if (evt.type() != EventType::KEY) {
1261 show_prompt = true;
1262 continue;
1263 }
1264 if (evt.key() == KEY_POWER || evt.key() == KEY_ENTER || evt.key() == KEY_BACKSPACE ||
1265 evt.key() == KEY_BACK || evt.key() == KEY_HOME || evt.key() == KEY_HOMEPAGE) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001266 return;
Alessandro Astone73961412020-10-04 18:11:40 +02001267 } else if (evt.key() == KEY_UP || evt.key() == KEY_VOLUMEUP || evt.key() == KEY_SCROLLUP) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001268 if (offsets.size() <= 1) {
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001269 show_prompt = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001270 } else {
1271 offsets.pop_back();
1272 fseek(fp, offsets.back(), SEEK_SET);
1273 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001274 } else {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001275 if (feof(fp)) {
1276 return;
1277 }
1278 offsets.push_back(ftello(fp));
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001279 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001280 }
1281 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001282 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001283
1284 int ch = getc(fp);
1285 if (ch == EOF) {
1286 while (text_row_ < text_rows_ - 1) PutChar('\n');
1287 show_prompt = true;
1288 } else {
1289 PutChar(ch);
1290 if (text_col_ == 0 && text_row_ >= text_rows_ - 1) {
1291 show_prompt = true;
1292 }
1293 }
1294 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001295}
1296
Tao Bao1d156b92018-05-02 12:43:18 -07001297void ScreenRecoveryUI::ShowFile(const std::string& filename) {
1298 std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(filename.c_str(), "re"), fclose);
1299 if (!fp) {
1300 Print(" Unable to open %s: %s\n", filename.c_str(), strerror(errno));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001301 return;
1302 }
Elliott Hughesc0491632015-05-06 12:40:05 -07001303
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001304 char** old_text = text_;
1305 size_t old_text_col = text_col_;
1306 size_t old_text_row = text_row_;
Elliott Hughesc0491632015-05-06 12:40:05 -07001307
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001308 // Swap in the alternate screen and clear it.
1309 text_ = file_viewer_text_;
1310 ClearText();
Elliott Hughesc0491632015-05-06 12:40:05 -07001311
Tao Bao1d156b92018-05-02 12:43:18 -07001312 ShowFile(fp.get());
Elliott Hughesc0491632015-05-06 12:40:05 -07001313
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001314 text_ = old_text;
1315 text_col_ = old_text_col;
1316 text_row_ = old_text_row;
Elliott Hughes8de52072015-04-08 20:06:50 -07001317}
1318
Tao Baoda409fb2018-10-21 23:36:26 -07001319std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(
1320 const GRSurface* graphic_header, const std::vector<const GRSurface*>& graphic_items,
1321 const std::vector<std::string>& text_headers, const std::vector<std::string>& text_items,
1322 size_t initial_selection) const {
Tianjie Xub99e6062018-10-16 15:13:09 -07001323 // horizontal unusable area: margin width + menu indent
1324 size_t max_width = ScreenWidth() - margin_width_ - kMenuIndent;
1325 // vertical unusable area: margin height + title lines + helper message + high light bar.
1326 // It is safe to reserve more space.
1327 size_t max_height = ScreenHeight() - margin_height_ - char_height_ * (title_lines_.size() + 3);
1328 if (GraphicMenu::Validate(max_width, max_height, graphic_header, graphic_items)) {
1329 return std::make_unique<GraphicMenu>(graphic_header, graphic_items, initial_selection, *this);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001330 }
Tianjie Xub99e6062018-10-16 15:13:09 -07001331
1332 fprintf(stderr, "Failed to initialize graphic menu, falling back to use the text menu.\n");
1333
1334 return CreateMenu(text_headers, text_items, initial_selection);
1335}
1336
1337std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(const std::vector<std::string>& text_headers,
1338 const std::vector<std::string>& text_items,
1339 size_t initial_selection) const {
Alessandro Astone4a0de5a2020-03-09 23:17:50 +01001340 int menu_char_width = MenuCharWidth();
1341 int menu_char_height = MenuCharHeight();
Alessandro Astone4a0de5a2020-03-09 23:17:50 +01001342 int menu_cols = (ScreenWidth() - margin_width_*2 - kMenuIndent) / menu_char_width;
1343 bool wrap_selection = !HasThreeButtons() && !HasTouchScreen();
Alessandro Astoneefc7a8e2020-06-25 19:54:18 +02001344 return std::make_unique<TextMenu>(wrap_selection, menu_cols, text_headers, text_items,
Alessandro Astone4a0de5a2020-03-09 23:17:50 +01001345 initial_selection, menu_char_height, *menu_draw_funcs_);
Doug Zongker211aebc2011-10-28 15:13:10 -07001346}
1347
1348int ScreenRecoveryUI::SelectMenu(int sel) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001349 std::lock_guard<std::mutex> lg(updateMutex);
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001350 if (menu_) {
1351 int old_sel = menu_->selection();
1352 sel = menu_->Select(sel);
Elliott Hughesfc06f872015-03-23 13:45:31 -07001353
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001354 if (sel != old_sel) {
1355 update_screen_locked();
1356 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001357 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001358 return sel;
Doug Zongker211aebc2011-10-28 15:13:10 -07001359}
1360
Alessandro Astone73961412020-10-04 18:11:40 +02001361int ScreenRecoveryUI::SelectMenu(const Point& point) {
1362 int new_sel = Device::kNoAction;
1363 std::lock_guard<std::mutex> lg(updateMutex);
1364 if (menu_) {
1365 if (!menu_->IsMain()) {
1366 // Back arrow hitbox
1367 const static int logo_width = gr_get_width(lineage_logo_.get());
1368 const static int logo_height = gr_get_height(lineage_logo_.get());
1369 const static int icon_w = gr_get_width(back_icon_.get());
1370 const static int icon_h = gr_get_height(back_icon_.get());
1371 const static int centered_x = ScreenWidth() / 2 - logo_width / 2;
1372 const static int icon_x = centered_x / 2 - icon_w / 2;
1373 const static int icon_y = margin_height_ + logo_height / 2 - icon_h / 2;
1374
1375 if (point.x() >= icon_x && point.x() <= icon_x + icon_w &&
1376 point.y() >= icon_y && point.y() <= icon_y + icon_h) {
1377 return Device::kGoBack;
1378 }
1379 }
1380
1381 if (point.y() >= menu_start_y_ &&
1382 point.y() < menu_start_y_ + menu_->ItemsCount() * MenuItemHeight()) {
1383 int old_sel = menu_->selection();
1384 int relative_sel = (point.y() - menu_start_y_) / MenuItemHeight();
1385 new_sel = menu_->SelectVisible(relative_sel);
1386 if (new_sel != -1 && new_sel != old_sel) {
1387 update_screen_locked();
1388 }
1389 }
1390 }
1391 return new_sel;
1392}
1393
1394int ScreenRecoveryUI::ScrollMenu(int updown) {
1395 std::lock_guard<std::mutex> lg(updateMutex);
1396 int sel = Device::kNoAction;
1397 if (menu_) {
1398 sel = menu_->Scroll(updown);
1399 update_screen_locked();
1400 }
1401 return sel;
1402}
1403
Tianjie Xub99e6062018-10-16 15:13:09 -07001404size_t ScreenRecoveryUI::ShowMenu(std::unique_ptr<Menu>&& menu, bool menu_only,
Tao Bao1fe1afe2018-05-01 15:56:05 -07001405 const std::function<int(int, bool)>& key_handler) {
Tao Bao3aec6962018-04-20 09:24:58 -07001406 // Throw away keys pressed previously, so user doesn't accidentally trigger menu items.
1407 FlushKeys();
1408
Jerry Zhangb76af932018-05-22 12:08:35 -07001409 // If there is a key interrupt in progress, return KeyError::INTERRUPTED without starting the
1410 // menu.
1411 if (IsKeyInterrupted()) return static_cast<size_t>(KeyError::INTERRUPTED);
1412
Tianjie Xub99e6062018-10-16 15:13:09 -07001413 CHECK(menu != nullptr);
Tao Bao3aec6962018-04-20 09:24:58 -07001414
Tianjie Xub99e6062018-10-16 15:13:09 -07001415 // Starts and displays the menu
1416 menu_ = std::move(menu);
1417 Redraw();
1418
1419 int selected = menu_->selection();
Tao Bao3aec6962018-04-20 09:24:58 -07001420 int chosen_item = -1;
1421 while (chosen_item < 0) {
Alessandro Astone73961412020-10-04 18:11:40 +02001422 InputEvent evt = WaitInputEvent();
1423 if (evt.type() == EventType::EXTRA) {
1424 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) {
1425 // WaitKey() was interrupted.
1426 return static_cast<size_t>(KeyError::INTERRUPTED);
1427 }
1428 if (evt.key() == static_cast<int>(KeyError::TIMED_OUT)) { // WaitKey() timed out.
1429 if (WasTextEverVisible()) {
1430 continue;
1431 } else {
1432 LOG(INFO) << "Timed out waiting for key input; rebooting.";
1433 menu_.reset();
1434 Redraw();
1435 return static_cast<size_t>(KeyError::TIMED_OUT);
1436 }
Tao Bao3aec6962018-04-20 09:24:58 -07001437 }
1438 }
1439
Alessandro Astone73961412020-10-04 18:11:40 +02001440 int action = Device::kNoAction;
1441 if (evt.type() == EventType::TOUCH) {
1442 int touch_sel = SelectMenu(evt.pos());
1443 if (touch_sel < 0) {
1444 action = touch_sel;
1445 } else {
1446 action = Device::kInvokeItem;
1447 selected = touch_sel;
1448 }
1449 } else {
1450 bool visible = IsTextVisible();
1451 action = key_handler(evt.key(), visible);
1452 }
Tao Bao3aec6962018-04-20 09:24:58 -07001453 if (action < 0) {
1454 switch (action) {
1455 case Device::kHighlightUp:
1456 selected = SelectMenu(--selected);
1457 break;
1458 case Device::kHighlightDown:
1459 selected = SelectMenu(++selected);
1460 break;
Alessandro Astone73961412020-10-04 18:11:40 +02001461 case Device::kScrollUp:
1462 selected = ScrollMenu(-1);
1463 break;
1464 case Device::kScrollDown:
1465 selected = ScrollMenu(1);
1466 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001467 case Device::kInvokeItem:
Alessandro Astone4a0de5a2020-03-09 23:17:50 +01001468 if (selected < 0) {
1469 chosen_item = Device::kGoBack;
1470 } else {
1471 chosen_item = selected;
1472 }
Tao Bao3aec6962018-04-20 09:24:58 -07001473 break;
1474 case Device::kNoAction:
1475 break;
Tom Marshall4b921692017-08-24 13:50:01 +00001476 case Device::kGoBack:
1477 chosen_item = Device::kGoBack;
1478 break;
1479 case Device::kGoHome:
1480 chosen_item = Device::kGoHome;
1481 break;
Tom Marshallfdf50332018-12-17 15:57:44 -08001482 case Device::kDoSideload:
1483 chosen_item = Device::kDoSideload;
1484 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001485 }
1486 } else if (!menu_only) {
1487 chosen_item = action;
1488 }
Tom Marshallfdf50332018-12-17 15:57:44 -08001489
1490 if (chosen_item == Device::kGoBack || chosen_item == Device::kGoHome ||
1491 chosen_item == Device::kDoSideload) {
Tom Marshall4b921692017-08-24 13:50:01 +00001492 break;
1493 }
Tao Bao3aec6962018-04-20 09:24:58 -07001494 }
1495
Tianjie Xub99e6062018-10-16 15:13:09 -07001496 menu_.reset();
Tianjie Xub99e6062018-10-16 15:13:09 -07001497
Tao Bao3aec6962018-04-20 09:24:58 -07001498 return chosen_item;
1499}
1500
Tianjie Xub99e6062018-10-16 15:13:09 -07001501size_t ScreenRecoveryUI::ShowMenu(const std::vector<std::string>& headers,
1502 const std::vector<std::string>& items, size_t initial_selection,
1503 bool menu_only,
1504 const std::function<int(int, bool)>& key_handler) {
1505 auto menu = CreateMenu(headers, items, initial_selection);
1506 if (menu == nullptr) {
1507 return initial_selection;
1508 }
1509
Tao Baofa8e02a2019-06-27 09:07:04 -07001510 return ShowMenu(std::move(menu), menu_only, key_handler);
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) {
Tao Baoda409fb2018-10-21 23:36:26 -07001516 auto wipe_data_menu = CreateMenu(wipe_data_menu_header_text_.get(),
1517 { try_again_text_.get(), factory_data_reset_text_.get() },
1518 backup_headers, backup_items, 0);
Tianjie Xub99e6062018-10-16 15:13:09 -07001519 if (wipe_data_menu == nullptr) {
1520 return 0;
1521 }
1522
1523 return ShowMenu(std::move(wipe_data_menu), true, key_handler);
1524}
1525
Tianjie Xu1a0a30a2018-10-25 15:22:07 -07001526size_t ScreenRecoveryUI::ShowPromptWipeDataConfirmationMenu(
1527 const std::vector<std::string>& backup_headers, const std::vector<std::string>& backup_items,
1528 const std::function<int(int, bool)>& key_handler) {
1529 auto confirmation_menu =
1530 CreateMenu(wipe_data_confirmation_text_.get(),
1531 { cancel_wipe_data_text_.get(), factory_data_reset_text_.get() }, backup_headers,
1532 backup_items, 0);
1533 if (confirmation_menu == nullptr) {
1534 return 0;
1535 }
1536
1537 return ShowMenu(std::move(confirmation_menu), true, key_handler);
1538}
1539
Elliott Hughes8de52072015-04-08 20:06:50 -07001540bool ScreenRecoveryUI::IsTextVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001541 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001542 int visible = show_text;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001543 return visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001544}
1545
Elliott Hughes8de52072015-04-08 20:06:50 -07001546bool ScreenRecoveryUI::WasTextEverVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001547 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001548 int ever_visible = show_text_ever;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001549 return ever_visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001550}
1551
Elliott Hughes8de52072015-04-08 20:06:50 -07001552void ScreenRecoveryUI::ShowText(bool visible) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001553 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001554 show_text = visible;
1555 if (show_text) show_text_ever = true;
1556 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001557}
Doug Zongkerc0441d12013-07-31 11:28:24 -07001558
Elliott Hughes8de52072015-04-08 20:06:50 -07001559void ScreenRecoveryUI::Redraw() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001560 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001561 update_screen_locked();
Doug Zongkerc0441d12013-07-31 11:28:24 -07001562}
Elliott Hughes642aaa72015-04-10 12:47:46 -07001563
1564void ScreenRecoveryUI::KeyLongPress(int) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001565 // Redraw so that if we're in the menu, the highlight
1566 // will change color to indicate a successful long press.
1567 Redraw();
Elliott Hughes642aaa72015-04-10 12:47:46 -07001568}
Tao Baoefb49ad2017-01-31 23:03:10 -08001569
1570void ScreenRecoveryUI::SetLocale(const std::string& new_locale) {
1571 locale_ = new_locale;
1572 rtl_locale_ = false;
1573
1574 if (!new_locale.empty()) {
Tao Bao347a6592018-05-08 15:58:29 -07001575 size_t separator = new_locale.find('-');
1576 // lang has the language prefix prior to the separator, or full string if none exists.
1577 std::string lang = new_locale.substr(0, separator);
Tao Baoefb49ad2017-01-31 23:03:10 -08001578
1579 // A bit cheesy: keep an explicit list of supported RTL languages.
1580 if (lang == "ar" || // Arabic
1581 lang == "fa" || // Persian (Farsi)
1582 lang == "he" || // Hebrew (new language code)
1583 lang == "iw" || // Hebrew (old language code)
1584 lang == "ur") { // Urdu
1585 rtl_locale_ = true;
1586 }
1587 }
1588}
fredchiouf2f5aa22022-02-11 16:39:53 +08001589
1590int ScreenRecoveryUI::SetSwCallback(int code, int value) {
1591 if (!is_graphics_available) { return -1; }
1592 if (code > SW_MAX) { return -1; }
1593 if (code != SW_LID) { return 0; }
1594
1595 /* detect dual display */
1596 if (!gr_has_multiple_connectors()) { return -1; }
1597
1598 /* turn off all screen */
1599 gr_fb_blank(true, DirectRenderManager::DRM_INNER);
1600 gr_fb_blank(true, DirectRenderManager::DRM_OUTER);
1601 gr_color(0, 0, 0, 255);
1602 gr_clear();
1603
1604 /* turn on the screen */
1605 gr_fb_blank(false, value);
1606 gr_flip();
1607
1608 /* set the retation */
1609 std::string rotation_str;
1610 if (value == DirectRenderManager::DRM_OUTER) {
1611 rotation_str =
1612 android::base::GetProperty("ro.minui.second_rotation", "ROTATION_NONE");
1613 } else {
1614 rotation_str =
1615 android::base::GetProperty("ro.minui.default_rotation", "ROTATION_NONE");
1616 }
1617
1618 if (rotation_str == "ROTATION_RIGHT") {
1619 gr_rotate(GRRotation::RIGHT);
1620 } else if (rotation_str == "ROTATION_DOWN") {
1621 gr_rotate(GRRotation::DOWN);
1622 } else if (rotation_str == "ROTATION_LEFT") {
1623 gr_rotate(GRRotation::LEFT);
1624 } else { // "ROTATION_NONE" or unknown string
1625 gr_rotate(GRRotation::NONE);
1626 }
1627 Redraw();
1628
1629 return 0;
1630}