blob: ed6ae19c6e4463c08c3ae653ab778350ed580a5e [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{
780 "Use volume up/down and power.",
781 };
782 static std::vector<std::string> LONG_PRESS_HELP{
783 "Any button cycles highlight.",
784 "Long-press activates.",
785 };
786 // clang-format on
787 return HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP;
788}
789
Tao Bao171b4c42017-06-19 23:10:44 -0700790// Redraws everything on the screen. Does not flip pages. Should only be called with updateMutex
791// locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700792void ScreenRecoveryUI::draw_screen_locked() {
Tao Bao171b4c42017-06-19 23:10:44 -0700793 if (!show_text) {
794 draw_background_locked();
795 draw_foreground_locked();
796 return;
797 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700798
Tao Bao171b4c42017-06-19 23:10:44 -0700799 gr_color(0, 0, 0, 255);
800 gr_clear();
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700801
Tianjie Xue5032212019-07-23 13:23:29 -0700802 draw_menu_and_text_buffer_locked(GetMenuHelpMessage());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700803}
804
805// Draws the menu and text buffer on the screen. Should only be called with updateMutex locked.
Tao Bao93e46ad2018-05-02 14:57:21 -0700806void ScreenRecoveryUI::draw_menu_and_text_buffer_locked(
807 const std::vector<std::string>& help_message) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700808 int y = margin_height_;
David Anderson983e2d52019-01-02 11:35:38 -0800809
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700810 if (menu_) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700811 int x = margin_width_ + kMenuIndent;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700812
Tianjie Xu66dbf632018-10-11 16:54:50 -0700813 SetColor(UIElement::INFO);
Jerry Zhang0e577ee2018-05-07 11:21:10 -0700814
Alessandro Astone56b46e12020-03-29 15:08:09 +0200815 auto& logo = fastbootd_logo_enabled_ ? fastbootd_logo_ : lineage_logo_;
816 if (logo && back_icon_) {
817 auto logo_width = gr_get_width(logo.get());
818 auto logo_height = gr_get_height(logo.get());
Alessandro Astone5a57a942020-03-09 23:17:50 +0100819 auto centered_x = ScreenWidth() / 2 - logo_width / 2;
Alessandro Astone56b46e12020-03-29 15:08:09 +0200820 DrawSurface(logo.get(), 0, 0, logo_width, logo_height, centered_x, y);
Alessandro Astone5a57a942020-03-09 23:17:50 +0100821 y += logo_height;
Tao Bao171b4c42017-06-19 23:10:44 -0700822
Alessandro Astone5a57a942020-03-09 23:17:50 +0100823 if (!menu_->IsMain()) {
824 auto icon_w = gr_get_width(back_icon_.get());
825 auto icon_h = gr_get_height(back_icon_.get());
826 auto icon_x = centered_x / 2 - icon_w / 2;
827 auto icon_y = y - logo_height / 2 - icon_h / 2;
828 gr_blit(back_icon_sel_ && menu_->selection() == -1 ? back_icon_sel_.get() : back_icon_.get(),
829 0, 0, icon_w, icon_h, icon_x, icon_y);
830 }
Jesse Chan968a21a2020-04-28 14:49:13 +0000831 y += MenuItemPadding();
Alessandro Astone5a57a942020-03-09 23:17:50 +0100832 } else {
833 for (size_t i = 0; i < title_lines_.size(); i++) {
834 y += DrawTextLine(x, y, title_lines_[i], i == 0);
835 }
836 y += DrawTextLines(x, y, help_message);
837 }
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700838
Tianjie Xu66dbf632018-10-11 16:54:50 -0700839 y += menu_->DrawHeader(x, y);
Alessandro Astone84042042020-10-04 18:11:40 +0200840 menu_start_y_ = y + 12; // Skip horizontal rule and some margin
Alessandro Astone5cec9d82020-06-25 19:54:18 +0200841 menu_->SetMenuHeight(std::max(0, ScreenHeight() - menu_start_y_));
Tianjie Xu66dbf632018-10-11 16:54:50 -0700842 y += menu_->DrawItems(x, y, ScreenWidth(), IsLongPress());
Tao Bao171b4c42017-06-19 23:10:44 -0700843 }
844
845 // Display from the bottom up, until we hit the top of the screen, the bottom of the menu, or
846 // we've displayed the entire text buffer.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700847 SetColor(UIElement::LOG);
Tao Baocb5524c2017-09-08 21:25:32 -0700848 int row = text_row_;
Tao Bao171b4c42017-06-19 23:10:44 -0700849 size_t count = 0;
Tao Bao0bc88de2018-07-31 14:53:16 -0700850 for (int ty = ScreenHeight() - margin_height_ - char_height_; ty >= y && count < text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700851 ty -= char_height_, ++count) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700852 DrawTextLine(margin_width_, ty, text_[row], false);
Tao Bao171b4c42017-06-19 23:10:44 -0700853 --row;
854 if (row < 0) row = text_rows_ - 1;
855 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700856}
857
858// Redraw everything on the screen and flip the screen (make it visible).
859// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700860void ScreenRecoveryUI::update_screen_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700861 draw_screen_locked();
862 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700863}
864
865// Updates only the progress bar, if possible, otherwise redraws the screen.
866// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700867void ScreenRecoveryUI::update_progress_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700868 if (show_text || !pagesIdentical) {
869 draw_screen_locked(); // Must redraw the whole screen
870 pagesIdentical = true;
871 } else {
872 draw_foreground_locked(); // Draw only the progress bar and overlays
873 }
874 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700875}
876
Elliott Hughes985022a2015-04-13 13:04:32 -0700877void ScreenRecoveryUI::ProgressThreadLoop() {
Tao Bao0bc88de2018-07-31 14:53:16 -0700878 double interval = 1.0 / animation_fps_;
Tao Bao26ea9592018-05-09 16:32:02 -0700879 while (!progress_thread_stopped_) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700880 double start = now();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700881 bool redraw = false;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700882 {
883 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker211aebc2011-10-28 15:13:10 -0700884
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700885 // update the installation animation, if active
886 // skip this if we have a text overlay (too expensive to update)
Tao Baoda409fb2018-10-21 23:36:26 -0700887 if ((current_icon_ == INSTALLING_UPDATE || current_icon_ == ERASING) && !show_text) {
888 if (!intro_done_) {
889 if (current_frame_ == intro_frames_.size() - 1) {
890 intro_done_ = true;
891 current_frame_ = 0;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700892 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700893 ++current_frame_;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700894 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700895 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700896 current_frame_ = (current_frame_ + 1) % loop_frames_.size();
Doug Zongker211aebc2011-10-28 15:13:10 -0700897 }
898
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700899 redraw = true;
900 }
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700901
902 // move the progress bar forward on timed intervals, if configured
903 int duration = progressScopeDuration;
904 if (progressBarType == DETERMINATE && duration > 0) {
905 double elapsed = now() - progressScopeTime;
906 float p = 1.0 * elapsed / duration;
907 if (p > 1.0) p = 1.0;
908 if (p > progress) {
909 progress = p;
910 redraw = true;
911 }
912 }
913
914 if (redraw) update_progress_locked();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700915 }
916
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700917 double end = now();
918 // minimum of 20ms delay between frames
919 double delay = interval - (end - start);
920 if (delay < 0.02) delay = 0.02;
921 usleep(static_cast<useconds_t>(delay * 1000000));
922 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700923}
924
Tao Baoda409fb2018-10-21 23:36:26 -0700925std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadBitmap(const std::string& filename) {
926 GRSurface* surface;
927 if (auto result = res_create_display_surface(filename.c_str(), &surface); result < 0) {
928 LOG(ERROR) << "Failed to load bitmap " << filename << " (error " << result << ")";
929 return nullptr;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700930 }
Tao Baoda409fb2018-10-21 23:36:26 -0700931 return std::unique_ptr<GRSurface>(surface);
Doug Zongkereac881c2014-03-07 09:21:25 -0800932}
933
Tao Baoda409fb2018-10-21 23:36:26 -0700934std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadLocalizedBitmap(const std::string& filename) {
935 GRSurface* surface;
xunchang9d05c8a2019-04-16 12:07:42 -0700936 auto result = res_create_localized_alpha_surface(filename.c_str(), locale_.c_str(), &surface);
937 if (result == 0) {
938 return std::unique_ptr<GRSurface>(surface);
Tao Bao736d59c2017-01-03 10:15:33 -0800939 }
xunchang9d05c8a2019-04-16 12:07:42 -0700940 // TODO(xunchang) create a error code enum to refine the retry condition.
941 LOG(WARNING) << "Failed to load bitmap " << filename << " for locale " << locale_ << " (error "
942 << result << "). Falling back to use default locale.";
943
944 result = res_create_localized_alpha_surface(filename.c_str(), DEFAULT_LOCALE, &surface);
945 if (result == 0) {
946 return std::unique_ptr<GRSurface>(surface);
947 }
948
949 LOG(ERROR) << "Failed to load bitmap " << filename << " for locale " << DEFAULT_LOCALE
950 << " (error " << result << ")";
951 return nullptr;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700952}
953
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700954static char** Alloc2d(size_t rows, size_t cols) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700955 char** result = new char*[rows];
956 for (size_t i = 0; i < rows; ++i) {
957 result[i] = new char[cols];
958 memset(result[i], 0, cols);
959 }
960 return result;
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700961}
962
Tianjie Xu35926c42016-04-28 18:06:26 -0700963// Choose the right background string to display during update.
964void ScreenRecoveryUI::SetSystemUpdateText(bool security_update) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700965 if (security_update) {
Tao Baoda409fb2018-10-21 23:36:26 -0700966 installing_text_ = LoadLocalizedBitmap("installing_security_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700967 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700968 installing_text_ = LoadLocalizedBitmap("installing_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700969 }
970 Redraw();
Tianjie Xu35926c42016-04-28 18:06:26 -0700971}
972
Sen Jiangd5304492016-12-09 16:20:49 -0800973bool ScreenRecoveryUI::InitTextParams() {
Tao Baoba4edb32018-06-13 11:22:50 -0700974 // gr_init() would return successfully on font initialization failure.
975 if (gr_sys_font() == nullptr) {
Tao Bao171b4c42017-06-19 23:10:44 -0700976 return false;
977 }
Tao Bao171b4c42017-06-19 23:10:44 -0700978 gr_font_size(gr_sys_font(), &char_width_, &char_height_);
Alessandro Astone5a57a942020-03-09 23:17:50 +0100979 gr_font_size(gr_menu_font(), &menu_char_width_, &menu_char_height_);
Tao Bao0bc88de2018-07-31 14:53:16 -0700980 text_rows_ = (ScreenHeight() - margin_height_ * 2) / char_height_;
981 text_cols_ = (ScreenWidth() - margin_width_ * 2) / char_width_;
Tao Bao171b4c42017-06-19 23:10:44 -0700982 return true;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -0700983}
984
Tianjie Xub99e6062018-10-16 15:13:09 -0700985bool ScreenRecoveryUI::LoadWipeDataMenuText() {
Tianjie Xu1a0a30a2018-10-25 15:22:07 -0700986 // Ignores the errors since the member variables will stay as nullptr.
987 cancel_wipe_data_text_ = LoadLocalizedBitmap("cancel_wipe_data_text");
988 factory_data_reset_text_ = LoadLocalizedBitmap("factory_data_reset_text");
989 try_again_text_ = LoadLocalizedBitmap("try_again_text");
990 wipe_data_confirmation_text_ = LoadLocalizedBitmap("wipe_data_confirmation_text");
991 wipe_data_menu_header_text_ = LoadLocalizedBitmap("wipe_data_menu_header_text");
Tianjie Xub99e6062018-10-16 15:13:09 -0700992 return true;
993}
994
Mark Salyzyn30017e72020-05-13 12:39:12 -0700995static bool InitGraphics() {
996 // Timeout is same as init wait for file default of 5 seconds and is arbitrary
997 const unsigned timeout = 500; // 10ms increments
998 for (auto retry = timeout; retry > 0; --retry) {
999 if (gr_init() == 0) {
1000 if (retry < timeout) {
1001 // Log message like init wait for file completion log for consistency.
1002 LOG(WARNING) << "wait for 'graphics' took " << ((timeout - retry) * 10) << "ms";
1003 }
1004 return true;
1005 }
1006 std::this_thread::sleep_for(10ms);
1007 }
1008 // Log message like init wait for file timeout log for consistency.
1009 LOG(ERROR) << "timeout wait for 'graphics' took " << (timeout * 10) << "ms";
1010 return false;
1011}
1012
Tao Bao736d59c2017-01-03 10:15:33 -08001013bool ScreenRecoveryUI::Init(const std::string& locale) {
1014 RecoveryUI::Init(locale);
Tao Baoefb49ad2017-01-31 23:03:10 -08001015
Mark Salyzyn30017e72020-05-13 12:39:12 -07001016 if (!InitGraphics()) {
Tao Baoba4edb32018-06-13 11:22:50 -07001017 return false;
1018 }
fredchiouf2f5aa22022-02-11 16:39:53 +08001019 is_graphics_available = true;
Tao Baoba4edb32018-06-13 11:22:50 -07001020
Tao Bao736d59c2017-01-03 10:15:33 -08001021 if (!InitTextParams()) {
1022 return false;
1023 }
Alessandro Astone5a57a942020-03-09 23:17:50 +01001024 menu_draw_funcs_ = std::make_unique<MenuDrawFunctions>(*this);
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001025
Michael Bestas19630862019-03-23 17:28:22 +02001026 if (blank_unblank_on_init_) {
1027 gr_fb_blank(true);
1028 gr_fb_blank(false);
1029 }
1030
Tao Bao736d59c2017-01-03 10:15:33 -08001031 // Are we portrait or landscape?
1032 layout_ = (gr_fb_width() > gr_fb_height()) ? LANDSCAPE : PORTRAIT;
1033 // Are we the large variant of our base layout?
1034 if (gr_fb_height() > PixelsFromDp(800)) ++layout_;
Elliott Hughesfaf36e02016-04-20 17:22:16 -07001035
Tao Bao736d59c2017-01-03 10:15:33 -08001036 text_ = Alloc2d(text_rows_, text_cols_ + 1);
1037 file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
Doug Zongker55a36ac2013-03-04 15:49:02 -08001038
Tao Bao736d59c2017-01-03 10:15:33 -08001039 text_col_ = text_row_ = 0;
Doug Zongker211aebc2011-10-28 15:13:10 -07001040
Tao Baoefb49ad2017-01-31 23:03:10 -08001041 // Set up the locale info.
1042 SetLocale(locale);
1043
Tao Baoda409fb2018-10-21 23:36:26 -07001044 error_icon_ = LoadBitmap("icon_error");
Doug Zongker02ec6b82012-08-22 17:26:40 -07001045
Tao Baoda409fb2018-10-21 23:36:26 -07001046 progress_bar_empty_ = LoadBitmap("progress_empty");
1047 progress_bar_fill_ = LoadBitmap("progress_fill");
1048 stage_marker_empty_ = LoadBitmap("stage_empty");
1049 stage_marker_fill_ = LoadBitmap("stage_fill");
Elliott Hughes498cda62016-04-14 16:49:04 -07001050
Tao Baoda409fb2018-10-21 23:36:26 -07001051 erasing_text_ = LoadLocalizedBitmap("erasing_text");
1052 no_command_text_ = LoadLocalizedBitmap("no_command_text");
1053 error_text_ = LoadLocalizedBitmap("error_text");
Doug Zongker211aebc2011-10-28 15:13:10 -07001054
Alessandro Astone5a57a942020-03-09 23:17:50 +01001055 back_icon_ = LoadBitmap("ic_back");
1056 back_icon_sel_ = LoadBitmap("ic_back_sel");
Alessandro Astone957d98e2020-02-26 17:25:54 +01001057 if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) ||
1058 android::base::GetBoolProperty("ro.fastbootd.available", false)) {
Alessandro Astone56b46e12020-03-29 15:08:09 +02001059 lineage_logo_ = LoadBitmap("logo_image_switch");
David Anderson983e2d52019-01-02 11:35:38 -08001060 fastbootd_logo_ = LoadBitmap("fastbootd");
Alessandro Astone56b46e12020-03-29 15:08:09 +02001061 } else {
1062 lineage_logo_ = LoadBitmap("logo_image");
David Anderson983e2d52019-01-02 11:35:38 -08001063 }
1064
Tao Baoda409fb2018-10-21 23:36:26 -07001065 // Background text for "installing_update" could be "installing update" or
1066 // "installing security update". It will be set after Init() according to the commands in BCB.
1067 installing_text_.reset();
Elliott Hughes498cda62016-04-14 16:49:04 -07001068
Tianjie Xub99e6062018-10-16 15:13:09 -07001069 LoadWipeDataMenuText();
1070
Tao Bao736d59c2017-01-03 10:15:33 -08001071 LoadAnimation();
Doug Zongker02ec6b82012-08-22 17:26:40 -07001072
Tao Bao26ea9592018-05-09 16:32:02 -07001073 // Keep the progress bar updated, even when the process is otherwise busy.
1074 progress_thread_ = std::thread(&ScreenRecoveryUI::ProgressThreadLoop, this);
Sen Jiangd5304492016-12-09 16:20:49 -08001075
fredchiouf2f5aa22022-02-11 16:39:53 +08001076 // set the callback for hall sensor event
1077 (void)ev_sync_sw_state([this](auto&& a, auto&& b) { return this->SetSwCallback(a, b);});
1078
Tao Bao736d59c2017-01-03 10:15:33 -08001079 return true;
Doug Zongker211aebc2011-10-28 15:13:10 -07001080}
1081
Tao Bao551d2c32018-05-09 20:53:13 -07001082std::string ScreenRecoveryUI::GetLocale() const {
Jerry Zhang2dea53e2018-05-02 17:15:03 -07001083 return locale_;
1084}
1085
Elliott Hughes498cda62016-04-14 16:49:04 -07001086void ScreenRecoveryUI::LoadAnimation() {
Tao Bao6cd81682018-05-03 21:53:11 -07001087 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(Paths::Get().resource_dir().c_str()),
1088 closedir);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001089 dirent* de;
1090 std::vector<std::string> intro_frame_names;
Joshua Lambert94a83be2019-12-09 20:24:18 +00001091 std::vector<std::string> loop_frame_names;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001092
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001093 while ((de = readdir(dir.get())) != nullptr) {
1094 int value, num_chars;
1095 if (sscanf(de->d_name, "intro%d%n.png", &value, &num_chars) == 1) {
1096 intro_frame_names.emplace_back(de->d_name, num_chars);
1097 } else if (sscanf(de->d_name, "loop%d%n.png", &value, &num_chars) == 1) {
Joshua Lambert94a83be2019-12-09 20:24:18 +00001098 loop_frame_names.emplace_back(de->d_name, num_chars);
Elliott Hughes498cda62016-04-14 16:49:04 -07001099 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001100 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001101
Tao Baoda409fb2018-10-21 23:36:26 -07001102 size_t intro_frames = intro_frame_names.size();
1103 size_t loop_frames = loop_frame_names.size();
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001104
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001105 // It's okay to not have an intro.
Tao Baoda409fb2018-10-21 23:36:26 -07001106 if (intro_frames == 0) intro_done_ = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001107 // But you must have an animation.
1108 if (loop_frames == 0) abort();
Elliott Hughes498cda62016-04-14 16:49:04 -07001109
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001110 std::sort(intro_frame_names.begin(), intro_frame_names.end());
1111 std::sort(loop_frame_names.begin(), loop_frame_names.end());
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001112
Tao Baoda409fb2018-10-21 23:36:26 -07001113 intro_frames_.clear();
1114 intro_frames_.reserve(intro_frames);
1115 for (const auto& frame_name : intro_frame_names) {
1116 intro_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001117 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001118
Tao Baoda409fb2018-10-21 23:36:26 -07001119 loop_frames_.clear();
1120 loop_frames_.reserve(loop_frames);
1121 for (const auto& frame_name : loop_frame_names) {
1122 loop_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001123 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001124}
1125
Elliott Hughes8de52072015-04-08 20:06:50 -07001126void ScreenRecoveryUI::SetBackground(Icon icon) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001127 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker02ec6b82012-08-22 17:26:40 -07001128
Tao Baoda409fb2018-10-21 23:36:26 -07001129 current_icon_ = icon;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001130 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001131}
1132
Elliott Hughes8de52072015-04-08 20:06:50 -07001133void ScreenRecoveryUI::SetProgressType(ProgressType type) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001134 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001135 if (progressBarType != type) {
1136 progressBarType = type;
1137 }
1138 progressScopeStart = 0;
1139 progressScopeSize = 0;
1140 progress = 0;
1141 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001142}
1143
Elliott Hughes8de52072015-04-08 20:06:50 -07001144void ScreenRecoveryUI::ShowProgress(float portion, float seconds) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001145 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001146 progressBarType = DETERMINATE;
1147 progressScopeStart += progressScopeSize;
1148 progressScopeSize = portion;
1149 progressScopeTime = now();
1150 progressScopeDuration = seconds;
1151 progress = 0;
1152 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001153}
1154
Elliott Hughes8de52072015-04-08 20:06:50 -07001155void ScreenRecoveryUI::SetProgress(float fraction) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001156 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001157 if (fraction < 0.0) fraction = 0.0;
1158 if (fraction > 1.0) fraction = 1.0;
1159 if (progressBarType == DETERMINATE && fraction > progress) {
1160 // Skip updates that aren't visibly different.
Tao Baoda409fb2018-10-21 23:36:26 -07001161 int width = gr_get_width(progress_bar_empty_.get());
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001162 float scale = width * progressScopeSize;
1163 if ((int)(progress * scale) != (int)(fraction * scale)) {
1164 progress = fraction;
1165 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001166 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001167 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001168}
1169
Doug Zongkerc87bab12013-11-25 13:53:25 -08001170void ScreenRecoveryUI::SetStage(int current, int max) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001171 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001172 stage = current;
1173 max_stage = max;
Doug Zongkerc87bab12013-11-25 13:53:25 -08001174}
1175
Tao Baob6918c72015-05-19 17:02:16 -07001176void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001177 std::string str;
1178 android::base::StringAppendV(&str, fmt, ap);
Doug Zongker211aebc2011-10-28 15:13:10 -07001179
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001180 if (copy_to_stdout) {
1181 fputs(str.c_str(), stdout);
1182 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001183
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001184 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001185 if (text_rows_ > 0 && text_cols_ > 0) {
1186 for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
1187 if (*ptr == '\n' || text_col_ >= text_cols_) {
Elliott Hughesc0491632015-05-06 12:40:05 -07001188 text_[text_row_][text_col_] = '\0';
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001189 text_col_ = 0;
1190 text_row_ = (text_row_ + 1) % text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001191 }
1192 if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
Doug Zongker211aebc2011-10-28 15:13:10 -07001193 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001194 text_[text_row_][text_col_] = '\0';
1195 update_screen_locked();
1196 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001197}
1198
Tao Baob6918c72015-05-19 17:02:16 -07001199void ScreenRecoveryUI::Print(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001200 va_list ap;
1201 va_start(ap, fmt);
1202 PrintV(fmt, true, ap);
1203 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001204}
1205
Tianjie Xu8f397302018-08-20 13:40:47 -07001206void ScreenRecoveryUI::PrintOnScreenOnly(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001207 va_list ap;
1208 va_start(ap, fmt);
1209 PrintV(fmt, false, ap);
1210 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001211}
1212
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001213void ScreenRecoveryUI::PutChar(char ch) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001214 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001215 if (ch != '\n') text_[text_row_][text_col_++] = ch;
1216 if (ch == '\n' || text_col_ >= text_cols_) {
1217 text_col_ = 0;
1218 ++text_row_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001219 }
Elliott Hughes8de52072015-04-08 20:06:50 -07001220}
1221
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001222void ScreenRecoveryUI::ClearText() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001223 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001224 text_col_ = 0;
1225 text_row_ = 0;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001226 for (size_t i = 0; i < text_rows_; ++i) {
1227 memset(text_[i], 0, text_cols_ + 1);
1228 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001229}
1230
1231void ScreenRecoveryUI::ShowFile(FILE* fp) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001232 std::vector<off_t> offsets;
1233 offsets.push_back(ftello(fp));
1234 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001235
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001236 struct stat sb;
1237 fstat(fileno(fp), &sb);
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001238
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001239 bool show_prompt = false;
1240 while (true) {
1241 if (show_prompt) {
1242 PrintOnScreenOnly("--(%d%% of %d bytes)--",
1243 static_cast<int>(100 * (double(ftello(fp)) / double(sb.st_size))),
1244 static_cast<int>(sb.st_size));
1245 Redraw();
1246 while (show_prompt) {
1247 show_prompt = false;
Alessandro Astone84042042020-10-04 18:11:40 +02001248 InputEvent evt = WaitInputEvent();
1249 if (evt.type() == EventType::EXTRA) {
1250 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) {
1251 return;
1252 }
1253 }
1254 if (evt.type() != EventType::KEY) {
1255 show_prompt = true;
1256 continue;
1257 }
1258 if (evt.key() == KEY_POWER || evt.key() == KEY_ENTER || evt.key() == KEY_BACKSPACE ||
1259 evt.key() == KEY_BACK || evt.key() == KEY_HOME || evt.key() == KEY_HOMEPAGE) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001260 return;
Alessandro Astone84042042020-10-04 18:11:40 +02001261 } else if (evt.key() == KEY_UP || evt.key() == KEY_VOLUMEUP || evt.key() == KEY_SCROLLUP) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001262 if (offsets.size() <= 1) {
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001263 show_prompt = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001264 } else {
1265 offsets.pop_back();
1266 fseek(fp, offsets.back(), SEEK_SET);
1267 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001268 } else {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001269 if (feof(fp)) {
1270 return;
1271 }
1272 offsets.push_back(ftello(fp));
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001273 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001274 }
1275 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001276 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001277
1278 int ch = getc(fp);
1279 if (ch == EOF) {
1280 while (text_row_ < text_rows_ - 1) PutChar('\n');
1281 show_prompt = true;
1282 } else {
1283 PutChar(ch);
1284 if (text_col_ == 0 && text_row_ >= text_rows_ - 1) {
1285 show_prompt = true;
1286 }
1287 }
1288 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001289}
1290
Tao Bao1d156b92018-05-02 12:43:18 -07001291void ScreenRecoveryUI::ShowFile(const std::string& filename) {
1292 std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(filename.c_str(), "re"), fclose);
1293 if (!fp) {
1294 Print(" Unable to open %s: %s\n", filename.c_str(), strerror(errno));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001295 return;
1296 }
Elliott Hughesc0491632015-05-06 12:40:05 -07001297
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001298 char** old_text = text_;
1299 size_t old_text_col = text_col_;
1300 size_t old_text_row = text_row_;
Elliott Hughesc0491632015-05-06 12:40:05 -07001301
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001302 // Swap in the alternate screen and clear it.
1303 text_ = file_viewer_text_;
1304 ClearText();
Elliott Hughesc0491632015-05-06 12:40:05 -07001305
Tao Bao1d156b92018-05-02 12:43:18 -07001306 ShowFile(fp.get());
Elliott Hughesc0491632015-05-06 12:40:05 -07001307
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001308 text_ = old_text;
1309 text_col_ = old_text_col;
1310 text_row_ = old_text_row;
Elliott Hughes8de52072015-04-08 20:06:50 -07001311}
1312
Tao Baoda409fb2018-10-21 23:36:26 -07001313std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(
1314 const GRSurface* graphic_header, const std::vector<const GRSurface*>& graphic_items,
1315 const std::vector<std::string>& text_headers, const std::vector<std::string>& text_items,
1316 size_t initial_selection) const {
Tianjie Xub99e6062018-10-16 15:13:09 -07001317 // horizontal unusable area: margin width + menu indent
1318 size_t max_width = ScreenWidth() - margin_width_ - kMenuIndent;
1319 // vertical unusable area: margin height + title lines + helper message + high light bar.
1320 // It is safe to reserve more space.
1321 size_t max_height = ScreenHeight() - margin_height_ - char_height_ * (title_lines_.size() + 3);
1322 if (GraphicMenu::Validate(max_width, max_height, graphic_header, graphic_items)) {
1323 return std::make_unique<GraphicMenu>(graphic_header, graphic_items, initial_selection, *this);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001324 }
Tianjie Xub99e6062018-10-16 15:13:09 -07001325
1326 fprintf(stderr, "Failed to initialize graphic menu, falling back to use the text menu.\n");
1327
1328 return CreateMenu(text_headers, text_items, initial_selection);
1329}
1330
1331std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(const std::vector<std::string>& text_headers,
1332 const std::vector<std::string>& text_items,
1333 size_t initial_selection) const {
Alessandro Astone5a57a942020-03-09 23:17:50 +01001334 int menu_char_width = MenuCharWidth();
1335 int menu_char_height = MenuCharHeight();
Alessandro Astone5a57a942020-03-09 23:17:50 +01001336 int menu_cols = (ScreenWidth() - margin_width_*2 - kMenuIndent) / menu_char_width;
1337 bool wrap_selection = !HasThreeButtons() && !HasTouchScreen();
Alessandro Astone5cec9d82020-06-25 19:54:18 +02001338 return std::make_unique<TextMenu>(wrap_selection, menu_cols, text_headers, text_items,
Alessandro Astone5a57a942020-03-09 23:17:50 +01001339 initial_selection, menu_char_height, *menu_draw_funcs_);
Doug Zongker211aebc2011-10-28 15:13:10 -07001340}
1341
1342int ScreenRecoveryUI::SelectMenu(int sel) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001343 std::lock_guard<std::mutex> lg(updateMutex);
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001344 if (menu_) {
1345 int old_sel = menu_->selection();
1346 sel = menu_->Select(sel);
Elliott Hughesfc06f872015-03-23 13:45:31 -07001347
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001348 if (sel != old_sel) {
1349 update_screen_locked();
1350 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001351 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001352 return sel;
Doug Zongker211aebc2011-10-28 15:13:10 -07001353}
1354
Alessandro Astone84042042020-10-04 18:11:40 +02001355int ScreenRecoveryUI::SelectMenu(const Point& point) {
1356 int new_sel = Device::kNoAction;
1357 std::lock_guard<std::mutex> lg(updateMutex);
1358 if (menu_) {
1359 if (!menu_->IsMain()) {
1360 // Back arrow hitbox
1361 const static int logo_width = gr_get_width(lineage_logo_.get());
1362 const static int logo_height = gr_get_height(lineage_logo_.get());
1363 const static int icon_w = gr_get_width(back_icon_.get());
1364 const static int icon_h = gr_get_height(back_icon_.get());
1365 const static int centered_x = ScreenWidth() / 2 - logo_width / 2;
1366 const static int icon_x = centered_x / 2 - icon_w / 2;
1367 const static int icon_y = margin_height_ + logo_height / 2 - icon_h / 2;
1368
1369 if (point.x() >= icon_x && point.x() <= icon_x + icon_w &&
1370 point.y() >= icon_y && point.y() <= icon_y + icon_h) {
1371 return Device::kGoBack;
1372 }
1373 }
1374
1375 if (point.y() >= menu_start_y_ &&
1376 point.y() < menu_start_y_ + menu_->ItemsCount() * MenuItemHeight()) {
1377 int old_sel = menu_->selection();
1378 int relative_sel = (point.y() - menu_start_y_) / MenuItemHeight();
1379 new_sel = menu_->SelectVisible(relative_sel);
1380 if (new_sel != -1 && new_sel != old_sel) {
1381 update_screen_locked();
1382 }
1383 }
1384 }
1385 return new_sel;
1386}
1387
1388int ScreenRecoveryUI::ScrollMenu(int updown) {
1389 std::lock_guard<std::mutex> lg(updateMutex);
1390 int sel = Device::kNoAction;
1391 if (menu_) {
1392 sel = menu_->Scroll(updown);
1393 update_screen_locked();
1394 }
1395 return sel;
1396}
1397
Tianjie Xub99e6062018-10-16 15:13:09 -07001398size_t ScreenRecoveryUI::ShowMenu(std::unique_ptr<Menu>&& menu, bool menu_only,
Tao Bao1fe1afe2018-05-01 15:56:05 -07001399 const std::function<int(int, bool)>& key_handler) {
Tao Bao3aec6962018-04-20 09:24:58 -07001400 // Throw away keys pressed previously, so user doesn't accidentally trigger menu items.
1401 FlushKeys();
1402
Jerry Zhangb76af932018-05-22 12:08:35 -07001403 // If there is a key interrupt in progress, return KeyError::INTERRUPTED without starting the
1404 // menu.
1405 if (IsKeyInterrupted()) return static_cast<size_t>(KeyError::INTERRUPTED);
1406
Tianjie Xub99e6062018-10-16 15:13:09 -07001407 CHECK(menu != nullptr);
Tao Bao3aec6962018-04-20 09:24:58 -07001408
Tianjie Xub99e6062018-10-16 15:13:09 -07001409 // Starts and displays the menu
1410 menu_ = std::move(menu);
1411 Redraw();
1412
1413 int selected = menu_->selection();
Tao Bao3aec6962018-04-20 09:24:58 -07001414 int chosen_item = -1;
1415 while (chosen_item < 0) {
Alessandro Astone84042042020-10-04 18:11:40 +02001416 InputEvent evt = WaitInputEvent();
1417 if (evt.type() == EventType::EXTRA) {
1418 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) {
1419 // WaitKey() was interrupted.
1420 return static_cast<size_t>(KeyError::INTERRUPTED);
1421 }
1422 if (evt.key() == static_cast<int>(KeyError::TIMED_OUT)) { // WaitKey() timed out.
1423 if (WasTextEverVisible()) {
1424 continue;
1425 } else {
1426 LOG(INFO) << "Timed out waiting for key input; rebooting.";
1427 menu_.reset();
1428 Redraw();
1429 return static_cast<size_t>(KeyError::TIMED_OUT);
1430 }
Tao Bao3aec6962018-04-20 09:24:58 -07001431 }
1432 }
1433
Alessandro Astone84042042020-10-04 18:11:40 +02001434 int action = Device::kNoAction;
1435 if (evt.type() == EventType::TOUCH) {
1436 int touch_sel = SelectMenu(evt.pos());
1437 if (touch_sel < 0) {
1438 action = touch_sel;
1439 } else {
1440 action = Device::kInvokeItem;
1441 selected = touch_sel;
1442 }
1443 } else {
1444 bool visible = IsTextVisible();
1445 action = key_handler(evt.key(), visible);
1446 }
Tao Bao3aec6962018-04-20 09:24:58 -07001447 if (action < 0) {
1448 switch (action) {
1449 case Device::kHighlightUp:
1450 selected = SelectMenu(--selected);
1451 break;
1452 case Device::kHighlightDown:
1453 selected = SelectMenu(++selected);
1454 break;
Alessandro Astone84042042020-10-04 18:11:40 +02001455 case Device::kScrollUp:
1456 selected = ScrollMenu(-1);
1457 break;
1458 case Device::kScrollDown:
1459 selected = ScrollMenu(1);
1460 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001461 case Device::kInvokeItem:
Alessandro Astone5a57a942020-03-09 23:17:50 +01001462 if (selected < 0) {
1463 chosen_item = Device::kGoBack;
1464 } else {
1465 chosen_item = selected;
1466 }
Tao Bao3aec6962018-04-20 09:24:58 -07001467 break;
1468 case Device::kNoAction:
1469 break;
Tom Marshallf20b3e32017-08-24 13:50:01 +00001470 case Device::kGoBack:
1471 chosen_item = Device::kGoBack;
1472 break;
1473 case Device::kGoHome:
1474 chosen_item = Device::kGoHome;
1475 break;
Tom Marshall9f2f3282018-12-17 15:57:44 -08001476 case Device::kDoSideload:
1477 chosen_item = Device::kDoSideload;
1478 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001479 }
1480 } else if (!menu_only) {
1481 chosen_item = action;
1482 }
Tom Marshall9f2f3282018-12-17 15:57:44 -08001483
1484 if (chosen_item == Device::kGoBack || chosen_item == Device::kGoHome ||
1485 chosen_item == Device::kDoSideload) {
Tom Marshallf20b3e32017-08-24 13:50:01 +00001486 break;
1487 }
Tao Bao3aec6962018-04-20 09:24:58 -07001488 }
1489
Tianjie Xub99e6062018-10-16 15:13:09 -07001490 menu_.reset();
Tianjie Xub99e6062018-10-16 15:13:09 -07001491
Tao Bao3aec6962018-04-20 09:24:58 -07001492 return chosen_item;
1493}
1494
Tianjie Xub99e6062018-10-16 15:13:09 -07001495size_t ScreenRecoveryUI::ShowMenu(const std::vector<std::string>& headers,
1496 const std::vector<std::string>& items, size_t initial_selection,
1497 bool menu_only,
1498 const std::function<int(int, bool)>& key_handler) {
1499 auto menu = CreateMenu(headers, items, initial_selection);
1500 if (menu == nullptr) {
1501 return initial_selection;
1502 }
1503
Tao Baofa8e02a2019-06-27 09:07:04 -07001504 return ShowMenu(std::move(menu), menu_only, key_handler);
Tianjie Xub99e6062018-10-16 15:13:09 -07001505}
1506
1507size_t ScreenRecoveryUI::ShowPromptWipeDataMenu(const std::vector<std::string>& backup_headers,
1508 const std::vector<std::string>& backup_items,
1509 const std::function<int(int, bool)>& key_handler) {
Tao Baoda409fb2018-10-21 23:36:26 -07001510 auto wipe_data_menu = CreateMenu(wipe_data_menu_header_text_.get(),
1511 { try_again_text_.get(), factory_data_reset_text_.get() },
1512 backup_headers, backup_items, 0);
Tianjie Xub99e6062018-10-16 15:13:09 -07001513 if (wipe_data_menu == nullptr) {
1514 return 0;
1515 }
1516
1517 return ShowMenu(std::move(wipe_data_menu), true, key_handler);
1518}
1519
Tianjie Xu1a0a30a2018-10-25 15:22:07 -07001520size_t ScreenRecoveryUI::ShowPromptWipeDataConfirmationMenu(
1521 const std::vector<std::string>& backup_headers, const std::vector<std::string>& backup_items,
1522 const std::function<int(int, bool)>& key_handler) {
1523 auto confirmation_menu =
1524 CreateMenu(wipe_data_confirmation_text_.get(),
1525 { cancel_wipe_data_text_.get(), factory_data_reset_text_.get() }, backup_headers,
1526 backup_items, 0);
1527 if (confirmation_menu == nullptr) {
1528 return 0;
1529 }
1530
1531 return ShowMenu(std::move(confirmation_menu), true, key_handler);
1532}
1533
Elliott Hughes8de52072015-04-08 20:06:50 -07001534bool ScreenRecoveryUI::IsTextVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001535 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001536 int visible = show_text;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001537 return visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001538}
1539
Elliott Hughes8de52072015-04-08 20:06:50 -07001540bool ScreenRecoveryUI::WasTextEverVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001541 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001542 int ever_visible = show_text_ever;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001543 return ever_visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001544}
1545
Elliott Hughes8de52072015-04-08 20:06:50 -07001546void ScreenRecoveryUI::ShowText(bool visible) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001547 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001548 show_text = visible;
1549 if (show_text) show_text_ever = true;
1550 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001551}
Doug Zongkerc0441d12013-07-31 11:28:24 -07001552
Elliott Hughes8de52072015-04-08 20:06:50 -07001553void ScreenRecoveryUI::Redraw() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001554 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001555 update_screen_locked();
Doug Zongkerc0441d12013-07-31 11:28:24 -07001556}
Elliott Hughes642aaa72015-04-10 12:47:46 -07001557
1558void ScreenRecoveryUI::KeyLongPress(int) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001559 // Redraw so that if we're in the menu, the highlight
1560 // will change color to indicate a successful long press.
1561 Redraw();
Elliott Hughes642aaa72015-04-10 12:47:46 -07001562}
Tao Baoefb49ad2017-01-31 23:03:10 -08001563
1564void ScreenRecoveryUI::SetLocale(const std::string& new_locale) {
1565 locale_ = new_locale;
1566 rtl_locale_ = false;
1567
1568 if (!new_locale.empty()) {
Tao Bao347a6592018-05-08 15:58:29 -07001569 size_t separator = new_locale.find('-');
1570 // lang has the language prefix prior to the separator, or full string if none exists.
1571 std::string lang = new_locale.substr(0, separator);
Tao Baoefb49ad2017-01-31 23:03:10 -08001572
1573 // A bit cheesy: keep an explicit list of supported RTL languages.
1574 if (lang == "ar" || // Arabic
1575 lang == "fa" || // Persian (Farsi)
1576 lang == "he" || // Hebrew (new language code)
1577 lang == "iw" || // Hebrew (old language code)
1578 lang == "ur") { // Urdu
1579 rtl_locale_ = true;
1580 }
1581 }
1582}
fredchiouf2f5aa22022-02-11 16:39:53 +08001583
1584int ScreenRecoveryUI::SetSwCallback(int code, int value) {
1585 if (!is_graphics_available) { return -1; }
1586 if (code > SW_MAX) { return -1; }
1587 if (code != SW_LID) { return 0; }
1588
1589 /* detect dual display */
1590 if (!gr_has_multiple_connectors()) { return -1; }
1591
1592 /* turn off all screen */
1593 gr_fb_blank(true, DirectRenderManager::DRM_INNER);
1594 gr_fb_blank(true, DirectRenderManager::DRM_OUTER);
1595 gr_color(0, 0, 0, 255);
1596 gr_clear();
1597
1598 /* turn on the screen */
1599 gr_fb_blank(false, value);
1600 gr_flip();
1601
1602 /* set the retation */
1603 std::string rotation_str;
1604 if (value == DirectRenderManager::DRM_OUTER) {
1605 rotation_str =
1606 android::base::GetProperty("ro.minui.second_rotation", "ROTATION_NONE");
1607 } else {
1608 rotation_str =
1609 android::base::GetProperty("ro.minui.default_rotation", "ROTATION_NONE");
1610 }
1611
1612 if (rotation_str == "ROTATION_RIGHT") {
1613 gr_rotate(GRRotation::RIGHT);
1614 } else if (rotation_str == "ROTATION_DOWN") {
1615 gr_rotate(GRRotation::DOWN);
1616 } else if (rotation_str == "ROTATION_LEFT") {
1617 gr_rotate(GRRotation::LEFT);
1618 } else { // "ROTATION_NONE" or unknown string
1619 gr_rotate(GRRotation::NONE);
1620 }
1621 Redraw();
1622
1623 return 0;
1624}