blob: 07af7ac0694fc6eddaef7edbca2633aac225dc33 [file] [log] [blame]
Doug Zongker211aebc2011-10-28 15:13:10 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Tianjie Xu8f397302018-08-20 13:40:47 -070017#include "recovery_ui/screen_ui.h"
Tao Baoefb49ad2017-01-31 23:03:10 -080018
Elliott Hughes498cda62016-04-14 16:49:04 -070019#include <dirent.h>
Doug Zongker211aebc2011-10-28 15:13:10 -070020#include <errno.h>
21#include <fcntl.h>
Doug Zongker211aebc2011-10-28 15:13:10 -070022#include <stdarg.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/stat.h>
27#include <sys/time.h>
28#include <sys/types.h>
29#include <time.h>
30#include <unistd.h>
31
Tao Bao1fe1afe2018-05-01 15:56:05 -070032#include <algorithm>
Tao Bao26ea9592018-05-09 16:32:02 -070033#include <chrono>
Tianjie Xu29d55752017-09-20 17:53:46 -070034#include <memory>
Tao Bao736d59c2017-01-03 10:15:33 -080035#include <string>
Tao Bao26ea9592018-05-09 16:32:02 -070036#include <thread>
Tianjie Xu29d55752017-09-20 17:53:46 -070037#include <unordered_map>
Elliott Hughes95fc63e2015-04-10 19:12:01 -070038#include <vector>
39
Mark Salyzyn30017e72020-05-13 12:39:12 -070040#include <android-base/chrono_utils.h>
Tianjie Xuc21edd42016-08-05 18:00:04 -070041#include <android-base/logging.h>
Elliott Hughescb220402016-09-23 15:30:55 -070042#include <android-base/properties.h>
Elliott Hughes4b166f02015-12-04 15:30:20 -080043#include <android-base/stringprintf.h>
Tao Baocb5524c2017-09-08 21:25:32 -070044#include <android-base/strings.h>
Tao Baob6918c72015-05-19 17:02:16 -070045
Tao Bao6cd81682018-05-03 21:53:11 -070046#include "minui/minui.h"
47#include "otautil/paths.h"
Tianjie Xu8f397302018-08-20 13:40:47 -070048#include "recovery_ui/device.h"
49#include "recovery_ui/ui.h"
Doug Zongker211aebc2011-10-28 15:13:10 -070050
fredchiouf2f5aa22022-02-11 16:39:53 +080051enum DirectRenderManager {
52 DRM_INNER,
53 DRM_OUTER,
54};
55
Doug Zongker211aebc2011-10-28 15:13:10 -070056// Return the current time as a double (including fractions of a second).
57static double now() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -070058 struct timeval tv;
59 gettimeofday(&tv, nullptr);
60 return tv.tv_sec + tv.tv_usec / 1000000.0;
Doug Zongker211aebc2011-10-28 15:13:10 -070061}
62
Tianjie Xu66dbf632018-10-11 16:54:50 -070063Menu::Menu(size_t initial_selection, const DrawInterface& draw_func)
64 : selection_(initial_selection), draw_funcs_(draw_func) {}
65
Alessandro Astone4a0de5a2020-03-09 23:17:50 +010066int Menu::selection() const {
Tianjie Xu66dbf632018-10-11 16:54:50 -070067 return selection_;
68}
69
Alessandro Astoneefc7a8e2020-06-25 19:54:18 +020070TextMenu::TextMenu(bool wrappable, size_t max_length,
Tianjie Xu66dbf632018-10-11 16:54:50 -070071 const std::vector<std::string>& headers, const std::vector<std::string>& items,
72 size_t initial_selection, int char_height, const DrawInterface& draw_funcs)
73 : Menu(initial_selection, draw_funcs),
Alessandro Astone4a0de5a2020-03-09 23:17:50 +010074 wrappable_(wrappable),
Alessandro Astoneefc7a8e2020-06-25 19:54:18 +020075 calibrated_height_(false),
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070076 max_item_length_(max_length),
Tao Baoe02a5b22018-05-02 15:46:11 -070077 text_headers_(headers),
Tianjie Xu66dbf632018-10-11 16:54:50 -070078 char_height_(char_height) {
Tao Baoe02a5b22018-05-02 15:46:11 -070079
Alessandro Astone4a0de5a2020-03-09 23:17:50 +010080 size_t items_count = items.size();
Tao Bao1fe1afe2018-05-01 15:56:05 -070081 for (size_t i = 0; i < items_count; ++i) {
82 text_items_.emplace_back(items[i].substr(0, max_item_length_));
Tao Baoe02a5b22018-05-02 15:46:11 -070083 }
84
85 CHECK(!text_items_.empty());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070086}
87
Tianjie Xu66dbf632018-10-11 16:54:50 -070088const std::vector<std::string>& TextMenu::text_headers() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070089 return text_headers_;
90}
91
Tianjie Xu66dbf632018-10-11 16:54:50 -070092std::string TextMenu::TextItem(size_t index) const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070093 CHECK_LT(index, text_items_.size());
94
95 return text_items_[index];
96}
97
Tianjie Xu66dbf632018-10-11 16:54:50 -070098size_t TextMenu::MenuStart() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070099 return menu_start_;
100}
101
Tianjie Xu66dbf632018-10-11 16:54:50 -0700102size_t TextMenu::MenuEnd() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700103 return std::min(ItemsCount(), menu_start_ + max_display_items_);
104}
105
Tianjie Xu66dbf632018-10-11 16:54:50 -0700106size_t TextMenu::ItemsCount() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700107 return text_items_.size();
108}
109
Tianjie Xu66dbf632018-10-11 16:54:50 -0700110bool TextMenu::ItemsOverflow(std::string* cur_selection_str) const {
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100111 if (ItemsCount() <= max_display_items_) {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700112 return false;
113 }
114
115 *cur_selection_str =
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100116 android::base::StringPrintf("Current item: %d/%zu", selection_ + 1, ItemsCount());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700117 return true;
118}
119
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700120// TODO(xunchang) modify the function parameters to button up & down.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700121int TextMenu::Select(int sel) {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700122 CHECK_LE(ItemsCount(), static_cast<size_t>(std::numeric_limits<int>::max()));
123 int count = ItemsCount();
124
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100125 int min = IsMain() ? 0 : -1; // -1 is back arrow
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700126
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100127 if (sel < min) {
128 selection_ = wrappable() ? count - 1 : min;
129 } else if (sel >= count) {
130 selection_ = wrappable() ? min : count - 1;
131 } else {
132 selection_ = sel;
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700133 }
134
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100135 if (selection_ >= 0) {
136 if (selection_ < menu_start_) {
137 menu_start_ = selection_;
138 } else if (static_cast<size_t>(selection_) >= MenuEnd()) {
139 menu_start_ = selection_ - max_display_items_ + 1;
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700140 }
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700141 }
142
143 return selection_;
144}
145
Alessandro Astone73961412020-10-04 18:11:40 +0200146int TextMenu::SelectVisible(int relative_sel) {
147 int sel = relative_sel;
148 if (menu_start_ > 0) {
149 sel += menu_start_;
150 }
151
152 return Select(sel);
153}
154
155int TextMenu::Scroll(int updown) {
156 if ((updown > 0 && menu_start_ + max_display_items_ < ItemsCount()) ||
157 (updown < 0 && menu_start_ > 0)) {
158 menu_start_ += updown;
159
160 /* We can receive a kInvokeItem event from a different source than touch,
161 like from Power button. For this reason, selection should not get out of
162 the screen. Constrain it to the first or last visible item of the list */
163 if (selection_ < menu_start_) {
164 selection_ = menu_start_;
165 } else if (selection_ >= menu_start_ + max_display_items_) {
166 selection_ = menu_start_ + max_display_items_ - 1;
167 }
168 }
169 return selection_;
170}
171
Tianjie Xu66dbf632018-10-11 16:54:50 -0700172int TextMenu::DrawHeader(int x, int y) const {
173 int offset = 0;
174
175 draw_funcs_.SetColor(UIElement::HEADER);
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100176 offset += draw_funcs_.DrawWrappedTextLines(x, y + offset, text_headers());
Tianjie Xu66dbf632018-10-11 16:54:50 -0700177
178 return offset;
179}
180
181int TextMenu::DrawItems(int x, int y, int screen_width, bool long_press) const {
182 int offset = 0;
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100183 int padding = draw_funcs_.MenuItemPadding();
Tianjie Xu66dbf632018-10-11 16:54:50 -0700184
185 draw_funcs_.SetColor(UIElement::MENU);
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100186 offset += draw_funcs_.DrawHorizontalRule(y + offset) + 4;
187
188 int item_container_offset = offset; // store it for drawing scrollbar on most top
189
Tianjie Xu66dbf632018-10-11 16:54:50 -0700190 for (size_t i = MenuStart(); i < MenuEnd(); ++i) {
191 bool bold = false;
192 if (i == selection()) {
193 // Draw the highlight bar.
194 draw_funcs_.SetColor(long_press ? UIElement::MENU_SEL_BG_ACTIVE : UIElement::MENU_SEL_BG);
195
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100196 int bar_height = padding + char_height_ + padding;
197 draw_funcs_.DrawHighlightBar(0, y + offset, screen_width, bar_height);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700198
199 // Bold white text for the selected item.
200 draw_funcs_.SetColor(UIElement::MENU_SEL_FG);
201 bold = true;
202 }
203 offset += draw_funcs_.DrawTextLine(x, y + offset, TextItem(i), bold);
204
205 draw_funcs_.SetColor(UIElement::MENU);
206 }
207 offset += draw_funcs_.DrawHorizontalRule(y + offset);
208
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100209 std::string unused;
210 if (ItemsOverflow(&unused)) {
211 int container_height = max_display_items_ * (2 * padding + char_height_);
212 int bar_height = container_height / (text_items_.size() - max_display_items_ + 1);
213 int start_y = y + item_container_offset + bar_height * menu_start_;
214 draw_funcs_.SetColor(UIElement::SCROLLBAR);
215 draw_funcs_.DrawScrollBar(start_y, bar_height);
216 }
217
Tianjie Xu66dbf632018-10-11 16:54:50 -0700218 return offset;
219}
220
Tao Baoda409fb2018-10-21 23:36:26 -0700221GraphicMenu::GraphicMenu(const GRSurface* graphic_headers,
222 const std::vector<const GRSurface*>& graphic_items,
Tianjie Xub99e6062018-10-16 15:13:09 -0700223 size_t initial_selection, const DrawInterface& draw_funcs)
Tao Baoda409fb2018-10-21 23:36:26 -0700224 : Menu(initial_selection, draw_funcs) {
225 graphic_headers_ = graphic_headers->Clone();
226 graphic_items_.reserve(graphic_items.size());
227 for (const auto& item : graphic_items) {
228 graphic_items_.emplace_back(item->Clone());
229 }
230}
Tianjie Xu66dbf632018-10-11 16:54:50 -0700231
232int GraphicMenu::Select(int sel) {
233 CHECK_LE(graphic_items_.size(), static_cast<size_t>(std::numeric_limits<int>::max()));
234 int count = graphic_items_.size();
235
236 // Wraps the selection at boundary if the menu is not scrollable.
237 if (sel < 0) {
238 selection_ = count - 1;
239 } else if (sel >= count) {
240 selection_ = 0;
241 } else {
242 selection_ = sel;
243 }
244
245 return selection_;
246}
247
248int GraphicMenu::DrawHeader(int x, int y) const {
Tianjie Xub99e6062018-10-16 15:13:09 -0700249 draw_funcs_.SetColor(UIElement::HEADER);
Tao Baoda409fb2018-10-21 23:36:26 -0700250 draw_funcs_.DrawTextIcon(x, y, graphic_headers_.get());
Tianjie Xu66dbf632018-10-11 16:54:50 -0700251 return graphic_headers_->height;
252}
253
254int GraphicMenu::DrawItems(int x, int y, int screen_width, bool long_press) const {
255 int offset = 0;
256
257 draw_funcs_.SetColor(UIElement::MENU);
258 offset += draw_funcs_.DrawHorizontalRule(y + offset) + 4;
259
260 for (size_t i = 0; i < graphic_items_.size(); i++) {
261 auto& item = graphic_items_[i];
262 if (i == selection_) {
263 draw_funcs_.SetColor(long_press ? UIElement::MENU_SEL_BG_ACTIVE : UIElement::MENU_SEL_BG);
264
265 int bar_height = item->height + 4;
266 draw_funcs_.DrawHighlightBar(0, y + offset - 2, screen_width, bar_height);
267
268 // Bold white text for the selected item.
269 draw_funcs_.SetColor(UIElement::MENU_SEL_FG);
270 }
Tao Baoda409fb2018-10-21 23:36:26 -0700271 draw_funcs_.DrawTextIcon(x, y + offset, item.get());
Tianjie Xu66dbf632018-10-11 16:54:50 -0700272 offset += item->height;
273
274 draw_funcs_.SetColor(UIElement::MENU);
275 }
xunchangc7dbc732018-12-20 11:31:18 -0800276 offset += draw_funcs_.DrawHorizontalRule(y + offset);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700277
278 return offset;
279}
280
Alessandro Astone73961412020-10-04 18:11:40 +0200281size_t GraphicMenu::ItemsCount() const {
282 return graphic_items_.size();
283}
284
Tao Baoda409fb2018-10-21 23:36:26 -0700285bool GraphicMenu::Validate(size_t max_width, size_t max_height, const GRSurface* graphic_headers,
286 const std::vector<const GRSurface*>& graphic_items) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700287 int offset = 0;
Tianjie Xub99e6062018-10-16 15:13:09 -0700288 if (!ValidateGraphicSurface(max_width, max_height, offset, graphic_headers)) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700289 return false;
290 }
Tianjie Xub99e6062018-10-16 15:13:09 -0700291 offset += graphic_headers->height;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700292
Tianjie Xub99e6062018-10-16 15:13:09 -0700293 for (const auto& item : graphic_items) {
294 if (!ValidateGraphicSurface(max_width, max_height, offset, item)) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700295 return false;
296 }
297 offset += item->height;
298 }
299
300 return true;
301}
302
Tianjie Xub99e6062018-10-16 15:13:09 -0700303bool GraphicMenu::ValidateGraphicSurface(size_t max_width, size_t max_height, int y,
304 const GRSurface* surface) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700305 if (!surface) {
Tao Baoa00b4492019-01-16 09:29:47 -0800306 fprintf(stderr, "Graphic surface can not be null\n");
Tianjie Xu66dbf632018-10-11 16:54:50 -0700307 return false;
308 }
309
310 if (surface->pixel_bytes != 1 || surface->width != surface->row_bytes) {
Tao Baoa00b4492019-01-16 09:29:47 -0800311 fprintf(stderr, "Invalid graphic surface, pixel bytes: %zu, width: %zu row_bytes: %zu\n",
Tianjie Xu66dbf632018-10-11 16:54:50 -0700312 surface->pixel_bytes, surface->width, surface->row_bytes);
313 return false;
314 }
315
Tianjie Xub99e6062018-10-16 15:13:09 -0700316 if (surface->width > max_width || surface->height > max_height - y) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700317 fprintf(stderr,
Tao Baodd789822018-11-26 16:28:07 -0800318 "Graphic surface doesn't fit into the screen. width: %zu, height: %zu, max_width: %zu,"
Tianjie Xu66dbf632018-10-11 16:54:50 -0700319 " max_height: %zu, vertical offset: %d\n",
Tianjie Xub99e6062018-10-16 15:13:09 -0700320 surface->width, surface->height, max_width, max_height, y);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700321 return false;
322 }
323
324 return true;
325}
326
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100327MenuDrawFunctions::MenuDrawFunctions(const DrawInterface& wrappee)
328 : wrappee_(wrappee) {
329}
330
331int MenuDrawFunctions::DrawTextLine(int x, int y, const std::string& line, bool bold) const {
332 gr_text(gr_menu_font(), x, y + MenuItemPadding(), line.c_str(), bold);
333 return 2 * MenuItemPadding() + MenuCharHeight();
334}
335
336int MenuDrawFunctions::DrawTextLines(int x, int y, const std::vector<std::string>& lines) const {
337 int offset = 0;
338 for (const auto& line : lines) {
339 offset += DrawTextLine(x, y + offset, line, false);
340 }
341 return offset;
342}
343
344int MenuDrawFunctions::DrawWrappedTextLines(int x, int y, const std::vector<std::string>& lines) const {
345 // Keep symmetrical margins based on the given offset (i.e. x).
346 size_t text_cols = (gr_fb_width() - x * 2) / MenuCharWidth();
347 int offset = 0;
348 for (const auto& line : lines) {
349 size_t next_start = 0;
350 while (next_start < line.size()) {
351 std::string sub = line.substr(next_start, text_cols + 1);
352 if (sub.size() <= text_cols) {
353 next_start += sub.size();
354 } else {
355 // Line too long and must be wrapped to text_cols columns.
356 size_t last_space = sub.find_last_of(" \t\n");
357 if (last_space == std::string::npos) {
358 // No space found, just draw as much as we can.
359 sub.resize(text_cols);
360 next_start += text_cols;
361 } else {
362 sub.resize(last_space);
363 next_start += last_space + 1;
364 }
365 }
366 offset += DrawTextLine(x, y + offset, sub, false);
367 }
368 }
369 return offset;
370}
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700371
Tao Bao0bc88de2018-07-31 14:53:16 -0700372constexpr int kDefaultMarginHeight = 0;
373constexpr int kDefaultMarginWidth = 0;
374constexpr int kDefaultAnimationFps = 30;
375
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100376ScreenRecoveryUI::ScreenRecoveryUI()
Tao Bao0bc88de2018-07-31 14:53:16 -0700377 : margin_width_(
378 android::base::GetIntProperty("ro.recovery.ui.margin_width", kDefaultMarginWidth)),
379 margin_height_(
380 android::base::GetIntProperty("ro.recovery.ui.margin_height", kDefaultMarginHeight)),
381 animation_fps_(
382 android::base::GetIntProperty("ro.recovery.ui.animation_fps", kDefaultAnimationFps)),
383 density_(static_cast<float>(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f),
Michael Bestas43ff6792019-03-23 17:28:22 +0200384 blank_unblank_on_init_(
385 android::base::GetBoolProperty("ro.recovery.ui.blank_unblank_on_init", false)),
Tao Baoda409fb2018-10-21 23:36:26 -0700386 current_icon_(NONE),
387 current_frame_(0),
388 intro_done_(false),
Tao Bao736d59c2017-01-03 10:15:33 -0800389 progressBarType(EMPTY),
390 progressScopeStart(0),
391 progressScopeSize(0),
392 progress(0),
393 pagesIdentical(false),
394 text_cols_(0),
395 text_rows_(0),
396 text_(nullptr),
397 text_col_(0),
398 text_row_(0),
Tao Bao736d59c2017-01-03 10:15:33 -0800399 show_text(false),
400 show_text_ever(false),
Tao Bao736d59c2017-01-03 10:15:33 -0800401 file_viewer_text_(nullptr),
Tao Bao736d59c2017-01-03 10:15:33 -0800402 stage(-1),
403 max_stage(-1),
Tao Baoefb49ad2017-01-31 23:03:10 -0800404 locale_(""),
fredchiouf2f5aa22022-02-11 16:39:53 +0800405 rtl_locale_(false),
406 is_graphics_available(false) {}
Doug Zongker211aebc2011-10-28 15:13:10 -0700407
Tao Bao26ea9592018-05-09 16:32:02 -0700408ScreenRecoveryUI::~ScreenRecoveryUI() {
409 progress_thread_stopped_ = true;
Tao Bao94371fd2018-06-06 07:38:54 -0700410 if (progress_thread_.joinable()) {
411 progress_thread_.join();
412 }
Tao Bao60ac6222018-06-13 14:33:51 -0700413 // No-op if gr_init() (via Init()) was not called or had failed.
414 gr_exit();
Tao Bao26ea9592018-05-09 16:32:02 -0700415}
416
Tao Baoda409fb2018-10-21 23:36:26 -0700417const GRSurface* ScreenRecoveryUI::GetCurrentFrame() const {
418 if (current_icon_ == INSTALLING_UPDATE || current_icon_ == ERASING) {
419 return intro_done_ ? loop_frames_[current_frame_].get() : intro_frames_[current_frame_].get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700420 }
Tao Baoda409fb2018-10-21 23:36:26 -0700421 return error_icon_.get();
Elliott Hughes498cda62016-04-14 16:49:04 -0700422}
423
Tao Baoda409fb2018-10-21 23:36:26 -0700424const GRSurface* ScreenRecoveryUI::GetCurrentText() const {
425 switch (current_icon_) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700426 case ERASING:
Tao Baoda409fb2018-10-21 23:36:26 -0700427 return erasing_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700428 case ERROR:
Tao Baoda409fb2018-10-21 23:36:26 -0700429 return error_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700430 case INSTALLING_UPDATE:
Tao Baoda409fb2018-10-21 23:36:26 -0700431 return installing_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700432 case NO_COMMAND:
Tao Baoda409fb2018-10-21 23:36:26 -0700433 return no_command_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700434 case NONE:
435 abort();
436 }
Elliott Hughes498cda62016-04-14 16:49:04 -0700437}
438
Mikhail Lappob49767c2017-03-23 21:44:26 +0100439int ScreenRecoveryUI::PixelsFromDp(int dp) const {
Tao Bao0bc88de2018-07-31 14:53:16 -0700440 return dp * density_;
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700441}
442
443// Here's the intended layout:
444
Elliott Hughes6d089a92016-07-08 17:23:41 -0700445// | portrait large landscape large
446// ---------+-------------------------------------------------
Tao Bao3250f722017-06-29 14:32:05 -0700447// gap |
Elliott Hughes6d089a92016-07-08 17:23:41 -0700448// icon | (200dp)
449// gap | 68dp 68dp 56dp 112dp
450// text | (14sp)
451// gap | 32dp 32dp 26dp 52dp
452// progress | (2dp)
Tao Bao3250f722017-06-29 14:32:05 -0700453// gap |
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700454
Tao Bao3250f722017-06-29 14:32:05 -0700455// Note that "baseline" is actually the *top* of each icon (because that's how our drawing routines
456// 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 -0700457
Elliott Hughes6d089a92016-07-08 17:23:41 -0700458enum Layout { PORTRAIT = 0, PORTRAIT_LARGE = 1, LANDSCAPE = 2, LANDSCAPE_LARGE = 3, LAYOUT_MAX };
Tao Bao3250f722017-06-29 14:32:05 -0700459enum Dimension { TEXT = 0, ICON = 1, DIMENSION_MAX };
Elliott Hughes6d089a92016-07-08 17:23:41 -0700460static constexpr int kLayouts[LAYOUT_MAX][DIMENSION_MAX] = {
Tianjie Xu8f397302018-08-20 13:40:47 -0700461 { 32, 68 }, // PORTRAIT
462 { 32, 68 }, // PORTRAIT_LARGE
463 { 26, 56 }, // LANDSCAPE
464 { 52, 112 }, // LANDSCAPE_LARGE
Elliott Hughes6d089a92016-07-08 17:23:41 -0700465};
466
Tao Bao99b2d772017-06-23 22:47:03 -0700467int ScreenRecoveryUI::GetAnimationBaseline() const {
Tao Baoda409fb2018-10-21 23:36:26 -0700468 return GetTextBaseline() - PixelsFromDp(kLayouts[layout_][ICON]) -
469 gr_get_height(loop_frames_[0].get());
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700470}
471
Tao Bao99b2d772017-06-23 22:47:03 -0700472int ScreenRecoveryUI::GetTextBaseline() const {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700473 return GetProgressBaseline() - PixelsFromDp(kLayouts[layout_][TEXT]) -
Tao Baoda409fb2018-10-21 23:36:26 -0700474 gr_get_height(installing_text_.get());
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700475}
476
Tao Bao99b2d772017-06-23 22:47:03 -0700477int ScreenRecoveryUI::GetProgressBaseline() const {
Tao Baoda409fb2018-10-21 23:36:26 -0700478 int elements_sum = gr_get_height(loop_frames_[0].get()) + PixelsFromDp(kLayouts[layout_][ICON]) +
479 gr_get_height(installing_text_.get()) + PixelsFromDp(kLayouts[layout_][TEXT]) +
480 gr_get_height(progress_bar_fill_.get());
Luke Song92eda4d2017-09-19 10:51:35 -0700481 int bottom_gap = (ScreenHeight() - elements_sum) / 2;
Tao Baoda409fb2018-10-21 23:36:26 -0700482 return ScreenHeight() - bottom_gap - gr_get_height(progress_bar_fill_.get());
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700483}
484
Doug Zongker211aebc2011-10-28 15:13:10 -0700485// Clear the screen and draw the currently selected background icon (if any).
486// Should only be called with updateMutex locked.
Elliott Hughes498cda62016-04-14 16:49:04 -0700487void ScreenRecoveryUI::draw_background_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700488 pagesIdentical = false;
489 gr_color(0, 0, 0, 255);
490 gr_clear();
Tao Baoda409fb2018-10-21 23:36:26 -0700491 if (current_icon_ != NONE) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700492 if (max_stage != -1) {
Tao Baoda409fb2018-10-21 23:36:26 -0700493 int stage_height = gr_get_height(stage_marker_empty_.get());
494 int stage_width = gr_get_width(stage_marker_empty_.get());
495 int x = (ScreenWidth() - max_stage * gr_get_width(stage_marker_empty_.get())) / 2;
Tao Bao0bc88de2018-07-31 14:53:16 -0700496 int y = ScreenHeight() - stage_height - margin_height_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700497 for (int i = 0; i < max_stage; ++i) {
Tao Baoda409fb2018-10-21 23:36:26 -0700498 const auto& stage_surface = (i < stage) ? stage_marker_fill_ : stage_marker_empty_;
499 DrawSurface(stage_surface.get(), 0, 0, stage_width, stage_height, x, y);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700500 x += stage_width;
501 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700502 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700503
Tao Baoda409fb2018-10-21 23:36:26 -0700504 const auto& text_surface = GetCurrentText();
Luke Song92eda4d2017-09-19 10:51:35 -0700505 int text_x = (ScreenWidth() - gr_get_width(text_surface)) / 2;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700506 int text_y = GetTextBaseline();
507 gr_color(255, 255, 255, 255);
Luke Song92eda4d2017-09-19 10:51:35 -0700508 DrawTextIcon(text_x, text_y, text_surface);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700509 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700510}
511
Tao Baoea78d862017-06-28 14:52:17 -0700512// Draws the animation and progress bar (if any) on the screen. Does not flip pages. Should only be
513// called with updateMutex locked.
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700514void ScreenRecoveryUI::draw_foreground_locked() {
Tao Baoda409fb2018-10-21 23:36:26 -0700515 if (current_icon_ != NONE) {
516 const auto& frame = GetCurrentFrame();
Tao Bao736d59c2017-01-03 10:15:33 -0800517 int frame_width = gr_get_width(frame);
518 int frame_height = gr_get_height(frame);
Luke Song92eda4d2017-09-19 10:51:35 -0700519 int frame_x = (ScreenWidth() - frame_width) / 2;
Tao Bao736d59c2017-01-03 10:15:33 -0800520 int frame_y = GetAnimationBaseline();
zhang sanshan0d81b892019-08-07 16:21:10 +0800521 if (frame_x >= 0 && frame_y >= 0 && (frame_x + frame_width) < ScreenWidth() &&
522 (frame_y + frame_height) < ScreenHeight())
523 DrawSurface(frame, 0, 0, frame_width, frame_height, frame_x, frame_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800524 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700525
Tao Bao736d59c2017-01-03 10:15:33 -0800526 if (progressBarType != EMPTY) {
Tao Baoda409fb2018-10-21 23:36:26 -0700527 int width = gr_get_width(progress_bar_empty_.get());
528 int height = gr_get_height(progress_bar_empty_.get());
Doug Zongker211aebc2011-10-28 15:13:10 -0700529
Luke Song92eda4d2017-09-19 10:51:35 -0700530 int progress_x = (ScreenWidth() - width) / 2;
Tao Bao736d59c2017-01-03 10:15:33 -0800531 int progress_y = GetProgressBaseline();
Doug Zongker211aebc2011-10-28 15:13:10 -0700532
Tao Bao736d59c2017-01-03 10:15:33 -0800533 // Erase behind the progress bar (in case this was a progress-only update)
534 gr_color(0, 0, 0, 255);
Luke Song92eda4d2017-09-19 10:51:35 -0700535 DrawFill(progress_x, progress_y, width, height);
Doug Zongker211aebc2011-10-28 15:13:10 -0700536
Tao Bao736d59c2017-01-03 10:15:33 -0800537 if (progressBarType == DETERMINATE) {
538 float p = progressScopeStart + progress * progressScopeSize;
539 int pos = static_cast<int>(p * width);
Doug Zongker211aebc2011-10-28 15:13:10 -0700540
Tao Bao736d59c2017-01-03 10:15:33 -0800541 if (rtl_locale_) {
542 // Fill the progress bar from right to left.
543 if (pos > 0) {
Tao Baoda409fb2018-10-21 23:36:26 -0700544 DrawSurface(progress_bar_fill_.get(), width - pos, 0, pos, height,
545 progress_x + width - pos, progress_y);
Doug Zongker211aebc2011-10-28 15:13:10 -0700546 }
Tao Bao736d59c2017-01-03 10:15:33 -0800547 if (pos < width - 1) {
Tao Baoda409fb2018-10-21 23:36:26 -0700548 DrawSurface(progress_bar_empty_.get(), 0, 0, width - pos, height, progress_x, progress_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800549 }
550 } else {
551 // Fill the progress bar from left to right.
552 if (pos > 0) {
Tao Baoda409fb2018-10-21 23:36:26 -0700553 DrawSurface(progress_bar_fill_.get(), 0, 0, pos, height, progress_x, progress_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800554 }
555 if (pos < width - 1) {
Tao Baoda409fb2018-10-21 23:36:26 -0700556 DrawSurface(progress_bar_empty_.get(), pos, 0, width - pos, height, progress_x + pos,
557 progress_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800558 }
559 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700560 }
Tao Bao736d59c2017-01-03 10:15:33 -0800561 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700562}
563
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100564/* Lineage teal: #167c80 */
Tao Bao99b2d772017-06-23 22:47:03 -0700565void ScreenRecoveryUI::SetColor(UIElement e) const {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700566 switch (e) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700567 case UIElement::INFO:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700568 gr_color(249, 194, 0, 255);
569 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700570 case UIElement::HEADER:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700571 gr_color(247, 0, 6, 255);
572 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700573 case UIElement::MENU:
574 case UIElement::MENU_SEL_BG:
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100575 gr_color(0xd8, 0xd8, 0xd8, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700576 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700577 case UIElement::MENU_SEL_BG_ACTIVE:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700578 gr_color(0, 156, 100, 255);
579 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700580 case UIElement::MENU_SEL_FG:
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100581 case UIElement::SCROLLBAR:
582 gr_color(0x16, 0x7c, 0x80, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700583 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700584 case UIElement::LOG:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700585 gr_color(196, 196, 196, 255);
586 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700587 case UIElement::TEXT_FILL:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700588 gr_color(0, 0, 0, 160);
589 break;
590 default:
591 gr_color(255, 255, 255, 255);
592 break;
593 }
Doug Zongkerc0441d12013-07-31 11:28:24 -0700594}
Doug Zongker211aebc2011-10-28 15:13:10 -0700595
Tianjie Xu29d55752017-09-20 17:53:46 -0700596void ScreenRecoveryUI::SelectAndShowBackgroundText(const std::vector<std::string>& locales_entries,
597 size_t sel) {
598 SetLocale(locales_entries[sel]);
599 std::vector<std::string> text_name = { "erasing_text", "error_text", "installing_text",
600 "installing_security_text", "no_command_text" };
Tao Baoda409fb2018-10-21 23:36:26 -0700601 std::unordered_map<std::string, std::unique_ptr<GRSurface>> surfaces;
Tianjie Xu29d55752017-09-20 17:53:46 -0700602 for (const auto& name : text_name) {
Tao Baoda409fb2018-10-21 23:36:26 -0700603 auto text_image = LoadLocalizedBitmap(name);
Tianjie Xu29d55752017-09-20 17:53:46 -0700604 if (!text_image) {
605 Print("Failed to load %s\n", name.c_str());
606 return;
607 }
Tao Baoda409fb2018-10-21 23:36:26 -0700608 surfaces.emplace(name, std::move(text_image));
Tianjie Xu29d55752017-09-20 17:53:46 -0700609 }
610
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700611 std::lock_guard<std::mutex> lg(updateMutex);
Tianjie Xu29d55752017-09-20 17:53:46 -0700612 gr_color(0, 0, 0, 255);
613 gr_clear();
614
Tao Bao0bc88de2018-07-31 14:53:16 -0700615 int text_y = margin_height_;
616 int text_x = margin_width_;
Tianjie Xu29d55752017-09-20 17:53:46 -0700617 int line_spacing = gr_sys_font()->char_height; // Put some extra space between images.
618 // Write the header and descriptive texts.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700619 SetColor(UIElement::INFO);
Tianjie Xu29d55752017-09-20 17:53:46 -0700620 std::string header = "Show background text image";
Tao Bao93e46ad2018-05-02 14:57:21 -0700621 text_y += DrawTextLine(text_x, text_y, header, true);
Tianjie Xu29d55752017-09-20 17:53:46 -0700622 std::string locale_selection = android::base::StringPrintf(
Tao Bao39c49182018-05-07 22:50:33 -0700623 "Current locale: %s, %zu/%zu", locales_entries[sel].c_str(), sel + 1, locales_entries.size());
Tao Bao93e46ad2018-05-02 14:57:21 -0700624 // clang-format off
625 std::vector<std::string> instruction = {
626 locale_selection,
627 "Use volume up/down to switch locales and power to exit."
628 };
629 // clang-format on
Tianjie Xu29d55752017-09-20 17:53:46 -0700630 text_y += DrawWrappedTextLines(text_x, text_y, instruction);
631
632 // Iterate through the text images and display them in order for the current locale.
633 for (const auto& p : surfaces) {
634 text_y += line_spacing;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700635 SetColor(UIElement::LOG);
Tao Bao93e46ad2018-05-02 14:57:21 -0700636 text_y += DrawTextLine(text_x, text_y, p.first, false);
Tianjie Xu29d55752017-09-20 17:53:46 -0700637 gr_color(255, 255, 255, 255);
638 gr_texticon(text_x, text_y, p.second.get());
639 text_y += gr_get_height(p.second.get());
640 }
641 // Update the whole screen.
642 gr_flip();
Tianjie Xu29d55752017-09-20 17:53:46 -0700643}
644
Tao Bao39c49182018-05-07 22:50:33 -0700645void ScreenRecoveryUI::CheckBackgroundTextImages() {
Tianjie Xu29d55752017-09-20 17:53:46 -0700646 // Load a list of locales embedded in one of the resource files.
647 std::vector<std::string> locales_entries = get_locales_in_png("installing_text");
648 if (locales_entries.empty()) {
649 Print("Failed to load locales from the resource files\n");
650 return;
651 }
Tao Bao39c49182018-05-07 22:50:33 -0700652 std::string saved_locale = locale_;
Tianjie Xu29d55752017-09-20 17:53:46 -0700653 size_t selected = 0;
654 SelectAndShowBackgroundText(locales_entries, selected);
655
656 FlushKeys();
657 while (true) {
Alessandro Astone73961412020-10-04 18:11:40 +0200658 InputEvent evt = WaitInputEvent();
659 if (evt.type() == EventType::EXTRA) {
660 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) break;
661 }
662 if (evt.type() == EventType::KEY) {
663 if (evt.key() == KEY_POWER || evt.key() == KEY_ENTER) {
664 break;
665 } else if (evt.key() == KEY_UP || evt.key() == KEY_VOLUMEUP) {
666 selected = (selected == 0) ? locales_entries.size() - 1 : selected - 1;
667 SelectAndShowBackgroundText(locales_entries, selected);
668 } else if (evt.key() == KEY_DOWN || evt.key() == KEY_VOLUMEDOWN) {
669 selected = (selected == locales_entries.size() - 1) ? 0 : selected + 1;
670 SelectAndShowBackgroundText(locales_entries, selected);
671 }
Tianjie Xu29d55752017-09-20 17:53:46 -0700672 }
673 }
674
675 SetLocale(saved_locale);
676}
677
Luke Song92eda4d2017-09-19 10:51:35 -0700678int ScreenRecoveryUI::ScreenWidth() const {
679 return gr_fb_width();
680}
681
682int ScreenRecoveryUI::ScreenHeight() const {
683 return gr_fb_height();
684}
685
Tao Bao65815b62018-10-23 10:54:02 -0700686void ScreenRecoveryUI::DrawSurface(const GRSurface* surface, int sx, int sy, int w, int h, int dx,
Luke Song92eda4d2017-09-19 10:51:35 -0700687 int dy) const {
688 gr_blit(surface, sx, sy, w, h, dx, dy);
689}
690
Tao Baoea78d862017-06-28 14:52:17 -0700691int ScreenRecoveryUI::DrawHorizontalRule(int y) const {
Luke Song92eda4d2017-09-19 10:51:35 -0700692 gr_fill(0, y + 4, ScreenWidth(), y + 6);
Tao Baoea78d862017-06-28 14:52:17 -0700693 return 8;
Elliott Hughes95fc63e2015-04-10 19:12:01 -0700694}
695
Luke Songe2bd8762017-06-12 16:08:33 -0700696void ScreenRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const {
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100697 if (y + height > ScreenHeight())
698 height = ScreenHeight() - y;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700699 gr_fill(x, y, x + width, y + height);
Luke Songe2bd8762017-06-12 16:08:33 -0700700}
701
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100702void ScreenRecoveryUI::DrawScrollBar(int y, int height) const {
703 int x = ScreenWidth() - margin_width_;
704 int width = 8;
705 gr_fill(x - width, y, x, y + height);
706}
707
Luke Song92eda4d2017-09-19 10:51:35 -0700708void ScreenRecoveryUI::DrawFill(int x, int y, int w, int h) const {
709 gr_fill(x, y, w, h);
710}
711
Tao Bao65815b62018-10-23 10:54:02 -0700712void ScreenRecoveryUI::DrawTextIcon(int x, int y, const GRSurface* surface) const {
Luke Song92eda4d2017-09-19 10:51:35 -0700713 gr_texticon(x, y, surface);
714}
715
Tao Bao93e46ad2018-05-02 14:57:21 -0700716int ScreenRecoveryUI::DrawTextLine(int x, int y, const std::string& line, bool bold) const {
717 gr_text(gr_sys_font(), x, y, line.c_str(), bold);
Tao Baoea78d862017-06-28 14:52:17 -0700718 return char_height_ + 4;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700719}
720
Tao Bao93e46ad2018-05-02 14:57:21 -0700721int ScreenRecoveryUI::DrawTextLines(int x, int y, const std::vector<std::string>& lines) const {
Tao Baoea78d862017-06-28 14:52:17 -0700722 int offset = 0;
Tao Bao93e46ad2018-05-02 14:57:21 -0700723 for (const auto& line : lines) {
724 offset += DrawTextLine(x, y + offset, line, false);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700725 }
Tao Baoea78d862017-06-28 14:52:17 -0700726 return offset;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700727}
728
Tao Bao93e46ad2018-05-02 14:57:21 -0700729int ScreenRecoveryUI::DrawWrappedTextLines(int x, int y,
730 const std::vector<std::string>& lines) const {
Tao Bao452b4872018-05-09 11:52:09 -0700731 // Keep symmetrical margins based on the given offset (i.e. x).
732 size_t text_cols = (ScreenWidth() - x * 2) / char_width_;
Tao Bao2bbc6d62017-08-13 23:48:55 -0700733 int offset = 0;
Tao Bao93e46ad2018-05-02 14:57:21 -0700734 for (const auto& line : lines) {
Tao Bao2bbc6d62017-08-13 23:48:55 -0700735 size_t next_start = 0;
736 while (next_start < line.size()) {
Tao Bao452b4872018-05-09 11:52:09 -0700737 std::string sub = line.substr(next_start, text_cols + 1);
738 if (sub.size() <= text_cols) {
Tao Bao2bbc6d62017-08-13 23:48:55 -0700739 next_start += sub.size();
740 } else {
Tao Bao452b4872018-05-09 11:52:09 -0700741 // Line too long and must be wrapped to text_cols columns.
Tao Bao2bbc6d62017-08-13 23:48:55 -0700742 size_t last_space = sub.find_last_of(" \t\n");
743 if (last_space == std::string::npos) {
Tao Bao93e46ad2018-05-02 14:57:21 -0700744 // No space found, just draw as much as we can.
Tao Bao452b4872018-05-09 11:52:09 -0700745 sub.resize(text_cols);
746 next_start += text_cols;
Tao Bao2bbc6d62017-08-13 23:48:55 -0700747 } else {
748 sub.resize(last_space);
749 next_start += last_space + 1;
750 }
751 }
Tao Bao93e46ad2018-05-02 14:57:21 -0700752 offset += DrawTextLine(x, y + offset, sub, false);
Tao Bao2bbc6d62017-08-13 23:48:55 -0700753 }
754 }
755 return offset;
756}
757
Jerry Zhang0e577ee2018-05-07 11:21:10 -0700758void ScreenRecoveryUI::SetTitle(const std::vector<std::string>& lines) {
759 title_lines_ = lines;
760}
761
Tianjie Xue5032212019-07-23 13:23:29 -0700762std::vector<std::string> ScreenRecoveryUI::GetMenuHelpMessage() const {
763 // clang-format off
764 static std::vector<std::string> REGULAR_HELP{
765 "Use volume up/down and power.",
766 };
767 static std::vector<std::string> LONG_PRESS_HELP{
768 "Any button cycles highlight.",
769 "Long-press activates.",
770 };
771 // clang-format on
772 return HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP;
773}
774
Tao Bao171b4c42017-06-19 23:10:44 -0700775// Redraws everything on the screen. Does not flip pages. Should only be called with updateMutex
776// locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700777void ScreenRecoveryUI::draw_screen_locked() {
Tao Bao171b4c42017-06-19 23:10:44 -0700778 if (!show_text) {
779 draw_background_locked();
780 draw_foreground_locked();
781 return;
782 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700783
Tao Bao171b4c42017-06-19 23:10:44 -0700784 gr_color(0, 0, 0, 255);
785 gr_clear();
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700786
Tianjie Xue5032212019-07-23 13:23:29 -0700787 draw_menu_and_text_buffer_locked(GetMenuHelpMessage());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700788}
789
790// Draws the menu and text buffer on the screen. Should only be called with updateMutex locked.
Tao Bao93e46ad2018-05-02 14:57:21 -0700791void ScreenRecoveryUI::draw_menu_and_text_buffer_locked(
792 const std::vector<std::string>& help_message) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700793 int y = margin_height_;
David Anderson983e2d52019-01-02 11:35:38 -0800794
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700795 if (menu_) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700796 int x = margin_width_ + kMenuIndent;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700797
Tianjie Xu66dbf632018-10-11 16:54:50 -0700798 SetColor(UIElement::INFO);
Jerry Zhang0e577ee2018-05-07 11:21:10 -0700799
Alessandro Astone82ffc162020-03-29 15:08:09 +0200800 auto& logo = fastbootd_logo_enabled_ ? fastbootd_logo_ : lineage_logo_;
801 if (logo && back_icon_) {
802 auto logo_width = gr_get_width(logo.get());
803 auto logo_height = gr_get_height(logo.get());
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100804 auto centered_x = ScreenWidth() / 2 - logo_width / 2;
Alessandro Astone82ffc162020-03-29 15:08:09 +0200805 DrawSurface(logo.get(), 0, 0, logo_width, logo_height, centered_x, y);
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100806 y += logo_height;
Tao Bao171b4c42017-06-19 23:10:44 -0700807
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100808 if (!menu_->IsMain()) {
809 auto icon_w = gr_get_width(back_icon_.get());
810 auto icon_h = gr_get_height(back_icon_.get());
811 auto icon_x = centered_x / 2 - icon_w / 2;
812 auto icon_y = y - logo_height / 2 - icon_h / 2;
813 gr_blit(back_icon_sel_ && menu_->selection() == -1 ? back_icon_sel_.get() : back_icon_.get(),
814 0, 0, icon_w, icon_h, icon_x, icon_y);
815 }
816 } else {
817 for (size_t i = 0; i < title_lines_.size(); i++) {
818 y += DrawTextLine(x, y, title_lines_[i], i == 0);
819 }
820 y += DrawTextLines(x, y, help_message);
821 }
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700822
Tianjie Xu66dbf632018-10-11 16:54:50 -0700823 y += menu_->DrawHeader(x, y);
Alessandro Astone73961412020-10-04 18:11:40 +0200824 menu_start_y_ = y + 12; // Skip horizontal rule and some margin
Alessandro Astoneefc7a8e2020-06-25 19:54:18 +0200825 menu_->SetMenuHeight(std::max(0, ScreenHeight() - menu_start_y_));
Tianjie Xu66dbf632018-10-11 16:54:50 -0700826 y += menu_->DrawItems(x, y, ScreenWidth(), IsLongPress());
Tao Bao171b4c42017-06-19 23:10:44 -0700827 }
828
829 // Display from the bottom up, until we hit the top of the screen, the bottom of the menu, or
830 // we've displayed the entire text buffer.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700831 SetColor(UIElement::LOG);
Tao Baocb5524c2017-09-08 21:25:32 -0700832 int row = text_row_;
Tao Bao171b4c42017-06-19 23:10:44 -0700833 size_t count = 0;
Tao Bao0bc88de2018-07-31 14:53:16 -0700834 for (int ty = ScreenHeight() - margin_height_ - char_height_; ty >= y && count < text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700835 ty -= char_height_, ++count) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700836 DrawTextLine(margin_width_, ty, text_[row], false);
Tao Bao171b4c42017-06-19 23:10:44 -0700837 --row;
838 if (row < 0) row = text_rows_ - 1;
839 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700840}
841
842// Redraw everything on the screen and flip the screen (make it visible).
843// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700844void ScreenRecoveryUI::update_screen_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700845 draw_screen_locked();
846 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700847}
848
849// Updates only the progress bar, if possible, otherwise redraws the screen.
850// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700851void ScreenRecoveryUI::update_progress_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700852 if (show_text || !pagesIdentical) {
853 draw_screen_locked(); // Must redraw the whole screen
854 pagesIdentical = true;
855 } else {
856 draw_foreground_locked(); // Draw only the progress bar and overlays
857 }
858 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700859}
860
Elliott Hughes985022a2015-04-13 13:04:32 -0700861void ScreenRecoveryUI::ProgressThreadLoop() {
Tao Bao0bc88de2018-07-31 14:53:16 -0700862 double interval = 1.0 / animation_fps_;
Tao Bao26ea9592018-05-09 16:32:02 -0700863 while (!progress_thread_stopped_) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700864 double start = now();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700865 bool redraw = false;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700866 {
867 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker211aebc2011-10-28 15:13:10 -0700868
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700869 // update the installation animation, if active
870 // skip this if we have a text overlay (too expensive to update)
Tao Baoda409fb2018-10-21 23:36:26 -0700871 if ((current_icon_ == INSTALLING_UPDATE || current_icon_ == ERASING) && !show_text) {
872 if (!intro_done_) {
873 if (current_frame_ == intro_frames_.size() - 1) {
874 intro_done_ = true;
875 current_frame_ = 0;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700876 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700877 ++current_frame_;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700878 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700879 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700880 current_frame_ = (current_frame_ + 1) % loop_frames_.size();
Doug Zongker211aebc2011-10-28 15:13:10 -0700881 }
882
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700883 redraw = true;
884 }
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700885
886 // move the progress bar forward on timed intervals, if configured
887 int duration = progressScopeDuration;
888 if (progressBarType == DETERMINATE && duration > 0) {
889 double elapsed = now() - progressScopeTime;
890 float p = 1.0 * elapsed / duration;
891 if (p > 1.0) p = 1.0;
892 if (p > progress) {
893 progress = p;
894 redraw = true;
895 }
896 }
897
898 if (redraw) update_progress_locked();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700899 }
900
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700901 double end = now();
902 // minimum of 20ms delay between frames
903 double delay = interval - (end - start);
904 if (delay < 0.02) delay = 0.02;
905 usleep(static_cast<useconds_t>(delay * 1000000));
906 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700907}
908
Tao Baoda409fb2018-10-21 23:36:26 -0700909std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadBitmap(const std::string& filename) {
910 GRSurface* surface;
911 if (auto result = res_create_display_surface(filename.c_str(), &surface); result < 0) {
912 LOG(ERROR) << "Failed to load bitmap " << filename << " (error " << result << ")";
913 return nullptr;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700914 }
Tao Baoda409fb2018-10-21 23:36:26 -0700915 return std::unique_ptr<GRSurface>(surface);
Doug Zongkereac881c2014-03-07 09:21:25 -0800916}
917
Tao Baoda409fb2018-10-21 23:36:26 -0700918std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadLocalizedBitmap(const std::string& filename) {
919 GRSurface* surface;
xunchang9d05c8a2019-04-16 12:07:42 -0700920 auto result = res_create_localized_alpha_surface(filename.c_str(), locale_.c_str(), &surface);
921 if (result == 0) {
922 return std::unique_ptr<GRSurface>(surface);
Tao Bao736d59c2017-01-03 10:15:33 -0800923 }
xunchang9d05c8a2019-04-16 12:07:42 -0700924 // TODO(xunchang) create a error code enum to refine the retry condition.
925 LOG(WARNING) << "Failed to load bitmap " << filename << " for locale " << locale_ << " (error "
926 << result << "). Falling back to use default locale.";
927
928 result = res_create_localized_alpha_surface(filename.c_str(), DEFAULT_LOCALE, &surface);
929 if (result == 0) {
930 return std::unique_ptr<GRSurface>(surface);
931 }
932
933 LOG(ERROR) << "Failed to load bitmap " << filename << " for locale " << DEFAULT_LOCALE
934 << " (error " << result << ")";
935 return nullptr;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700936}
937
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700938static char** Alloc2d(size_t rows, size_t cols) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700939 char** result = new char*[rows];
940 for (size_t i = 0; i < rows; ++i) {
941 result[i] = new char[cols];
942 memset(result[i], 0, cols);
943 }
944 return result;
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700945}
946
Tianjie Xu35926c42016-04-28 18:06:26 -0700947// Choose the right background string to display during update.
948void ScreenRecoveryUI::SetSystemUpdateText(bool security_update) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700949 if (security_update) {
Tao Baoda409fb2018-10-21 23:36:26 -0700950 installing_text_ = LoadLocalizedBitmap("installing_security_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700951 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700952 installing_text_ = LoadLocalizedBitmap("installing_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700953 }
954 Redraw();
Tianjie Xu35926c42016-04-28 18:06:26 -0700955}
956
Sen Jiangd5304492016-12-09 16:20:49 -0800957bool ScreenRecoveryUI::InitTextParams() {
Tao Baoba4edb32018-06-13 11:22:50 -0700958 // gr_init() would return successfully on font initialization failure.
959 if (gr_sys_font() == nullptr) {
Tao Bao171b4c42017-06-19 23:10:44 -0700960 return false;
961 }
Tao Bao171b4c42017-06-19 23:10:44 -0700962 gr_font_size(gr_sys_font(), &char_width_, &char_height_);
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100963 gr_font_size(gr_menu_font(), &menu_char_width_, &menu_char_height_);
Tao Bao0bc88de2018-07-31 14:53:16 -0700964 text_rows_ = (ScreenHeight() - margin_height_ * 2) / char_height_;
965 text_cols_ = (ScreenWidth() - margin_width_ * 2) / char_width_;
Tao Bao171b4c42017-06-19 23:10:44 -0700966 return true;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -0700967}
968
Tianjie Xub99e6062018-10-16 15:13:09 -0700969bool ScreenRecoveryUI::LoadWipeDataMenuText() {
Tianjie Xu1a0a30a2018-10-25 15:22:07 -0700970 // Ignores the errors since the member variables will stay as nullptr.
971 cancel_wipe_data_text_ = LoadLocalizedBitmap("cancel_wipe_data_text");
972 factory_data_reset_text_ = LoadLocalizedBitmap("factory_data_reset_text");
973 try_again_text_ = LoadLocalizedBitmap("try_again_text");
974 wipe_data_confirmation_text_ = LoadLocalizedBitmap("wipe_data_confirmation_text");
975 wipe_data_menu_header_text_ = LoadLocalizedBitmap("wipe_data_menu_header_text");
Tianjie Xub99e6062018-10-16 15:13:09 -0700976 return true;
977}
978
Mark Salyzyn30017e72020-05-13 12:39:12 -0700979static bool InitGraphics() {
980 // Timeout is same as init wait for file default of 5 seconds and is arbitrary
981 const unsigned timeout = 500; // 10ms increments
982 for (auto retry = timeout; retry > 0; --retry) {
983 if (gr_init() == 0) {
984 if (retry < timeout) {
985 // Log message like init wait for file completion log for consistency.
986 LOG(WARNING) << "wait for 'graphics' took " << ((timeout - retry) * 10) << "ms";
987 }
988 return true;
989 }
990 std::this_thread::sleep_for(10ms);
991 }
992 // Log message like init wait for file timeout log for consistency.
993 LOG(ERROR) << "timeout wait for 'graphics' took " << (timeout * 10) << "ms";
994 return false;
995}
996
Tao Bao736d59c2017-01-03 10:15:33 -0800997bool ScreenRecoveryUI::Init(const std::string& locale) {
998 RecoveryUI::Init(locale);
Tao Baoefb49ad2017-01-31 23:03:10 -0800999
Mark Salyzyn30017e72020-05-13 12:39:12 -07001000 if (!InitGraphics()) {
Tao Baoba4edb32018-06-13 11:22:50 -07001001 return false;
1002 }
fredchiouf2f5aa22022-02-11 16:39:53 +08001003 is_graphics_available = true;
Tao Baoba4edb32018-06-13 11:22:50 -07001004
Tao Bao736d59c2017-01-03 10:15:33 -08001005 if (!InitTextParams()) {
1006 return false;
1007 }
Alessandro Astone4a0de5a2020-03-09 23:17:50 +01001008 menu_draw_funcs_ = std::make_unique<MenuDrawFunctions>(*this);
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001009
Michael Bestas43ff6792019-03-23 17:28:22 +02001010 if (blank_unblank_on_init_) {
1011 gr_fb_blank(true);
1012 gr_fb_blank(false);
1013 }
1014
Tao Bao736d59c2017-01-03 10:15:33 -08001015 // Are we portrait or landscape?
1016 layout_ = (gr_fb_width() > gr_fb_height()) ? LANDSCAPE : PORTRAIT;
1017 // Are we the large variant of our base layout?
1018 if (gr_fb_height() > PixelsFromDp(800)) ++layout_;
Elliott Hughesfaf36e02016-04-20 17:22:16 -07001019
Tao Bao736d59c2017-01-03 10:15:33 -08001020 text_ = Alloc2d(text_rows_, text_cols_ + 1);
1021 file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
Doug Zongker55a36ac2013-03-04 15:49:02 -08001022
Tao Bao736d59c2017-01-03 10:15:33 -08001023 text_col_ = text_row_ = 0;
Doug Zongker211aebc2011-10-28 15:13:10 -07001024
Tao Baoefb49ad2017-01-31 23:03:10 -08001025 // Set up the locale info.
1026 SetLocale(locale);
1027
Tao Baoda409fb2018-10-21 23:36:26 -07001028 error_icon_ = LoadBitmap("icon_error");
Doug Zongker02ec6b82012-08-22 17:26:40 -07001029
Tao Baoda409fb2018-10-21 23:36:26 -07001030 progress_bar_empty_ = LoadBitmap("progress_empty");
1031 progress_bar_fill_ = LoadBitmap("progress_fill");
1032 stage_marker_empty_ = LoadBitmap("stage_empty");
1033 stage_marker_fill_ = LoadBitmap("stage_fill");
Elliott Hughes498cda62016-04-14 16:49:04 -07001034
Tao Baoda409fb2018-10-21 23:36:26 -07001035 erasing_text_ = LoadLocalizedBitmap("erasing_text");
1036 no_command_text_ = LoadLocalizedBitmap("no_command_text");
1037 error_text_ = LoadLocalizedBitmap("error_text");
Doug Zongker211aebc2011-10-28 15:13:10 -07001038
Alessandro Astone4a0de5a2020-03-09 23:17:50 +01001039 back_icon_ = LoadBitmap("ic_back");
1040 back_icon_sel_ = LoadBitmap("ic_back_sel");
Alessandro Astonecae99222020-02-26 17:25:54 +01001041 if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) ||
1042 android::base::GetBoolProperty("ro.fastbootd.available", false)) {
Alessandro Astone82ffc162020-03-29 15:08:09 +02001043 lineage_logo_ = LoadBitmap("logo_image_switch");
David Anderson983e2d52019-01-02 11:35:38 -08001044 fastbootd_logo_ = LoadBitmap("fastbootd");
Alessandro Astone82ffc162020-03-29 15:08:09 +02001045 } else {
1046 lineage_logo_ = LoadBitmap("logo_image");
David Anderson983e2d52019-01-02 11:35:38 -08001047 }
1048
Tao Baoda409fb2018-10-21 23:36:26 -07001049 // Background text for "installing_update" could be "installing update" or
1050 // "installing security update". It will be set after Init() according to the commands in BCB.
1051 installing_text_.reset();
Elliott Hughes498cda62016-04-14 16:49:04 -07001052
Tianjie Xub99e6062018-10-16 15:13:09 -07001053 LoadWipeDataMenuText();
1054
Tao Bao736d59c2017-01-03 10:15:33 -08001055 LoadAnimation();
Doug Zongker02ec6b82012-08-22 17:26:40 -07001056
Tao Bao26ea9592018-05-09 16:32:02 -07001057 // Keep the progress bar updated, even when the process is otherwise busy.
1058 progress_thread_ = std::thread(&ScreenRecoveryUI::ProgressThreadLoop, this);
Sen Jiangd5304492016-12-09 16:20:49 -08001059
fredchiouf2f5aa22022-02-11 16:39:53 +08001060 // set the callback for hall sensor event
1061 (void)ev_sync_sw_state([this](auto&& a, auto&& b) { return this->SetSwCallback(a, b);});
1062
Tao Bao736d59c2017-01-03 10:15:33 -08001063 return true;
Doug Zongker211aebc2011-10-28 15:13:10 -07001064}
1065
Tao Bao551d2c32018-05-09 20:53:13 -07001066std::string ScreenRecoveryUI::GetLocale() const {
Jerry Zhang2dea53e2018-05-02 17:15:03 -07001067 return locale_;
1068}
1069
Elliott Hughes498cda62016-04-14 16:49:04 -07001070void ScreenRecoveryUI::LoadAnimation() {
Tao Bao6cd81682018-05-03 21:53:11 -07001071 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(Paths::Get().resource_dir().c_str()),
1072 closedir);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001073 dirent* de;
1074 std::vector<std::string> intro_frame_names;
Joshua Lambert94a83be2019-12-09 20:24:18 +00001075 std::vector<std::string> loop_frame_names;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001076
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001077 while ((de = readdir(dir.get())) != nullptr) {
1078 int value, num_chars;
1079 if (sscanf(de->d_name, "intro%d%n.png", &value, &num_chars) == 1) {
1080 intro_frame_names.emplace_back(de->d_name, num_chars);
1081 } else if (sscanf(de->d_name, "loop%d%n.png", &value, &num_chars) == 1) {
Joshua Lambert94a83be2019-12-09 20:24:18 +00001082 loop_frame_names.emplace_back(de->d_name, num_chars);
Elliott Hughes498cda62016-04-14 16:49:04 -07001083 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001084 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001085
Tao Baoda409fb2018-10-21 23:36:26 -07001086 size_t intro_frames = intro_frame_names.size();
1087 size_t loop_frames = loop_frame_names.size();
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001088
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001089 // It's okay to not have an intro.
Tao Baoda409fb2018-10-21 23:36:26 -07001090 if (intro_frames == 0) intro_done_ = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001091 // But you must have an animation.
1092 if (loop_frames == 0) abort();
Elliott Hughes498cda62016-04-14 16:49:04 -07001093
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001094 std::sort(intro_frame_names.begin(), intro_frame_names.end());
1095 std::sort(loop_frame_names.begin(), loop_frame_names.end());
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001096
Tao Baoda409fb2018-10-21 23:36:26 -07001097 intro_frames_.clear();
1098 intro_frames_.reserve(intro_frames);
1099 for (const auto& frame_name : intro_frame_names) {
1100 intro_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001101 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001102
Tao Baoda409fb2018-10-21 23:36:26 -07001103 loop_frames_.clear();
1104 loop_frames_.reserve(loop_frames);
1105 for (const auto& frame_name : loop_frame_names) {
1106 loop_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001107 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001108}
1109
Elliott Hughes8de52072015-04-08 20:06:50 -07001110void ScreenRecoveryUI::SetBackground(Icon icon) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001111 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker02ec6b82012-08-22 17:26:40 -07001112
Tao Baoda409fb2018-10-21 23:36:26 -07001113 current_icon_ = icon;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001114 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001115}
1116
Elliott Hughes8de52072015-04-08 20:06:50 -07001117void ScreenRecoveryUI::SetProgressType(ProgressType type) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001118 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001119 if (progressBarType != type) {
1120 progressBarType = type;
1121 }
1122 progressScopeStart = 0;
1123 progressScopeSize = 0;
1124 progress = 0;
1125 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001126}
1127
Elliott Hughes8de52072015-04-08 20:06:50 -07001128void ScreenRecoveryUI::ShowProgress(float portion, float seconds) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001129 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001130 progressBarType = DETERMINATE;
1131 progressScopeStart += progressScopeSize;
1132 progressScopeSize = portion;
1133 progressScopeTime = now();
1134 progressScopeDuration = seconds;
1135 progress = 0;
1136 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001137}
1138
Elliott Hughes8de52072015-04-08 20:06:50 -07001139void ScreenRecoveryUI::SetProgress(float fraction) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001140 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001141 if (fraction < 0.0) fraction = 0.0;
1142 if (fraction > 1.0) fraction = 1.0;
1143 if (progressBarType == DETERMINATE && fraction > progress) {
1144 // Skip updates that aren't visibly different.
Tao Baoda409fb2018-10-21 23:36:26 -07001145 int width = gr_get_width(progress_bar_empty_.get());
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001146 float scale = width * progressScopeSize;
1147 if ((int)(progress * scale) != (int)(fraction * scale)) {
1148 progress = fraction;
1149 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001150 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001151 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001152}
1153
Doug Zongkerc87bab12013-11-25 13:53:25 -08001154void ScreenRecoveryUI::SetStage(int current, int max) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001155 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001156 stage = current;
1157 max_stage = max;
Doug Zongkerc87bab12013-11-25 13:53:25 -08001158}
1159
Tao Baob6918c72015-05-19 17:02:16 -07001160void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001161 std::string str;
1162 android::base::StringAppendV(&str, fmt, ap);
Doug Zongker211aebc2011-10-28 15:13:10 -07001163
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001164 if (copy_to_stdout) {
1165 fputs(str.c_str(), stdout);
1166 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001167
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001168 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001169 if (text_rows_ > 0 && text_cols_ > 0) {
1170 for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
1171 if (*ptr == '\n' || text_col_ >= text_cols_) {
Elliott Hughesc0491632015-05-06 12:40:05 -07001172 text_[text_row_][text_col_] = '\0';
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001173 text_col_ = 0;
1174 text_row_ = (text_row_ + 1) % text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001175 }
1176 if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
Doug Zongker211aebc2011-10-28 15:13:10 -07001177 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001178 text_[text_row_][text_col_] = '\0';
1179 update_screen_locked();
1180 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001181}
1182
Tao Baob6918c72015-05-19 17:02:16 -07001183void ScreenRecoveryUI::Print(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001184 va_list ap;
1185 va_start(ap, fmt);
1186 PrintV(fmt, true, ap);
1187 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001188}
1189
Tianjie Xu8f397302018-08-20 13:40:47 -07001190void ScreenRecoveryUI::PrintOnScreenOnly(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001191 va_list ap;
1192 va_start(ap, fmt);
1193 PrintV(fmt, false, ap);
1194 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001195}
1196
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001197void ScreenRecoveryUI::PutChar(char ch) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001198 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001199 if (ch != '\n') text_[text_row_][text_col_++] = ch;
1200 if (ch == '\n' || text_col_ >= text_cols_) {
1201 text_col_ = 0;
1202 ++text_row_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001203 }
Elliott Hughes8de52072015-04-08 20:06:50 -07001204}
1205
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001206void ScreenRecoveryUI::ClearText() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001207 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001208 text_col_ = 0;
1209 text_row_ = 0;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001210 for (size_t i = 0; i < text_rows_; ++i) {
1211 memset(text_[i], 0, text_cols_ + 1);
1212 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001213}
1214
1215void ScreenRecoveryUI::ShowFile(FILE* fp) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001216 std::vector<off_t> offsets;
1217 offsets.push_back(ftello(fp));
1218 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001219
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001220 struct stat sb;
1221 fstat(fileno(fp), &sb);
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001222
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001223 bool show_prompt = false;
1224 while (true) {
1225 if (show_prompt) {
1226 PrintOnScreenOnly("--(%d%% of %d bytes)--",
1227 static_cast<int>(100 * (double(ftello(fp)) / double(sb.st_size))),
1228 static_cast<int>(sb.st_size));
1229 Redraw();
1230 while (show_prompt) {
1231 show_prompt = false;
Alessandro Astone73961412020-10-04 18:11:40 +02001232 InputEvent evt = WaitInputEvent();
1233 if (evt.type() == EventType::EXTRA) {
1234 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) {
1235 return;
1236 }
1237 }
1238 if (evt.type() != EventType::KEY) {
1239 show_prompt = true;
1240 continue;
1241 }
1242 if (evt.key() == KEY_POWER || evt.key() == KEY_ENTER || evt.key() == KEY_BACKSPACE ||
1243 evt.key() == KEY_BACK || evt.key() == KEY_HOME || evt.key() == KEY_HOMEPAGE) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001244 return;
Alessandro Astone73961412020-10-04 18:11:40 +02001245 } else if (evt.key() == KEY_UP || evt.key() == KEY_VOLUMEUP || evt.key() == KEY_SCROLLUP) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001246 if (offsets.size() <= 1) {
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001247 show_prompt = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001248 } else {
1249 offsets.pop_back();
1250 fseek(fp, offsets.back(), SEEK_SET);
1251 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001252 } else {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001253 if (feof(fp)) {
1254 return;
1255 }
1256 offsets.push_back(ftello(fp));
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001257 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001258 }
1259 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001260 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001261
1262 int ch = getc(fp);
1263 if (ch == EOF) {
1264 while (text_row_ < text_rows_ - 1) PutChar('\n');
1265 show_prompt = true;
1266 } else {
1267 PutChar(ch);
1268 if (text_col_ == 0 && text_row_ >= text_rows_ - 1) {
1269 show_prompt = true;
1270 }
1271 }
1272 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001273}
1274
Tao Bao1d156b92018-05-02 12:43:18 -07001275void ScreenRecoveryUI::ShowFile(const std::string& filename) {
1276 std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(filename.c_str(), "re"), fclose);
1277 if (!fp) {
1278 Print(" Unable to open %s: %s\n", filename.c_str(), strerror(errno));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001279 return;
1280 }
Elliott Hughesc0491632015-05-06 12:40:05 -07001281
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001282 char** old_text = text_;
1283 size_t old_text_col = text_col_;
1284 size_t old_text_row = text_row_;
Elliott Hughesc0491632015-05-06 12:40:05 -07001285
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001286 // Swap in the alternate screen and clear it.
1287 text_ = file_viewer_text_;
1288 ClearText();
Elliott Hughesc0491632015-05-06 12:40:05 -07001289
Tao Bao1d156b92018-05-02 12:43:18 -07001290 ShowFile(fp.get());
Elliott Hughesc0491632015-05-06 12:40:05 -07001291
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001292 text_ = old_text;
1293 text_col_ = old_text_col;
1294 text_row_ = old_text_row;
Elliott Hughes8de52072015-04-08 20:06:50 -07001295}
1296
Tao Baoda409fb2018-10-21 23:36:26 -07001297std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(
1298 const GRSurface* graphic_header, const std::vector<const GRSurface*>& graphic_items,
1299 const std::vector<std::string>& text_headers, const std::vector<std::string>& text_items,
1300 size_t initial_selection) const {
Tianjie Xub99e6062018-10-16 15:13:09 -07001301 // horizontal unusable area: margin width + menu indent
1302 size_t max_width = ScreenWidth() - margin_width_ - kMenuIndent;
1303 // vertical unusable area: margin height + title lines + helper message + high light bar.
1304 // It is safe to reserve more space.
1305 size_t max_height = ScreenHeight() - margin_height_ - char_height_ * (title_lines_.size() + 3);
1306 if (GraphicMenu::Validate(max_width, max_height, graphic_header, graphic_items)) {
1307 return std::make_unique<GraphicMenu>(graphic_header, graphic_items, initial_selection, *this);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001308 }
Tianjie Xub99e6062018-10-16 15:13:09 -07001309
1310 fprintf(stderr, "Failed to initialize graphic menu, falling back to use the text menu.\n");
1311
1312 return CreateMenu(text_headers, text_items, initial_selection);
1313}
1314
1315std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(const std::vector<std::string>& text_headers,
1316 const std::vector<std::string>& text_items,
1317 size_t initial_selection) const {
Alessandro Astone4a0de5a2020-03-09 23:17:50 +01001318 int menu_char_width = MenuCharWidth();
1319 int menu_char_height = MenuCharHeight();
Alessandro Astone4a0de5a2020-03-09 23:17:50 +01001320 int menu_cols = (ScreenWidth() - margin_width_*2 - kMenuIndent) / menu_char_width;
1321 bool wrap_selection = !HasThreeButtons() && !HasTouchScreen();
Alessandro Astoneefc7a8e2020-06-25 19:54:18 +02001322 return std::make_unique<TextMenu>(wrap_selection, menu_cols, text_headers, text_items,
Alessandro Astone4a0de5a2020-03-09 23:17:50 +01001323 initial_selection, menu_char_height, *menu_draw_funcs_);
Doug Zongker211aebc2011-10-28 15:13:10 -07001324}
1325
1326int ScreenRecoveryUI::SelectMenu(int sel) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001327 std::lock_guard<std::mutex> lg(updateMutex);
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001328 if (menu_) {
1329 int old_sel = menu_->selection();
1330 sel = menu_->Select(sel);
Elliott Hughesfc06f872015-03-23 13:45:31 -07001331
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001332 if (sel != old_sel) {
1333 update_screen_locked();
1334 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001335 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001336 return sel;
Doug Zongker211aebc2011-10-28 15:13:10 -07001337}
1338
Alessandro Astone73961412020-10-04 18:11:40 +02001339int ScreenRecoveryUI::SelectMenu(const Point& point) {
1340 int new_sel = Device::kNoAction;
1341 std::lock_guard<std::mutex> lg(updateMutex);
1342 if (menu_) {
1343 if (!menu_->IsMain()) {
1344 // Back arrow hitbox
1345 const static int logo_width = gr_get_width(lineage_logo_.get());
1346 const static int logo_height = gr_get_height(lineage_logo_.get());
1347 const static int icon_w = gr_get_width(back_icon_.get());
1348 const static int icon_h = gr_get_height(back_icon_.get());
1349 const static int centered_x = ScreenWidth() / 2 - logo_width / 2;
1350 const static int icon_x = centered_x / 2 - icon_w / 2;
1351 const static int icon_y = margin_height_ + logo_height / 2 - icon_h / 2;
1352
1353 if (point.x() >= icon_x && point.x() <= icon_x + icon_w &&
1354 point.y() >= icon_y && point.y() <= icon_y + icon_h) {
1355 return Device::kGoBack;
1356 }
1357 }
1358
1359 if (point.y() >= menu_start_y_ &&
1360 point.y() < menu_start_y_ + menu_->ItemsCount() * MenuItemHeight()) {
1361 int old_sel = menu_->selection();
1362 int relative_sel = (point.y() - menu_start_y_) / MenuItemHeight();
1363 new_sel = menu_->SelectVisible(relative_sel);
1364 if (new_sel != -1 && new_sel != old_sel) {
1365 update_screen_locked();
1366 }
1367 }
1368 }
1369 return new_sel;
1370}
1371
1372int ScreenRecoveryUI::ScrollMenu(int updown) {
1373 std::lock_guard<std::mutex> lg(updateMutex);
1374 int sel = Device::kNoAction;
1375 if (menu_) {
1376 sel = menu_->Scroll(updown);
1377 update_screen_locked();
1378 }
1379 return sel;
1380}
1381
Tianjie Xub99e6062018-10-16 15:13:09 -07001382size_t ScreenRecoveryUI::ShowMenu(std::unique_ptr<Menu>&& menu, bool menu_only,
Tao Bao1fe1afe2018-05-01 15:56:05 -07001383 const std::function<int(int, bool)>& key_handler) {
Tao Bao3aec6962018-04-20 09:24:58 -07001384 // Throw away keys pressed previously, so user doesn't accidentally trigger menu items.
1385 FlushKeys();
1386
Jerry Zhangb76af932018-05-22 12:08:35 -07001387 // If there is a key interrupt in progress, return KeyError::INTERRUPTED without starting the
1388 // menu.
1389 if (IsKeyInterrupted()) return static_cast<size_t>(KeyError::INTERRUPTED);
1390
Tianjie Xub99e6062018-10-16 15:13:09 -07001391 CHECK(menu != nullptr);
Tao Bao3aec6962018-04-20 09:24:58 -07001392
Tianjie Xub99e6062018-10-16 15:13:09 -07001393 // Starts and displays the menu
1394 menu_ = std::move(menu);
1395 Redraw();
1396
1397 int selected = menu_->selection();
Tao Bao3aec6962018-04-20 09:24:58 -07001398 int chosen_item = -1;
1399 while (chosen_item < 0) {
Alessandro Astone73961412020-10-04 18:11:40 +02001400 InputEvent evt = WaitInputEvent();
1401 if (evt.type() == EventType::EXTRA) {
1402 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) {
1403 // WaitKey() was interrupted.
1404 return static_cast<size_t>(KeyError::INTERRUPTED);
1405 }
1406 if (evt.key() == static_cast<int>(KeyError::TIMED_OUT)) { // WaitKey() timed out.
1407 if (WasTextEverVisible()) {
1408 continue;
1409 } else {
1410 LOG(INFO) << "Timed out waiting for key input; rebooting.";
1411 menu_.reset();
1412 Redraw();
1413 return static_cast<size_t>(KeyError::TIMED_OUT);
1414 }
Tao Bao3aec6962018-04-20 09:24:58 -07001415 }
1416 }
1417
Alessandro Astone73961412020-10-04 18:11:40 +02001418 int action = Device::kNoAction;
1419 if (evt.type() == EventType::TOUCH) {
1420 int touch_sel = SelectMenu(evt.pos());
1421 if (touch_sel < 0) {
1422 action = touch_sel;
1423 } else {
1424 action = Device::kInvokeItem;
1425 selected = touch_sel;
1426 }
1427 } else {
1428 bool visible = IsTextVisible();
1429 action = key_handler(evt.key(), visible);
1430 }
Tao Bao3aec6962018-04-20 09:24:58 -07001431 if (action < 0) {
1432 switch (action) {
1433 case Device::kHighlightUp:
1434 selected = SelectMenu(--selected);
1435 break;
1436 case Device::kHighlightDown:
1437 selected = SelectMenu(++selected);
1438 break;
Alessandro Astone73961412020-10-04 18:11:40 +02001439 case Device::kScrollUp:
1440 selected = ScrollMenu(-1);
1441 break;
1442 case Device::kScrollDown:
1443 selected = ScrollMenu(1);
1444 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001445 case Device::kInvokeItem:
Alessandro Astone4a0de5a2020-03-09 23:17:50 +01001446 if (selected < 0) {
1447 chosen_item = Device::kGoBack;
1448 } else {
1449 chosen_item = selected;
1450 }
Tao Bao3aec6962018-04-20 09:24:58 -07001451 break;
1452 case Device::kNoAction:
1453 break;
Tom Marshall4b921692017-08-24 13:50:01 +00001454 case Device::kGoBack:
1455 chosen_item = Device::kGoBack;
1456 break;
1457 case Device::kGoHome:
1458 chosen_item = Device::kGoHome;
1459 break;
Tom Marshallfdf50332018-12-17 15:57:44 -08001460 case Device::kDoSideload:
1461 chosen_item = Device::kDoSideload;
1462 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001463 }
1464 } else if (!menu_only) {
1465 chosen_item = action;
1466 }
Tom Marshallfdf50332018-12-17 15:57:44 -08001467
1468 if (chosen_item == Device::kGoBack || chosen_item == Device::kGoHome ||
1469 chosen_item == Device::kDoSideload) {
Tom Marshall4b921692017-08-24 13:50:01 +00001470 break;
1471 }
Tao Bao3aec6962018-04-20 09:24:58 -07001472 }
1473
Tianjie Xub99e6062018-10-16 15:13:09 -07001474 menu_.reset();
Tianjie Xub99e6062018-10-16 15:13:09 -07001475
Tao Bao3aec6962018-04-20 09:24:58 -07001476 return chosen_item;
1477}
1478
Tianjie Xub99e6062018-10-16 15:13:09 -07001479size_t ScreenRecoveryUI::ShowMenu(const std::vector<std::string>& headers,
1480 const std::vector<std::string>& items, size_t initial_selection,
1481 bool menu_only,
1482 const std::function<int(int, bool)>& key_handler) {
1483 auto menu = CreateMenu(headers, items, initial_selection);
1484 if (menu == nullptr) {
1485 return initial_selection;
1486 }
1487
Tao Baofa8e02a2019-06-27 09:07:04 -07001488 return ShowMenu(std::move(menu), menu_only, key_handler);
Tianjie Xub99e6062018-10-16 15:13:09 -07001489}
1490
1491size_t ScreenRecoveryUI::ShowPromptWipeDataMenu(const std::vector<std::string>& backup_headers,
1492 const std::vector<std::string>& backup_items,
1493 const std::function<int(int, bool)>& key_handler) {
Tao Baoda409fb2018-10-21 23:36:26 -07001494 auto wipe_data_menu = CreateMenu(wipe_data_menu_header_text_.get(),
1495 { try_again_text_.get(), factory_data_reset_text_.get() },
1496 backup_headers, backup_items, 0);
Tianjie Xub99e6062018-10-16 15:13:09 -07001497 if (wipe_data_menu == nullptr) {
1498 return 0;
1499 }
1500
1501 return ShowMenu(std::move(wipe_data_menu), true, key_handler);
1502}
1503
Tianjie Xu1a0a30a2018-10-25 15:22:07 -07001504size_t ScreenRecoveryUI::ShowPromptWipeDataConfirmationMenu(
1505 const std::vector<std::string>& backup_headers, const std::vector<std::string>& backup_items,
1506 const std::function<int(int, bool)>& key_handler) {
1507 auto confirmation_menu =
1508 CreateMenu(wipe_data_confirmation_text_.get(),
1509 { cancel_wipe_data_text_.get(), factory_data_reset_text_.get() }, backup_headers,
1510 backup_items, 0);
1511 if (confirmation_menu == nullptr) {
1512 return 0;
1513 }
1514
1515 return ShowMenu(std::move(confirmation_menu), true, key_handler);
1516}
1517
Elliott Hughes8de52072015-04-08 20:06:50 -07001518bool ScreenRecoveryUI::IsTextVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001519 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001520 int visible = show_text;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001521 return visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001522}
1523
Elliott Hughes8de52072015-04-08 20:06:50 -07001524bool ScreenRecoveryUI::WasTextEverVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001525 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001526 int ever_visible = show_text_ever;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001527 return ever_visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001528}
1529
Elliott Hughes8de52072015-04-08 20:06:50 -07001530void ScreenRecoveryUI::ShowText(bool visible) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001531 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001532 show_text = visible;
1533 if (show_text) show_text_ever = true;
1534 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001535}
Doug Zongkerc0441d12013-07-31 11:28:24 -07001536
Elliott Hughes8de52072015-04-08 20:06:50 -07001537void ScreenRecoveryUI::Redraw() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001538 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001539 update_screen_locked();
Doug Zongkerc0441d12013-07-31 11:28:24 -07001540}
Elliott Hughes642aaa72015-04-10 12:47:46 -07001541
1542void ScreenRecoveryUI::KeyLongPress(int) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001543 // Redraw so that if we're in the menu, the highlight
1544 // will change color to indicate a successful long press.
1545 Redraw();
Elliott Hughes642aaa72015-04-10 12:47:46 -07001546}
Tao Baoefb49ad2017-01-31 23:03:10 -08001547
1548void ScreenRecoveryUI::SetLocale(const std::string& new_locale) {
1549 locale_ = new_locale;
1550 rtl_locale_ = false;
1551
1552 if (!new_locale.empty()) {
Tao Bao347a6592018-05-08 15:58:29 -07001553 size_t separator = new_locale.find('-');
1554 // lang has the language prefix prior to the separator, or full string if none exists.
1555 std::string lang = new_locale.substr(0, separator);
Tao Baoefb49ad2017-01-31 23:03:10 -08001556
1557 // A bit cheesy: keep an explicit list of supported RTL languages.
1558 if (lang == "ar" || // Arabic
1559 lang == "fa" || // Persian (Farsi)
1560 lang == "he" || // Hebrew (new language code)
1561 lang == "iw" || // Hebrew (old language code)
1562 lang == "ur") { // Urdu
1563 rtl_locale_ = true;
1564 }
1565 }
1566}
fredchiouf2f5aa22022-02-11 16:39:53 +08001567
1568int ScreenRecoveryUI::SetSwCallback(int code, int value) {
1569 if (!is_graphics_available) { return -1; }
1570 if (code > SW_MAX) { return -1; }
1571 if (code != SW_LID) { return 0; }
1572
1573 /* detect dual display */
1574 if (!gr_has_multiple_connectors()) { return -1; }
1575
1576 /* turn off all screen */
1577 gr_fb_blank(true, DirectRenderManager::DRM_INNER);
1578 gr_fb_blank(true, DirectRenderManager::DRM_OUTER);
1579 gr_color(0, 0, 0, 255);
1580 gr_clear();
1581
1582 /* turn on the screen */
1583 gr_fb_blank(false, value);
1584 gr_flip();
1585
1586 /* set the retation */
1587 std::string rotation_str;
1588 if (value == DirectRenderManager::DRM_OUTER) {
1589 rotation_str =
1590 android::base::GetProperty("ro.minui.second_rotation", "ROTATION_NONE");
1591 } else {
1592 rotation_str =
1593 android::base::GetProperty("ro.minui.default_rotation", "ROTATION_NONE");
1594 }
1595
1596 if (rotation_str == "ROTATION_RIGHT") {
1597 gr_rotate(GRRotation::RIGHT);
1598 } else if (rotation_str == "ROTATION_DOWN") {
1599 gr_rotate(GRRotation::DOWN);
1600 } else if (rotation_str == "ROTATION_LEFT") {
1601 gr_rotate(GRRotation::LEFT);
1602 } else { // "ROTATION_NONE" or unknown string
1603 gr_rotate(GRRotation::NONE);
1604 }
1605 Redraw();
1606
1607 return 0;
1608}