blob: 94fc33ad196cabdc358ed1d1b2aff619c413fa60 [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:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700569 gr_color(249, 194, 0, 255);
570 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700571 case UIElement::HEADER:
Jesse Chan968a21a2020-04-28 14:49:13 +0000572 if (fastbootd_logo_enabled_)
573 gr_color(0xfd, 0xd8,0x35, 255);
574 else
575 gr_color(0xf8, 0x90, 0xff, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700576 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700577 case UIElement::MENU:
Alessandro Astone5a57a942020-03-09 23:17:50 +0100578 gr_color(0xd8, 0xd8, 0xd8, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700579 break;
Jesse Chan968a21a2020-04-28 14:49:13 +0000580 case UIElement::MENU_SEL_BG:
581 case UIElement::SCROLLBAR:
582 if (fastbootd_logo_enabled_)
583 gr_color(0xe6, 0x51, 0x00, 255);
584 else
585 gr_color(0x7c, 0x4d, 0xff, 255);
586 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700587 case UIElement::MENU_SEL_BG_ACTIVE:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700588 gr_color(0, 156, 100, 255);
589 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700590 case UIElement::MENU_SEL_FG:
Jesse Chan968a21a2020-04-28 14:49:13 +0000591 if (fastbootd_logo_enabled_)
592 gr_color(0, 0, 0, 255);
593 else
594 gr_color(0xd8, 0xd8, 0xd8, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700595 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700596 case UIElement::LOG:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700597 gr_color(196, 196, 196, 255);
598 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700599 case UIElement::TEXT_FILL:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700600 gr_color(0, 0, 0, 160);
601 break;
602 default:
603 gr_color(255, 255, 255, 255);
604 break;
605 }
Doug Zongkerc0441d12013-07-31 11:28:24 -0700606}
Doug Zongker211aebc2011-10-28 15:13:10 -0700607
Tianjie Xu29d55752017-09-20 17:53:46 -0700608void ScreenRecoveryUI::SelectAndShowBackgroundText(const std::vector<std::string>& locales_entries,
609 size_t sel) {
610 SetLocale(locales_entries[sel]);
611 std::vector<std::string> text_name = { "erasing_text", "error_text", "installing_text",
612 "installing_security_text", "no_command_text" };
Tao Baoda409fb2018-10-21 23:36:26 -0700613 std::unordered_map<std::string, std::unique_ptr<GRSurface>> surfaces;
Tianjie Xu29d55752017-09-20 17:53:46 -0700614 for (const auto& name : text_name) {
Tao Baoda409fb2018-10-21 23:36:26 -0700615 auto text_image = LoadLocalizedBitmap(name);
Tianjie Xu29d55752017-09-20 17:53:46 -0700616 if (!text_image) {
617 Print("Failed to load %s\n", name.c_str());
618 return;
619 }
Tao Baoda409fb2018-10-21 23:36:26 -0700620 surfaces.emplace(name, std::move(text_image));
Tianjie Xu29d55752017-09-20 17:53:46 -0700621 }
622
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700623 std::lock_guard<std::mutex> lg(updateMutex);
Tianjie Xu29d55752017-09-20 17:53:46 -0700624 gr_color(0, 0, 0, 255);
625 gr_clear();
626
Tao Bao0bc88de2018-07-31 14:53:16 -0700627 int text_y = margin_height_;
628 int text_x = margin_width_;
Tianjie Xu29d55752017-09-20 17:53:46 -0700629 int line_spacing = gr_sys_font()->char_height; // Put some extra space between images.
630 // Write the header and descriptive texts.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700631 SetColor(UIElement::INFO);
Tianjie Xu29d55752017-09-20 17:53:46 -0700632 std::string header = "Show background text image";
Tao Bao93e46ad2018-05-02 14:57:21 -0700633 text_y += DrawTextLine(text_x, text_y, header, true);
Tianjie Xu29d55752017-09-20 17:53:46 -0700634 std::string locale_selection = android::base::StringPrintf(
Tao Bao39c49182018-05-07 22:50:33 -0700635 "Current locale: %s, %zu/%zu", locales_entries[sel].c_str(), sel + 1, locales_entries.size());
Tao Bao93e46ad2018-05-02 14:57:21 -0700636 // clang-format off
637 std::vector<std::string> instruction = {
638 locale_selection,
639 "Use volume up/down to switch locales and power to exit."
640 };
641 // clang-format on
Tianjie Xu29d55752017-09-20 17:53:46 -0700642 text_y += DrawWrappedTextLines(text_x, text_y, instruction);
643
644 // Iterate through the text images and display them in order for the current locale.
645 for (const auto& p : surfaces) {
646 text_y += line_spacing;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700647 SetColor(UIElement::LOG);
Tao Bao93e46ad2018-05-02 14:57:21 -0700648 text_y += DrawTextLine(text_x, text_y, p.first, false);
Tianjie Xu29d55752017-09-20 17:53:46 -0700649 gr_color(255, 255, 255, 255);
650 gr_texticon(text_x, text_y, p.second.get());
651 text_y += gr_get_height(p.second.get());
652 }
653 // Update the whole screen.
654 gr_flip();
Tianjie Xu29d55752017-09-20 17:53:46 -0700655}
656
Tao Bao39c49182018-05-07 22:50:33 -0700657void ScreenRecoveryUI::CheckBackgroundTextImages() {
Tianjie Xu29d55752017-09-20 17:53:46 -0700658 // Load a list of locales embedded in one of the resource files.
659 std::vector<std::string> locales_entries = get_locales_in_png("installing_text");
660 if (locales_entries.empty()) {
661 Print("Failed to load locales from the resource files\n");
662 return;
663 }
Tao Bao39c49182018-05-07 22:50:33 -0700664 std::string saved_locale = locale_;
Tianjie Xu29d55752017-09-20 17:53:46 -0700665 size_t selected = 0;
666 SelectAndShowBackgroundText(locales_entries, selected);
667
668 FlushKeys();
669 while (true) {
Alessandro Astone84042042020-10-04 18:11:40 +0200670 InputEvent evt = WaitInputEvent();
671 if (evt.type() == EventType::EXTRA) {
672 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) break;
673 }
674 if (evt.type() == EventType::KEY) {
675 if (evt.key() == KEY_POWER || evt.key() == KEY_ENTER) {
676 break;
677 } else if (evt.key() == KEY_UP || evt.key() == KEY_VOLUMEUP) {
678 selected = (selected == 0) ? locales_entries.size() - 1 : selected - 1;
679 SelectAndShowBackgroundText(locales_entries, selected);
680 } else if (evt.key() == KEY_DOWN || evt.key() == KEY_VOLUMEDOWN) {
681 selected = (selected == locales_entries.size() - 1) ? 0 : selected + 1;
682 SelectAndShowBackgroundText(locales_entries, selected);
683 }
Tianjie Xu29d55752017-09-20 17:53:46 -0700684 }
685 }
686
687 SetLocale(saved_locale);
688}
689
Luke Song92eda4d2017-09-19 10:51:35 -0700690int ScreenRecoveryUI::ScreenWidth() const {
691 return gr_fb_width();
692}
693
694int ScreenRecoveryUI::ScreenHeight() const {
695 return gr_fb_height();
696}
697
Tao Bao65815b62018-10-23 10:54:02 -0700698void ScreenRecoveryUI::DrawSurface(const GRSurface* surface, int sx, int sy, int w, int h, int dx,
Luke Song92eda4d2017-09-19 10:51:35 -0700699 int dy) const {
700 gr_blit(surface, sx, sy, w, h, dx, dy);
701}
702
Tao Baoea78d862017-06-28 14:52:17 -0700703int ScreenRecoveryUI::DrawHorizontalRule(int y) const {
Luke Song92eda4d2017-09-19 10:51:35 -0700704 gr_fill(0, y + 4, ScreenWidth(), y + 6);
Tao Baoea78d862017-06-28 14:52:17 -0700705 return 8;
Elliott Hughes95fc63e2015-04-10 19:12:01 -0700706}
707
Luke Songe2bd8762017-06-12 16:08:33 -0700708void ScreenRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const {
Alessandro Astone5a57a942020-03-09 23:17:50 +0100709 if (y + height > ScreenHeight())
710 height = ScreenHeight() - y;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700711 gr_fill(x, y, x + width, y + height);
Luke Songe2bd8762017-06-12 16:08:33 -0700712}
713
Alessandro Astone5a57a942020-03-09 23:17:50 +0100714void ScreenRecoveryUI::DrawScrollBar(int y, int height) const {
715 int x = ScreenWidth() - margin_width_;
716 int width = 8;
717 gr_fill(x - width, y, x, y + height);
718}
719
Luke Song92eda4d2017-09-19 10:51:35 -0700720void ScreenRecoveryUI::DrawFill(int x, int y, int w, int h) const {
721 gr_fill(x, y, w, h);
722}
723
Tao Bao65815b62018-10-23 10:54:02 -0700724void ScreenRecoveryUI::DrawTextIcon(int x, int y, const GRSurface* surface) const {
Luke Song92eda4d2017-09-19 10:51:35 -0700725 gr_texticon(x, y, surface);
726}
727
Tao Bao93e46ad2018-05-02 14:57:21 -0700728int ScreenRecoveryUI::DrawTextLine(int x, int y, const std::string& line, bool bold) const {
729 gr_text(gr_sys_font(), x, y, line.c_str(), bold);
Tao Baoea78d862017-06-28 14:52:17 -0700730 return char_height_ + 4;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700731}
732
Tao Bao93e46ad2018-05-02 14:57:21 -0700733int ScreenRecoveryUI::DrawTextLines(int x, int y, const std::vector<std::string>& lines) const {
Tao Baoea78d862017-06-28 14:52:17 -0700734 int offset = 0;
Tao Bao93e46ad2018-05-02 14:57:21 -0700735 for (const auto& line : lines) {
736 offset += DrawTextLine(x, y + offset, line, false);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700737 }
Tao Baoea78d862017-06-28 14:52:17 -0700738 return offset;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700739}
740
Tao Bao93e46ad2018-05-02 14:57:21 -0700741int ScreenRecoveryUI::DrawWrappedTextLines(int x, int y,
742 const std::vector<std::string>& lines) const {
Tao Bao452b4872018-05-09 11:52:09 -0700743 // Keep symmetrical margins based on the given offset (i.e. x).
744 size_t text_cols = (ScreenWidth() - x * 2) / char_width_;
Tao Bao2bbc6d62017-08-13 23:48:55 -0700745 int offset = 0;
Tao Bao93e46ad2018-05-02 14:57:21 -0700746 for (const auto& line : lines) {
Tao Bao2bbc6d62017-08-13 23:48:55 -0700747 size_t next_start = 0;
748 while (next_start < line.size()) {
Tao Bao452b4872018-05-09 11:52:09 -0700749 std::string sub = line.substr(next_start, text_cols + 1);
750 if (sub.size() <= text_cols) {
Tao Bao2bbc6d62017-08-13 23:48:55 -0700751 next_start += sub.size();
752 } else {
Tao Bao452b4872018-05-09 11:52:09 -0700753 // Line too long and must be wrapped to text_cols columns.
Tao Bao2bbc6d62017-08-13 23:48:55 -0700754 size_t last_space = sub.find_last_of(" \t\n");
755 if (last_space == std::string::npos) {
Tao Bao93e46ad2018-05-02 14:57:21 -0700756 // No space found, just draw as much as we can.
Tao Bao452b4872018-05-09 11:52:09 -0700757 sub.resize(text_cols);
758 next_start += text_cols;
Tao Bao2bbc6d62017-08-13 23:48:55 -0700759 } else {
760 sub.resize(last_space);
761 next_start += last_space + 1;
762 }
763 }
Tao Bao93e46ad2018-05-02 14:57:21 -0700764 offset += DrawTextLine(x, y + offset, sub, false);
Tao Bao2bbc6d62017-08-13 23:48:55 -0700765 }
766 }
767 return offset;
768}
769
Jerry Zhang0e577ee2018-05-07 11:21:10 -0700770void ScreenRecoveryUI::SetTitle(const std::vector<std::string>& lines) {
771 title_lines_ = lines;
772}
773
Tianjie Xue5032212019-07-23 13:23:29 -0700774std::vector<std::string> ScreenRecoveryUI::GetMenuHelpMessage() const {
775 // clang-format off
776 static std::vector<std::string> REGULAR_HELP{
777 "Use volume up/down and power.",
778 };
779 static std::vector<std::string> LONG_PRESS_HELP{
780 "Any button cycles highlight.",
781 "Long-press activates.",
782 };
783 // clang-format on
784 return HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP;
785}
786
Tao Bao171b4c42017-06-19 23:10:44 -0700787// Redraws everything on the screen. Does not flip pages. Should only be called with updateMutex
788// locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700789void ScreenRecoveryUI::draw_screen_locked() {
Tao Bao171b4c42017-06-19 23:10:44 -0700790 if (!show_text) {
791 draw_background_locked();
792 draw_foreground_locked();
793 return;
794 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700795
Tao Bao171b4c42017-06-19 23:10:44 -0700796 gr_color(0, 0, 0, 255);
797 gr_clear();
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700798
Tianjie Xue5032212019-07-23 13:23:29 -0700799 draw_menu_and_text_buffer_locked(GetMenuHelpMessage());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700800}
801
802// Draws the menu and text buffer on the screen. Should only be called with updateMutex locked.
Tao Bao93e46ad2018-05-02 14:57:21 -0700803void ScreenRecoveryUI::draw_menu_and_text_buffer_locked(
804 const std::vector<std::string>& help_message) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700805 int y = margin_height_;
David Anderson983e2d52019-01-02 11:35:38 -0800806
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700807 if (menu_) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700808 int x = margin_width_ + kMenuIndent;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700809
Tianjie Xu66dbf632018-10-11 16:54:50 -0700810 SetColor(UIElement::INFO);
Jerry Zhang0e577ee2018-05-07 11:21:10 -0700811
Alessandro Astone56b46e12020-03-29 15:08:09 +0200812 auto& logo = fastbootd_logo_enabled_ ? fastbootd_logo_ : lineage_logo_;
813 if (logo && back_icon_) {
814 auto logo_width = gr_get_width(logo.get());
815 auto logo_height = gr_get_height(logo.get());
Alessandro Astone5a57a942020-03-09 23:17:50 +0100816 auto centered_x = ScreenWidth() / 2 - logo_width / 2;
Alessandro Astone56b46e12020-03-29 15:08:09 +0200817 DrawSurface(logo.get(), 0, 0, logo_width, logo_height, centered_x, y);
Alessandro Astone5a57a942020-03-09 23:17:50 +0100818 y += logo_height;
Tao Bao171b4c42017-06-19 23:10:44 -0700819
Alessandro Astone5a57a942020-03-09 23:17:50 +0100820 if (!menu_->IsMain()) {
821 auto icon_w = gr_get_width(back_icon_.get());
822 auto icon_h = gr_get_height(back_icon_.get());
823 auto icon_x = centered_x / 2 - icon_w / 2;
824 auto icon_y = y - logo_height / 2 - icon_h / 2;
825 gr_blit(back_icon_sel_ && menu_->selection() == -1 ? back_icon_sel_.get() : back_icon_.get(),
826 0, 0, icon_w, icon_h, icon_x, icon_y);
827 }
Jesse Chan968a21a2020-04-28 14:49:13 +0000828 y += MenuItemPadding();
Alessandro Astone5a57a942020-03-09 23:17:50 +0100829 } else {
830 for (size_t i = 0; i < title_lines_.size(); i++) {
831 y += DrawTextLine(x, y, title_lines_[i], i == 0);
832 }
833 y += DrawTextLines(x, y, help_message);
834 }
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700835
Tianjie Xu66dbf632018-10-11 16:54:50 -0700836 y += menu_->DrawHeader(x, y);
Alessandro Astone84042042020-10-04 18:11:40 +0200837 menu_start_y_ = y + 12; // Skip horizontal rule and some margin
Alessandro Astone5cec9d82020-06-25 19:54:18 +0200838 menu_->SetMenuHeight(std::max(0, ScreenHeight() - menu_start_y_));
Tianjie Xu66dbf632018-10-11 16:54:50 -0700839 y += menu_->DrawItems(x, y, ScreenWidth(), IsLongPress());
Tao Bao171b4c42017-06-19 23:10:44 -0700840 }
841
842 // Display from the bottom up, until we hit the top of the screen, the bottom of the menu, or
843 // we've displayed the entire text buffer.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700844 SetColor(UIElement::LOG);
Tao Baocb5524c2017-09-08 21:25:32 -0700845 int row = text_row_;
Tao Bao171b4c42017-06-19 23:10:44 -0700846 size_t count = 0;
Tao Bao0bc88de2018-07-31 14:53:16 -0700847 for (int ty = ScreenHeight() - margin_height_ - char_height_; ty >= y && count < text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700848 ty -= char_height_, ++count) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700849 DrawTextLine(margin_width_, ty, text_[row], false);
Tao Bao171b4c42017-06-19 23:10:44 -0700850 --row;
851 if (row < 0) row = text_rows_ - 1;
852 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700853}
854
855// Redraw everything on the screen and flip the screen (make it visible).
856// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700857void ScreenRecoveryUI::update_screen_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700858 draw_screen_locked();
859 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700860}
861
862// Updates only the progress bar, if possible, otherwise redraws the screen.
863// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700864void ScreenRecoveryUI::update_progress_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700865 if (show_text || !pagesIdentical) {
866 draw_screen_locked(); // Must redraw the whole screen
867 pagesIdentical = true;
868 } else {
869 draw_foreground_locked(); // Draw only the progress bar and overlays
870 }
871 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700872}
873
Elliott Hughes985022a2015-04-13 13:04:32 -0700874void ScreenRecoveryUI::ProgressThreadLoop() {
Tao Bao0bc88de2018-07-31 14:53:16 -0700875 double interval = 1.0 / animation_fps_;
Tao Bao26ea9592018-05-09 16:32:02 -0700876 while (!progress_thread_stopped_) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700877 double start = now();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700878 bool redraw = false;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700879 {
880 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker211aebc2011-10-28 15:13:10 -0700881
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700882 // update the installation animation, if active
883 // skip this if we have a text overlay (too expensive to update)
Tao Baoda409fb2018-10-21 23:36:26 -0700884 if ((current_icon_ == INSTALLING_UPDATE || current_icon_ == ERASING) && !show_text) {
885 if (!intro_done_) {
886 if (current_frame_ == intro_frames_.size() - 1) {
887 intro_done_ = true;
888 current_frame_ = 0;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700889 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700890 ++current_frame_;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700891 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700892 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700893 current_frame_ = (current_frame_ + 1) % loop_frames_.size();
Doug Zongker211aebc2011-10-28 15:13:10 -0700894 }
895
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700896 redraw = true;
897 }
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700898
899 // move the progress bar forward on timed intervals, if configured
900 int duration = progressScopeDuration;
901 if (progressBarType == DETERMINATE && duration > 0) {
902 double elapsed = now() - progressScopeTime;
903 float p = 1.0 * elapsed / duration;
904 if (p > 1.0) p = 1.0;
905 if (p > progress) {
906 progress = p;
907 redraw = true;
908 }
909 }
910
911 if (redraw) update_progress_locked();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700912 }
913
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700914 double end = now();
915 // minimum of 20ms delay between frames
916 double delay = interval - (end - start);
917 if (delay < 0.02) delay = 0.02;
918 usleep(static_cast<useconds_t>(delay * 1000000));
919 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700920}
921
Tao Baoda409fb2018-10-21 23:36:26 -0700922std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadBitmap(const std::string& filename) {
923 GRSurface* surface;
924 if (auto result = res_create_display_surface(filename.c_str(), &surface); result < 0) {
925 LOG(ERROR) << "Failed to load bitmap " << filename << " (error " << result << ")";
926 return nullptr;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700927 }
Tao Baoda409fb2018-10-21 23:36:26 -0700928 return std::unique_ptr<GRSurface>(surface);
Doug Zongkereac881c2014-03-07 09:21:25 -0800929}
930
Tao Baoda409fb2018-10-21 23:36:26 -0700931std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadLocalizedBitmap(const std::string& filename) {
932 GRSurface* surface;
xunchang9d05c8a2019-04-16 12:07:42 -0700933 auto result = res_create_localized_alpha_surface(filename.c_str(), locale_.c_str(), &surface);
934 if (result == 0) {
935 return std::unique_ptr<GRSurface>(surface);
Tao Bao736d59c2017-01-03 10:15:33 -0800936 }
xunchang9d05c8a2019-04-16 12:07:42 -0700937 // TODO(xunchang) create a error code enum to refine the retry condition.
938 LOG(WARNING) << "Failed to load bitmap " << filename << " for locale " << locale_ << " (error "
939 << result << "). Falling back to use default locale.";
940
941 result = res_create_localized_alpha_surface(filename.c_str(), DEFAULT_LOCALE, &surface);
942 if (result == 0) {
943 return std::unique_ptr<GRSurface>(surface);
944 }
945
946 LOG(ERROR) << "Failed to load bitmap " << filename << " for locale " << DEFAULT_LOCALE
947 << " (error " << result << ")";
948 return nullptr;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700949}
950
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700951static char** Alloc2d(size_t rows, size_t cols) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700952 char** result = new char*[rows];
953 for (size_t i = 0; i < rows; ++i) {
954 result[i] = new char[cols];
955 memset(result[i], 0, cols);
956 }
957 return result;
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700958}
959
Tianjie Xu35926c42016-04-28 18:06:26 -0700960// Choose the right background string to display during update.
961void ScreenRecoveryUI::SetSystemUpdateText(bool security_update) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700962 if (security_update) {
Tao Baoda409fb2018-10-21 23:36:26 -0700963 installing_text_ = LoadLocalizedBitmap("installing_security_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700964 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700965 installing_text_ = LoadLocalizedBitmap("installing_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700966 }
967 Redraw();
Tianjie Xu35926c42016-04-28 18:06:26 -0700968}
969
Sen Jiangd5304492016-12-09 16:20:49 -0800970bool ScreenRecoveryUI::InitTextParams() {
Tao Baoba4edb32018-06-13 11:22:50 -0700971 // gr_init() would return successfully on font initialization failure.
972 if (gr_sys_font() == nullptr) {
Tao Bao171b4c42017-06-19 23:10:44 -0700973 return false;
974 }
Tao Bao171b4c42017-06-19 23:10:44 -0700975 gr_font_size(gr_sys_font(), &char_width_, &char_height_);
Alessandro Astone5a57a942020-03-09 23:17:50 +0100976 gr_font_size(gr_menu_font(), &menu_char_width_, &menu_char_height_);
Tao Bao0bc88de2018-07-31 14:53:16 -0700977 text_rows_ = (ScreenHeight() - margin_height_ * 2) / char_height_;
978 text_cols_ = (ScreenWidth() - margin_width_ * 2) / char_width_;
Tao Bao171b4c42017-06-19 23:10:44 -0700979 return true;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -0700980}
981
Tianjie Xub99e6062018-10-16 15:13:09 -0700982bool ScreenRecoveryUI::LoadWipeDataMenuText() {
Tianjie Xu1a0a30a2018-10-25 15:22:07 -0700983 // Ignores the errors since the member variables will stay as nullptr.
984 cancel_wipe_data_text_ = LoadLocalizedBitmap("cancel_wipe_data_text");
985 factory_data_reset_text_ = LoadLocalizedBitmap("factory_data_reset_text");
986 try_again_text_ = LoadLocalizedBitmap("try_again_text");
987 wipe_data_confirmation_text_ = LoadLocalizedBitmap("wipe_data_confirmation_text");
988 wipe_data_menu_header_text_ = LoadLocalizedBitmap("wipe_data_menu_header_text");
Tianjie Xub99e6062018-10-16 15:13:09 -0700989 return true;
990}
991
Mark Salyzyn30017e72020-05-13 12:39:12 -0700992static bool InitGraphics() {
993 // Timeout is same as init wait for file default of 5 seconds and is arbitrary
994 const unsigned timeout = 500; // 10ms increments
995 for (auto retry = timeout; retry > 0; --retry) {
996 if (gr_init() == 0) {
997 if (retry < timeout) {
998 // Log message like init wait for file completion log for consistency.
999 LOG(WARNING) << "wait for 'graphics' took " << ((timeout - retry) * 10) << "ms";
1000 }
1001 return true;
1002 }
1003 std::this_thread::sleep_for(10ms);
1004 }
1005 // Log message like init wait for file timeout log for consistency.
1006 LOG(ERROR) << "timeout wait for 'graphics' took " << (timeout * 10) << "ms";
1007 return false;
1008}
1009
Tao Bao736d59c2017-01-03 10:15:33 -08001010bool ScreenRecoveryUI::Init(const std::string& locale) {
1011 RecoveryUI::Init(locale);
Tao Baoefb49ad2017-01-31 23:03:10 -08001012
Mark Salyzyn30017e72020-05-13 12:39:12 -07001013 if (!InitGraphics()) {
Tao Baoba4edb32018-06-13 11:22:50 -07001014 return false;
1015 }
fredchiouf2f5aa22022-02-11 16:39:53 +08001016 is_graphics_available = true;
Tao Baoba4edb32018-06-13 11:22:50 -07001017
Tao Bao736d59c2017-01-03 10:15:33 -08001018 if (!InitTextParams()) {
1019 return false;
1020 }
Alessandro Astone5a57a942020-03-09 23:17:50 +01001021 menu_draw_funcs_ = std::make_unique<MenuDrawFunctions>(*this);
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001022
Michael Bestas19630862019-03-23 17:28:22 +02001023 if (blank_unblank_on_init_) {
1024 gr_fb_blank(true);
1025 gr_fb_blank(false);
1026 }
1027
Tao Bao736d59c2017-01-03 10:15:33 -08001028 // Are we portrait or landscape?
1029 layout_ = (gr_fb_width() > gr_fb_height()) ? LANDSCAPE : PORTRAIT;
1030 // Are we the large variant of our base layout?
1031 if (gr_fb_height() > PixelsFromDp(800)) ++layout_;
Elliott Hughesfaf36e02016-04-20 17:22:16 -07001032
Tao Bao736d59c2017-01-03 10:15:33 -08001033 text_ = Alloc2d(text_rows_, text_cols_ + 1);
1034 file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
Doug Zongker55a36ac2013-03-04 15:49:02 -08001035
Tao Bao736d59c2017-01-03 10:15:33 -08001036 text_col_ = text_row_ = 0;
Doug Zongker211aebc2011-10-28 15:13:10 -07001037
Tao Baoefb49ad2017-01-31 23:03:10 -08001038 // Set up the locale info.
1039 SetLocale(locale);
1040
Tao Baoda409fb2018-10-21 23:36:26 -07001041 error_icon_ = LoadBitmap("icon_error");
Doug Zongker02ec6b82012-08-22 17:26:40 -07001042
Tao Baoda409fb2018-10-21 23:36:26 -07001043 progress_bar_empty_ = LoadBitmap("progress_empty");
1044 progress_bar_fill_ = LoadBitmap("progress_fill");
1045 stage_marker_empty_ = LoadBitmap("stage_empty");
1046 stage_marker_fill_ = LoadBitmap("stage_fill");
Elliott Hughes498cda62016-04-14 16:49:04 -07001047
Tao Baoda409fb2018-10-21 23:36:26 -07001048 erasing_text_ = LoadLocalizedBitmap("erasing_text");
1049 no_command_text_ = LoadLocalizedBitmap("no_command_text");
1050 error_text_ = LoadLocalizedBitmap("error_text");
Doug Zongker211aebc2011-10-28 15:13:10 -07001051
Alessandro Astone5a57a942020-03-09 23:17:50 +01001052 back_icon_ = LoadBitmap("ic_back");
1053 back_icon_sel_ = LoadBitmap("ic_back_sel");
Alessandro Astone957d98e2020-02-26 17:25:54 +01001054 if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) ||
1055 android::base::GetBoolProperty("ro.fastbootd.available", false)) {
Alessandro Astone56b46e12020-03-29 15:08:09 +02001056 lineage_logo_ = LoadBitmap("logo_image_switch");
David Anderson983e2d52019-01-02 11:35:38 -08001057 fastbootd_logo_ = LoadBitmap("fastbootd");
Alessandro Astone56b46e12020-03-29 15:08:09 +02001058 } else {
1059 lineage_logo_ = LoadBitmap("logo_image");
David Anderson983e2d52019-01-02 11:35:38 -08001060 }
1061
Tao Baoda409fb2018-10-21 23:36:26 -07001062 // Background text for "installing_update" could be "installing update" or
1063 // "installing security update". It will be set after Init() according to the commands in BCB.
1064 installing_text_.reset();
Elliott Hughes498cda62016-04-14 16:49:04 -07001065
Tianjie Xub99e6062018-10-16 15:13:09 -07001066 LoadWipeDataMenuText();
1067
Tao Bao736d59c2017-01-03 10:15:33 -08001068 LoadAnimation();
Doug Zongker02ec6b82012-08-22 17:26:40 -07001069
Tao Bao26ea9592018-05-09 16:32:02 -07001070 // Keep the progress bar updated, even when the process is otherwise busy.
1071 progress_thread_ = std::thread(&ScreenRecoveryUI::ProgressThreadLoop, this);
Sen Jiangd5304492016-12-09 16:20:49 -08001072
fredchiouf2f5aa22022-02-11 16:39:53 +08001073 // set the callback for hall sensor event
1074 (void)ev_sync_sw_state([this](auto&& a, auto&& b) { return this->SetSwCallback(a, b);});
1075
Tao Bao736d59c2017-01-03 10:15:33 -08001076 return true;
Doug Zongker211aebc2011-10-28 15:13:10 -07001077}
1078
Tao Bao551d2c32018-05-09 20:53:13 -07001079std::string ScreenRecoveryUI::GetLocale() const {
Jerry Zhang2dea53e2018-05-02 17:15:03 -07001080 return locale_;
1081}
1082
Elliott Hughes498cda62016-04-14 16:49:04 -07001083void ScreenRecoveryUI::LoadAnimation() {
Tao Bao6cd81682018-05-03 21:53:11 -07001084 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(Paths::Get().resource_dir().c_str()),
1085 closedir);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001086 dirent* de;
1087 std::vector<std::string> intro_frame_names;
Joshua Lambert94a83be2019-12-09 20:24:18 +00001088 std::vector<std::string> loop_frame_names;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001089
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001090 while ((de = readdir(dir.get())) != nullptr) {
1091 int value, num_chars;
1092 if (sscanf(de->d_name, "intro%d%n.png", &value, &num_chars) == 1) {
1093 intro_frame_names.emplace_back(de->d_name, num_chars);
1094 } else if (sscanf(de->d_name, "loop%d%n.png", &value, &num_chars) == 1) {
Joshua Lambert94a83be2019-12-09 20:24:18 +00001095 loop_frame_names.emplace_back(de->d_name, num_chars);
Elliott Hughes498cda62016-04-14 16:49:04 -07001096 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001097 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001098
Tao Baoda409fb2018-10-21 23:36:26 -07001099 size_t intro_frames = intro_frame_names.size();
1100 size_t loop_frames = loop_frame_names.size();
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001101
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001102 // It's okay to not have an intro.
Tao Baoda409fb2018-10-21 23:36:26 -07001103 if (intro_frames == 0) intro_done_ = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001104 // But you must have an animation.
1105 if (loop_frames == 0) abort();
Elliott Hughes498cda62016-04-14 16:49:04 -07001106
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001107 std::sort(intro_frame_names.begin(), intro_frame_names.end());
1108 std::sort(loop_frame_names.begin(), loop_frame_names.end());
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001109
Tao Baoda409fb2018-10-21 23:36:26 -07001110 intro_frames_.clear();
1111 intro_frames_.reserve(intro_frames);
1112 for (const auto& frame_name : intro_frame_names) {
1113 intro_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001114 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001115
Tao Baoda409fb2018-10-21 23:36:26 -07001116 loop_frames_.clear();
1117 loop_frames_.reserve(loop_frames);
1118 for (const auto& frame_name : loop_frame_names) {
1119 loop_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001120 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001121}
1122
Elliott Hughes8de52072015-04-08 20:06:50 -07001123void ScreenRecoveryUI::SetBackground(Icon icon) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001124 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker02ec6b82012-08-22 17:26:40 -07001125
Tao Baoda409fb2018-10-21 23:36:26 -07001126 current_icon_ = icon;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001127 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001128}
1129
Elliott Hughes8de52072015-04-08 20:06:50 -07001130void ScreenRecoveryUI::SetProgressType(ProgressType type) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001131 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001132 if (progressBarType != type) {
1133 progressBarType = type;
1134 }
1135 progressScopeStart = 0;
1136 progressScopeSize = 0;
1137 progress = 0;
1138 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001139}
1140
Elliott Hughes8de52072015-04-08 20:06:50 -07001141void ScreenRecoveryUI::ShowProgress(float portion, float seconds) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001142 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001143 progressBarType = DETERMINATE;
1144 progressScopeStart += progressScopeSize;
1145 progressScopeSize = portion;
1146 progressScopeTime = now();
1147 progressScopeDuration = seconds;
1148 progress = 0;
1149 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001150}
1151
Elliott Hughes8de52072015-04-08 20:06:50 -07001152void ScreenRecoveryUI::SetProgress(float fraction) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001153 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001154 if (fraction < 0.0) fraction = 0.0;
1155 if (fraction > 1.0) fraction = 1.0;
1156 if (progressBarType == DETERMINATE && fraction > progress) {
1157 // Skip updates that aren't visibly different.
Tao Baoda409fb2018-10-21 23:36:26 -07001158 int width = gr_get_width(progress_bar_empty_.get());
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001159 float scale = width * progressScopeSize;
1160 if ((int)(progress * scale) != (int)(fraction * scale)) {
1161 progress = fraction;
1162 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001163 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001164 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001165}
1166
Doug Zongkerc87bab12013-11-25 13:53:25 -08001167void ScreenRecoveryUI::SetStage(int current, int max) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001168 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001169 stage = current;
1170 max_stage = max;
Doug Zongkerc87bab12013-11-25 13:53:25 -08001171}
1172
Tao Baob6918c72015-05-19 17:02:16 -07001173void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001174 std::string str;
1175 android::base::StringAppendV(&str, fmt, ap);
Doug Zongker211aebc2011-10-28 15:13:10 -07001176
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001177 if (copy_to_stdout) {
1178 fputs(str.c_str(), stdout);
1179 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001180
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001181 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001182 if (text_rows_ > 0 && text_cols_ > 0) {
1183 for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
1184 if (*ptr == '\n' || text_col_ >= text_cols_) {
Elliott Hughesc0491632015-05-06 12:40:05 -07001185 text_[text_row_][text_col_] = '\0';
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001186 text_col_ = 0;
1187 text_row_ = (text_row_ + 1) % text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001188 }
1189 if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
Doug Zongker211aebc2011-10-28 15:13:10 -07001190 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001191 text_[text_row_][text_col_] = '\0';
1192 update_screen_locked();
1193 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001194}
1195
Tao Baob6918c72015-05-19 17:02:16 -07001196void ScreenRecoveryUI::Print(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001197 va_list ap;
1198 va_start(ap, fmt);
1199 PrintV(fmt, true, ap);
1200 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001201}
1202
Tianjie Xu8f397302018-08-20 13:40:47 -07001203void ScreenRecoveryUI::PrintOnScreenOnly(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001204 va_list ap;
1205 va_start(ap, fmt);
1206 PrintV(fmt, false, ap);
1207 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001208}
1209
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001210void ScreenRecoveryUI::PutChar(char ch) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001211 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001212 if (ch != '\n') text_[text_row_][text_col_++] = ch;
1213 if (ch == '\n' || text_col_ >= text_cols_) {
1214 text_col_ = 0;
1215 ++text_row_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001216 }
Elliott Hughes8de52072015-04-08 20:06:50 -07001217}
1218
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001219void ScreenRecoveryUI::ClearText() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001220 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001221 text_col_ = 0;
1222 text_row_ = 0;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001223 for (size_t i = 0; i < text_rows_; ++i) {
1224 memset(text_[i], 0, text_cols_ + 1);
1225 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001226}
1227
1228void ScreenRecoveryUI::ShowFile(FILE* fp) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001229 std::vector<off_t> offsets;
1230 offsets.push_back(ftello(fp));
1231 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001232
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001233 struct stat sb;
1234 fstat(fileno(fp), &sb);
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001235
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001236 bool show_prompt = false;
1237 while (true) {
1238 if (show_prompt) {
1239 PrintOnScreenOnly("--(%d%% of %d bytes)--",
1240 static_cast<int>(100 * (double(ftello(fp)) / double(sb.st_size))),
1241 static_cast<int>(sb.st_size));
1242 Redraw();
1243 while (show_prompt) {
1244 show_prompt = false;
Alessandro Astone84042042020-10-04 18:11:40 +02001245 InputEvent evt = WaitInputEvent();
1246 if (evt.type() == EventType::EXTRA) {
1247 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) {
1248 return;
1249 }
1250 }
1251 if (evt.type() != EventType::KEY) {
1252 show_prompt = true;
1253 continue;
1254 }
1255 if (evt.key() == KEY_POWER || evt.key() == KEY_ENTER || evt.key() == KEY_BACKSPACE ||
1256 evt.key() == KEY_BACK || evt.key() == KEY_HOME || evt.key() == KEY_HOMEPAGE) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001257 return;
Alessandro Astone84042042020-10-04 18:11:40 +02001258 } else if (evt.key() == KEY_UP || evt.key() == KEY_VOLUMEUP || evt.key() == KEY_SCROLLUP) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001259 if (offsets.size() <= 1) {
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001260 show_prompt = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001261 } else {
1262 offsets.pop_back();
1263 fseek(fp, offsets.back(), SEEK_SET);
1264 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001265 } else {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001266 if (feof(fp)) {
1267 return;
1268 }
1269 offsets.push_back(ftello(fp));
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001270 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001271 }
1272 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001273 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001274
1275 int ch = getc(fp);
1276 if (ch == EOF) {
1277 while (text_row_ < text_rows_ - 1) PutChar('\n');
1278 show_prompt = true;
1279 } else {
1280 PutChar(ch);
1281 if (text_col_ == 0 && text_row_ >= text_rows_ - 1) {
1282 show_prompt = true;
1283 }
1284 }
1285 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001286}
1287
Tao Bao1d156b92018-05-02 12:43:18 -07001288void ScreenRecoveryUI::ShowFile(const std::string& filename) {
1289 std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(filename.c_str(), "re"), fclose);
1290 if (!fp) {
1291 Print(" Unable to open %s: %s\n", filename.c_str(), strerror(errno));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001292 return;
1293 }
Elliott Hughesc0491632015-05-06 12:40:05 -07001294
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001295 char** old_text = text_;
1296 size_t old_text_col = text_col_;
1297 size_t old_text_row = text_row_;
Elliott Hughesc0491632015-05-06 12:40:05 -07001298
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001299 // Swap in the alternate screen and clear it.
1300 text_ = file_viewer_text_;
1301 ClearText();
Elliott Hughesc0491632015-05-06 12:40:05 -07001302
Tao Bao1d156b92018-05-02 12:43:18 -07001303 ShowFile(fp.get());
Elliott Hughesc0491632015-05-06 12:40:05 -07001304
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001305 text_ = old_text;
1306 text_col_ = old_text_col;
1307 text_row_ = old_text_row;
Elliott Hughes8de52072015-04-08 20:06:50 -07001308}
1309
Tao Baoda409fb2018-10-21 23:36:26 -07001310std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(
1311 const GRSurface* graphic_header, const std::vector<const GRSurface*>& graphic_items,
1312 const std::vector<std::string>& text_headers, const std::vector<std::string>& text_items,
1313 size_t initial_selection) const {
Tianjie Xub99e6062018-10-16 15:13:09 -07001314 // horizontal unusable area: margin width + menu indent
1315 size_t max_width = ScreenWidth() - margin_width_ - kMenuIndent;
1316 // vertical unusable area: margin height + title lines + helper message + high light bar.
1317 // It is safe to reserve more space.
1318 size_t max_height = ScreenHeight() - margin_height_ - char_height_ * (title_lines_.size() + 3);
1319 if (GraphicMenu::Validate(max_width, max_height, graphic_header, graphic_items)) {
1320 return std::make_unique<GraphicMenu>(graphic_header, graphic_items, initial_selection, *this);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001321 }
Tianjie Xub99e6062018-10-16 15:13:09 -07001322
1323 fprintf(stderr, "Failed to initialize graphic menu, falling back to use the text menu.\n");
1324
1325 return CreateMenu(text_headers, text_items, initial_selection);
1326}
1327
1328std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(const std::vector<std::string>& text_headers,
1329 const std::vector<std::string>& text_items,
1330 size_t initial_selection) const {
Alessandro Astone5a57a942020-03-09 23:17:50 +01001331 int menu_char_width = MenuCharWidth();
1332 int menu_char_height = MenuCharHeight();
Alessandro Astone5a57a942020-03-09 23:17:50 +01001333 int menu_cols = (ScreenWidth() - margin_width_*2 - kMenuIndent) / menu_char_width;
1334 bool wrap_selection = !HasThreeButtons() && !HasTouchScreen();
Alessandro Astone5cec9d82020-06-25 19:54:18 +02001335 return std::make_unique<TextMenu>(wrap_selection, menu_cols, text_headers, text_items,
Alessandro Astone5a57a942020-03-09 23:17:50 +01001336 initial_selection, menu_char_height, *menu_draw_funcs_);
Doug Zongker211aebc2011-10-28 15:13:10 -07001337}
1338
1339int ScreenRecoveryUI::SelectMenu(int sel) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001340 std::lock_guard<std::mutex> lg(updateMutex);
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001341 if (menu_) {
1342 int old_sel = menu_->selection();
1343 sel = menu_->Select(sel);
Elliott Hughesfc06f872015-03-23 13:45:31 -07001344
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001345 if (sel != old_sel) {
1346 update_screen_locked();
1347 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001348 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001349 return sel;
Doug Zongker211aebc2011-10-28 15:13:10 -07001350}
1351
Alessandro Astone84042042020-10-04 18:11:40 +02001352int ScreenRecoveryUI::SelectMenu(const Point& point) {
1353 int new_sel = Device::kNoAction;
1354 std::lock_guard<std::mutex> lg(updateMutex);
1355 if (menu_) {
1356 if (!menu_->IsMain()) {
1357 // Back arrow hitbox
1358 const static int logo_width = gr_get_width(lineage_logo_.get());
1359 const static int logo_height = gr_get_height(lineage_logo_.get());
1360 const static int icon_w = gr_get_width(back_icon_.get());
1361 const static int icon_h = gr_get_height(back_icon_.get());
1362 const static int centered_x = ScreenWidth() / 2 - logo_width / 2;
1363 const static int icon_x = centered_x / 2 - icon_w / 2;
1364 const static int icon_y = margin_height_ + logo_height / 2 - icon_h / 2;
1365
1366 if (point.x() >= icon_x && point.x() <= icon_x + icon_w &&
1367 point.y() >= icon_y && point.y() <= icon_y + icon_h) {
1368 return Device::kGoBack;
1369 }
1370 }
1371
1372 if (point.y() >= menu_start_y_ &&
1373 point.y() < menu_start_y_ + menu_->ItemsCount() * MenuItemHeight()) {
1374 int old_sel = menu_->selection();
1375 int relative_sel = (point.y() - menu_start_y_) / MenuItemHeight();
1376 new_sel = menu_->SelectVisible(relative_sel);
1377 if (new_sel != -1 && new_sel != old_sel) {
1378 update_screen_locked();
1379 }
1380 }
1381 }
1382 return new_sel;
1383}
1384
1385int ScreenRecoveryUI::ScrollMenu(int updown) {
1386 std::lock_guard<std::mutex> lg(updateMutex);
1387 int sel = Device::kNoAction;
1388 if (menu_) {
1389 sel = menu_->Scroll(updown);
1390 update_screen_locked();
1391 }
1392 return sel;
1393}
1394
Tianjie Xub99e6062018-10-16 15:13:09 -07001395size_t ScreenRecoveryUI::ShowMenu(std::unique_ptr<Menu>&& menu, bool menu_only,
Tao Bao1fe1afe2018-05-01 15:56:05 -07001396 const std::function<int(int, bool)>& key_handler) {
Tao Bao3aec6962018-04-20 09:24:58 -07001397 // Throw away keys pressed previously, so user doesn't accidentally trigger menu items.
1398 FlushKeys();
1399
Jerry Zhangb76af932018-05-22 12:08:35 -07001400 // If there is a key interrupt in progress, return KeyError::INTERRUPTED without starting the
1401 // menu.
1402 if (IsKeyInterrupted()) return static_cast<size_t>(KeyError::INTERRUPTED);
1403
Tianjie Xub99e6062018-10-16 15:13:09 -07001404 CHECK(menu != nullptr);
Tao Bao3aec6962018-04-20 09:24:58 -07001405
Tianjie Xub99e6062018-10-16 15:13:09 -07001406 // Starts and displays the menu
1407 menu_ = std::move(menu);
1408 Redraw();
1409
1410 int selected = menu_->selection();
Tao Bao3aec6962018-04-20 09:24:58 -07001411 int chosen_item = -1;
1412 while (chosen_item < 0) {
Alessandro Astone84042042020-10-04 18:11:40 +02001413 InputEvent evt = WaitInputEvent();
1414 if (evt.type() == EventType::EXTRA) {
1415 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) {
1416 // WaitKey() was interrupted.
1417 return static_cast<size_t>(KeyError::INTERRUPTED);
1418 }
1419 if (evt.key() == static_cast<int>(KeyError::TIMED_OUT)) { // WaitKey() timed out.
1420 if (WasTextEverVisible()) {
1421 continue;
1422 } else {
1423 LOG(INFO) << "Timed out waiting for key input; rebooting.";
1424 menu_.reset();
1425 Redraw();
1426 return static_cast<size_t>(KeyError::TIMED_OUT);
1427 }
Tao Bao3aec6962018-04-20 09:24:58 -07001428 }
1429 }
1430
Alessandro Astone84042042020-10-04 18:11:40 +02001431 int action = Device::kNoAction;
1432 if (evt.type() == EventType::TOUCH) {
1433 int touch_sel = SelectMenu(evt.pos());
1434 if (touch_sel < 0) {
1435 action = touch_sel;
1436 } else {
1437 action = Device::kInvokeItem;
1438 selected = touch_sel;
1439 }
1440 } else {
1441 bool visible = IsTextVisible();
1442 action = key_handler(evt.key(), visible);
1443 }
Tao Bao3aec6962018-04-20 09:24:58 -07001444 if (action < 0) {
1445 switch (action) {
1446 case Device::kHighlightUp:
1447 selected = SelectMenu(--selected);
1448 break;
1449 case Device::kHighlightDown:
1450 selected = SelectMenu(++selected);
1451 break;
Alessandro Astone84042042020-10-04 18:11:40 +02001452 case Device::kScrollUp:
1453 selected = ScrollMenu(-1);
1454 break;
1455 case Device::kScrollDown:
1456 selected = ScrollMenu(1);
1457 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001458 case Device::kInvokeItem:
Alessandro Astone5a57a942020-03-09 23:17:50 +01001459 if (selected < 0) {
1460 chosen_item = Device::kGoBack;
1461 } else {
1462 chosen_item = selected;
1463 }
Tao Bao3aec6962018-04-20 09:24:58 -07001464 break;
1465 case Device::kNoAction:
1466 break;
Tom Marshallf20b3e32017-08-24 13:50:01 +00001467 case Device::kGoBack:
1468 chosen_item = Device::kGoBack;
1469 break;
1470 case Device::kGoHome:
1471 chosen_item = Device::kGoHome;
1472 break;
Tom Marshall9f2f3282018-12-17 15:57:44 -08001473 case Device::kDoSideload:
1474 chosen_item = Device::kDoSideload;
1475 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001476 }
1477 } else if (!menu_only) {
1478 chosen_item = action;
1479 }
Tom Marshall9f2f3282018-12-17 15:57:44 -08001480
1481 if (chosen_item == Device::kGoBack || chosen_item == Device::kGoHome ||
1482 chosen_item == Device::kDoSideload) {
Tom Marshallf20b3e32017-08-24 13:50:01 +00001483 break;
1484 }
Tao Bao3aec6962018-04-20 09:24:58 -07001485 }
1486
Tianjie Xub99e6062018-10-16 15:13:09 -07001487 menu_.reset();
Tianjie Xub99e6062018-10-16 15:13:09 -07001488
Tao Bao3aec6962018-04-20 09:24:58 -07001489 return chosen_item;
1490}
1491
Tianjie Xub99e6062018-10-16 15:13:09 -07001492size_t ScreenRecoveryUI::ShowMenu(const std::vector<std::string>& headers,
1493 const std::vector<std::string>& items, size_t initial_selection,
1494 bool menu_only,
1495 const std::function<int(int, bool)>& key_handler) {
1496 auto menu = CreateMenu(headers, items, initial_selection);
1497 if (menu == nullptr) {
1498 return initial_selection;
1499 }
1500
Tao Baofa8e02a2019-06-27 09:07:04 -07001501 return ShowMenu(std::move(menu), menu_only, key_handler);
Tianjie Xub99e6062018-10-16 15:13:09 -07001502}
1503
1504size_t ScreenRecoveryUI::ShowPromptWipeDataMenu(const std::vector<std::string>& backup_headers,
1505 const std::vector<std::string>& backup_items,
1506 const std::function<int(int, bool)>& key_handler) {
Tao Baoda409fb2018-10-21 23:36:26 -07001507 auto wipe_data_menu = CreateMenu(wipe_data_menu_header_text_.get(),
1508 { try_again_text_.get(), factory_data_reset_text_.get() },
1509 backup_headers, backup_items, 0);
Tianjie Xub99e6062018-10-16 15:13:09 -07001510 if (wipe_data_menu == nullptr) {
1511 return 0;
1512 }
1513
1514 return ShowMenu(std::move(wipe_data_menu), true, key_handler);
1515}
1516
Tianjie Xu1a0a30a2018-10-25 15:22:07 -07001517size_t ScreenRecoveryUI::ShowPromptWipeDataConfirmationMenu(
1518 const std::vector<std::string>& backup_headers, const std::vector<std::string>& backup_items,
1519 const std::function<int(int, bool)>& key_handler) {
1520 auto confirmation_menu =
1521 CreateMenu(wipe_data_confirmation_text_.get(),
1522 { cancel_wipe_data_text_.get(), factory_data_reset_text_.get() }, backup_headers,
1523 backup_items, 0);
1524 if (confirmation_menu == nullptr) {
1525 return 0;
1526 }
1527
1528 return ShowMenu(std::move(confirmation_menu), true, key_handler);
1529}
1530
Elliott Hughes8de52072015-04-08 20:06:50 -07001531bool ScreenRecoveryUI::IsTextVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001532 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001533 int visible = show_text;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001534 return visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001535}
1536
Elliott Hughes8de52072015-04-08 20:06:50 -07001537bool ScreenRecoveryUI::WasTextEverVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001538 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001539 int ever_visible = show_text_ever;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001540 return ever_visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001541}
1542
Elliott Hughes8de52072015-04-08 20:06:50 -07001543void ScreenRecoveryUI::ShowText(bool visible) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001544 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001545 show_text = visible;
1546 if (show_text) show_text_ever = true;
1547 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001548}
Doug Zongkerc0441d12013-07-31 11:28:24 -07001549
Elliott Hughes8de52072015-04-08 20:06:50 -07001550void ScreenRecoveryUI::Redraw() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001551 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001552 update_screen_locked();
Doug Zongkerc0441d12013-07-31 11:28:24 -07001553}
Elliott Hughes642aaa72015-04-10 12:47:46 -07001554
1555void ScreenRecoveryUI::KeyLongPress(int) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001556 // Redraw so that if we're in the menu, the highlight
1557 // will change color to indicate a successful long press.
1558 Redraw();
Elliott Hughes642aaa72015-04-10 12:47:46 -07001559}
Tao Baoefb49ad2017-01-31 23:03:10 -08001560
1561void ScreenRecoveryUI::SetLocale(const std::string& new_locale) {
1562 locale_ = new_locale;
1563 rtl_locale_ = false;
1564
1565 if (!new_locale.empty()) {
Tao Bao347a6592018-05-08 15:58:29 -07001566 size_t separator = new_locale.find('-');
1567 // lang has the language prefix prior to the separator, or full string if none exists.
1568 std::string lang = new_locale.substr(0, separator);
Tao Baoefb49ad2017-01-31 23:03:10 -08001569
1570 // A bit cheesy: keep an explicit list of supported RTL languages.
1571 if (lang == "ar" || // Arabic
1572 lang == "fa" || // Persian (Farsi)
1573 lang == "he" || // Hebrew (new language code)
1574 lang == "iw" || // Hebrew (old language code)
1575 lang == "ur") { // Urdu
1576 rtl_locale_ = true;
1577 }
1578 }
1579}
fredchiouf2f5aa22022-02-11 16:39:53 +08001580
1581int ScreenRecoveryUI::SetSwCallback(int code, int value) {
1582 if (!is_graphics_available) { return -1; }
1583 if (code > SW_MAX) { return -1; }
1584 if (code != SW_LID) { return 0; }
1585
1586 /* detect dual display */
1587 if (!gr_has_multiple_connectors()) { return -1; }
1588
1589 /* turn off all screen */
1590 gr_fb_blank(true, DirectRenderManager::DRM_INNER);
1591 gr_fb_blank(true, DirectRenderManager::DRM_OUTER);
1592 gr_color(0, 0, 0, 255);
1593 gr_clear();
1594
1595 /* turn on the screen */
1596 gr_fb_blank(false, value);
1597 gr_flip();
1598
1599 /* set the retation */
1600 std::string rotation_str;
1601 if (value == DirectRenderManager::DRM_OUTER) {
1602 rotation_str =
1603 android::base::GetProperty("ro.minui.second_rotation", "ROTATION_NONE");
1604 } else {
1605 rotation_str =
1606 android::base::GetProperty("ro.minui.default_rotation", "ROTATION_NONE");
1607 }
1608
1609 if (rotation_str == "ROTATION_RIGHT") {
1610 gr_rotate(GRRotation::RIGHT);
1611 } else if (rotation_str == "ROTATION_DOWN") {
1612 gr_rotate(GRRotation::DOWN);
1613 } else if (rotation_str == "ROTATION_LEFT") {
1614 gr_rotate(GRRotation::LEFT);
1615 } else { // "ROTATION_NONE" or unknown string
1616 gr_rotate(GRRotation::NONE);
1617 }
1618 Redraw();
1619
1620 return 0;
1621}