blob: f636a5b36ba32bfb6a4773ab0b0c9c6a4d75b1be [file] [log] [blame]
Doug Zongker211aebc2011-10-28 15:13:10 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Tianjie Xu8f397302018-08-20 13:40:47 -070017#include "recovery_ui/screen_ui.h"
Tao Baoefb49ad2017-01-31 23:03:10 -080018
Elliott Hughes498cda62016-04-14 16:49:04 -070019#include <dirent.h>
Doug Zongker211aebc2011-10-28 15:13:10 -070020#include <errno.h>
21#include <fcntl.h>
Doug Zongker211aebc2011-10-28 15:13:10 -070022#include <stdarg.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/stat.h>
27#include <sys/time.h>
28#include <sys/types.h>
29#include <time.h>
30#include <unistd.h>
31
Tao Bao1fe1afe2018-05-01 15:56:05 -070032#include <algorithm>
Tao Bao26ea9592018-05-09 16:32:02 -070033#include <chrono>
Tianjie Xu29d55752017-09-20 17:53:46 -070034#include <memory>
Tao Bao736d59c2017-01-03 10:15:33 -080035#include <string>
Tao Bao26ea9592018-05-09 16:32:02 -070036#include <thread>
Tianjie Xu29d55752017-09-20 17:53:46 -070037#include <unordered_map>
Elliott Hughes95fc63e2015-04-10 19:12:01 -070038#include <vector>
39
Mark Salyzyn30017e72020-05-13 12:39:12 -070040#include <android-base/chrono_utils.h>
Tianjie Xuc21edd42016-08-05 18:00:04 -070041#include <android-base/logging.h>
Elliott Hughescb220402016-09-23 15:30:55 -070042#include <android-base/properties.h>
Elliott Hughes4b166f02015-12-04 15:30:20 -080043#include <android-base/stringprintf.h>
Tao Baocb5524c2017-09-08 21:25:32 -070044#include <android-base/strings.h>
Tao Baob6918c72015-05-19 17:02:16 -070045
Tao Bao6cd81682018-05-03 21:53:11 -070046#include "minui/minui.h"
47#include "otautil/paths.h"
Tianjie Xu8f397302018-08-20 13:40:47 -070048#include "recovery_ui/device.h"
49#include "recovery_ui/ui.h"
Doug Zongker211aebc2011-10-28 15:13:10 -070050
fredchiouf2f5aa22022-02-11 16:39:53 +080051enum DirectRenderManager {
52 DRM_INNER,
53 DRM_OUTER,
54};
55
Doug Zongker211aebc2011-10-28 15:13:10 -070056// Return the current time as a double (including fractions of a second).
57static double now() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -070058 struct timeval tv;
59 gettimeofday(&tv, nullptr);
60 return tv.tv_sec + tv.tv_usec / 1000000.0;
Doug Zongker211aebc2011-10-28 15:13:10 -070061}
62
Tianjie Xu66dbf632018-10-11 16:54:50 -070063Menu::Menu(size_t initial_selection, const DrawInterface& draw_func)
64 : selection_(initial_selection), draw_funcs_(draw_func) {}
65
Alessandro Astone4a0de5a2020-03-09 23:17:50 +010066int Menu::selection() const {
Tianjie Xu66dbf632018-10-11 16:54:50 -070067 return selection_;
68}
69
Alessandro Astone4a0de5a2020-03-09 23:17:50 +010070TextMenu::TextMenu(bool wrappable, size_t max_items, size_t max_length,
Tianjie Xu66dbf632018-10-11 16:54:50 -070071 const std::vector<std::string>& headers, const std::vector<std::string>& items,
72 size_t initial_selection, int char_height, const DrawInterface& draw_funcs)
73 : Menu(initial_selection, draw_funcs),
Alessandro Astone4a0de5a2020-03-09 23:17:50 +010074 wrappable_(wrappable),
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070075 max_display_items_(max_items),
76 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) {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070079 CHECK_LE(max_items, static_cast<size_t>(std::numeric_limits<int>::max()));
Tao Baoe02a5b22018-05-02 15:46:11 -070080
Alessandro Astone4a0de5a2020-03-09 23:17:50 +010081 size_t items_count = items.size();
Tao Bao1fe1afe2018-05-01 15:56:05 -070082 for (size_t i = 0; i < items_count; ++i) {
83 text_items_.emplace_back(items[i].substr(0, max_item_length_));
Tao Baoe02a5b22018-05-02 15:46:11 -070084 }
Alessandro Astone4a0de5a2020-03-09 23:17:50 +010085 menu_start_ = std::max(0, (int)selection_ - (int)max_display_items_ + 1);
Tao Baoe02a5b22018-05-02 15:46:11 -070086
87 CHECK(!text_items_.empty());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070088}
89
Tianjie Xu66dbf632018-10-11 16:54:50 -070090const std::vector<std::string>& TextMenu::text_headers() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070091 return text_headers_;
92}
93
Tianjie Xu66dbf632018-10-11 16:54:50 -070094std::string TextMenu::TextItem(size_t index) const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070095 CHECK_LT(index, text_items_.size());
96
97 return text_items_[index];
98}
99
Tianjie Xu66dbf632018-10-11 16:54:50 -0700100size_t TextMenu::MenuStart() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700101 return menu_start_;
102}
103
Tianjie Xu66dbf632018-10-11 16:54:50 -0700104size_t TextMenu::MenuEnd() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700105 return std::min(ItemsCount(), menu_start_ + max_display_items_);
106}
107
Tianjie Xu66dbf632018-10-11 16:54:50 -0700108size_t TextMenu::ItemsCount() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700109 return text_items_.size();
110}
111
Tianjie Xu66dbf632018-10-11 16:54:50 -0700112bool TextMenu::ItemsOverflow(std::string* cur_selection_str) const {
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100113 if (ItemsCount() <= max_display_items_) {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700114 return false;
115 }
116
117 *cur_selection_str =
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100118 android::base::StringPrintf("Current item: %d/%zu", selection_ + 1, ItemsCount());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700119 return true;
120}
121
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700122// TODO(xunchang) modify the function parameters to button up & down.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700123int TextMenu::Select(int sel) {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700124 CHECK_LE(ItemsCount(), static_cast<size_t>(std::numeric_limits<int>::max()));
125 int count = ItemsCount();
126
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100127 int min = IsMain() ? 0 : -1; // -1 is back arrow
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700128
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100129 if (sel < min) {
130 selection_ = wrappable() ? count - 1 : min;
131 } else if (sel >= count) {
132 selection_ = wrappable() ? min : count - 1;
133 } else {
134 selection_ = sel;
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700135 }
136
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100137 if (selection_ >= 0) {
138 if (selection_ < menu_start_) {
139 menu_start_ = selection_;
140 } else if (static_cast<size_t>(selection_) >= MenuEnd()) {
141 menu_start_ = selection_ - max_display_items_ + 1;
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700142 }
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700143 }
144
145 return selection_;
146}
147
Alessandro Astone73961412020-10-04 18:11:40 +0200148int TextMenu::SelectVisible(int relative_sel) {
149 int sel = relative_sel;
150 if (menu_start_ > 0) {
151 sel += menu_start_;
152 }
153
154 return Select(sel);
155}
156
157int TextMenu::Scroll(int updown) {
158 if ((updown > 0 && menu_start_ + max_display_items_ < ItemsCount()) ||
159 (updown < 0 && menu_start_ > 0)) {
160 menu_start_ += updown;
161
162 /* We can receive a kInvokeItem event from a different source than touch,
163 like from Power button. For this reason, selection should not get out of
164 the screen. Constrain it to the first or last visible item of the list */
165 if (selection_ < menu_start_) {
166 selection_ = menu_start_;
167 } else if (selection_ >= menu_start_ + max_display_items_) {
168 selection_ = menu_start_ + max_display_items_ - 1;
169 }
170 }
171 return selection_;
172}
173
Tianjie Xu66dbf632018-10-11 16:54:50 -0700174int TextMenu::DrawHeader(int x, int y) const {
175 int offset = 0;
176
177 draw_funcs_.SetColor(UIElement::HEADER);
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100178 offset += draw_funcs_.DrawWrappedTextLines(x, y + offset, text_headers());
Tianjie Xu66dbf632018-10-11 16:54:50 -0700179
180 return offset;
181}
182
183int TextMenu::DrawItems(int x, int y, int screen_width, bool long_press) const {
184 int offset = 0;
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100185 int padding = draw_funcs_.MenuItemPadding();
Tianjie Xu66dbf632018-10-11 16:54:50 -0700186
187 draw_funcs_.SetColor(UIElement::MENU);
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100188 offset += draw_funcs_.DrawHorizontalRule(y + offset) + 4;
189
190 int item_container_offset = offset; // store it for drawing scrollbar on most top
191
Tianjie Xu66dbf632018-10-11 16:54:50 -0700192 for (size_t i = MenuStart(); i < MenuEnd(); ++i) {
193 bool bold = false;
194 if (i == selection()) {
195 // Draw the highlight bar.
196 draw_funcs_.SetColor(long_press ? UIElement::MENU_SEL_BG_ACTIVE : UIElement::MENU_SEL_BG);
197
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100198 int bar_height = padding + char_height_ + padding;
199 draw_funcs_.DrawHighlightBar(0, y + offset, screen_width, bar_height);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700200
201 // Bold white text for the selected item.
202 draw_funcs_.SetColor(UIElement::MENU_SEL_FG);
203 bold = true;
204 }
205 offset += draw_funcs_.DrawTextLine(x, y + offset, TextItem(i), bold);
206
207 draw_funcs_.SetColor(UIElement::MENU);
208 }
209 offset += draw_funcs_.DrawHorizontalRule(y + offset);
210
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100211 std::string unused;
212 if (ItemsOverflow(&unused)) {
213 int container_height = max_display_items_ * (2 * padding + char_height_);
214 int bar_height = container_height / (text_items_.size() - max_display_items_ + 1);
215 int start_y = y + item_container_offset + bar_height * menu_start_;
216 draw_funcs_.SetColor(UIElement::SCROLLBAR);
217 draw_funcs_.DrawScrollBar(start_y, bar_height);
218 }
219
Tianjie Xu66dbf632018-10-11 16:54:50 -0700220 return offset;
221}
222
Tao Baoda409fb2018-10-21 23:36:26 -0700223GraphicMenu::GraphicMenu(const GRSurface* graphic_headers,
224 const std::vector<const GRSurface*>& graphic_items,
Tianjie Xub99e6062018-10-16 15:13:09 -0700225 size_t initial_selection, const DrawInterface& draw_funcs)
Tao Baoda409fb2018-10-21 23:36:26 -0700226 : Menu(initial_selection, draw_funcs) {
227 graphic_headers_ = graphic_headers->Clone();
228 graphic_items_.reserve(graphic_items.size());
229 for (const auto& item : graphic_items) {
230 graphic_items_.emplace_back(item->Clone());
231 }
232}
Tianjie Xu66dbf632018-10-11 16:54:50 -0700233
234int GraphicMenu::Select(int sel) {
235 CHECK_LE(graphic_items_.size(), static_cast<size_t>(std::numeric_limits<int>::max()));
236 int count = graphic_items_.size();
237
238 // Wraps the selection at boundary if the menu is not scrollable.
239 if (sel < 0) {
240 selection_ = count - 1;
241 } else if (sel >= count) {
242 selection_ = 0;
243 } else {
244 selection_ = sel;
245 }
246
247 return selection_;
248}
249
250int GraphicMenu::DrawHeader(int x, int y) const {
Tianjie Xub99e6062018-10-16 15:13:09 -0700251 draw_funcs_.SetColor(UIElement::HEADER);
Tao Baoda409fb2018-10-21 23:36:26 -0700252 draw_funcs_.DrawTextIcon(x, y, graphic_headers_.get());
Tianjie Xu66dbf632018-10-11 16:54:50 -0700253 return graphic_headers_->height;
254}
255
256int GraphicMenu::DrawItems(int x, int y, int screen_width, bool long_press) const {
257 int offset = 0;
258
259 draw_funcs_.SetColor(UIElement::MENU);
260 offset += draw_funcs_.DrawHorizontalRule(y + offset) + 4;
261
262 for (size_t i = 0; i < graphic_items_.size(); i++) {
263 auto& item = graphic_items_[i];
264 if (i == selection_) {
265 draw_funcs_.SetColor(long_press ? UIElement::MENU_SEL_BG_ACTIVE : UIElement::MENU_SEL_BG);
266
267 int bar_height = item->height + 4;
268 draw_funcs_.DrawHighlightBar(0, y + offset - 2, screen_width, bar_height);
269
270 // Bold white text for the selected item.
271 draw_funcs_.SetColor(UIElement::MENU_SEL_FG);
272 }
Tao Baoda409fb2018-10-21 23:36:26 -0700273 draw_funcs_.DrawTextIcon(x, y + offset, item.get());
Tianjie Xu66dbf632018-10-11 16:54:50 -0700274 offset += item->height;
275
276 draw_funcs_.SetColor(UIElement::MENU);
277 }
xunchangc7dbc732018-12-20 11:31:18 -0800278 offset += draw_funcs_.DrawHorizontalRule(y + offset);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700279
280 return offset;
281}
282
Alessandro Astone73961412020-10-04 18:11:40 +0200283size_t GraphicMenu::ItemsCount() const {
284 return graphic_items_.size();
285}
286
Tao Baoda409fb2018-10-21 23:36:26 -0700287bool GraphicMenu::Validate(size_t max_width, size_t max_height, const GRSurface* graphic_headers,
288 const std::vector<const GRSurface*>& graphic_items) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700289 int offset = 0;
Tianjie Xub99e6062018-10-16 15:13:09 -0700290 if (!ValidateGraphicSurface(max_width, max_height, offset, graphic_headers)) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700291 return false;
292 }
Tianjie Xub99e6062018-10-16 15:13:09 -0700293 offset += graphic_headers->height;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700294
Tianjie Xub99e6062018-10-16 15:13:09 -0700295 for (const auto& item : graphic_items) {
296 if (!ValidateGraphicSurface(max_width, max_height, offset, item)) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700297 return false;
298 }
299 offset += item->height;
300 }
301
302 return true;
303}
304
Tianjie Xub99e6062018-10-16 15:13:09 -0700305bool GraphicMenu::ValidateGraphicSurface(size_t max_width, size_t max_height, int y,
306 const GRSurface* surface) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700307 if (!surface) {
Tao Baoa00b4492019-01-16 09:29:47 -0800308 fprintf(stderr, "Graphic surface can not be null\n");
Tianjie Xu66dbf632018-10-11 16:54:50 -0700309 return false;
310 }
311
312 if (surface->pixel_bytes != 1 || surface->width != surface->row_bytes) {
Tao Baoa00b4492019-01-16 09:29:47 -0800313 fprintf(stderr, "Invalid graphic surface, pixel bytes: %zu, width: %zu row_bytes: %zu\n",
Tianjie Xu66dbf632018-10-11 16:54:50 -0700314 surface->pixel_bytes, surface->width, surface->row_bytes);
315 return false;
316 }
317
Tianjie Xub99e6062018-10-16 15:13:09 -0700318 if (surface->width > max_width || surface->height > max_height - y) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700319 fprintf(stderr,
Tao Baodd789822018-11-26 16:28:07 -0800320 "Graphic surface doesn't fit into the screen. width: %zu, height: %zu, max_width: %zu,"
Tianjie Xu66dbf632018-10-11 16:54:50 -0700321 " max_height: %zu, vertical offset: %d\n",
Tianjie Xub99e6062018-10-16 15:13:09 -0700322 surface->width, surface->height, max_width, max_height, y);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700323 return false;
324 }
325
326 return true;
327}
328
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100329MenuDrawFunctions::MenuDrawFunctions(const DrawInterface& wrappee)
330 : wrappee_(wrappee) {
331}
332
333int MenuDrawFunctions::DrawTextLine(int x, int y, const std::string& line, bool bold) const {
334 gr_text(gr_menu_font(), x, y + MenuItemPadding(), line.c_str(), bold);
335 return 2 * MenuItemPadding() + MenuCharHeight();
336}
337
338int MenuDrawFunctions::DrawTextLines(int x, int y, const std::vector<std::string>& lines) const {
339 int offset = 0;
340 for (const auto& line : lines) {
341 offset += DrawTextLine(x, y + offset, line, false);
342 }
343 return offset;
344}
345
346int MenuDrawFunctions::DrawWrappedTextLines(int x, int y, const std::vector<std::string>& lines) const {
347 // Keep symmetrical margins based on the given offset (i.e. x).
348 size_t text_cols = (gr_fb_width() - x * 2) / MenuCharWidth();
349 int offset = 0;
350 for (const auto& line : lines) {
351 size_t next_start = 0;
352 while (next_start < line.size()) {
353 std::string sub = line.substr(next_start, text_cols + 1);
354 if (sub.size() <= text_cols) {
355 next_start += sub.size();
356 } else {
357 // Line too long and must be wrapped to text_cols columns.
358 size_t last_space = sub.find_last_of(" \t\n");
359 if (last_space == std::string::npos) {
360 // No space found, just draw as much as we can.
361 sub.resize(text_cols);
362 next_start += text_cols;
363 } else {
364 sub.resize(last_space);
365 next_start += last_space + 1;
366 }
367 }
368 offset += DrawTextLine(x, y + offset, sub, false);
369 }
370 }
371 return offset;
372}
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700373
Tao Bao0bc88de2018-07-31 14:53:16 -0700374constexpr int kDefaultMarginHeight = 0;
375constexpr int kDefaultMarginWidth = 0;
376constexpr int kDefaultAnimationFps = 30;
377
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100378ScreenRecoveryUI::ScreenRecoveryUI()
Tao Bao0bc88de2018-07-31 14:53:16 -0700379 : margin_width_(
380 android::base::GetIntProperty("ro.recovery.ui.margin_width", kDefaultMarginWidth)),
381 margin_height_(
382 android::base::GetIntProperty("ro.recovery.ui.margin_height", kDefaultMarginHeight)),
383 animation_fps_(
384 android::base::GetIntProperty("ro.recovery.ui.animation_fps", kDefaultAnimationFps)),
385 density_(static_cast<float>(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f),
Michael Bestas43ff6792019-03-23 17:28:22 +0200386 blank_unblank_on_init_(
387 android::base::GetBoolProperty("ro.recovery.ui.blank_unblank_on_init", false)),
Tao Baoda409fb2018-10-21 23:36:26 -0700388 current_icon_(NONE),
389 current_frame_(0),
390 intro_done_(false),
Tao Bao736d59c2017-01-03 10:15:33 -0800391 progressBarType(EMPTY),
392 progressScopeStart(0),
393 progressScopeSize(0),
394 progress(0),
395 pagesIdentical(false),
396 text_cols_(0),
397 text_rows_(0),
398 text_(nullptr),
399 text_col_(0),
400 text_row_(0),
Tao Bao736d59c2017-01-03 10:15:33 -0800401 show_text(false),
402 show_text_ever(false),
Tao Bao736d59c2017-01-03 10:15:33 -0800403 file_viewer_text_(nullptr),
Tao Bao736d59c2017-01-03 10:15:33 -0800404 stage(-1),
405 max_stage(-1),
Tao Baoefb49ad2017-01-31 23:03:10 -0800406 locale_(""),
fredchiouf2f5aa22022-02-11 16:39:53 +0800407 rtl_locale_(false),
408 is_graphics_available(false) {}
Doug Zongker211aebc2011-10-28 15:13:10 -0700409
Tao Bao26ea9592018-05-09 16:32:02 -0700410ScreenRecoveryUI::~ScreenRecoveryUI() {
411 progress_thread_stopped_ = true;
Tao Bao94371fd2018-06-06 07:38:54 -0700412 if (progress_thread_.joinable()) {
413 progress_thread_.join();
414 }
Tao Bao60ac6222018-06-13 14:33:51 -0700415 // No-op if gr_init() (via Init()) was not called or had failed.
416 gr_exit();
Tao Bao26ea9592018-05-09 16:32:02 -0700417}
418
Tao Baoda409fb2018-10-21 23:36:26 -0700419const GRSurface* ScreenRecoveryUI::GetCurrentFrame() const {
420 if (current_icon_ == INSTALLING_UPDATE || current_icon_ == ERASING) {
421 return intro_done_ ? loop_frames_[current_frame_].get() : intro_frames_[current_frame_].get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700422 }
Tao Baoda409fb2018-10-21 23:36:26 -0700423 return error_icon_.get();
Elliott Hughes498cda62016-04-14 16:49:04 -0700424}
425
Tao Baoda409fb2018-10-21 23:36:26 -0700426const GRSurface* ScreenRecoveryUI::GetCurrentText() const {
427 switch (current_icon_) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700428 case ERASING:
Tao Baoda409fb2018-10-21 23:36:26 -0700429 return erasing_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700430 case ERROR:
Tao Baoda409fb2018-10-21 23:36:26 -0700431 return error_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700432 case INSTALLING_UPDATE:
Tao Baoda409fb2018-10-21 23:36:26 -0700433 return installing_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700434 case NO_COMMAND:
Tao Baoda409fb2018-10-21 23:36:26 -0700435 return no_command_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700436 case NONE:
437 abort();
438 }
Elliott Hughes498cda62016-04-14 16:49:04 -0700439}
440
Mikhail Lappob49767c2017-03-23 21:44:26 +0100441int ScreenRecoveryUI::PixelsFromDp(int dp) const {
Tao Bao0bc88de2018-07-31 14:53:16 -0700442 return dp * density_;
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700443}
444
445// Here's the intended layout:
446
Elliott Hughes6d089a92016-07-08 17:23:41 -0700447// | portrait large landscape large
448// ---------+-------------------------------------------------
Tao Bao3250f722017-06-29 14:32:05 -0700449// gap |
Elliott Hughes6d089a92016-07-08 17:23:41 -0700450// icon | (200dp)
451// gap | 68dp 68dp 56dp 112dp
452// text | (14sp)
453// gap | 32dp 32dp 26dp 52dp
454// progress | (2dp)
Tao Bao3250f722017-06-29 14:32:05 -0700455// gap |
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700456
Tao Bao3250f722017-06-29 14:32:05 -0700457// Note that "baseline" is actually the *top* of each icon (because that's how our drawing routines
458// 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 -0700459
Elliott Hughes6d089a92016-07-08 17:23:41 -0700460enum Layout { PORTRAIT = 0, PORTRAIT_LARGE = 1, LANDSCAPE = 2, LANDSCAPE_LARGE = 3, LAYOUT_MAX };
Tao Bao3250f722017-06-29 14:32:05 -0700461enum Dimension { TEXT = 0, ICON = 1, DIMENSION_MAX };
Elliott Hughes6d089a92016-07-08 17:23:41 -0700462static constexpr int kLayouts[LAYOUT_MAX][DIMENSION_MAX] = {
Tianjie Xu8f397302018-08-20 13:40:47 -0700463 { 32, 68 }, // PORTRAIT
464 { 32, 68 }, // PORTRAIT_LARGE
465 { 26, 56 }, // LANDSCAPE
466 { 52, 112 }, // LANDSCAPE_LARGE
Elliott Hughes6d089a92016-07-08 17:23:41 -0700467};
468
Tao Bao99b2d772017-06-23 22:47:03 -0700469int ScreenRecoveryUI::GetAnimationBaseline() const {
Tao Baoda409fb2018-10-21 23:36:26 -0700470 return GetTextBaseline() - PixelsFromDp(kLayouts[layout_][ICON]) -
471 gr_get_height(loop_frames_[0].get());
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700472}
473
Tao Bao99b2d772017-06-23 22:47:03 -0700474int ScreenRecoveryUI::GetTextBaseline() const {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700475 return GetProgressBaseline() - PixelsFromDp(kLayouts[layout_][TEXT]) -
Tao Baoda409fb2018-10-21 23:36:26 -0700476 gr_get_height(installing_text_.get());
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700477}
478
Tao Bao99b2d772017-06-23 22:47:03 -0700479int ScreenRecoveryUI::GetProgressBaseline() const {
Tao Baoda409fb2018-10-21 23:36:26 -0700480 int elements_sum = gr_get_height(loop_frames_[0].get()) + PixelsFromDp(kLayouts[layout_][ICON]) +
481 gr_get_height(installing_text_.get()) + PixelsFromDp(kLayouts[layout_][TEXT]) +
482 gr_get_height(progress_bar_fill_.get());
Luke Song92eda4d2017-09-19 10:51:35 -0700483 int bottom_gap = (ScreenHeight() - elements_sum) / 2;
Tao Baoda409fb2018-10-21 23:36:26 -0700484 return ScreenHeight() - bottom_gap - gr_get_height(progress_bar_fill_.get());
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700485}
486
Doug Zongker211aebc2011-10-28 15:13:10 -0700487// Clear the screen and draw the currently selected background icon (if any).
488// Should only be called with updateMutex locked.
Elliott Hughes498cda62016-04-14 16:49:04 -0700489void ScreenRecoveryUI::draw_background_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700490 pagesIdentical = false;
491 gr_color(0, 0, 0, 255);
492 gr_clear();
Tao Baoda409fb2018-10-21 23:36:26 -0700493 if (current_icon_ != NONE) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700494 if (max_stage != -1) {
Tao Baoda409fb2018-10-21 23:36:26 -0700495 int stage_height = gr_get_height(stage_marker_empty_.get());
496 int stage_width = gr_get_width(stage_marker_empty_.get());
497 int x = (ScreenWidth() - max_stage * gr_get_width(stage_marker_empty_.get())) / 2;
Tao Bao0bc88de2018-07-31 14:53:16 -0700498 int y = ScreenHeight() - stage_height - margin_height_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700499 for (int i = 0; i < max_stage; ++i) {
Tao Baoda409fb2018-10-21 23:36:26 -0700500 const auto& stage_surface = (i < stage) ? stage_marker_fill_ : stage_marker_empty_;
501 DrawSurface(stage_surface.get(), 0, 0, stage_width, stage_height, x, y);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700502 x += stage_width;
503 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700504 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700505
Tao Baoda409fb2018-10-21 23:36:26 -0700506 const auto& text_surface = GetCurrentText();
Luke Song92eda4d2017-09-19 10:51:35 -0700507 int text_x = (ScreenWidth() - gr_get_width(text_surface)) / 2;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700508 int text_y = GetTextBaseline();
509 gr_color(255, 255, 255, 255);
Luke Song92eda4d2017-09-19 10:51:35 -0700510 DrawTextIcon(text_x, text_y, text_surface);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700511 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700512}
513
Tao Baoea78d862017-06-28 14:52:17 -0700514// Draws the animation and progress bar (if any) on the screen. Does not flip pages. Should only be
515// called with updateMutex locked.
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700516void ScreenRecoveryUI::draw_foreground_locked() {
Tao Baoda409fb2018-10-21 23:36:26 -0700517 if (current_icon_ != NONE) {
518 const auto& frame = GetCurrentFrame();
Tao Bao736d59c2017-01-03 10:15:33 -0800519 int frame_width = gr_get_width(frame);
520 int frame_height = gr_get_height(frame);
Luke Song92eda4d2017-09-19 10:51:35 -0700521 int frame_x = (ScreenWidth() - frame_width) / 2;
Tao Bao736d59c2017-01-03 10:15:33 -0800522 int frame_y = GetAnimationBaseline();
zhang sanshan0d81b892019-08-07 16:21:10 +0800523 if (frame_x >= 0 && frame_y >= 0 && (frame_x + frame_width) < ScreenWidth() &&
524 (frame_y + frame_height) < ScreenHeight())
525 DrawSurface(frame, 0, 0, frame_width, frame_height, frame_x, frame_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800526 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700527
Tao Bao736d59c2017-01-03 10:15:33 -0800528 if (progressBarType != EMPTY) {
Tao Baoda409fb2018-10-21 23:36:26 -0700529 int width = gr_get_width(progress_bar_empty_.get());
530 int height = gr_get_height(progress_bar_empty_.get());
Doug Zongker211aebc2011-10-28 15:13:10 -0700531
Luke Song92eda4d2017-09-19 10:51:35 -0700532 int progress_x = (ScreenWidth() - width) / 2;
Tao Bao736d59c2017-01-03 10:15:33 -0800533 int progress_y = GetProgressBaseline();
Doug Zongker211aebc2011-10-28 15:13:10 -0700534
Tao Bao736d59c2017-01-03 10:15:33 -0800535 // Erase behind the progress bar (in case this was a progress-only update)
536 gr_color(0, 0, 0, 255);
Luke Song92eda4d2017-09-19 10:51:35 -0700537 DrawFill(progress_x, progress_y, width, height);
Doug Zongker211aebc2011-10-28 15:13:10 -0700538
Tao Bao736d59c2017-01-03 10:15:33 -0800539 if (progressBarType == DETERMINATE) {
540 float p = progressScopeStart + progress * progressScopeSize;
541 int pos = static_cast<int>(p * width);
Doug Zongker211aebc2011-10-28 15:13:10 -0700542
Tao Bao736d59c2017-01-03 10:15:33 -0800543 if (rtl_locale_) {
544 // Fill the progress bar from right to left.
545 if (pos > 0) {
Tao Baoda409fb2018-10-21 23:36:26 -0700546 DrawSurface(progress_bar_fill_.get(), width - pos, 0, pos, height,
547 progress_x + width - pos, progress_y);
Doug Zongker211aebc2011-10-28 15:13:10 -0700548 }
Tao Bao736d59c2017-01-03 10:15:33 -0800549 if (pos < width - 1) {
Tao Baoda409fb2018-10-21 23:36:26 -0700550 DrawSurface(progress_bar_empty_.get(), 0, 0, width - pos, height, progress_x, progress_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800551 }
552 } else {
553 // Fill the progress bar from left to right.
554 if (pos > 0) {
Tao Baoda409fb2018-10-21 23:36:26 -0700555 DrawSurface(progress_bar_fill_.get(), 0, 0, pos, height, progress_x, progress_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800556 }
557 if (pos < width - 1) {
Tao Baoda409fb2018-10-21 23:36:26 -0700558 DrawSurface(progress_bar_empty_.get(), pos, 0, width - pos, height, progress_x + pos,
559 progress_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800560 }
561 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700562 }
Tao Bao736d59c2017-01-03 10:15:33 -0800563 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700564}
565
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100566/* Lineage teal: #167c80 */
Tao Bao99b2d772017-06-23 22:47:03 -0700567void ScreenRecoveryUI::SetColor(UIElement e) const {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700568 switch (e) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700569 case UIElement::INFO:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700570 gr_color(249, 194, 0, 255);
571 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700572 case UIElement::HEADER:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700573 gr_color(247, 0, 6, 255);
574 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700575 case UIElement::MENU:
576 case UIElement::MENU_SEL_BG:
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100577 gr_color(0xd8, 0xd8, 0xd8, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700578 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700579 case UIElement::MENU_SEL_BG_ACTIVE:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700580 gr_color(0, 156, 100, 255);
581 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700582 case UIElement::MENU_SEL_FG:
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100583 case UIElement::SCROLLBAR:
584 gr_color(0x16, 0x7c, 0x80, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700585 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700586 case UIElement::LOG:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700587 gr_color(196, 196, 196, 255);
588 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700589 case UIElement::TEXT_FILL:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700590 gr_color(0, 0, 0, 160);
591 break;
592 default:
593 gr_color(255, 255, 255, 255);
594 break;
595 }
Doug Zongkerc0441d12013-07-31 11:28:24 -0700596}
Doug Zongker211aebc2011-10-28 15:13:10 -0700597
Tianjie Xu29d55752017-09-20 17:53:46 -0700598void ScreenRecoveryUI::SelectAndShowBackgroundText(const std::vector<std::string>& locales_entries,
599 size_t sel) {
600 SetLocale(locales_entries[sel]);
601 std::vector<std::string> text_name = { "erasing_text", "error_text", "installing_text",
602 "installing_security_text", "no_command_text" };
Tao Baoda409fb2018-10-21 23:36:26 -0700603 std::unordered_map<std::string, std::unique_ptr<GRSurface>> surfaces;
Tianjie Xu29d55752017-09-20 17:53:46 -0700604 for (const auto& name : text_name) {
Tao Baoda409fb2018-10-21 23:36:26 -0700605 auto text_image = LoadLocalizedBitmap(name);
Tianjie Xu29d55752017-09-20 17:53:46 -0700606 if (!text_image) {
607 Print("Failed to load %s\n", name.c_str());
608 return;
609 }
Tao Baoda409fb2018-10-21 23:36:26 -0700610 surfaces.emplace(name, std::move(text_image));
Tianjie Xu29d55752017-09-20 17:53:46 -0700611 }
612
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700613 std::lock_guard<std::mutex> lg(updateMutex);
Tianjie Xu29d55752017-09-20 17:53:46 -0700614 gr_color(0, 0, 0, 255);
615 gr_clear();
616
Tao Bao0bc88de2018-07-31 14:53:16 -0700617 int text_y = margin_height_;
618 int text_x = margin_width_;
Tianjie Xu29d55752017-09-20 17:53:46 -0700619 int line_spacing = gr_sys_font()->char_height; // Put some extra space between images.
620 // Write the header and descriptive texts.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700621 SetColor(UIElement::INFO);
Tianjie Xu29d55752017-09-20 17:53:46 -0700622 std::string header = "Show background text image";
Tao Bao93e46ad2018-05-02 14:57:21 -0700623 text_y += DrawTextLine(text_x, text_y, header, true);
Tianjie Xu29d55752017-09-20 17:53:46 -0700624 std::string locale_selection = android::base::StringPrintf(
Tao Bao39c49182018-05-07 22:50:33 -0700625 "Current locale: %s, %zu/%zu", locales_entries[sel].c_str(), sel + 1, locales_entries.size());
Tao Bao93e46ad2018-05-02 14:57:21 -0700626 // clang-format off
627 std::vector<std::string> instruction = {
628 locale_selection,
629 "Use volume up/down to switch locales and power to exit."
630 };
631 // clang-format on
Tianjie Xu29d55752017-09-20 17:53:46 -0700632 text_y += DrawWrappedTextLines(text_x, text_y, instruction);
633
634 // Iterate through the text images and display them in order for the current locale.
635 for (const auto& p : surfaces) {
636 text_y += line_spacing;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700637 SetColor(UIElement::LOG);
Tao Bao93e46ad2018-05-02 14:57:21 -0700638 text_y += DrawTextLine(text_x, text_y, p.first, false);
Tianjie Xu29d55752017-09-20 17:53:46 -0700639 gr_color(255, 255, 255, 255);
640 gr_texticon(text_x, text_y, p.second.get());
641 text_y += gr_get_height(p.second.get());
642 }
643 // Update the whole screen.
644 gr_flip();
Tianjie Xu29d55752017-09-20 17:53:46 -0700645}
646
Tao Bao39c49182018-05-07 22:50:33 -0700647void ScreenRecoveryUI::CheckBackgroundTextImages() {
Tianjie Xu29d55752017-09-20 17:53:46 -0700648 // Load a list of locales embedded in one of the resource files.
649 std::vector<std::string> locales_entries = get_locales_in_png("installing_text");
650 if (locales_entries.empty()) {
651 Print("Failed to load locales from the resource files\n");
652 return;
653 }
Tao Bao39c49182018-05-07 22:50:33 -0700654 std::string saved_locale = locale_;
Tianjie Xu29d55752017-09-20 17:53:46 -0700655 size_t selected = 0;
656 SelectAndShowBackgroundText(locales_entries, selected);
657
658 FlushKeys();
659 while (true) {
Alessandro Astone73961412020-10-04 18:11:40 +0200660 InputEvent evt = WaitInputEvent();
661 if (evt.type() == EventType::EXTRA) {
662 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) break;
663 }
664 if (evt.type() == EventType::KEY) {
665 if (evt.key() == KEY_POWER || evt.key() == KEY_ENTER) {
666 break;
667 } else if (evt.key() == KEY_UP || evt.key() == KEY_VOLUMEUP) {
668 selected = (selected == 0) ? locales_entries.size() - 1 : selected - 1;
669 SelectAndShowBackgroundText(locales_entries, selected);
670 } else if (evt.key() == KEY_DOWN || evt.key() == KEY_VOLUMEDOWN) {
671 selected = (selected == locales_entries.size() - 1) ? 0 : selected + 1;
672 SelectAndShowBackgroundText(locales_entries, selected);
673 }
Tianjie Xu29d55752017-09-20 17:53:46 -0700674 }
675 }
676
677 SetLocale(saved_locale);
678}
679
Luke Song92eda4d2017-09-19 10:51:35 -0700680int ScreenRecoveryUI::ScreenWidth() const {
681 return gr_fb_width();
682}
683
684int ScreenRecoveryUI::ScreenHeight() const {
685 return gr_fb_height();
686}
687
Tao Bao65815b62018-10-23 10:54:02 -0700688void ScreenRecoveryUI::DrawSurface(const GRSurface* surface, int sx, int sy, int w, int h, int dx,
Luke Song92eda4d2017-09-19 10:51:35 -0700689 int dy) const {
690 gr_blit(surface, sx, sy, w, h, dx, dy);
691}
692
Tao Baoea78d862017-06-28 14:52:17 -0700693int ScreenRecoveryUI::DrawHorizontalRule(int y) const {
Luke Song92eda4d2017-09-19 10:51:35 -0700694 gr_fill(0, y + 4, ScreenWidth(), y + 6);
Tao Baoea78d862017-06-28 14:52:17 -0700695 return 8;
Elliott Hughes95fc63e2015-04-10 19:12:01 -0700696}
697
Luke Songe2bd8762017-06-12 16:08:33 -0700698void ScreenRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const {
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100699 if (y + height > ScreenHeight())
700 height = ScreenHeight() - y;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700701 gr_fill(x, y, x + width, y + height);
Luke Songe2bd8762017-06-12 16:08:33 -0700702}
703
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100704void ScreenRecoveryUI::DrawScrollBar(int y, int height) const {
705 int x = ScreenWidth() - margin_width_;
706 int width = 8;
707 gr_fill(x - width, y, x, y + height);
708}
709
Luke Song92eda4d2017-09-19 10:51:35 -0700710void ScreenRecoveryUI::DrawFill(int x, int y, int w, int h) const {
711 gr_fill(x, y, w, h);
712}
713
Tao Bao65815b62018-10-23 10:54:02 -0700714void ScreenRecoveryUI::DrawTextIcon(int x, int y, const GRSurface* surface) const {
Luke Song92eda4d2017-09-19 10:51:35 -0700715 gr_texticon(x, y, surface);
716}
717
Tao Bao93e46ad2018-05-02 14:57:21 -0700718int ScreenRecoveryUI::DrawTextLine(int x, int y, const std::string& line, bool bold) const {
719 gr_text(gr_sys_font(), x, y, line.c_str(), bold);
Tao Baoea78d862017-06-28 14:52:17 -0700720 return char_height_ + 4;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700721}
722
Tao Bao93e46ad2018-05-02 14:57:21 -0700723int ScreenRecoveryUI::DrawTextLines(int x, int y, const std::vector<std::string>& lines) const {
Tao Baoea78d862017-06-28 14:52:17 -0700724 int offset = 0;
Tao Bao93e46ad2018-05-02 14:57:21 -0700725 for (const auto& line : lines) {
726 offset += DrawTextLine(x, y + offset, line, false);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700727 }
Tao Baoea78d862017-06-28 14:52:17 -0700728 return offset;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700729}
730
Tao Bao93e46ad2018-05-02 14:57:21 -0700731int ScreenRecoveryUI::DrawWrappedTextLines(int x, int y,
732 const std::vector<std::string>& lines) const {
Tao Bao452b4872018-05-09 11:52:09 -0700733 // Keep symmetrical margins based on the given offset (i.e. x).
734 size_t text_cols = (ScreenWidth() - x * 2) / char_width_;
Tao Bao2bbc6d62017-08-13 23:48:55 -0700735 int offset = 0;
Tao Bao93e46ad2018-05-02 14:57:21 -0700736 for (const auto& line : lines) {
Tao Bao2bbc6d62017-08-13 23:48:55 -0700737 size_t next_start = 0;
738 while (next_start < line.size()) {
Tao Bao452b4872018-05-09 11:52:09 -0700739 std::string sub = line.substr(next_start, text_cols + 1);
740 if (sub.size() <= text_cols) {
Tao Bao2bbc6d62017-08-13 23:48:55 -0700741 next_start += sub.size();
742 } else {
Tao Bao452b4872018-05-09 11:52:09 -0700743 // Line too long and must be wrapped to text_cols columns.
Tao Bao2bbc6d62017-08-13 23:48:55 -0700744 size_t last_space = sub.find_last_of(" \t\n");
745 if (last_space == std::string::npos) {
Tao Bao93e46ad2018-05-02 14:57:21 -0700746 // No space found, just draw as much as we can.
Tao Bao452b4872018-05-09 11:52:09 -0700747 sub.resize(text_cols);
748 next_start += text_cols;
Tao Bao2bbc6d62017-08-13 23:48:55 -0700749 } else {
750 sub.resize(last_space);
751 next_start += last_space + 1;
752 }
753 }
Tao Bao93e46ad2018-05-02 14:57:21 -0700754 offset += DrawTextLine(x, y + offset, sub, false);
Tao Bao2bbc6d62017-08-13 23:48:55 -0700755 }
756 }
757 return offset;
758}
759
Jerry Zhang0e577ee2018-05-07 11:21:10 -0700760void ScreenRecoveryUI::SetTitle(const std::vector<std::string>& lines) {
761 title_lines_ = lines;
762}
763
Tianjie Xue5032212019-07-23 13:23:29 -0700764std::vector<std::string> ScreenRecoveryUI::GetMenuHelpMessage() const {
765 // clang-format off
766 static std::vector<std::string> REGULAR_HELP{
767 "Use volume up/down and power.",
768 };
769 static std::vector<std::string> LONG_PRESS_HELP{
770 "Any button cycles highlight.",
771 "Long-press activates.",
772 };
773 // clang-format on
774 return HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP;
775}
776
Tao Bao171b4c42017-06-19 23:10:44 -0700777// Redraws everything on the screen. Does not flip pages. Should only be called with updateMutex
778// locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700779void ScreenRecoveryUI::draw_screen_locked() {
Tao Bao171b4c42017-06-19 23:10:44 -0700780 if (!show_text) {
781 draw_background_locked();
782 draw_foreground_locked();
783 return;
784 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700785
Tao Bao171b4c42017-06-19 23:10:44 -0700786 gr_color(0, 0, 0, 255);
787 gr_clear();
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700788
Tianjie Xue5032212019-07-23 13:23:29 -0700789 draw_menu_and_text_buffer_locked(GetMenuHelpMessage());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700790}
791
792// Draws the menu and text buffer on the screen. Should only be called with updateMutex locked.
Tao Bao93e46ad2018-05-02 14:57:21 -0700793void ScreenRecoveryUI::draw_menu_and_text_buffer_locked(
794 const std::vector<std::string>& help_message) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700795 int y = margin_height_;
David Anderson983e2d52019-01-02 11:35:38 -0800796
797 if (fastbootd_logo_ && fastbootd_logo_enabled_) {
798 // Try to get this centered on screen.
799 auto width = gr_get_width(fastbootd_logo_.get());
800 auto height = gr_get_height(fastbootd_logo_.get());
801 auto centered_x = ScreenWidth() / 2 - width / 2;
802 DrawSurface(fastbootd_logo_.get(), 0, 0, width, height, centered_x, y);
803 y += height;
804 }
805
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700806 if (menu_) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700807 int x = margin_width_ + kMenuIndent;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700808
Tianjie Xu66dbf632018-10-11 16:54:50 -0700809 SetColor(UIElement::INFO);
Jerry Zhang0e577ee2018-05-07 11:21:10 -0700810
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100811 if (lineage_logo_ && back_icon_) {
812 auto logo_width = gr_get_width(lineage_logo_.get());
813 auto logo_height = gr_get_height(lineage_logo_.get());
814 auto centered_x = ScreenWidth() / 2 - logo_width / 2;
815 DrawSurface(lineage_logo_.get(), 0, 0, logo_width, logo_height, centered_x, y);
816 y += logo_height;
Tao Bao171b4c42017-06-19 23:10:44 -0700817
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100818 if (!menu_->IsMain()) {
819 auto icon_w = gr_get_width(back_icon_.get());
820 auto icon_h = gr_get_height(back_icon_.get());
821 auto icon_x = centered_x / 2 - icon_w / 2;
822 auto icon_y = y - logo_height / 2 - icon_h / 2;
823 gr_blit(back_icon_sel_ && menu_->selection() == -1 ? back_icon_sel_.get() : back_icon_.get(),
824 0, 0, icon_w, icon_h, icon_x, icon_y);
825 }
826 } else {
827 for (size_t i = 0; i < title_lines_.size(); i++) {
828 y += DrawTextLine(x, y, title_lines_[i], i == 0);
829 }
830 y += DrawTextLines(x, y, help_message);
831 }
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700832
Tianjie Xu66dbf632018-10-11 16:54:50 -0700833 y += menu_->DrawHeader(x, y);
Alessandro Astone73961412020-10-04 18:11:40 +0200834 menu_start_y_ = y + 12; // Skip horizontal rule and some margin
Tianjie Xu66dbf632018-10-11 16:54:50 -0700835 y += menu_->DrawItems(x, y, ScreenWidth(), IsLongPress());
Tao Bao171b4c42017-06-19 23:10:44 -0700836 }
837
838 // Display from the bottom up, until we hit the top of the screen, the bottom of the menu, or
839 // we've displayed the entire text buffer.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700840 SetColor(UIElement::LOG);
Tao Baocb5524c2017-09-08 21:25:32 -0700841 int row = text_row_;
Tao Bao171b4c42017-06-19 23:10:44 -0700842 size_t count = 0;
Tao Bao0bc88de2018-07-31 14:53:16 -0700843 for (int ty = ScreenHeight() - margin_height_ - char_height_; ty >= y && count < text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700844 ty -= char_height_, ++count) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700845 DrawTextLine(margin_width_, ty, text_[row], false);
Tao Bao171b4c42017-06-19 23:10:44 -0700846 --row;
847 if (row < 0) row = text_rows_ - 1;
848 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700849}
850
851// Redraw everything on the screen and flip the screen (make it visible).
852// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700853void ScreenRecoveryUI::update_screen_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700854 draw_screen_locked();
855 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700856}
857
858// Updates only the progress bar, if possible, otherwise redraws the screen.
859// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700860void ScreenRecoveryUI::update_progress_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700861 if (show_text || !pagesIdentical) {
862 draw_screen_locked(); // Must redraw the whole screen
863 pagesIdentical = true;
864 } else {
865 draw_foreground_locked(); // Draw only the progress bar and overlays
866 }
867 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700868}
869
Elliott Hughes985022a2015-04-13 13:04:32 -0700870void ScreenRecoveryUI::ProgressThreadLoop() {
Tao Bao0bc88de2018-07-31 14:53:16 -0700871 double interval = 1.0 / animation_fps_;
Tao Bao26ea9592018-05-09 16:32:02 -0700872 while (!progress_thread_stopped_) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700873 double start = now();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700874 bool redraw = false;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700875 {
876 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker211aebc2011-10-28 15:13:10 -0700877
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700878 // update the installation animation, if active
879 // skip this if we have a text overlay (too expensive to update)
Tao Baoda409fb2018-10-21 23:36:26 -0700880 if ((current_icon_ == INSTALLING_UPDATE || current_icon_ == ERASING) && !show_text) {
881 if (!intro_done_) {
882 if (current_frame_ == intro_frames_.size() - 1) {
883 intro_done_ = true;
884 current_frame_ = 0;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700885 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700886 ++current_frame_;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700887 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700888 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700889 current_frame_ = (current_frame_ + 1) % loop_frames_.size();
Doug Zongker211aebc2011-10-28 15:13:10 -0700890 }
891
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700892 redraw = true;
893 }
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700894
895 // move the progress bar forward on timed intervals, if configured
896 int duration = progressScopeDuration;
897 if (progressBarType == DETERMINATE && duration > 0) {
898 double elapsed = now() - progressScopeTime;
899 float p = 1.0 * elapsed / duration;
900 if (p > 1.0) p = 1.0;
901 if (p > progress) {
902 progress = p;
903 redraw = true;
904 }
905 }
906
907 if (redraw) update_progress_locked();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700908 }
909
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700910 double end = now();
911 // minimum of 20ms delay between frames
912 double delay = interval - (end - start);
913 if (delay < 0.02) delay = 0.02;
914 usleep(static_cast<useconds_t>(delay * 1000000));
915 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700916}
917
Tao Baoda409fb2018-10-21 23:36:26 -0700918std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadBitmap(const std::string& filename) {
919 GRSurface* surface;
920 if (auto result = res_create_display_surface(filename.c_str(), &surface); result < 0) {
921 LOG(ERROR) << "Failed to load bitmap " << filename << " (error " << result << ")";
922 return nullptr;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700923 }
Tao Baoda409fb2018-10-21 23:36:26 -0700924 return std::unique_ptr<GRSurface>(surface);
Doug Zongkereac881c2014-03-07 09:21:25 -0800925}
926
Tao Baoda409fb2018-10-21 23:36:26 -0700927std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadLocalizedBitmap(const std::string& filename) {
928 GRSurface* surface;
xunchang9d05c8a2019-04-16 12:07:42 -0700929 auto result = res_create_localized_alpha_surface(filename.c_str(), locale_.c_str(), &surface);
930 if (result == 0) {
931 return std::unique_ptr<GRSurface>(surface);
Tao Bao736d59c2017-01-03 10:15:33 -0800932 }
xunchang9d05c8a2019-04-16 12:07:42 -0700933 // TODO(xunchang) create a error code enum to refine the retry condition.
934 LOG(WARNING) << "Failed to load bitmap " << filename << " for locale " << locale_ << " (error "
935 << result << "). Falling back to use default locale.";
936
937 result = res_create_localized_alpha_surface(filename.c_str(), DEFAULT_LOCALE, &surface);
938 if (result == 0) {
939 return std::unique_ptr<GRSurface>(surface);
940 }
941
942 LOG(ERROR) << "Failed to load bitmap " << filename << " for locale " << DEFAULT_LOCALE
943 << " (error " << result << ")";
944 return nullptr;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700945}
946
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700947static char** Alloc2d(size_t rows, size_t cols) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700948 char** result = new char*[rows];
949 for (size_t i = 0; i < rows; ++i) {
950 result[i] = new char[cols];
951 memset(result[i], 0, cols);
952 }
953 return result;
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700954}
955
Tianjie Xu35926c42016-04-28 18:06:26 -0700956// Choose the right background string to display during update.
957void ScreenRecoveryUI::SetSystemUpdateText(bool security_update) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700958 if (security_update) {
Tao Baoda409fb2018-10-21 23:36:26 -0700959 installing_text_ = LoadLocalizedBitmap("installing_security_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700960 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700961 installing_text_ = LoadLocalizedBitmap("installing_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700962 }
963 Redraw();
Tianjie Xu35926c42016-04-28 18:06:26 -0700964}
965
Sen Jiangd5304492016-12-09 16:20:49 -0800966bool ScreenRecoveryUI::InitTextParams() {
Tao Baoba4edb32018-06-13 11:22:50 -0700967 // gr_init() would return successfully on font initialization failure.
968 if (gr_sys_font() == nullptr) {
Tao Bao171b4c42017-06-19 23:10:44 -0700969 return false;
970 }
Tao Bao171b4c42017-06-19 23:10:44 -0700971 gr_font_size(gr_sys_font(), &char_width_, &char_height_);
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100972 gr_font_size(gr_menu_font(), &menu_char_width_, &menu_char_height_);
Tao Bao0bc88de2018-07-31 14:53:16 -0700973 text_rows_ = (ScreenHeight() - margin_height_ * 2) / char_height_;
974 text_cols_ = (ScreenWidth() - margin_width_ * 2) / char_width_;
Tao Bao171b4c42017-06-19 23:10:44 -0700975 return true;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -0700976}
977
Tianjie Xub99e6062018-10-16 15:13:09 -0700978bool ScreenRecoveryUI::LoadWipeDataMenuText() {
Tianjie Xu1a0a30a2018-10-25 15:22:07 -0700979 // Ignores the errors since the member variables will stay as nullptr.
980 cancel_wipe_data_text_ = LoadLocalizedBitmap("cancel_wipe_data_text");
981 factory_data_reset_text_ = LoadLocalizedBitmap("factory_data_reset_text");
982 try_again_text_ = LoadLocalizedBitmap("try_again_text");
983 wipe_data_confirmation_text_ = LoadLocalizedBitmap("wipe_data_confirmation_text");
984 wipe_data_menu_header_text_ = LoadLocalizedBitmap("wipe_data_menu_header_text");
Tianjie Xub99e6062018-10-16 15:13:09 -0700985 return true;
986}
987
Mark Salyzyn30017e72020-05-13 12:39:12 -0700988static bool InitGraphics() {
989 // Timeout is same as init wait for file default of 5 seconds and is arbitrary
990 const unsigned timeout = 500; // 10ms increments
991 for (auto retry = timeout; retry > 0; --retry) {
992 if (gr_init() == 0) {
993 if (retry < timeout) {
994 // Log message like init wait for file completion log for consistency.
995 LOG(WARNING) << "wait for 'graphics' took " << ((timeout - retry) * 10) << "ms";
996 }
997 return true;
998 }
999 std::this_thread::sleep_for(10ms);
1000 }
1001 // Log message like init wait for file timeout log for consistency.
1002 LOG(ERROR) << "timeout wait for 'graphics' took " << (timeout * 10) << "ms";
1003 return false;
1004}
1005
Tao Bao736d59c2017-01-03 10:15:33 -08001006bool ScreenRecoveryUI::Init(const std::string& locale) {
1007 RecoveryUI::Init(locale);
Tao Baoefb49ad2017-01-31 23:03:10 -08001008
Mark Salyzyn30017e72020-05-13 12:39:12 -07001009 if (!InitGraphics()) {
Tao Baoba4edb32018-06-13 11:22:50 -07001010 return false;
1011 }
fredchiouf2f5aa22022-02-11 16:39:53 +08001012 is_graphics_available = true;
Tao Baoba4edb32018-06-13 11:22:50 -07001013
Tao Bao736d59c2017-01-03 10:15:33 -08001014 if (!InitTextParams()) {
1015 return false;
1016 }
Alessandro Astone4a0de5a2020-03-09 23:17:50 +01001017 menu_draw_funcs_ = std::make_unique<MenuDrawFunctions>(*this);
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001018
Michael Bestas43ff6792019-03-23 17:28:22 +02001019 if (blank_unblank_on_init_) {
1020 gr_fb_blank(true);
1021 gr_fb_blank(false);
1022 }
1023
Tao Bao736d59c2017-01-03 10:15:33 -08001024 // Are we portrait or landscape?
1025 layout_ = (gr_fb_width() > gr_fb_height()) ? LANDSCAPE : PORTRAIT;
1026 // Are we the large variant of our base layout?
1027 if (gr_fb_height() > PixelsFromDp(800)) ++layout_;
Elliott Hughesfaf36e02016-04-20 17:22:16 -07001028
Tao Bao736d59c2017-01-03 10:15:33 -08001029 text_ = Alloc2d(text_rows_, text_cols_ + 1);
1030 file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
Doug Zongker55a36ac2013-03-04 15:49:02 -08001031
Tao Bao736d59c2017-01-03 10:15:33 -08001032 text_col_ = text_row_ = 0;
Doug Zongker211aebc2011-10-28 15:13:10 -07001033
Tao Baoefb49ad2017-01-31 23:03:10 -08001034 // Set up the locale info.
1035 SetLocale(locale);
1036
Tao Baoda409fb2018-10-21 23:36:26 -07001037 error_icon_ = LoadBitmap("icon_error");
Doug Zongker02ec6b82012-08-22 17:26:40 -07001038
Tao Baoda409fb2018-10-21 23:36:26 -07001039 progress_bar_empty_ = LoadBitmap("progress_empty");
1040 progress_bar_fill_ = LoadBitmap("progress_fill");
1041 stage_marker_empty_ = LoadBitmap("stage_empty");
1042 stage_marker_fill_ = LoadBitmap("stage_fill");
Elliott Hughes498cda62016-04-14 16:49:04 -07001043
Tao Baoda409fb2018-10-21 23:36:26 -07001044 erasing_text_ = LoadLocalizedBitmap("erasing_text");
1045 no_command_text_ = LoadLocalizedBitmap("no_command_text");
1046 error_text_ = LoadLocalizedBitmap("error_text");
Doug Zongker211aebc2011-10-28 15:13:10 -07001047
Alessandro Astone4a0de5a2020-03-09 23:17:50 +01001048 lineage_logo_ = LoadBitmap("logo_image");
1049 back_icon_ = LoadBitmap("ic_back");
1050 back_icon_sel_ = LoadBitmap("ic_back_sel");
Alessandro Astonecae99222020-02-26 17:25:54 +01001051 if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) ||
1052 android::base::GetBoolProperty("ro.fastbootd.available", false)) {
David Anderson983e2d52019-01-02 11:35:38 -08001053 fastbootd_logo_ = LoadBitmap("fastbootd");
1054 }
1055
Tao Baoda409fb2018-10-21 23:36:26 -07001056 // Background text for "installing_update" could be "installing update" or
1057 // "installing security update". It will be set after Init() according to the commands in BCB.
1058 installing_text_.reset();
Elliott Hughes498cda62016-04-14 16:49:04 -07001059
Tianjie Xub99e6062018-10-16 15:13:09 -07001060 LoadWipeDataMenuText();
1061
Tao Bao736d59c2017-01-03 10:15:33 -08001062 LoadAnimation();
Doug Zongker02ec6b82012-08-22 17:26:40 -07001063
Tao Bao26ea9592018-05-09 16:32:02 -07001064 // Keep the progress bar updated, even when the process is otherwise busy.
1065 progress_thread_ = std::thread(&ScreenRecoveryUI::ProgressThreadLoop, this);
Sen Jiangd5304492016-12-09 16:20:49 -08001066
fredchiouf2f5aa22022-02-11 16:39:53 +08001067 // set the callback for hall sensor event
1068 (void)ev_sync_sw_state([this](auto&& a, auto&& b) { return this->SetSwCallback(a, b);});
1069
Tao Bao736d59c2017-01-03 10:15:33 -08001070 return true;
Doug Zongker211aebc2011-10-28 15:13:10 -07001071}
1072
Tao Bao551d2c32018-05-09 20:53:13 -07001073std::string ScreenRecoveryUI::GetLocale() const {
Jerry Zhang2dea53e2018-05-02 17:15:03 -07001074 return locale_;
1075}
1076
Elliott Hughes498cda62016-04-14 16:49:04 -07001077void ScreenRecoveryUI::LoadAnimation() {
Tao Bao6cd81682018-05-03 21:53:11 -07001078 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(Paths::Get().resource_dir().c_str()),
1079 closedir);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001080 dirent* de;
1081 std::vector<std::string> intro_frame_names;
Joshua Lambert94a83be2019-12-09 20:24:18 +00001082 std::vector<std::string> loop_frame_names;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001083
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001084 while ((de = readdir(dir.get())) != nullptr) {
1085 int value, num_chars;
1086 if (sscanf(de->d_name, "intro%d%n.png", &value, &num_chars) == 1) {
1087 intro_frame_names.emplace_back(de->d_name, num_chars);
1088 } else if (sscanf(de->d_name, "loop%d%n.png", &value, &num_chars) == 1) {
Joshua Lambert94a83be2019-12-09 20:24:18 +00001089 loop_frame_names.emplace_back(de->d_name, num_chars);
Elliott Hughes498cda62016-04-14 16:49:04 -07001090 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001091 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001092
Tao Baoda409fb2018-10-21 23:36:26 -07001093 size_t intro_frames = intro_frame_names.size();
1094 size_t loop_frames = loop_frame_names.size();
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001095
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001096 // It's okay to not have an intro.
Tao Baoda409fb2018-10-21 23:36:26 -07001097 if (intro_frames == 0) intro_done_ = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001098 // But you must have an animation.
1099 if (loop_frames == 0) abort();
Elliott Hughes498cda62016-04-14 16:49:04 -07001100
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001101 std::sort(intro_frame_names.begin(), intro_frame_names.end());
1102 std::sort(loop_frame_names.begin(), loop_frame_names.end());
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001103
Tao Baoda409fb2018-10-21 23:36:26 -07001104 intro_frames_.clear();
1105 intro_frames_.reserve(intro_frames);
1106 for (const auto& frame_name : intro_frame_names) {
1107 intro_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001108 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001109
Tao Baoda409fb2018-10-21 23:36:26 -07001110 loop_frames_.clear();
1111 loop_frames_.reserve(loop_frames);
1112 for (const auto& frame_name : loop_frame_names) {
1113 loop_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001114 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001115}
1116
Elliott Hughes8de52072015-04-08 20:06:50 -07001117void ScreenRecoveryUI::SetBackground(Icon icon) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001118 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker02ec6b82012-08-22 17:26:40 -07001119
Tao Baoda409fb2018-10-21 23:36:26 -07001120 current_icon_ = icon;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001121 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001122}
1123
Elliott Hughes8de52072015-04-08 20:06:50 -07001124void ScreenRecoveryUI::SetProgressType(ProgressType type) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001125 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001126 if (progressBarType != type) {
1127 progressBarType = type;
1128 }
1129 progressScopeStart = 0;
1130 progressScopeSize = 0;
1131 progress = 0;
1132 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001133}
1134
Elliott Hughes8de52072015-04-08 20:06:50 -07001135void ScreenRecoveryUI::ShowProgress(float portion, float seconds) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001136 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001137 progressBarType = DETERMINATE;
1138 progressScopeStart += progressScopeSize;
1139 progressScopeSize = portion;
1140 progressScopeTime = now();
1141 progressScopeDuration = seconds;
1142 progress = 0;
1143 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001144}
1145
Elliott Hughes8de52072015-04-08 20:06:50 -07001146void ScreenRecoveryUI::SetProgress(float fraction) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001147 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001148 if (fraction < 0.0) fraction = 0.0;
1149 if (fraction > 1.0) fraction = 1.0;
1150 if (progressBarType == DETERMINATE && fraction > progress) {
1151 // Skip updates that aren't visibly different.
Tao Baoda409fb2018-10-21 23:36:26 -07001152 int width = gr_get_width(progress_bar_empty_.get());
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001153 float scale = width * progressScopeSize;
1154 if ((int)(progress * scale) != (int)(fraction * scale)) {
1155 progress = fraction;
1156 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001157 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001158 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001159}
1160
Doug Zongkerc87bab12013-11-25 13:53:25 -08001161void ScreenRecoveryUI::SetStage(int current, int max) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001162 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001163 stage = current;
1164 max_stage = max;
Doug Zongkerc87bab12013-11-25 13:53:25 -08001165}
1166
Tao Baob6918c72015-05-19 17:02:16 -07001167void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001168 std::string str;
1169 android::base::StringAppendV(&str, fmt, ap);
Doug Zongker211aebc2011-10-28 15:13:10 -07001170
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001171 if (copy_to_stdout) {
1172 fputs(str.c_str(), stdout);
1173 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001174
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001175 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001176 if (text_rows_ > 0 && text_cols_ > 0) {
1177 for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
1178 if (*ptr == '\n' || text_col_ >= text_cols_) {
Elliott Hughesc0491632015-05-06 12:40:05 -07001179 text_[text_row_][text_col_] = '\0';
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001180 text_col_ = 0;
1181 text_row_ = (text_row_ + 1) % text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001182 }
1183 if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
Doug Zongker211aebc2011-10-28 15:13:10 -07001184 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001185 text_[text_row_][text_col_] = '\0';
1186 update_screen_locked();
1187 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001188}
1189
Tao Baob6918c72015-05-19 17:02:16 -07001190void ScreenRecoveryUI::Print(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001191 va_list ap;
1192 va_start(ap, fmt);
1193 PrintV(fmt, true, ap);
1194 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001195}
1196
Tianjie Xu8f397302018-08-20 13:40:47 -07001197void ScreenRecoveryUI::PrintOnScreenOnly(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001198 va_list ap;
1199 va_start(ap, fmt);
1200 PrintV(fmt, false, ap);
1201 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001202}
1203
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001204void ScreenRecoveryUI::PutChar(char ch) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001205 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001206 if (ch != '\n') text_[text_row_][text_col_++] = ch;
1207 if (ch == '\n' || text_col_ >= text_cols_) {
1208 text_col_ = 0;
1209 ++text_row_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001210 }
Elliott Hughes8de52072015-04-08 20:06:50 -07001211}
1212
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001213void ScreenRecoveryUI::ClearText() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001214 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001215 text_col_ = 0;
1216 text_row_ = 0;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001217 for (size_t i = 0; i < text_rows_; ++i) {
1218 memset(text_[i], 0, text_cols_ + 1);
1219 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001220}
1221
1222void ScreenRecoveryUI::ShowFile(FILE* fp) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001223 std::vector<off_t> offsets;
1224 offsets.push_back(ftello(fp));
1225 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001226
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001227 struct stat sb;
1228 fstat(fileno(fp), &sb);
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001229
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001230 bool show_prompt = false;
1231 while (true) {
1232 if (show_prompt) {
1233 PrintOnScreenOnly("--(%d%% of %d bytes)--",
1234 static_cast<int>(100 * (double(ftello(fp)) / double(sb.st_size))),
1235 static_cast<int>(sb.st_size));
1236 Redraw();
1237 while (show_prompt) {
1238 show_prompt = false;
Alessandro Astone73961412020-10-04 18:11:40 +02001239 InputEvent evt = WaitInputEvent();
1240 if (evt.type() == EventType::EXTRA) {
1241 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) {
1242 return;
1243 }
1244 }
1245 if (evt.type() != EventType::KEY) {
1246 show_prompt = true;
1247 continue;
1248 }
1249 if (evt.key() == KEY_POWER || evt.key() == KEY_ENTER || evt.key() == KEY_BACKSPACE ||
1250 evt.key() == KEY_BACK || evt.key() == KEY_HOME || evt.key() == KEY_HOMEPAGE) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001251 return;
Alessandro Astone73961412020-10-04 18:11:40 +02001252 } else if (evt.key() == KEY_UP || evt.key() == KEY_VOLUMEUP || evt.key() == KEY_SCROLLUP) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001253 if (offsets.size() <= 1) {
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001254 show_prompt = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001255 } else {
1256 offsets.pop_back();
1257 fseek(fp, offsets.back(), SEEK_SET);
1258 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001259 } else {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001260 if (feof(fp)) {
1261 return;
1262 }
1263 offsets.push_back(ftello(fp));
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001264 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001265 }
1266 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001267 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001268
1269 int ch = getc(fp);
1270 if (ch == EOF) {
1271 while (text_row_ < text_rows_ - 1) PutChar('\n');
1272 show_prompt = true;
1273 } else {
1274 PutChar(ch);
1275 if (text_col_ == 0 && text_row_ >= text_rows_ - 1) {
1276 show_prompt = true;
1277 }
1278 }
1279 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001280}
1281
Tao Bao1d156b92018-05-02 12:43:18 -07001282void ScreenRecoveryUI::ShowFile(const std::string& filename) {
1283 std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(filename.c_str(), "re"), fclose);
1284 if (!fp) {
1285 Print(" Unable to open %s: %s\n", filename.c_str(), strerror(errno));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001286 return;
1287 }
Elliott Hughesc0491632015-05-06 12:40:05 -07001288
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001289 char** old_text = text_;
1290 size_t old_text_col = text_col_;
1291 size_t old_text_row = text_row_;
Elliott Hughesc0491632015-05-06 12:40:05 -07001292
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001293 // Swap in the alternate screen and clear it.
1294 text_ = file_viewer_text_;
1295 ClearText();
Elliott Hughesc0491632015-05-06 12:40:05 -07001296
Tao Bao1d156b92018-05-02 12:43:18 -07001297 ShowFile(fp.get());
Elliott Hughesc0491632015-05-06 12:40:05 -07001298
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001299 text_ = old_text;
1300 text_col_ = old_text_col;
1301 text_row_ = old_text_row;
Elliott Hughes8de52072015-04-08 20:06:50 -07001302}
1303
Tao Baoda409fb2018-10-21 23:36:26 -07001304std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(
1305 const GRSurface* graphic_header, const std::vector<const GRSurface*>& graphic_items,
1306 const std::vector<std::string>& text_headers, const std::vector<std::string>& text_items,
1307 size_t initial_selection) const {
Tianjie Xub99e6062018-10-16 15:13:09 -07001308 // horizontal unusable area: margin width + menu indent
1309 size_t max_width = ScreenWidth() - margin_width_ - kMenuIndent;
1310 // vertical unusable area: margin height + title lines + helper message + high light bar.
1311 // It is safe to reserve more space.
1312 size_t max_height = ScreenHeight() - margin_height_ - char_height_ * (title_lines_.size() + 3);
1313 if (GraphicMenu::Validate(max_width, max_height, graphic_header, graphic_items)) {
1314 return std::make_unique<GraphicMenu>(graphic_header, graphic_items, initial_selection, *this);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001315 }
Tianjie Xub99e6062018-10-16 15:13:09 -07001316
1317 fprintf(stderr, "Failed to initialize graphic menu, falling back to use the text menu.\n");
1318
1319 return CreateMenu(text_headers, text_items, initial_selection);
1320}
1321
1322std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(const std::vector<std::string>& text_headers,
1323 const std::vector<std::string>& text_items,
1324 size_t initial_selection) const {
Alessandro Astone4a0de5a2020-03-09 23:17:50 +01001325 int menu_char_width = MenuCharWidth();
1326 int menu_char_height = MenuCharHeight();
1327 int menu_item_padding = MenuItemPadding();
1328 int menu_rows = (ScreenHeight() - margin_height_*2 - gr_get_height(lineage_logo_.get()))
1329 / (menu_char_height + 2 * menu_item_padding) - text_headers.size();
1330 int menu_cols = (ScreenWidth() - margin_width_*2 - kMenuIndent) / menu_char_width;
1331 bool wrap_selection = !HasThreeButtons() && !HasTouchScreen();
1332 return std::make_unique<TextMenu>(wrap_selection, menu_rows, menu_cols, text_headers, text_items,
1333 initial_selection, menu_char_height, *menu_draw_funcs_);
Doug Zongker211aebc2011-10-28 15:13:10 -07001334}
1335
1336int ScreenRecoveryUI::SelectMenu(int sel) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001337 std::lock_guard<std::mutex> lg(updateMutex);
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001338 if (menu_) {
1339 int old_sel = menu_->selection();
1340 sel = menu_->Select(sel);
Elliott Hughesfc06f872015-03-23 13:45:31 -07001341
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001342 if (sel != old_sel) {
1343 update_screen_locked();
1344 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001345 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001346 return sel;
Doug Zongker211aebc2011-10-28 15:13:10 -07001347}
1348
Alessandro Astone73961412020-10-04 18:11:40 +02001349int ScreenRecoveryUI::SelectMenu(const Point& point) {
1350 int new_sel = Device::kNoAction;
1351 std::lock_guard<std::mutex> lg(updateMutex);
1352 if (menu_) {
1353 if (!menu_->IsMain()) {
1354 // Back arrow hitbox
1355 const static int logo_width = gr_get_width(lineage_logo_.get());
1356 const static int logo_height = gr_get_height(lineage_logo_.get());
1357 const static int icon_w = gr_get_width(back_icon_.get());
1358 const static int icon_h = gr_get_height(back_icon_.get());
1359 const static int centered_x = ScreenWidth() / 2 - logo_width / 2;
1360 const static int icon_x = centered_x / 2 - icon_w / 2;
1361 const static int icon_y = margin_height_ + logo_height / 2 - icon_h / 2;
1362
1363 if (point.x() >= icon_x && point.x() <= icon_x + icon_w &&
1364 point.y() >= icon_y && point.y() <= icon_y + icon_h) {
1365 return Device::kGoBack;
1366 }
1367 }
1368
1369 if (point.y() >= menu_start_y_ &&
1370 point.y() < menu_start_y_ + menu_->ItemsCount() * MenuItemHeight()) {
1371 int old_sel = menu_->selection();
1372 int relative_sel = (point.y() - menu_start_y_) / MenuItemHeight();
1373 new_sel = menu_->SelectVisible(relative_sel);
1374 if (new_sel != -1 && new_sel != old_sel) {
1375 update_screen_locked();
1376 }
1377 }
1378 }
1379 return new_sel;
1380}
1381
1382int ScreenRecoveryUI::ScrollMenu(int updown) {
1383 std::lock_guard<std::mutex> lg(updateMutex);
1384 int sel = Device::kNoAction;
1385 if (menu_) {
1386 sel = menu_->Scroll(updown);
1387 update_screen_locked();
1388 }
1389 return sel;
1390}
1391
Tianjie Xub99e6062018-10-16 15:13:09 -07001392size_t ScreenRecoveryUI::ShowMenu(std::unique_ptr<Menu>&& menu, bool menu_only,
Tao Bao1fe1afe2018-05-01 15:56:05 -07001393 const std::function<int(int, bool)>& key_handler) {
Tao Bao3aec6962018-04-20 09:24:58 -07001394 // Throw away keys pressed previously, so user doesn't accidentally trigger menu items.
1395 FlushKeys();
1396
Jerry Zhangb76af932018-05-22 12:08:35 -07001397 // If there is a key interrupt in progress, return KeyError::INTERRUPTED without starting the
1398 // menu.
1399 if (IsKeyInterrupted()) return static_cast<size_t>(KeyError::INTERRUPTED);
1400
Tianjie Xub99e6062018-10-16 15:13:09 -07001401 CHECK(menu != nullptr);
Tao Bao3aec6962018-04-20 09:24:58 -07001402
Tianjie Xub99e6062018-10-16 15:13:09 -07001403 // Starts and displays the menu
1404 menu_ = std::move(menu);
1405 Redraw();
1406
1407 int selected = menu_->selection();
Tao Bao3aec6962018-04-20 09:24:58 -07001408 int chosen_item = -1;
1409 while (chosen_item < 0) {
Alessandro Astone73961412020-10-04 18:11:40 +02001410 InputEvent evt = WaitInputEvent();
1411 if (evt.type() == EventType::EXTRA) {
1412 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) {
1413 // WaitKey() was interrupted.
1414 return static_cast<size_t>(KeyError::INTERRUPTED);
1415 }
1416 if (evt.key() == static_cast<int>(KeyError::TIMED_OUT)) { // WaitKey() timed out.
1417 if (WasTextEverVisible()) {
1418 continue;
1419 } else {
1420 LOG(INFO) << "Timed out waiting for key input; rebooting.";
1421 menu_.reset();
1422 Redraw();
1423 return static_cast<size_t>(KeyError::TIMED_OUT);
1424 }
Tao Bao3aec6962018-04-20 09:24:58 -07001425 }
1426 }
1427
Alessandro Astone73961412020-10-04 18:11:40 +02001428 int action = Device::kNoAction;
1429 if (evt.type() == EventType::TOUCH) {
1430 int touch_sel = SelectMenu(evt.pos());
1431 if (touch_sel < 0) {
1432 action = touch_sel;
1433 } else {
1434 action = Device::kInvokeItem;
1435 selected = touch_sel;
1436 }
1437 } else {
1438 bool visible = IsTextVisible();
1439 action = key_handler(evt.key(), visible);
1440 }
Tao Bao3aec6962018-04-20 09:24:58 -07001441 if (action < 0) {
1442 switch (action) {
1443 case Device::kHighlightUp:
1444 selected = SelectMenu(--selected);
1445 break;
1446 case Device::kHighlightDown:
1447 selected = SelectMenu(++selected);
1448 break;
Alessandro Astone73961412020-10-04 18:11:40 +02001449 case Device::kScrollUp:
1450 selected = ScrollMenu(-1);
1451 break;
1452 case Device::kScrollDown:
1453 selected = ScrollMenu(1);
1454 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001455 case Device::kInvokeItem:
Alessandro Astone4a0de5a2020-03-09 23:17:50 +01001456 if (selected < 0) {
1457 chosen_item = Device::kGoBack;
1458 } else {
1459 chosen_item = selected;
1460 }
Tao Bao3aec6962018-04-20 09:24:58 -07001461 break;
1462 case Device::kNoAction:
1463 break;
Tom Marshall4b921692017-08-24 13:50:01 +00001464 case Device::kGoBack:
1465 chosen_item = Device::kGoBack;
1466 break;
1467 case Device::kGoHome:
1468 chosen_item = Device::kGoHome;
1469 break;
Tom Marshallfdf50332018-12-17 15:57:44 -08001470 case Device::kDoSideload:
1471 chosen_item = Device::kDoSideload;
1472 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001473 }
1474 } else if (!menu_only) {
1475 chosen_item = action;
1476 }
Tom Marshallfdf50332018-12-17 15:57:44 -08001477
1478 if (chosen_item == Device::kGoBack || chosen_item == Device::kGoHome ||
1479 chosen_item == Device::kDoSideload) {
Tom Marshall4b921692017-08-24 13:50:01 +00001480 break;
1481 }
Tao Bao3aec6962018-04-20 09:24:58 -07001482 }
1483
Tianjie Xub99e6062018-10-16 15:13:09 -07001484 menu_.reset();
1485 Redraw();
1486
Tao Bao3aec6962018-04-20 09:24:58 -07001487 return chosen_item;
1488}
1489
Tianjie Xub99e6062018-10-16 15:13:09 -07001490size_t ScreenRecoveryUI::ShowMenu(const std::vector<std::string>& headers,
1491 const std::vector<std::string>& items, size_t initial_selection,
1492 bool menu_only,
1493 const std::function<int(int, bool)>& key_handler) {
1494 auto menu = CreateMenu(headers, items, initial_selection);
1495 if (menu == nullptr) {
1496 return initial_selection;
1497 }
1498
Tao Baofa8e02a2019-06-27 09:07:04 -07001499 return ShowMenu(std::move(menu), menu_only, key_handler);
Tianjie Xub99e6062018-10-16 15:13:09 -07001500}
1501
1502size_t ScreenRecoveryUI::ShowPromptWipeDataMenu(const std::vector<std::string>& backup_headers,
1503 const std::vector<std::string>& backup_items,
1504 const std::function<int(int, bool)>& key_handler) {
Tao Baoda409fb2018-10-21 23:36:26 -07001505 auto wipe_data_menu = CreateMenu(wipe_data_menu_header_text_.get(),
1506 { try_again_text_.get(), factory_data_reset_text_.get() },
1507 backup_headers, backup_items, 0);
Tianjie Xub99e6062018-10-16 15:13:09 -07001508 if (wipe_data_menu == nullptr) {
1509 return 0;
1510 }
1511
1512 return ShowMenu(std::move(wipe_data_menu), true, key_handler);
1513}
1514
Tianjie Xu1a0a30a2018-10-25 15:22:07 -07001515size_t ScreenRecoveryUI::ShowPromptWipeDataConfirmationMenu(
1516 const std::vector<std::string>& backup_headers, const std::vector<std::string>& backup_items,
1517 const std::function<int(int, bool)>& key_handler) {
1518 auto confirmation_menu =
1519 CreateMenu(wipe_data_confirmation_text_.get(),
1520 { cancel_wipe_data_text_.get(), factory_data_reset_text_.get() }, backup_headers,
1521 backup_items, 0);
1522 if (confirmation_menu == nullptr) {
1523 return 0;
1524 }
1525
1526 return ShowMenu(std::move(confirmation_menu), true, key_handler);
1527}
1528
Elliott Hughes8de52072015-04-08 20:06:50 -07001529bool ScreenRecoveryUI::IsTextVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001530 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001531 int visible = show_text;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001532 return visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001533}
1534
Elliott Hughes8de52072015-04-08 20:06:50 -07001535bool ScreenRecoveryUI::WasTextEverVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001536 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001537 int ever_visible = show_text_ever;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001538 return ever_visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001539}
1540
Elliott Hughes8de52072015-04-08 20:06:50 -07001541void ScreenRecoveryUI::ShowText(bool visible) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001542 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001543 show_text = visible;
1544 if (show_text) show_text_ever = true;
1545 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001546}
Doug Zongkerc0441d12013-07-31 11:28:24 -07001547
Elliott Hughes8de52072015-04-08 20:06:50 -07001548void ScreenRecoveryUI::Redraw() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001549 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001550 update_screen_locked();
Doug Zongkerc0441d12013-07-31 11:28:24 -07001551}
Elliott Hughes642aaa72015-04-10 12:47:46 -07001552
1553void ScreenRecoveryUI::KeyLongPress(int) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001554 // Redraw so that if we're in the menu, the highlight
1555 // will change color to indicate a successful long press.
1556 Redraw();
Elliott Hughes642aaa72015-04-10 12:47:46 -07001557}
Tao Baoefb49ad2017-01-31 23:03:10 -08001558
1559void ScreenRecoveryUI::SetLocale(const std::string& new_locale) {
1560 locale_ = new_locale;
1561 rtl_locale_ = false;
1562
1563 if (!new_locale.empty()) {
Tao Bao347a6592018-05-08 15:58:29 -07001564 size_t separator = new_locale.find('-');
1565 // lang has the language prefix prior to the separator, or full string if none exists.
1566 std::string lang = new_locale.substr(0, separator);
Tao Baoefb49ad2017-01-31 23:03:10 -08001567
1568 // A bit cheesy: keep an explicit list of supported RTL languages.
1569 if (lang == "ar" || // Arabic
1570 lang == "fa" || // Persian (Farsi)
1571 lang == "he" || // Hebrew (new language code)
1572 lang == "iw" || // Hebrew (old language code)
1573 lang == "ur") { // Urdu
1574 rtl_locale_ = true;
1575 }
1576 }
1577}
fredchiouf2f5aa22022-02-11 16:39:53 +08001578
1579int ScreenRecoveryUI::SetSwCallback(int code, int value) {
1580 if (!is_graphics_available) { return -1; }
1581 if (code > SW_MAX) { return -1; }
1582 if (code != SW_LID) { return 0; }
1583
1584 /* detect dual display */
1585 if (!gr_has_multiple_connectors()) { return -1; }
1586
1587 /* turn off all screen */
1588 gr_fb_blank(true, DirectRenderManager::DRM_INNER);
1589 gr_fb_blank(true, DirectRenderManager::DRM_OUTER);
1590 gr_color(0, 0, 0, 255);
1591 gr_clear();
1592
1593 /* turn on the screen */
1594 gr_fb_blank(false, value);
1595 gr_flip();
1596
1597 /* set the retation */
1598 std::string rotation_str;
1599 if (value == DirectRenderManager::DRM_OUTER) {
1600 rotation_str =
1601 android::base::GetProperty("ro.minui.second_rotation", "ROTATION_NONE");
1602 } else {
1603 rotation_str =
1604 android::base::GetProperty("ro.minui.default_rotation", "ROTATION_NONE");
1605 }
1606
1607 if (rotation_str == "ROTATION_RIGHT") {
1608 gr_rotate(GRRotation::RIGHT);
1609 } else if (rotation_str == "ROTATION_DOWN") {
1610 gr_rotate(GRRotation::DOWN);
1611 } else if (rotation_str == "ROTATION_LEFT") {
1612 gr_rotate(GRRotation::LEFT);
1613 } else { // "ROTATION_NONE" or unknown string
1614 gr_rotate(GRRotation::NONE);
1615 }
1616 Redraw();
1617
1618 return 0;
1619}