blob: 952dd7e5c72395066266b2411bc146362c582d60 [file] [log] [blame]
Doug Zongker211aebc2011-10-28 15:13:10 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Tianjie Xu8f397302018-08-20 13:40:47 -070017#include "recovery_ui/screen_ui.h"
Tao Baoefb49ad2017-01-31 23:03:10 -080018
Elliott Hughes498cda62016-04-14 16:49:04 -070019#include <dirent.h>
Doug Zongker211aebc2011-10-28 15:13:10 -070020#include <errno.h>
21#include <fcntl.h>
Doug Zongker211aebc2011-10-28 15:13:10 -070022#include <stdarg.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/stat.h>
27#include <sys/time.h>
28#include <sys/types.h>
29#include <time.h>
30#include <unistd.h>
31
Tao Bao1fe1afe2018-05-01 15:56:05 -070032#include <algorithm>
Tao Bao26ea9592018-05-09 16:32:02 -070033#include <chrono>
Tianjie Xu29d55752017-09-20 17:53:46 -070034#include <memory>
Tao Bao736d59c2017-01-03 10:15:33 -080035#include <string>
Tao Bao26ea9592018-05-09 16:32:02 -070036#include <thread>
Tianjie Xu29d55752017-09-20 17:53:46 -070037#include <unordered_map>
Elliott Hughes95fc63e2015-04-10 19:12:01 -070038#include <vector>
39
Mark Salyzyn30017e72020-05-13 12:39:12 -070040#include <android-base/chrono_utils.h>
Tianjie Xuc21edd42016-08-05 18:00:04 -070041#include <android-base/logging.h>
Elliott Hughescb220402016-09-23 15:30:55 -070042#include <android-base/properties.h>
Elliott Hughes4b166f02015-12-04 15:30:20 -080043#include <android-base/stringprintf.h>
Tao Baocb5524c2017-09-08 21:25:32 -070044#include <android-base/strings.h>
Tao Baob6918c72015-05-19 17:02:16 -070045
Tao Bao6cd81682018-05-03 21:53:11 -070046#include "minui/minui.h"
47#include "otautil/paths.h"
Tianjie Xu8f397302018-08-20 13:40:47 -070048#include "recovery_ui/device.h"
49#include "recovery_ui/ui.h"
Doug Zongker211aebc2011-10-28 15:13:10 -070050
fredchiouf2f5aa22022-02-11 16:39:53 +080051enum DirectRenderManager {
52 DRM_INNER,
53 DRM_OUTER,
54};
55
Doug Zongker211aebc2011-10-28 15:13:10 -070056// Return the current time as a double (including fractions of a second).
57static double now() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -070058 struct timeval tv;
59 gettimeofday(&tv, nullptr);
60 return tv.tv_sec + tv.tv_usec / 1000000.0;
Doug Zongker211aebc2011-10-28 15:13:10 -070061}
62
Tianjie Xu66dbf632018-10-11 16:54:50 -070063Menu::Menu(size_t initial_selection, const DrawInterface& draw_func)
64 : selection_(initial_selection), draw_funcs_(draw_func) {}
65
Alessandro Astone5a57a942020-03-09 23:17:50 +010066int Menu::selection() const {
Tianjie Xu66dbf632018-10-11 16:54:50 -070067 return selection_;
68}
69
Alessandro Astone5cec9d82020-06-25 19:54:18 +020070TextMenu::TextMenu(bool wrappable, size_t max_length,
Tianjie Xu66dbf632018-10-11 16:54:50 -070071 const std::vector<std::string>& headers, const std::vector<std::string>& items,
72 size_t initial_selection, int char_height, const DrawInterface& draw_funcs)
73 : Menu(initial_selection, draw_funcs),
Alessandro Astone5a57a942020-03-09 23:17:50 +010074 wrappable_(wrappable),
Alessandro Astone5cec9d82020-06-25 19:54:18 +020075 calibrated_height_(false),
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070076 max_item_length_(max_length),
Tao Baoe02a5b22018-05-02 15:46:11 -070077 text_headers_(headers),
Tianjie Xu66dbf632018-10-11 16:54:50 -070078 char_height_(char_height) {
Tao Baoe02a5b22018-05-02 15:46:11 -070079
Alessandro Astone5a57a942020-03-09 23:17:50 +010080 size_t items_count = items.size();
Tao Bao1fe1afe2018-05-01 15:56:05 -070081 for (size_t i = 0; i < items_count; ++i) {
82 text_items_.emplace_back(items[i].substr(0, max_item_length_));
Tao Baoe02a5b22018-05-02 15:46:11 -070083 }
84
85 CHECK(!text_items_.empty());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070086}
87
Tianjie Xu66dbf632018-10-11 16:54:50 -070088const std::vector<std::string>& TextMenu::text_headers() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070089 return text_headers_;
90}
91
Tianjie Xu66dbf632018-10-11 16:54:50 -070092std::string TextMenu::TextItem(size_t index) const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070093 CHECK_LT(index, text_items_.size());
94
95 return text_items_[index];
96}
97
Tianjie Xu66dbf632018-10-11 16:54:50 -070098size_t TextMenu::MenuStart() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070099 return menu_start_;
100}
101
Tianjie Xu66dbf632018-10-11 16:54:50 -0700102size_t TextMenu::MenuEnd() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700103 return std::min(ItemsCount(), menu_start_ + max_display_items_);
104}
105
Tianjie Xu66dbf632018-10-11 16:54:50 -0700106size_t TextMenu::ItemsCount() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700107 return text_items_.size();
108}
109
Tianjie Xu66dbf632018-10-11 16:54:50 -0700110bool TextMenu::ItemsOverflow(std::string* cur_selection_str) const {
Alessandro Astone5a57a942020-03-09 23:17:50 +0100111 if (ItemsCount() <= max_display_items_) {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700112 return false;
113 }
114
115 *cur_selection_str =
Alessandro Astone5a57a942020-03-09 23:17:50 +0100116 android::base::StringPrintf("Current item: %d/%zu", selection_ + 1, ItemsCount());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700117 return true;
118}
119
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700120// TODO(xunchang) modify the function parameters to button up & down.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700121int TextMenu::Select(int sel) {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700122 CHECK_LE(ItemsCount(), static_cast<size_t>(std::numeric_limits<int>::max()));
123 int count = ItemsCount();
124
Alessandro Astone5a57a942020-03-09 23:17:50 +0100125 int min = IsMain() ? 0 : -1; // -1 is back arrow
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700126
Alessandro Astone5a57a942020-03-09 23:17:50 +0100127 if (sel < min) {
128 selection_ = wrappable() ? count - 1 : min;
129 } else if (sel >= count) {
130 selection_ = wrappable() ? min : count - 1;
131 } else {
132 selection_ = sel;
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700133 }
134
Alessandro Astone5a57a942020-03-09 23:17:50 +0100135 if (selection_ >= 0) {
136 if (selection_ < menu_start_) {
137 menu_start_ = selection_;
138 } else if (static_cast<size_t>(selection_) >= MenuEnd()) {
139 menu_start_ = selection_ - max_display_items_ + 1;
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700140 }
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700141 }
142
143 return selection_;
144}
145
Alessandro Astone84042042020-10-04 18:11:40 +0200146int TextMenu::SelectVisible(int relative_sel) {
147 int sel = relative_sel;
148 if (menu_start_ > 0) {
149 sel += menu_start_;
150 }
151
152 return Select(sel);
153}
154
155int TextMenu::Scroll(int updown) {
156 if ((updown > 0 && menu_start_ + max_display_items_ < ItemsCount()) ||
157 (updown < 0 && menu_start_ > 0)) {
158 menu_start_ += updown;
159
160 /* We can receive a kInvokeItem event from a different source than touch,
161 like from Power button. For this reason, selection should not get out of
162 the screen. Constrain it to the first or last visible item of the list */
163 if (selection_ < menu_start_) {
164 selection_ = menu_start_;
165 } else if (selection_ >= menu_start_ + max_display_items_) {
166 selection_ = menu_start_ + max_display_items_ - 1;
167 }
168 }
169 return selection_;
170}
171
Tianjie Xu66dbf632018-10-11 16:54:50 -0700172int TextMenu::DrawHeader(int x, int y) const {
173 int offset = 0;
174
175 draw_funcs_.SetColor(UIElement::HEADER);
Alessandro Astone5a57a942020-03-09 23:17:50 +0100176 offset += draw_funcs_.DrawWrappedTextLines(x, y + offset, text_headers());
Tianjie Xu66dbf632018-10-11 16:54:50 -0700177
178 return offset;
179}
180
181int TextMenu::DrawItems(int x, int y, int screen_width, bool long_press) const {
182 int offset = 0;
Alessandro Astone5a57a942020-03-09 23:17:50 +0100183 int padding = draw_funcs_.MenuItemPadding();
Tianjie Xu66dbf632018-10-11 16:54:50 -0700184
185 draw_funcs_.SetColor(UIElement::MENU);
Alessandro Astone5a57a942020-03-09 23:17:50 +0100186 offset += draw_funcs_.DrawHorizontalRule(y + offset) + 4;
187
188 int item_container_offset = offset; // store it for drawing scrollbar on most top
189
Tianjie Xu66dbf632018-10-11 16:54:50 -0700190 for (size_t i = MenuStart(); i < MenuEnd(); ++i) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700191 if (i == selection()) {
192 // Draw the highlight bar.
193 draw_funcs_.SetColor(long_press ? UIElement::MENU_SEL_BG_ACTIVE : UIElement::MENU_SEL_BG);
194
Alessandro Astone5a57a942020-03-09 23:17:50 +0100195 int bar_height = padding + char_height_ + padding;
196 draw_funcs_.DrawHighlightBar(0, y + offset, screen_width, bar_height);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700197
Jesse Chan968a21a2020-04-28 14:49:13 +0000198 // Colored text for the selected item.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700199 draw_funcs_.SetColor(UIElement::MENU_SEL_FG);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700200 }
Jesse Chan968a21a2020-04-28 14:49:13 +0000201 offset += draw_funcs_.DrawTextLine(x, y + offset, TextItem(i), false /* bold */);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700202
203 draw_funcs_.SetColor(UIElement::MENU);
204 }
205 offset += draw_funcs_.DrawHorizontalRule(y + offset);
206
Alessandro Astone5a57a942020-03-09 23:17:50 +0100207 std::string unused;
208 if (ItemsOverflow(&unused)) {
209 int container_height = max_display_items_ * (2 * padding + char_height_);
210 int bar_height = container_height / (text_items_.size() - max_display_items_ + 1);
211 int start_y = y + item_container_offset + bar_height * menu_start_;
212 draw_funcs_.SetColor(UIElement::SCROLLBAR);
213 draw_funcs_.DrawScrollBar(start_y, bar_height);
214 }
215
Tianjie Xu66dbf632018-10-11 16:54:50 -0700216 return offset;
217}
218
Tao Baoda409fb2018-10-21 23:36:26 -0700219GraphicMenu::GraphicMenu(const GRSurface* graphic_headers,
220 const std::vector<const GRSurface*>& graphic_items,
Tianjie Xub99e6062018-10-16 15:13:09 -0700221 size_t initial_selection, const DrawInterface& draw_funcs)
Tao Baoda409fb2018-10-21 23:36:26 -0700222 : Menu(initial_selection, draw_funcs) {
223 graphic_headers_ = graphic_headers->Clone();
224 graphic_items_.reserve(graphic_items.size());
225 for (const auto& item : graphic_items) {
226 graphic_items_.emplace_back(item->Clone());
227 }
228}
Tianjie Xu66dbf632018-10-11 16:54:50 -0700229
230int GraphicMenu::Select(int sel) {
231 CHECK_LE(graphic_items_.size(), static_cast<size_t>(std::numeric_limits<int>::max()));
232 int count = graphic_items_.size();
233
234 // Wraps the selection at boundary if the menu is not scrollable.
235 if (sel < 0) {
236 selection_ = count - 1;
237 } else if (sel >= count) {
238 selection_ = 0;
239 } else {
240 selection_ = sel;
241 }
242
243 return selection_;
244}
245
246int GraphicMenu::DrawHeader(int x, int y) const {
Tianjie Xub99e6062018-10-16 15:13:09 -0700247 draw_funcs_.SetColor(UIElement::HEADER);
Tao Baoda409fb2018-10-21 23:36:26 -0700248 draw_funcs_.DrawTextIcon(x, y, graphic_headers_.get());
Tianjie Xu66dbf632018-10-11 16:54:50 -0700249 return graphic_headers_->height;
250}
251
252int GraphicMenu::DrawItems(int x, int y, int screen_width, bool long_press) const {
253 int offset = 0;
254
255 draw_funcs_.SetColor(UIElement::MENU);
256 offset += draw_funcs_.DrawHorizontalRule(y + offset) + 4;
257
258 for (size_t i = 0; i < graphic_items_.size(); i++) {
259 auto& item = graphic_items_[i];
260 if (i == selection_) {
261 draw_funcs_.SetColor(long_press ? UIElement::MENU_SEL_BG_ACTIVE : UIElement::MENU_SEL_BG);
262
263 int bar_height = item->height + 4;
264 draw_funcs_.DrawHighlightBar(0, y + offset - 2, screen_width, bar_height);
265
266 // Bold white text for the selected item.
267 draw_funcs_.SetColor(UIElement::MENU_SEL_FG);
268 }
Tao Baoda409fb2018-10-21 23:36:26 -0700269 draw_funcs_.DrawTextIcon(x, y + offset, item.get());
Tianjie Xu66dbf632018-10-11 16:54:50 -0700270 offset += item->height;
271
272 draw_funcs_.SetColor(UIElement::MENU);
273 }
xunchangc7dbc732018-12-20 11:31:18 -0800274 offset += draw_funcs_.DrawHorizontalRule(y + offset);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700275
276 return offset;
277}
278
Alessandro Astone84042042020-10-04 18:11:40 +0200279size_t GraphicMenu::ItemsCount() const {
280 return graphic_items_.size();
281}
282
Tao Baoda409fb2018-10-21 23:36:26 -0700283bool GraphicMenu::Validate(size_t max_width, size_t max_height, const GRSurface* graphic_headers,
284 const std::vector<const GRSurface*>& graphic_items) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700285 int offset = 0;
Tianjie Xub99e6062018-10-16 15:13:09 -0700286 if (!ValidateGraphicSurface(max_width, max_height, offset, graphic_headers)) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700287 return false;
288 }
Tianjie Xub99e6062018-10-16 15:13:09 -0700289 offset += graphic_headers->height;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700290
Tianjie Xub99e6062018-10-16 15:13:09 -0700291 for (const auto& item : graphic_items) {
292 if (!ValidateGraphicSurface(max_width, max_height, offset, item)) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700293 return false;
294 }
295 offset += item->height;
296 }
297
298 return true;
299}
300
Tianjie Xub99e6062018-10-16 15:13:09 -0700301bool GraphicMenu::ValidateGraphicSurface(size_t max_width, size_t max_height, int y,
302 const GRSurface* surface) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700303 if (!surface) {
Tao Baoa00b4492019-01-16 09:29:47 -0800304 fprintf(stderr, "Graphic surface can not be null\n");
Tianjie Xu66dbf632018-10-11 16:54:50 -0700305 return false;
306 }
307
308 if (surface->pixel_bytes != 1 || surface->width != surface->row_bytes) {
Tao Baoa00b4492019-01-16 09:29:47 -0800309 fprintf(stderr, "Invalid graphic surface, pixel bytes: %zu, width: %zu row_bytes: %zu\n",
Tianjie Xu66dbf632018-10-11 16:54:50 -0700310 surface->pixel_bytes, surface->width, surface->row_bytes);
311 return false;
312 }
313
Tianjie Xub99e6062018-10-16 15:13:09 -0700314 if (surface->width > max_width || surface->height > max_height - y) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700315 fprintf(stderr,
Tao Baodd789822018-11-26 16:28:07 -0800316 "Graphic surface doesn't fit into the screen. width: %zu, height: %zu, max_width: %zu,"
Tianjie Xu66dbf632018-10-11 16:54:50 -0700317 " max_height: %zu, vertical offset: %d\n",
Tianjie Xub99e6062018-10-16 15:13:09 -0700318 surface->width, surface->height, max_width, max_height, y);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700319 return false;
320 }
321
322 return true;
323}
324
Alessandro Astone5a57a942020-03-09 23:17:50 +0100325MenuDrawFunctions::MenuDrawFunctions(const DrawInterface& wrappee)
326 : wrappee_(wrappee) {
327}
328
329int MenuDrawFunctions::DrawTextLine(int x, int y, const std::string& line, bool bold) const {
330 gr_text(gr_menu_font(), x, y + MenuItemPadding(), line.c_str(), bold);
331 return 2 * MenuItemPadding() + MenuCharHeight();
332}
333
334int MenuDrawFunctions::DrawTextLines(int x, int y, const std::vector<std::string>& lines) const {
335 int offset = 0;
336 for (const auto& line : lines) {
337 offset += DrawTextLine(x, y + offset, line, false);
338 }
339 return offset;
340}
341
342int MenuDrawFunctions::DrawWrappedTextLines(int x, int y, const std::vector<std::string>& lines) const {
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 Astone5a57a942020-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 Bestas19630862019-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 Chan968a21a2020-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 Hansenc7be4452020-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 Chan968a21a2020-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 Astone5a57a942020-03-09 23:17:50 +0100581 gr_color(0xd8, 0xd8, 0xd8, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700582 break;
Jesse Chan968a21a2020-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 Chan968a21a2020-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 Astone84042042020-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 Astone5a57a942020-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 Astone5a57a942020-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 Hansen5dd4f972020-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 };
787 // clang-format on
788 return HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP;
789}
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_) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700812 int x = margin_width_ + kMenuIndent;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700813
Tianjie Xu66dbf632018-10-11 16:54:50 -0700814 SetColor(UIElement::INFO);
Jerry Zhang0e577ee2018-05-07 11:21:10 -0700815
Alessandro Astone56b46e12020-03-29 15:08:09 +0200816 auto& logo = fastbootd_logo_enabled_ ? fastbootd_logo_ : lineage_logo_;
817 if (logo && back_icon_) {
818 auto logo_width = gr_get_width(logo.get());
819 auto logo_height = gr_get_height(logo.get());
Alessandro Astone5a57a942020-03-09 23:17:50 +0100820 auto centered_x = ScreenWidth() / 2 - logo_width / 2;
Alessandro Astone56b46e12020-03-29 15:08:09 +0200821 DrawSurface(logo.get(), 0, 0, logo_width, logo_height, centered_x, y);
Alessandro Astone5a57a942020-03-09 23:17:50 +0100822 y += logo_height;
Tao Bao171b4c42017-06-19 23:10:44 -0700823
Alessandro Astone5a57a942020-03-09 23:17:50 +0100824 if (!menu_->IsMain()) {
825 auto icon_w = gr_get_width(back_icon_.get());
826 auto icon_h = gr_get_height(back_icon_.get());
827 auto icon_x = centered_x / 2 - icon_w / 2;
828 auto icon_y = y - logo_height / 2 - icon_h / 2;
829 gr_blit(back_icon_sel_ && menu_->selection() == -1 ? back_icon_sel_.get() : back_icon_.get(),
830 0, 0, icon_w, icon_h, icon_x, icon_y);
831 }
Jesse Chan968a21a2020-04-28 14:49:13 +0000832 y += MenuItemPadding();
Alessandro Astone5a57a942020-03-09 23:17:50 +0100833 } else {
834 for (size_t i = 0; i < title_lines_.size(); i++) {
835 y += DrawTextLine(x, y, title_lines_[i], i == 0);
836 }
837 y += DrawTextLines(x, y, help_message);
838 }
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700839
Tianjie Xu66dbf632018-10-11 16:54:50 -0700840 y += menu_->DrawHeader(x, y);
Alessandro Astone84042042020-10-04 18:11:40 +0200841 menu_start_y_ = y + 12; // Skip horizontal rule and some margin
Alessandro Astone5cec9d82020-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());
Tao Bao171b4c42017-06-19 23:10:44 -0700844 }
845
846 // Display from the bottom up, until we hit the top of the screen, the bottom of the menu, or
847 // we've displayed the entire text buffer.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700848 SetColor(UIElement::LOG);
Tao Baocb5524c2017-09-08 21:25:32 -0700849 int row = text_row_;
Tao Bao171b4c42017-06-19 23:10:44 -0700850 size_t count = 0;
Tao Bao0bc88de2018-07-31 14:53:16 -0700851 for (int ty = ScreenHeight() - margin_height_ - char_height_; ty >= y && count < text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700852 ty -= char_height_, ++count) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700853 DrawTextLine(margin_width_, ty, text_[row], false);
Tao Bao171b4c42017-06-19 23:10:44 -0700854 --row;
855 if (row < 0) row = text_rows_ - 1;
856 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700857}
858
859// Redraw everything on the screen and flip the screen (make it visible).
860// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700861void ScreenRecoveryUI::update_screen_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700862 draw_screen_locked();
863 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700864}
865
866// Updates only the progress bar, if possible, otherwise redraws the screen.
867// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700868void ScreenRecoveryUI::update_progress_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700869 if (show_text || !pagesIdentical) {
870 draw_screen_locked(); // Must redraw the whole screen
871 pagesIdentical = true;
872 } else {
873 draw_foreground_locked(); // Draw only the progress bar and overlays
874 }
875 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700876}
877
Elliott Hughes985022a2015-04-13 13:04:32 -0700878void ScreenRecoveryUI::ProgressThreadLoop() {
Tao Bao0bc88de2018-07-31 14:53:16 -0700879 double interval = 1.0 / animation_fps_;
Tao Bao26ea9592018-05-09 16:32:02 -0700880 while (!progress_thread_stopped_) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700881 double start = now();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700882 bool redraw = false;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700883 {
884 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker211aebc2011-10-28 15:13:10 -0700885
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700886 // update the installation animation, if active
887 // skip this if we have a text overlay (too expensive to update)
Tao Baoda409fb2018-10-21 23:36:26 -0700888 if ((current_icon_ == INSTALLING_UPDATE || current_icon_ == ERASING) && !show_text) {
889 if (!intro_done_) {
890 if (current_frame_ == intro_frames_.size() - 1) {
891 intro_done_ = true;
892 current_frame_ = 0;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700893 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700894 ++current_frame_;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700895 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700896 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700897 current_frame_ = (current_frame_ + 1) % loop_frames_.size();
Doug Zongker211aebc2011-10-28 15:13:10 -0700898 }
899
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700900 redraw = true;
901 }
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700902
903 // move the progress bar forward on timed intervals, if configured
904 int duration = progressScopeDuration;
905 if (progressBarType == DETERMINATE && duration > 0) {
906 double elapsed = now() - progressScopeTime;
907 float p = 1.0 * elapsed / duration;
908 if (p > 1.0) p = 1.0;
909 if (p > progress) {
910 progress = p;
911 redraw = true;
912 }
913 }
914
915 if (redraw) update_progress_locked();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700916 }
917
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700918 double end = now();
919 // minimum of 20ms delay between frames
920 double delay = interval - (end - start);
921 if (delay < 0.02) delay = 0.02;
922 usleep(static_cast<useconds_t>(delay * 1000000));
923 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700924}
925
Tao Baoda409fb2018-10-21 23:36:26 -0700926std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadBitmap(const std::string& filename) {
927 GRSurface* surface;
928 if (auto result = res_create_display_surface(filename.c_str(), &surface); result < 0) {
929 LOG(ERROR) << "Failed to load bitmap " << filename << " (error " << result << ")";
930 return nullptr;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700931 }
Tao Baoda409fb2018-10-21 23:36:26 -0700932 return std::unique_ptr<GRSurface>(surface);
Doug Zongkereac881c2014-03-07 09:21:25 -0800933}
934
Tao Baoda409fb2018-10-21 23:36:26 -0700935std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadLocalizedBitmap(const std::string& filename) {
936 GRSurface* surface;
xunchang9d05c8a2019-04-16 12:07:42 -0700937 auto result = res_create_localized_alpha_surface(filename.c_str(), locale_.c_str(), &surface);
938 if (result == 0) {
939 return std::unique_ptr<GRSurface>(surface);
Tao Bao736d59c2017-01-03 10:15:33 -0800940 }
xunchang9d05c8a2019-04-16 12:07:42 -0700941 // TODO(xunchang) create a error code enum to refine the retry condition.
942 LOG(WARNING) << "Failed to load bitmap " << filename << " for locale " << locale_ << " (error "
943 << result << "). Falling back to use default locale.";
944
945 result = res_create_localized_alpha_surface(filename.c_str(), DEFAULT_LOCALE, &surface);
946 if (result == 0) {
947 return std::unique_ptr<GRSurface>(surface);
948 }
949
950 LOG(ERROR) << "Failed to load bitmap " << filename << " for locale " << DEFAULT_LOCALE
951 << " (error " << result << ")";
952 return nullptr;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700953}
954
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700955static char** Alloc2d(size_t rows, size_t cols) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700956 char** result = new char*[rows];
957 for (size_t i = 0; i < rows; ++i) {
958 result[i] = new char[cols];
959 memset(result[i], 0, cols);
960 }
961 return result;
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700962}
963
Tianjie Xu35926c42016-04-28 18:06:26 -0700964// Choose the right background string to display during update.
965void ScreenRecoveryUI::SetSystemUpdateText(bool security_update) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700966 if (security_update) {
Tao Baoda409fb2018-10-21 23:36:26 -0700967 installing_text_ = LoadLocalizedBitmap("installing_security_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700968 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700969 installing_text_ = LoadLocalizedBitmap("installing_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700970 }
971 Redraw();
Tianjie Xu35926c42016-04-28 18:06:26 -0700972}
973
Sen Jiangd5304492016-12-09 16:20:49 -0800974bool ScreenRecoveryUI::InitTextParams() {
Tao Baoba4edb32018-06-13 11:22:50 -0700975 // gr_init() would return successfully on font initialization failure.
976 if (gr_sys_font() == nullptr) {
Tao Bao171b4c42017-06-19 23:10:44 -0700977 return false;
978 }
Tao Bao171b4c42017-06-19 23:10:44 -0700979 gr_font_size(gr_sys_font(), &char_width_, &char_height_);
Alessandro Astone5a57a942020-03-09 23:17:50 +0100980 gr_font_size(gr_menu_font(), &menu_char_width_, &menu_char_height_);
Tao Bao0bc88de2018-07-31 14:53:16 -0700981 text_rows_ = (ScreenHeight() - margin_height_ * 2) / char_height_;
982 text_cols_ = (ScreenWidth() - margin_width_ * 2) / char_width_;
Tao Bao171b4c42017-06-19 23:10:44 -0700983 return true;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -0700984}
985
Tianjie Xub99e6062018-10-16 15:13:09 -0700986bool ScreenRecoveryUI::LoadWipeDataMenuText() {
Tianjie Xu1a0a30a2018-10-25 15:22:07 -0700987 // Ignores the errors since the member variables will stay as nullptr.
988 cancel_wipe_data_text_ = LoadLocalizedBitmap("cancel_wipe_data_text");
989 factory_data_reset_text_ = LoadLocalizedBitmap("factory_data_reset_text");
990 try_again_text_ = LoadLocalizedBitmap("try_again_text");
991 wipe_data_confirmation_text_ = LoadLocalizedBitmap("wipe_data_confirmation_text");
992 wipe_data_menu_header_text_ = LoadLocalizedBitmap("wipe_data_menu_header_text");
Tianjie Xub99e6062018-10-16 15:13:09 -0700993 return true;
994}
995
Mark Salyzyn30017e72020-05-13 12:39:12 -0700996static bool InitGraphics() {
997 // Timeout is same as init wait for file default of 5 seconds and is arbitrary
998 const unsigned timeout = 500; // 10ms increments
999 for (auto retry = timeout; retry > 0; --retry) {
1000 if (gr_init() == 0) {
1001 if (retry < timeout) {
1002 // Log message like init wait for file completion log for consistency.
1003 LOG(WARNING) << "wait for 'graphics' took " << ((timeout - retry) * 10) << "ms";
1004 }
1005 return true;
1006 }
1007 std::this_thread::sleep_for(10ms);
1008 }
1009 // Log message like init wait for file timeout log for consistency.
1010 LOG(ERROR) << "timeout wait for 'graphics' took " << (timeout * 10) << "ms";
1011 return false;
1012}
1013
Tao Bao736d59c2017-01-03 10:15:33 -08001014bool ScreenRecoveryUI::Init(const std::string& locale) {
1015 RecoveryUI::Init(locale);
Tao Baoefb49ad2017-01-31 23:03:10 -08001016
Mark Salyzyn30017e72020-05-13 12:39:12 -07001017 if (!InitGraphics()) {
Tao Baoba4edb32018-06-13 11:22:50 -07001018 return false;
1019 }
fredchiouf2f5aa22022-02-11 16:39:53 +08001020 is_graphics_available = true;
Tao Baoba4edb32018-06-13 11:22:50 -07001021
Tao Bao736d59c2017-01-03 10:15:33 -08001022 if (!InitTextParams()) {
1023 return false;
1024 }
Alessandro Astone5a57a942020-03-09 23:17:50 +01001025 menu_draw_funcs_ = std::make_unique<MenuDrawFunctions>(*this);
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001026
Michael Bestas19630862019-03-23 17:28:22 +02001027 if (blank_unblank_on_init_) {
1028 gr_fb_blank(true);
1029 gr_fb_blank(false);
1030 }
1031
Tao Bao736d59c2017-01-03 10:15:33 -08001032 // Are we portrait or landscape?
1033 layout_ = (gr_fb_width() > gr_fb_height()) ? LANDSCAPE : PORTRAIT;
1034 // Are we the large variant of our base layout?
1035 if (gr_fb_height() > PixelsFromDp(800)) ++layout_;
Elliott Hughesfaf36e02016-04-20 17:22:16 -07001036
Tao Bao736d59c2017-01-03 10:15:33 -08001037 text_ = Alloc2d(text_rows_, text_cols_ + 1);
1038 file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
Doug Zongker55a36ac2013-03-04 15:49:02 -08001039
Tao Bao736d59c2017-01-03 10:15:33 -08001040 text_col_ = text_row_ = 0;
Doug Zongker211aebc2011-10-28 15:13:10 -07001041
Tao Baoefb49ad2017-01-31 23:03:10 -08001042 // Set up the locale info.
1043 SetLocale(locale);
1044
Tao Baoda409fb2018-10-21 23:36:26 -07001045 error_icon_ = LoadBitmap("icon_error");
Doug Zongker02ec6b82012-08-22 17:26:40 -07001046
Tao Baoda409fb2018-10-21 23:36:26 -07001047 progress_bar_empty_ = LoadBitmap("progress_empty");
1048 progress_bar_fill_ = LoadBitmap("progress_fill");
1049 stage_marker_empty_ = LoadBitmap("stage_empty");
1050 stage_marker_fill_ = LoadBitmap("stage_fill");
Elliott Hughes498cda62016-04-14 16:49:04 -07001051
Tao Baoda409fb2018-10-21 23:36:26 -07001052 erasing_text_ = LoadLocalizedBitmap("erasing_text");
1053 no_command_text_ = LoadLocalizedBitmap("no_command_text");
1054 error_text_ = LoadLocalizedBitmap("error_text");
Doug Zongker211aebc2011-10-28 15:13:10 -07001055
Alessandro Astone5a57a942020-03-09 23:17:50 +01001056 back_icon_ = LoadBitmap("ic_back");
1057 back_icon_sel_ = LoadBitmap("ic_back_sel");
Alessandro Astone957d98e2020-02-26 17:25:54 +01001058 if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) ||
1059 android::base::GetBoolProperty("ro.fastbootd.available", false)) {
Alessandro Astone56b46e12020-03-29 15:08:09 +02001060 lineage_logo_ = LoadBitmap("logo_image_switch");
David Anderson983e2d52019-01-02 11:35:38 -08001061 fastbootd_logo_ = LoadBitmap("fastbootd");
Alessandro Astone56b46e12020-03-29 15:08:09 +02001062 } else {
1063 lineage_logo_ = LoadBitmap("logo_image");
David Anderson983e2d52019-01-02 11:35:38 -08001064 }
1065
Tao Baoda409fb2018-10-21 23:36:26 -07001066 // Background text for "installing_update" could be "installing update" or
1067 // "installing security update". It will be set after Init() according to the commands in BCB.
1068 installing_text_.reset();
Elliott Hughes498cda62016-04-14 16:49:04 -07001069
Tianjie Xub99e6062018-10-16 15:13:09 -07001070 LoadWipeDataMenuText();
1071
Tao Bao736d59c2017-01-03 10:15:33 -08001072 LoadAnimation();
Doug Zongker02ec6b82012-08-22 17:26:40 -07001073
Tao Bao26ea9592018-05-09 16:32:02 -07001074 // Keep the progress bar updated, even when the process is otherwise busy.
1075 progress_thread_ = std::thread(&ScreenRecoveryUI::ProgressThreadLoop, this);
Sen Jiangd5304492016-12-09 16:20:49 -08001076
fredchiouf2f5aa22022-02-11 16:39:53 +08001077 // set the callback for hall sensor event
1078 (void)ev_sync_sw_state([this](auto&& a, auto&& b) { return this->SetSwCallback(a, b);});
1079
Tao Bao736d59c2017-01-03 10:15:33 -08001080 return true;
Doug Zongker211aebc2011-10-28 15:13:10 -07001081}
1082
Tao Bao551d2c32018-05-09 20:53:13 -07001083std::string ScreenRecoveryUI::GetLocale() const {
Jerry Zhang2dea53e2018-05-02 17:15:03 -07001084 return locale_;
1085}
1086
Elliott Hughes498cda62016-04-14 16:49:04 -07001087void ScreenRecoveryUI::LoadAnimation() {
Tao Bao6cd81682018-05-03 21:53:11 -07001088 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(Paths::Get().resource_dir().c_str()),
1089 closedir);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001090 dirent* de;
1091 std::vector<std::string> intro_frame_names;
Joshua Lambert94a83be2019-12-09 20:24:18 +00001092 std::vector<std::string> loop_frame_names;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001093
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001094 while ((de = readdir(dir.get())) != nullptr) {
1095 int value, num_chars;
1096 if (sscanf(de->d_name, "intro%d%n.png", &value, &num_chars) == 1) {
1097 intro_frame_names.emplace_back(de->d_name, num_chars);
1098 } else if (sscanf(de->d_name, "loop%d%n.png", &value, &num_chars) == 1) {
Joshua Lambert94a83be2019-12-09 20:24:18 +00001099 loop_frame_names.emplace_back(de->d_name, num_chars);
Elliott Hughes498cda62016-04-14 16:49:04 -07001100 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001101 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001102
Tao Baoda409fb2018-10-21 23:36:26 -07001103 size_t intro_frames = intro_frame_names.size();
1104 size_t loop_frames = loop_frame_names.size();
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001105
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001106 // It's okay to not have an intro.
Tao Baoda409fb2018-10-21 23:36:26 -07001107 if (intro_frames == 0) intro_done_ = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001108 // But you must have an animation.
1109 if (loop_frames == 0) abort();
Elliott Hughes498cda62016-04-14 16:49:04 -07001110
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001111 std::sort(intro_frame_names.begin(), intro_frame_names.end());
1112 std::sort(loop_frame_names.begin(), loop_frame_names.end());
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001113
Tao Baoda409fb2018-10-21 23:36:26 -07001114 intro_frames_.clear();
1115 intro_frames_.reserve(intro_frames);
1116 for (const auto& frame_name : intro_frame_names) {
1117 intro_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001118 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001119
Tao Baoda409fb2018-10-21 23:36:26 -07001120 loop_frames_.clear();
1121 loop_frames_.reserve(loop_frames);
1122 for (const auto& frame_name : loop_frame_names) {
1123 loop_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001124 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001125}
1126
Elliott Hughes8de52072015-04-08 20:06:50 -07001127void ScreenRecoveryUI::SetBackground(Icon icon) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001128 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker02ec6b82012-08-22 17:26:40 -07001129
Tao Baoda409fb2018-10-21 23:36:26 -07001130 current_icon_ = icon;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001131 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001132}
1133
Elliott Hughes8de52072015-04-08 20:06:50 -07001134void ScreenRecoveryUI::SetProgressType(ProgressType type) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001135 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001136 if (progressBarType != type) {
1137 progressBarType = type;
1138 }
1139 progressScopeStart = 0;
1140 progressScopeSize = 0;
1141 progress = 0;
1142 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001143}
1144
Elliott Hughes8de52072015-04-08 20:06:50 -07001145void ScreenRecoveryUI::ShowProgress(float portion, float seconds) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001146 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001147 progressBarType = DETERMINATE;
1148 progressScopeStart += progressScopeSize;
1149 progressScopeSize = portion;
1150 progressScopeTime = now();
1151 progressScopeDuration = seconds;
1152 progress = 0;
1153 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001154}
1155
Elliott Hughes8de52072015-04-08 20:06:50 -07001156void ScreenRecoveryUI::SetProgress(float fraction) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001157 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001158 if (fraction < 0.0) fraction = 0.0;
1159 if (fraction > 1.0) fraction = 1.0;
1160 if (progressBarType == DETERMINATE && fraction > progress) {
1161 // Skip updates that aren't visibly different.
Tao Baoda409fb2018-10-21 23:36:26 -07001162 int width = gr_get_width(progress_bar_empty_.get());
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001163 float scale = width * progressScopeSize;
1164 if ((int)(progress * scale) != (int)(fraction * scale)) {
1165 progress = fraction;
1166 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001167 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001168 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001169}
1170
Doug Zongkerc87bab12013-11-25 13:53:25 -08001171void ScreenRecoveryUI::SetStage(int current, int max) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001172 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001173 stage = current;
1174 max_stage = max;
Doug Zongkerc87bab12013-11-25 13:53:25 -08001175}
1176
Tao Baob6918c72015-05-19 17:02:16 -07001177void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001178 std::string str;
1179 android::base::StringAppendV(&str, fmt, ap);
Doug Zongker211aebc2011-10-28 15:13:10 -07001180
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001181 if (copy_to_stdout) {
1182 fputs(str.c_str(), stdout);
1183 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001184
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001185 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001186 if (text_rows_ > 0 && text_cols_ > 0) {
1187 for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
1188 if (*ptr == '\n' || text_col_ >= text_cols_) {
Elliott Hughesc0491632015-05-06 12:40:05 -07001189 text_[text_row_][text_col_] = '\0';
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001190 text_col_ = 0;
1191 text_row_ = (text_row_ + 1) % text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001192 }
1193 if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
Doug Zongker211aebc2011-10-28 15:13:10 -07001194 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001195 text_[text_row_][text_col_] = '\0';
1196 update_screen_locked();
1197 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001198}
1199
Tao Baob6918c72015-05-19 17:02:16 -07001200void ScreenRecoveryUI::Print(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001201 va_list ap;
1202 va_start(ap, fmt);
1203 PrintV(fmt, true, ap);
1204 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001205}
1206
Tianjie Xu8f397302018-08-20 13:40:47 -07001207void ScreenRecoveryUI::PrintOnScreenOnly(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001208 va_list ap;
1209 va_start(ap, fmt);
1210 PrintV(fmt, false, ap);
1211 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001212}
1213
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001214void ScreenRecoveryUI::PutChar(char ch) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001215 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001216 if (ch != '\n') text_[text_row_][text_col_++] = ch;
1217 if (ch == '\n' || text_col_ >= text_cols_) {
1218 text_col_ = 0;
1219 ++text_row_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001220 }
Elliott Hughes8de52072015-04-08 20:06:50 -07001221}
1222
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001223void ScreenRecoveryUI::ClearText() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001224 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001225 text_col_ = 0;
1226 text_row_ = 0;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001227 for (size_t i = 0; i < text_rows_; ++i) {
1228 memset(text_[i], 0, text_cols_ + 1);
1229 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001230}
1231
1232void ScreenRecoveryUI::ShowFile(FILE* fp) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001233 std::vector<off_t> offsets;
1234 offsets.push_back(ftello(fp));
1235 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001236
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001237 struct stat sb;
1238 fstat(fileno(fp), &sb);
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001239
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001240 bool show_prompt = false;
1241 while (true) {
1242 if (show_prompt) {
1243 PrintOnScreenOnly("--(%d%% of %d bytes)--",
1244 static_cast<int>(100 * (double(ftello(fp)) / double(sb.st_size))),
1245 static_cast<int>(sb.st_size));
1246 Redraw();
1247 while (show_prompt) {
1248 show_prompt = false;
Alessandro Astone84042042020-10-04 18:11:40 +02001249 InputEvent evt = WaitInputEvent();
1250 if (evt.type() == EventType::EXTRA) {
1251 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) {
1252 return;
1253 }
1254 }
1255 if (evt.type() != EventType::KEY) {
1256 show_prompt = true;
1257 continue;
1258 }
1259 if (evt.key() == KEY_POWER || evt.key() == KEY_ENTER || evt.key() == KEY_BACKSPACE ||
1260 evt.key() == KEY_BACK || evt.key() == KEY_HOME || evt.key() == KEY_HOMEPAGE) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001261 return;
Alessandro Astone84042042020-10-04 18:11:40 +02001262 } else if (evt.key() == KEY_UP || evt.key() == KEY_VOLUMEUP || evt.key() == KEY_SCROLLUP) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001263 if (offsets.size() <= 1) {
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001264 show_prompt = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001265 } else {
1266 offsets.pop_back();
1267 fseek(fp, offsets.back(), SEEK_SET);
1268 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001269 } else {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001270 if (feof(fp)) {
1271 return;
1272 }
1273 offsets.push_back(ftello(fp));
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001274 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001275 }
1276 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001277 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001278
1279 int ch = getc(fp);
1280 if (ch == EOF) {
1281 while (text_row_ < text_rows_ - 1) PutChar('\n');
1282 show_prompt = true;
1283 } else {
1284 PutChar(ch);
1285 if (text_col_ == 0 && text_row_ >= text_rows_ - 1) {
1286 show_prompt = true;
1287 }
1288 }
1289 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001290}
1291
Tao Bao1d156b92018-05-02 12:43:18 -07001292void ScreenRecoveryUI::ShowFile(const std::string& filename) {
1293 std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(filename.c_str(), "re"), fclose);
1294 if (!fp) {
1295 Print(" Unable to open %s: %s\n", filename.c_str(), strerror(errno));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001296 return;
1297 }
Elliott Hughesc0491632015-05-06 12:40:05 -07001298
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001299 char** old_text = text_;
1300 size_t old_text_col = text_col_;
1301 size_t old_text_row = text_row_;
Elliott Hughesc0491632015-05-06 12:40:05 -07001302
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001303 // Swap in the alternate screen and clear it.
1304 text_ = file_viewer_text_;
1305 ClearText();
Elliott Hughesc0491632015-05-06 12:40:05 -07001306
Tao Bao1d156b92018-05-02 12:43:18 -07001307 ShowFile(fp.get());
Elliott Hughesc0491632015-05-06 12:40:05 -07001308
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001309 text_ = old_text;
1310 text_col_ = old_text_col;
1311 text_row_ = old_text_row;
Elliott Hughes8de52072015-04-08 20:06:50 -07001312}
1313
Tao Baoda409fb2018-10-21 23:36:26 -07001314std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(
1315 const GRSurface* graphic_header, const std::vector<const GRSurface*>& graphic_items,
1316 const std::vector<std::string>& text_headers, const std::vector<std::string>& text_items,
1317 size_t initial_selection) const {
Tianjie Xub99e6062018-10-16 15:13:09 -07001318 // horizontal unusable area: margin width + menu indent
1319 size_t max_width = ScreenWidth() - margin_width_ - kMenuIndent;
1320 // vertical unusable area: margin height + title lines + helper message + high light bar.
1321 // It is safe to reserve more space.
1322 size_t max_height = ScreenHeight() - margin_height_ - char_height_ * (title_lines_.size() + 3);
1323 if (GraphicMenu::Validate(max_width, max_height, graphic_header, graphic_items)) {
1324 return std::make_unique<GraphicMenu>(graphic_header, graphic_items, initial_selection, *this);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001325 }
Tianjie Xub99e6062018-10-16 15:13:09 -07001326
1327 fprintf(stderr, "Failed to initialize graphic menu, falling back to use the text menu.\n");
1328
1329 return CreateMenu(text_headers, text_items, initial_selection);
1330}
1331
1332std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(const std::vector<std::string>& text_headers,
1333 const std::vector<std::string>& text_items,
1334 size_t initial_selection) const {
Alessandro Astone5a57a942020-03-09 23:17:50 +01001335 int menu_char_width = MenuCharWidth();
1336 int menu_char_height = MenuCharHeight();
Alessandro Astone5a57a942020-03-09 23:17:50 +01001337 int menu_cols = (ScreenWidth() - margin_width_*2 - kMenuIndent) / menu_char_width;
1338 bool wrap_selection = !HasThreeButtons() && !HasTouchScreen();
Alessandro Astone5cec9d82020-06-25 19:54:18 +02001339 return std::make_unique<TextMenu>(wrap_selection, menu_cols, text_headers, text_items,
Alessandro Astone5a57a942020-03-09 23:17:50 +01001340 initial_selection, menu_char_height, *menu_draw_funcs_);
Doug Zongker211aebc2011-10-28 15:13:10 -07001341}
1342
1343int ScreenRecoveryUI::SelectMenu(int sel) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001344 std::lock_guard<std::mutex> lg(updateMutex);
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001345 if (menu_) {
1346 int old_sel = menu_->selection();
1347 sel = menu_->Select(sel);
Elliott Hughesfc06f872015-03-23 13:45:31 -07001348
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001349 if (sel != old_sel) {
1350 update_screen_locked();
1351 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001352 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001353 return sel;
Doug Zongker211aebc2011-10-28 15:13:10 -07001354}
1355
Alessandro Astone84042042020-10-04 18:11:40 +02001356int ScreenRecoveryUI::SelectMenu(const Point& point) {
1357 int new_sel = Device::kNoAction;
1358 std::lock_guard<std::mutex> lg(updateMutex);
1359 if (menu_) {
1360 if (!menu_->IsMain()) {
1361 // Back arrow hitbox
1362 const static int logo_width = gr_get_width(lineage_logo_.get());
1363 const static int logo_height = gr_get_height(lineage_logo_.get());
1364 const static int icon_w = gr_get_width(back_icon_.get());
1365 const static int icon_h = gr_get_height(back_icon_.get());
1366 const static int centered_x = ScreenWidth() / 2 - logo_width / 2;
1367 const static int icon_x = centered_x / 2 - icon_w / 2;
1368 const static int icon_y = margin_height_ + logo_height / 2 - icon_h / 2;
1369
1370 if (point.x() >= icon_x && point.x() <= icon_x + icon_w &&
1371 point.y() >= icon_y && point.y() <= icon_y + icon_h) {
1372 return Device::kGoBack;
1373 }
1374 }
1375
1376 if (point.y() >= menu_start_y_ &&
1377 point.y() < menu_start_y_ + menu_->ItemsCount() * MenuItemHeight()) {
1378 int old_sel = menu_->selection();
1379 int relative_sel = (point.y() - menu_start_y_) / MenuItemHeight();
1380 new_sel = menu_->SelectVisible(relative_sel);
1381 if (new_sel != -1 && new_sel != old_sel) {
1382 update_screen_locked();
1383 }
1384 }
1385 }
1386 return new_sel;
1387}
1388
1389int ScreenRecoveryUI::ScrollMenu(int updown) {
1390 std::lock_guard<std::mutex> lg(updateMutex);
1391 int sel = Device::kNoAction;
1392 if (menu_) {
1393 sel = menu_->Scroll(updown);
1394 update_screen_locked();
1395 }
1396 return sel;
1397}
1398
Tianjie Xub99e6062018-10-16 15:13:09 -07001399size_t ScreenRecoveryUI::ShowMenu(std::unique_ptr<Menu>&& menu, bool menu_only,
Tao Bao1fe1afe2018-05-01 15:56:05 -07001400 const std::function<int(int, bool)>& key_handler) {
Tao Bao3aec6962018-04-20 09:24:58 -07001401 // Throw away keys pressed previously, so user doesn't accidentally trigger menu items.
1402 FlushKeys();
1403
Jerry Zhangb76af932018-05-22 12:08:35 -07001404 // If there is a key interrupt in progress, return KeyError::INTERRUPTED without starting the
1405 // menu.
1406 if (IsKeyInterrupted()) return static_cast<size_t>(KeyError::INTERRUPTED);
1407
Tianjie Xub99e6062018-10-16 15:13:09 -07001408 CHECK(menu != nullptr);
Tao Bao3aec6962018-04-20 09:24:58 -07001409
Tianjie Xub99e6062018-10-16 15:13:09 -07001410 // Starts and displays the menu
1411 menu_ = std::move(menu);
1412 Redraw();
1413
1414 int selected = menu_->selection();
Tao Bao3aec6962018-04-20 09:24:58 -07001415 int chosen_item = -1;
1416 while (chosen_item < 0) {
Alessandro Astone84042042020-10-04 18:11:40 +02001417 InputEvent evt = WaitInputEvent();
1418 if (evt.type() == EventType::EXTRA) {
1419 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) {
1420 // WaitKey() was interrupted.
1421 return static_cast<size_t>(KeyError::INTERRUPTED);
1422 }
1423 if (evt.key() == static_cast<int>(KeyError::TIMED_OUT)) { // WaitKey() timed out.
1424 if (WasTextEverVisible()) {
1425 continue;
1426 } else {
1427 LOG(INFO) << "Timed out waiting for key input; rebooting.";
1428 menu_.reset();
1429 Redraw();
1430 return static_cast<size_t>(KeyError::TIMED_OUT);
1431 }
Tao Bao3aec6962018-04-20 09:24:58 -07001432 }
1433 }
1434
Alessandro Astone84042042020-10-04 18:11:40 +02001435 int action = Device::kNoAction;
1436 if (evt.type() == EventType::TOUCH) {
1437 int touch_sel = SelectMenu(evt.pos());
1438 if (touch_sel < 0) {
1439 action = touch_sel;
1440 } else {
1441 action = Device::kInvokeItem;
1442 selected = touch_sel;
1443 }
1444 } else {
1445 bool visible = IsTextVisible();
1446 action = key_handler(evt.key(), visible);
1447 }
Tao Bao3aec6962018-04-20 09:24:58 -07001448 if (action < 0) {
1449 switch (action) {
1450 case Device::kHighlightUp:
1451 selected = SelectMenu(--selected);
1452 break;
1453 case Device::kHighlightDown:
1454 selected = SelectMenu(++selected);
1455 break;
Alessandro Astone84042042020-10-04 18:11:40 +02001456 case Device::kScrollUp:
1457 selected = ScrollMenu(-1);
1458 break;
1459 case Device::kScrollDown:
1460 selected = ScrollMenu(1);
1461 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001462 case Device::kInvokeItem:
Alessandro Astone5a57a942020-03-09 23:17:50 +01001463 if (selected < 0) {
1464 chosen_item = Device::kGoBack;
1465 } else {
1466 chosen_item = selected;
1467 }
Tao Bao3aec6962018-04-20 09:24:58 -07001468 break;
1469 case Device::kNoAction:
1470 break;
Tom Marshallf20b3e32017-08-24 13:50:01 +00001471 case Device::kGoBack:
1472 chosen_item = Device::kGoBack;
1473 break;
1474 case Device::kGoHome:
1475 chosen_item = Device::kGoHome;
1476 break;
Tom Marshall9f2f3282018-12-17 15:57:44 -08001477 case Device::kDoSideload:
1478 chosen_item = Device::kDoSideload;
1479 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001480 }
1481 } else if (!menu_only) {
1482 chosen_item = action;
1483 }
Tom Marshall9f2f3282018-12-17 15:57:44 -08001484
1485 if (chosen_item == Device::kGoBack || chosen_item == Device::kGoHome ||
1486 chosen_item == Device::kDoSideload) {
Tom Marshallf20b3e32017-08-24 13:50:01 +00001487 break;
1488 }
Tao Bao3aec6962018-04-20 09:24:58 -07001489 }
1490
Tianjie Xub99e6062018-10-16 15:13:09 -07001491 menu_.reset();
Tianjie Xub99e6062018-10-16 15:13:09 -07001492
Tao Bao3aec6962018-04-20 09:24:58 -07001493 return chosen_item;
1494}
1495
Tianjie Xub99e6062018-10-16 15:13:09 -07001496size_t ScreenRecoveryUI::ShowMenu(const std::vector<std::string>& headers,
1497 const std::vector<std::string>& items, size_t initial_selection,
1498 bool menu_only,
1499 const std::function<int(int, bool)>& key_handler) {
1500 auto menu = CreateMenu(headers, items, initial_selection);
1501 if (menu == nullptr) {
1502 return initial_selection;
1503 }
1504
Tao Baofa8e02a2019-06-27 09:07:04 -07001505 return ShowMenu(std::move(menu), menu_only, key_handler);
Tianjie Xub99e6062018-10-16 15:13:09 -07001506}
1507
1508size_t ScreenRecoveryUI::ShowPromptWipeDataMenu(const std::vector<std::string>& backup_headers,
1509 const std::vector<std::string>& backup_items,
1510 const std::function<int(int, bool)>& key_handler) {
Tao Baoda409fb2018-10-21 23:36:26 -07001511 auto wipe_data_menu = CreateMenu(wipe_data_menu_header_text_.get(),
1512 { try_again_text_.get(), factory_data_reset_text_.get() },
1513 backup_headers, backup_items, 0);
Tianjie Xub99e6062018-10-16 15:13:09 -07001514 if (wipe_data_menu == nullptr) {
1515 return 0;
1516 }
1517
1518 return ShowMenu(std::move(wipe_data_menu), true, key_handler);
1519}
1520
Tianjie Xu1a0a30a2018-10-25 15:22:07 -07001521size_t ScreenRecoveryUI::ShowPromptWipeDataConfirmationMenu(
1522 const std::vector<std::string>& backup_headers, const std::vector<std::string>& backup_items,
1523 const std::function<int(int, bool)>& key_handler) {
1524 auto confirmation_menu =
1525 CreateMenu(wipe_data_confirmation_text_.get(),
1526 { cancel_wipe_data_text_.get(), factory_data_reset_text_.get() }, backup_headers,
1527 backup_items, 0);
1528 if (confirmation_menu == nullptr) {
1529 return 0;
1530 }
1531
1532 return ShowMenu(std::move(confirmation_menu), true, key_handler);
1533}
1534
Elliott Hughes8de52072015-04-08 20:06:50 -07001535bool ScreenRecoveryUI::IsTextVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001536 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001537 int visible = show_text;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001538 return visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001539}
1540
Elliott Hughes8de52072015-04-08 20:06:50 -07001541bool ScreenRecoveryUI::WasTextEverVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001542 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001543 int ever_visible = show_text_ever;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001544 return ever_visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001545}
1546
Elliott Hughes8de52072015-04-08 20:06:50 -07001547void ScreenRecoveryUI::ShowText(bool visible) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001548 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001549 show_text = visible;
1550 if (show_text) show_text_ever = true;
1551 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001552}
Doug Zongkerc0441d12013-07-31 11:28:24 -07001553
Elliott Hughes8de52072015-04-08 20:06:50 -07001554void ScreenRecoveryUI::Redraw() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001555 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001556 update_screen_locked();
Doug Zongkerc0441d12013-07-31 11:28:24 -07001557}
Elliott Hughes642aaa72015-04-10 12:47:46 -07001558
1559void ScreenRecoveryUI::KeyLongPress(int) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001560 // Redraw so that if we're in the menu, the highlight
1561 // will change color to indicate a successful long press.
1562 Redraw();
Elliott Hughes642aaa72015-04-10 12:47:46 -07001563}
Tao Baoefb49ad2017-01-31 23:03:10 -08001564
1565void ScreenRecoveryUI::SetLocale(const std::string& new_locale) {
1566 locale_ = new_locale;
1567 rtl_locale_ = false;
1568
1569 if (!new_locale.empty()) {
Tao Bao347a6592018-05-08 15:58:29 -07001570 size_t separator = new_locale.find('-');
1571 // lang has the language prefix prior to the separator, or full string if none exists.
1572 std::string lang = new_locale.substr(0, separator);
Tao Baoefb49ad2017-01-31 23:03:10 -08001573
1574 // A bit cheesy: keep an explicit list of supported RTL languages.
1575 if (lang == "ar" || // Arabic
1576 lang == "fa" || // Persian (Farsi)
1577 lang == "he" || // Hebrew (new language code)
1578 lang == "iw" || // Hebrew (old language code)
1579 lang == "ur") { // Urdu
1580 rtl_locale_ = true;
1581 }
1582 }
1583}
fredchiouf2f5aa22022-02-11 16:39:53 +08001584
1585int ScreenRecoveryUI::SetSwCallback(int code, int value) {
1586 if (!is_graphics_available) { return -1; }
1587 if (code > SW_MAX) { return -1; }
1588 if (code != SW_LID) { return 0; }
1589
1590 /* detect dual display */
1591 if (!gr_has_multiple_connectors()) { return -1; }
1592
1593 /* turn off all screen */
1594 gr_fb_blank(true, DirectRenderManager::DRM_INNER);
1595 gr_fb_blank(true, DirectRenderManager::DRM_OUTER);
1596 gr_color(0, 0, 0, 255);
1597 gr_clear();
1598
1599 /* turn on the screen */
1600 gr_fb_blank(false, value);
1601 gr_flip();
1602
1603 /* set the retation */
1604 std::string rotation_str;
1605 if (value == DirectRenderManager::DRM_OUTER) {
1606 rotation_str =
1607 android::base::GetProperty("ro.minui.second_rotation", "ROTATION_NONE");
1608 } else {
1609 rotation_str =
1610 android::base::GetProperty("ro.minui.default_rotation", "ROTATION_NONE");
1611 }
1612
1613 if (rotation_str == "ROTATION_RIGHT") {
1614 gr_rotate(GRRotation::RIGHT);
1615 } else if (rotation_str == "ROTATION_DOWN") {
1616 gr_rotate(GRRotation::DOWN);
1617 } else if (rotation_str == "ROTATION_LEFT") {
1618 gr_rotate(GRRotation::LEFT);
1619 } else { // "ROTATION_NONE" or unknown string
1620 gr_rotate(GRRotation::NONE);
1621 }
1622 Redraw();
1623
1624 return 0;
1625}