blob: 63c4d4a9f41435746d4fb35003717f3547f5ef02 [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
795 if (fastbootd_logo_ && fastbootd_logo_enabled_) {
796 // Try to get this centered on screen.
797 auto width = gr_get_width(fastbootd_logo_.get());
798 auto height = gr_get_height(fastbootd_logo_.get());
799 auto centered_x = ScreenWidth() / 2 - width / 2;
800 DrawSurface(fastbootd_logo_.get(), 0, 0, width, height, centered_x, y);
801 y += height;
802 }
803
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700804 if (menu_) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700805 int x = margin_width_ + kMenuIndent;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700806
Tianjie Xu66dbf632018-10-11 16:54:50 -0700807 SetColor(UIElement::INFO);
Jerry Zhang0e577ee2018-05-07 11:21:10 -0700808
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100809 if (lineage_logo_ && back_icon_) {
810 auto logo_width = gr_get_width(lineage_logo_.get());
811 auto logo_height = gr_get_height(lineage_logo_.get());
812 auto centered_x = ScreenWidth() / 2 - logo_width / 2;
813 DrawSurface(lineage_logo_.get(), 0, 0, logo_width, logo_height, centered_x, y);
814 y += logo_height;
Tao Bao171b4c42017-06-19 23:10:44 -0700815
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100816 if (!menu_->IsMain()) {
817 auto icon_w = gr_get_width(back_icon_.get());
818 auto icon_h = gr_get_height(back_icon_.get());
819 auto icon_x = centered_x / 2 - icon_w / 2;
820 auto icon_y = y - logo_height / 2 - icon_h / 2;
821 gr_blit(back_icon_sel_ && menu_->selection() == -1 ? back_icon_sel_.get() : back_icon_.get(),
822 0, 0, icon_w, icon_h, icon_x, icon_y);
823 }
824 } else {
825 for (size_t i = 0; i < title_lines_.size(); i++) {
826 y += DrawTextLine(x, y, title_lines_[i], i == 0);
827 }
828 y += DrawTextLines(x, y, help_message);
829 }
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700830
Tianjie Xu66dbf632018-10-11 16:54:50 -0700831 y += menu_->DrawHeader(x, y);
Alessandro Astone73961412020-10-04 18:11:40 +0200832 menu_start_y_ = y + 12; // Skip horizontal rule and some margin
Alessandro Astoneefc7a8e2020-06-25 19:54:18 +0200833 menu_->SetMenuHeight(std::max(0, ScreenHeight() - menu_start_y_));
Tianjie Xu66dbf632018-10-11 16:54:50 -0700834 y += menu_->DrawItems(x, y, ScreenWidth(), IsLongPress());
Tao Bao171b4c42017-06-19 23:10:44 -0700835 }
836
837 // Display from the bottom up, until we hit the top of the screen, the bottom of the menu, or
838 // we've displayed the entire text buffer.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700839 SetColor(UIElement::LOG);
Tao Baocb5524c2017-09-08 21:25:32 -0700840 int row = text_row_;
Tao Bao171b4c42017-06-19 23:10:44 -0700841 size_t count = 0;
Tao Bao0bc88de2018-07-31 14:53:16 -0700842 for (int ty = ScreenHeight() - margin_height_ - char_height_; ty >= y && count < text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700843 ty -= char_height_, ++count) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700844 DrawTextLine(margin_width_, ty, text_[row], false);
Tao Bao171b4c42017-06-19 23:10:44 -0700845 --row;
846 if (row < 0) row = text_rows_ - 1;
847 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700848}
849
850// Redraw everything on the screen and flip the screen (make it visible).
851// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700852void ScreenRecoveryUI::update_screen_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700853 draw_screen_locked();
854 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700855}
856
857// Updates only the progress bar, if possible, otherwise redraws the screen.
858// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700859void ScreenRecoveryUI::update_progress_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700860 if (show_text || !pagesIdentical) {
861 draw_screen_locked(); // Must redraw the whole screen
862 pagesIdentical = true;
863 } else {
864 draw_foreground_locked(); // Draw only the progress bar and overlays
865 }
866 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700867}
868
Elliott Hughes985022a2015-04-13 13:04:32 -0700869void ScreenRecoveryUI::ProgressThreadLoop() {
Tao Bao0bc88de2018-07-31 14:53:16 -0700870 double interval = 1.0 / animation_fps_;
Tao Bao26ea9592018-05-09 16:32:02 -0700871 while (!progress_thread_stopped_) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700872 double start = now();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700873 bool redraw = false;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700874 {
875 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker211aebc2011-10-28 15:13:10 -0700876
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700877 // update the installation animation, if active
878 // skip this if we have a text overlay (too expensive to update)
Tao Baoda409fb2018-10-21 23:36:26 -0700879 if ((current_icon_ == INSTALLING_UPDATE || current_icon_ == ERASING) && !show_text) {
880 if (!intro_done_) {
881 if (current_frame_ == intro_frames_.size() - 1) {
882 intro_done_ = true;
883 current_frame_ = 0;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700884 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700885 ++current_frame_;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700886 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700887 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700888 current_frame_ = (current_frame_ + 1) % loop_frames_.size();
Doug Zongker211aebc2011-10-28 15:13:10 -0700889 }
890
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700891 redraw = true;
892 }
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700893
894 // move the progress bar forward on timed intervals, if configured
895 int duration = progressScopeDuration;
896 if (progressBarType == DETERMINATE && duration > 0) {
897 double elapsed = now() - progressScopeTime;
898 float p = 1.0 * elapsed / duration;
899 if (p > 1.0) p = 1.0;
900 if (p > progress) {
901 progress = p;
902 redraw = true;
903 }
904 }
905
906 if (redraw) update_progress_locked();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700907 }
908
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700909 double end = now();
910 // minimum of 20ms delay between frames
911 double delay = interval - (end - start);
912 if (delay < 0.02) delay = 0.02;
913 usleep(static_cast<useconds_t>(delay * 1000000));
914 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700915}
916
Tao Baoda409fb2018-10-21 23:36:26 -0700917std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadBitmap(const std::string& filename) {
918 GRSurface* surface;
919 if (auto result = res_create_display_surface(filename.c_str(), &surface); result < 0) {
920 LOG(ERROR) << "Failed to load bitmap " << filename << " (error " << result << ")";
921 return nullptr;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700922 }
Tao Baoda409fb2018-10-21 23:36:26 -0700923 return std::unique_ptr<GRSurface>(surface);
Doug Zongkereac881c2014-03-07 09:21:25 -0800924}
925
Tao Baoda409fb2018-10-21 23:36:26 -0700926std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadLocalizedBitmap(const std::string& filename) {
927 GRSurface* surface;
xunchang9d05c8a2019-04-16 12:07:42 -0700928 auto result = res_create_localized_alpha_surface(filename.c_str(), locale_.c_str(), &surface);
929 if (result == 0) {
930 return std::unique_ptr<GRSurface>(surface);
Tao Bao736d59c2017-01-03 10:15:33 -0800931 }
xunchang9d05c8a2019-04-16 12:07:42 -0700932 // TODO(xunchang) create a error code enum to refine the retry condition.
933 LOG(WARNING) << "Failed to load bitmap " << filename << " for locale " << locale_ << " (error "
934 << result << "). Falling back to use default locale.";
935
936 result = res_create_localized_alpha_surface(filename.c_str(), DEFAULT_LOCALE, &surface);
937 if (result == 0) {
938 return std::unique_ptr<GRSurface>(surface);
939 }
940
941 LOG(ERROR) << "Failed to load bitmap " << filename << " for locale " << DEFAULT_LOCALE
942 << " (error " << result << ")";
943 return nullptr;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700944}
945
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700946static char** Alloc2d(size_t rows, size_t cols) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700947 char** result = new char*[rows];
948 for (size_t i = 0; i < rows; ++i) {
949 result[i] = new char[cols];
950 memset(result[i], 0, cols);
951 }
952 return result;
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700953}
954
Tianjie Xu35926c42016-04-28 18:06:26 -0700955// Choose the right background string to display during update.
956void ScreenRecoveryUI::SetSystemUpdateText(bool security_update) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700957 if (security_update) {
Tao Baoda409fb2018-10-21 23:36:26 -0700958 installing_text_ = LoadLocalizedBitmap("installing_security_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700959 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700960 installing_text_ = LoadLocalizedBitmap("installing_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700961 }
962 Redraw();
Tianjie Xu35926c42016-04-28 18:06:26 -0700963}
964
Sen Jiangd5304492016-12-09 16:20:49 -0800965bool ScreenRecoveryUI::InitTextParams() {
Tao Baoba4edb32018-06-13 11:22:50 -0700966 // gr_init() would return successfully on font initialization failure.
967 if (gr_sys_font() == nullptr) {
Tao Bao171b4c42017-06-19 23:10:44 -0700968 return false;
969 }
Tao Bao171b4c42017-06-19 23:10:44 -0700970 gr_font_size(gr_sys_font(), &char_width_, &char_height_);
Alessandro Astone4a0de5a2020-03-09 23:17:50 +0100971 gr_font_size(gr_menu_font(), &menu_char_width_, &menu_char_height_);
Tao Bao0bc88de2018-07-31 14:53:16 -0700972 text_rows_ = (ScreenHeight() - margin_height_ * 2) / char_height_;
973 text_cols_ = (ScreenWidth() - margin_width_ * 2) / char_width_;
Tao Bao171b4c42017-06-19 23:10:44 -0700974 return true;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -0700975}
976
Tianjie Xub99e6062018-10-16 15:13:09 -0700977bool ScreenRecoveryUI::LoadWipeDataMenuText() {
Tianjie Xu1a0a30a2018-10-25 15:22:07 -0700978 // Ignores the errors since the member variables will stay as nullptr.
979 cancel_wipe_data_text_ = LoadLocalizedBitmap("cancel_wipe_data_text");
980 factory_data_reset_text_ = LoadLocalizedBitmap("factory_data_reset_text");
981 try_again_text_ = LoadLocalizedBitmap("try_again_text");
982 wipe_data_confirmation_text_ = LoadLocalizedBitmap("wipe_data_confirmation_text");
983 wipe_data_menu_header_text_ = LoadLocalizedBitmap("wipe_data_menu_header_text");
Tianjie Xub99e6062018-10-16 15:13:09 -0700984 return true;
985}
986
Mark Salyzyn30017e72020-05-13 12:39:12 -0700987static bool InitGraphics() {
988 // Timeout is same as init wait for file default of 5 seconds and is arbitrary
989 const unsigned timeout = 500; // 10ms increments
990 for (auto retry = timeout; retry > 0; --retry) {
991 if (gr_init() == 0) {
992 if (retry < timeout) {
993 // Log message like init wait for file completion log for consistency.
994 LOG(WARNING) << "wait for 'graphics' took " << ((timeout - retry) * 10) << "ms";
995 }
996 return true;
997 }
998 std::this_thread::sleep_for(10ms);
999 }
1000 // Log message like init wait for file timeout log for consistency.
1001 LOG(ERROR) << "timeout wait for 'graphics' took " << (timeout * 10) << "ms";
1002 return false;
1003}
1004
Tao Bao736d59c2017-01-03 10:15:33 -08001005bool ScreenRecoveryUI::Init(const std::string& locale) {
1006 RecoveryUI::Init(locale);
Tao Baoefb49ad2017-01-31 23:03:10 -08001007
Mark Salyzyn30017e72020-05-13 12:39:12 -07001008 if (!InitGraphics()) {
Tao Baoba4edb32018-06-13 11:22:50 -07001009 return false;
1010 }
fredchiouf2f5aa22022-02-11 16:39:53 +08001011 is_graphics_available = true;
Tao Baoba4edb32018-06-13 11:22:50 -07001012
Tao Bao736d59c2017-01-03 10:15:33 -08001013 if (!InitTextParams()) {
1014 return false;
1015 }
Alessandro Astone4a0de5a2020-03-09 23:17:50 +01001016 menu_draw_funcs_ = std::make_unique<MenuDrawFunctions>(*this);
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001017
Michael Bestas43ff6792019-03-23 17:28:22 +02001018 if (blank_unblank_on_init_) {
1019 gr_fb_blank(true);
1020 gr_fb_blank(false);
1021 }
1022
Tao Bao736d59c2017-01-03 10:15:33 -08001023 // Are we portrait or landscape?
1024 layout_ = (gr_fb_width() > gr_fb_height()) ? LANDSCAPE : PORTRAIT;
1025 // Are we the large variant of our base layout?
1026 if (gr_fb_height() > PixelsFromDp(800)) ++layout_;
Elliott Hughesfaf36e02016-04-20 17:22:16 -07001027
Tao Bao736d59c2017-01-03 10:15:33 -08001028 text_ = Alloc2d(text_rows_, text_cols_ + 1);
1029 file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
Doug Zongker55a36ac2013-03-04 15:49:02 -08001030
Tao Bao736d59c2017-01-03 10:15:33 -08001031 text_col_ = text_row_ = 0;
Doug Zongker211aebc2011-10-28 15:13:10 -07001032
Tao Baoefb49ad2017-01-31 23:03:10 -08001033 // Set up the locale info.
1034 SetLocale(locale);
1035
Tao Baoda409fb2018-10-21 23:36:26 -07001036 error_icon_ = LoadBitmap("icon_error");
Doug Zongker02ec6b82012-08-22 17:26:40 -07001037
Tao Baoda409fb2018-10-21 23:36:26 -07001038 progress_bar_empty_ = LoadBitmap("progress_empty");
1039 progress_bar_fill_ = LoadBitmap("progress_fill");
1040 stage_marker_empty_ = LoadBitmap("stage_empty");
1041 stage_marker_fill_ = LoadBitmap("stage_fill");
Elliott Hughes498cda62016-04-14 16:49:04 -07001042
Tao Baoda409fb2018-10-21 23:36:26 -07001043 erasing_text_ = LoadLocalizedBitmap("erasing_text");
1044 no_command_text_ = LoadLocalizedBitmap("no_command_text");
1045 error_text_ = LoadLocalizedBitmap("error_text");
Doug Zongker211aebc2011-10-28 15:13:10 -07001046
Alessandro Astone4a0de5a2020-03-09 23:17:50 +01001047 lineage_logo_ = LoadBitmap("logo_image");
1048 back_icon_ = LoadBitmap("ic_back");
1049 back_icon_sel_ = LoadBitmap("ic_back_sel");
Alessandro Astonecae99222020-02-26 17:25:54 +01001050 if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) ||
1051 android::base::GetBoolProperty("ro.fastbootd.available", false)) {
David Anderson983e2d52019-01-02 11:35:38 -08001052 fastbootd_logo_ = LoadBitmap("fastbootd");
1053 }
1054
Tao Baoda409fb2018-10-21 23:36:26 -07001055 // Background text for "installing_update" could be "installing update" or
1056 // "installing security update". It will be set after Init() according to the commands in BCB.
1057 installing_text_.reset();
Elliott Hughes498cda62016-04-14 16:49:04 -07001058
Tianjie Xub99e6062018-10-16 15:13:09 -07001059 LoadWipeDataMenuText();
1060
Tao Bao736d59c2017-01-03 10:15:33 -08001061 LoadAnimation();
Doug Zongker02ec6b82012-08-22 17:26:40 -07001062
Tao Bao26ea9592018-05-09 16:32:02 -07001063 // Keep the progress bar updated, even when the process is otherwise busy.
1064 progress_thread_ = std::thread(&ScreenRecoveryUI::ProgressThreadLoop, this);
Sen Jiangd5304492016-12-09 16:20:49 -08001065
fredchiouf2f5aa22022-02-11 16:39:53 +08001066 // set the callback for hall sensor event
1067 (void)ev_sync_sw_state([this](auto&& a, auto&& b) { return this->SetSwCallback(a, b);});
1068
Tao Bao736d59c2017-01-03 10:15:33 -08001069 return true;
Doug Zongker211aebc2011-10-28 15:13:10 -07001070}
1071
Tao Bao551d2c32018-05-09 20:53:13 -07001072std::string ScreenRecoveryUI::GetLocale() const {
Jerry Zhang2dea53e2018-05-02 17:15:03 -07001073 return locale_;
1074}
1075
Elliott Hughes498cda62016-04-14 16:49:04 -07001076void ScreenRecoveryUI::LoadAnimation() {
Tao Bao6cd81682018-05-03 21:53:11 -07001077 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(Paths::Get().resource_dir().c_str()),
1078 closedir);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001079 dirent* de;
1080 std::vector<std::string> intro_frame_names;
Joshua Lambert94a83be2019-12-09 20:24:18 +00001081 std::vector<std::string> loop_frame_names;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001082
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001083 while ((de = readdir(dir.get())) != nullptr) {
1084 int value, num_chars;
1085 if (sscanf(de->d_name, "intro%d%n.png", &value, &num_chars) == 1) {
1086 intro_frame_names.emplace_back(de->d_name, num_chars);
1087 } else if (sscanf(de->d_name, "loop%d%n.png", &value, &num_chars) == 1) {
Joshua Lambert94a83be2019-12-09 20:24:18 +00001088 loop_frame_names.emplace_back(de->d_name, num_chars);
Elliott Hughes498cda62016-04-14 16:49:04 -07001089 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001090 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001091
Tao Baoda409fb2018-10-21 23:36:26 -07001092 size_t intro_frames = intro_frame_names.size();
1093 size_t loop_frames = loop_frame_names.size();
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001094
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001095 // It's okay to not have an intro.
Tao Baoda409fb2018-10-21 23:36:26 -07001096 if (intro_frames == 0) intro_done_ = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001097 // But you must have an animation.
1098 if (loop_frames == 0) abort();
Elliott Hughes498cda62016-04-14 16:49:04 -07001099
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001100 std::sort(intro_frame_names.begin(), intro_frame_names.end());
1101 std::sort(loop_frame_names.begin(), loop_frame_names.end());
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001102
Tao Baoda409fb2018-10-21 23:36:26 -07001103 intro_frames_.clear();
1104 intro_frames_.reserve(intro_frames);
1105 for (const auto& frame_name : intro_frame_names) {
1106 intro_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001107 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001108
Tao Baoda409fb2018-10-21 23:36:26 -07001109 loop_frames_.clear();
1110 loop_frames_.reserve(loop_frames);
1111 for (const auto& frame_name : loop_frame_names) {
1112 loop_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001113 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001114}
1115
Elliott Hughes8de52072015-04-08 20:06:50 -07001116void ScreenRecoveryUI::SetBackground(Icon icon) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001117 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker02ec6b82012-08-22 17:26:40 -07001118
Tao Baoda409fb2018-10-21 23:36:26 -07001119 current_icon_ = icon;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001120 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001121}
1122
Elliott Hughes8de52072015-04-08 20:06:50 -07001123void ScreenRecoveryUI::SetProgressType(ProgressType type) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001124 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001125 if (progressBarType != type) {
1126 progressBarType = type;
1127 }
1128 progressScopeStart = 0;
1129 progressScopeSize = 0;
1130 progress = 0;
1131 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001132}
1133
Elliott Hughes8de52072015-04-08 20:06:50 -07001134void ScreenRecoveryUI::ShowProgress(float portion, float seconds) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001135 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001136 progressBarType = DETERMINATE;
1137 progressScopeStart += progressScopeSize;
1138 progressScopeSize = portion;
1139 progressScopeTime = now();
1140 progressScopeDuration = seconds;
1141 progress = 0;
1142 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001143}
1144
Elliott Hughes8de52072015-04-08 20:06:50 -07001145void ScreenRecoveryUI::SetProgress(float fraction) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001146 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001147 if (fraction < 0.0) fraction = 0.0;
1148 if (fraction > 1.0) fraction = 1.0;
1149 if (progressBarType == DETERMINATE && fraction > progress) {
1150 // Skip updates that aren't visibly different.
Tao Baoda409fb2018-10-21 23:36:26 -07001151 int width = gr_get_width(progress_bar_empty_.get());
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001152 float scale = width * progressScopeSize;
1153 if ((int)(progress * scale) != (int)(fraction * scale)) {
1154 progress = fraction;
1155 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001156 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001157 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001158}
1159
Doug Zongkerc87bab12013-11-25 13:53:25 -08001160void ScreenRecoveryUI::SetStage(int current, int max) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001161 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001162 stage = current;
1163 max_stage = max;
Doug Zongkerc87bab12013-11-25 13:53:25 -08001164}
1165
Tao Baob6918c72015-05-19 17:02:16 -07001166void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001167 std::string str;
1168 android::base::StringAppendV(&str, fmt, ap);
Doug Zongker211aebc2011-10-28 15:13:10 -07001169
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001170 if (copy_to_stdout) {
1171 fputs(str.c_str(), stdout);
1172 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001173
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001174 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001175 if (text_rows_ > 0 && text_cols_ > 0) {
1176 for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
1177 if (*ptr == '\n' || text_col_ >= text_cols_) {
Elliott Hughesc0491632015-05-06 12:40:05 -07001178 text_[text_row_][text_col_] = '\0';
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001179 text_col_ = 0;
1180 text_row_ = (text_row_ + 1) % text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001181 }
1182 if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
Doug Zongker211aebc2011-10-28 15:13:10 -07001183 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001184 text_[text_row_][text_col_] = '\0';
1185 update_screen_locked();
1186 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001187}
1188
Tao Baob6918c72015-05-19 17:02:16 -07001189void ScreenRecoveryUI::Print(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001190 va_list ap;
1191 va_start(ap, fmt);
1192 PrintV(fmt, true, ap);
1193 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001194}
1195
Tianjie Xu8f397302018-08-20 13:40:47 -07001196void ScreenRecoveryUI::PrintOnScreenOnly(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001197 va_list ap;
1198 va_start(ap, fmt);
1199 PrintV(fmt, false, ap);
1200 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001201}
1202
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001203void ScreenRecoveryUI::PutChar(char ch) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001204 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001205 if (ch != '\n') text_[text_row_][text_col_++] = ch;
1206 if (ch == '\n' || text_col_ >= text_cols_) {
1207 text_col_ = 0;
1208 ++text_row_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001209 }
Elliott Hughes8de52072015-04-08 20:06:50 -07001210}
1211
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001212void ScreenRecoveryUI::ClearText() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001213 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001214 text_col_ = 0;
1215 text_row_ = 0;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001216 for (size_t i = 0; i < text_rows_; ++i) {
1217 memset(text_[i], 0, text_cols_ + 1);
1218 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001219}
1220
1221void ScreenRecoveryUI::ShowFile(FILE* fp) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001222 std::vector<off_t> offsets;
1223 offsets.push_back(ftello(fp));
1224 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001225
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001226 struct stat sb;
1227 fstat(fileno(fp), &sb);
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001228
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001229 bool show_prompt = false;
1230 while (true) {
1231 if (show_prompt) {
1232 PrintOnScreenOnly("--(%d%% of %d bytes)--",
1233 static_cast<int>(100 * (double(ftello(fp)) / double(sb.st_size))),
1234 static_cast<int>(sb.st_size));
1235 Redraw();
1236 while (show_prompt) {
1237 show_prompt = false;
Alessandro Astone73961412020-10-04 18:11:40 +02001238 InputEvent evt = WaitInputEvent();
1239 if (evt.type() == EventType::EXTRA) {
1240 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) {
1241 return;
1242 }
1243 }
1244 if (evt.type() != EventType::KEY) {
1245 show_prompt = true;
1246 continue;
1247 }
1248 if (evt.key() == KEY_POWER || evt.key() == KEY_ENTER || evt.key() == KEY_BACKSPACE ||
1249 evt.key() == KEY_BACK || evt.key() == KEY_HOME || evt.key() == KEY_HOMEPAGE) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001250 return;
Alessandro Astone73961412020-10-04 18:11:40 +02001251 } else if (evt.key() == KEY_UP || evt.key() == KEY_VOLUMEUP || evt.key() == KEY_SCROLLUP) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001252 if (offsets.size() <= 1) {
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001253 show_prompt = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001254 } else {
1255 offsets.pop_back();
1256 fseek(fp, offsets.back(), SEEK_SET);
1257 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001258 } else {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001259 if (feof(fp)) {
1260 return;
1261 }
1262 offsets.push_back(ftello(fp));
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001263 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001264 }
1265 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001266 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001267
1268 int ch = getc(fp);
1269 if (ch == EOF) {
1270 while (text_row_ < text_rows_ - 1) PutChar('\n');
1271 show_prompt = true;
1272 } else {
1273 PutChar(ch);
1274 if (text_col_ == 0 && text_row_ >= text_rows_ - 1) {
1275 show_prompt = true;
1276 }
1277 }
1278 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001279}
1280
Tao Bao1d156b92018-05-02 12:43:18 -07001281void ScreenRecoveryUI::ShowFile(const std::string& filename) {
1282 std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(filename.c_str(), "re"), fclose);
1283 if (!fp) {
1284 Print(" Unable to open %s: %s\n", filename.c_str(), strerror(errno));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001285 return;
1286 }
Elliott Hughesc0491632015-05-06 12:40:05 -07001287
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001288 char** old_text = text_;
1289 size_t old_text_col = text_col_;
1290 size_t old_text_row = text_row_;
Elliott Hughesc0491632015-05-06 12:40:05 -07001291
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001292 // Swap in the alternate screen and clear it.
1293 text_ = file_viewer_text_;
1294 ClearText();
Elliott Hughesc0491632015-05-06 12:40:05 -07001295
Tao Bao1d156b92018-05-02 12:43:18 -07001296 ShowFile(fp.get());
Elliott Hughesc0491632015-05-06 12:40:05 -07001297
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001298 text_ = old_text;
1299 text_col_ = old_text_col;
1300 text_row_ = old_text_row;
Elliott Hughes8de52072015-04-08 20:06:50 -07001301}
1302
Tao Baoda409fb2018-10-21 23:36:26 -07001303std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(
1304 const GRSurface* graphic_header, const std::vector<const GRSurface*>& graphic_items,
1305 const std::vector<std::string>& text_headers, const std::vector<std::string>& text_items,
1306 size_t initial_selection) const {
Tianjie Xub99e6062018-10-16 15:13:09 -07001307 // horizontal unusable area: margin width + menu indent
1308 size_t max_width = ScreenWidth() - margin_width_ - kMenuIndent;
1309 // vertical unusable area: margin height + title lines + helper message + high light bar.
1310 // It is safe to reserve more space.
1311 size_t max_height = ScreenHeight() - margin_height_ - char_height_ * (title_lines_.size() + 3);
1312 if (GraphicMenu::Validate(max_width, max_height, graphic_header, graphic_items)) {
1313 return std::make_unique<GraphicMenu>(graphic_header, graphic_items, initial_selection, *this);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001314 }
Tianjie Xub99e6062018-10-16 15:13:09 -07001315
1316 fprintf(stderr, "Failed to initialize graphic menu, falling back to use the text menu.\n");
1317
1318 return CreateMenu(text_headers, text_items, initial_selection);
1319}
1320
1321std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(const std::vector<std::string>& text_headers,
1322 const std::vector<std::string>& text_items,
1323 size_t initial_selection) const {
Alessandro Astone4a0de5a2020-03-09 23:17:50 +01001324 int menu_char_width = MenuCharWidth();
1325 int menu_char_height = MenuCharHeight();
Alessandro Astone4a0de5a2020-03-09 23:17:50 +01001326 int menu_cols = (ScreenWidth() - margin_width_*2 - kMenuIndent) / menu_char_width;
1327 bool wrap_selection = !HasThreeButtons() && !HasTouchScreen();
Alessandro Astoneefc7a8e2020-06-25 19:54:18 +02001328 return std::make_unique<TextMenu>(wrap_selection, menu_cols, text_headers, text_items,
Alessandro Astone4a0de5a2020-03-09 23:17:50 +01001329 initial_selection, menu_char_height, *menu_draw_funcs_);
Doug Zongker211aebc2011-10-28 15:13:10 -07001330}
1331
1332int ScreenRecoveryUI::SelectMenu(int sel) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001333 std::lock_guard<std::mutex> lg(updateMutex);
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001334 if (menu_) {
1335 int old_sel = menu_->selection();
1336 sel = menu_->Select(sel);
Elliott Hughesfc06f872015-03-23 13:45:31 -07001337
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001338 if (sel != old_sel) {
1339 update_screen_locked();
1340 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001341 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001342 return sel;
Doug Zongker211aebc2011-10-28 15:13:10 -07001343}
1344
Alessandro Astone73961412020-10-04 18:11:40 +02001345int ScreenRecoveryUI::SelectMenu(const Point& point) {
1346 int new_sel = Device::kNoAction;
1347 std::lock_guard<std::mutex> lg(updateMutex);
1348 if (menu_) {
1349 if (!menu_->IsMain()) {
1350 // Back arrow hitbox
1351 const static int logo_width = gr_get_width(lineage_logo_.get());
1352 const static int logo_height = gr_get_height(lineage_logo_.get());
1353 const static int icon_w = gr_get_width(back_icon_.get());
1354 const static int icon_h = gr_get_height(back_icon_.get());
1355 const static int centered_x = ScreenWidth() / 2 - logo_width / 2;
1356 const static int icon_x = centered_x / 2 - icon_w / 2;
1357 const static int icon_y = margin_height_ + logo_height / 2 - icon_h / 2;
1358
1359 if (point.x() >= icon_x && point.x() <= icon_x + icon_w &&
1360 point.y() >= icon_y && point.y() <= icon_y + icon_h) {
1361 return Device::kGoBack;
1362 }
1363 }
1364
1365 if (point.y() >= menu_start_y_ &&
1366 point.y() < menu_start_y_ + menu_->ItemsCount() * MenuItemHeight()) {
1367 int old_sel = menu_->selection();
1368 int relative_sel = (point.y() - menu_start_y_) / MenuItemHeight();
1369 new_sel = menu_->SelectVisible(relative_sel);
1370 if (new_sel != -1 && new_sel != old_sel) {
1371 update_screen_locked();
1372 }
1373 }
1374 }
1375 return new_sel;
1376}
1377
1378int ScreenRecoveryUI::ScrollMenu(int updown) {
1379 std::lock_guard<std::mutex> lg(updateMutex);
1380 int sel = Device::kNoAction;
1381 if (menu_) {
1382 sel = menu_->Scroll(updown);
1383 update_screen_locked();
1384 }
1385 return sel;
1386}
1387
Tianjie Xub99e6062018-10-16 15:13:09 -07001388size_t ScreenRecoveryUI::ShowMenu(std::unique_ptr<Menu>&& menu, bool menu_only,
Tao Bao1fe1afe2018-05-01 15:56:05 -07001389 const std::function<int(int, bool)>& key_handler) {
Tao Bao3aec6962018-04-20 09:24:58 -07001390 // Throw away keys pressed previously, so user doesn't accidentally trigger menu items.
1391 FlushKeys();
1392
Jerry Zhangb76af932018-05-22 12:08:35 -07001393 // If there is a key interrupt in progress, return KeyError::INTERRUPTED without starting the
1394 // menu.
1395 if (IsKeyInterrupted()) return static_cast<size_t>(KeyError::INTERRUPTED);
1396
Tianjie Xub99e6062018-10-16 15:13:09 -07001397 CHECK(menu != nullptr);
Tao Bao3aec6962018-04-20 09:24:58 -07001398
Tianjie Xub99e6062018-10-16 15:13:09 -07001399 // Starts and displays the menu
1400 menu_ = std::move(menu);
1401 Redraw();
1402
1403 int selected = menu_->selection();
Tao Bao3aec6962018-04-20 09:24:58 -07001404 int chosen_item = -1;
1405 while (chosen_item < 0) {
Alessandro Astone73961412020-10-04 18:11:40 +02001406 InputEvent evt = WaitInputEvent();
1407 if (evt.type() == EventType::EXTRA) {
1408 if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) {
1409 // WaitKey() was interrupted.
1410 return static_cast<size_t>(KeyError::INTERRUPTED);
1411 }
1412 if (evt.key() == static_cast<int>(KeyError::TIMED_OUT)) { // WaitKey() timed out.
1413 if (WasTextEverVisible()) {
1414 continue;
1415 } else {
1416 LOG(INFO) << "Timed out waiting for key input; rebooting.";
1417 menu_.reset();
1418 Redraw();
1419 return static_cast<size_t>(KeyError::TIMED_OUT);
1420 }
Tao Bao3aec6962018-04-20 09:24:58 -07001421 }
1422 }
1423
Alessandro Astone73961412020-10-04 18:11:40 +02001424 int action = Device::kNoAction;
1425 if (evt.type() == EventType::TOUCH) {
1426 int touch_sel = SelectMenu(evt.pos());
1427 if (touch_sel < 0) {
1428 action = touch_sel;
1429 } else {
1430 action = Device::kInvokeItem;
1431 selected = touch_sel;
1432 }
1433 } else {
1434 bool visible = IsTextVisible();
1435 action = key_handler(evt.key(), visible);
1436 }
Tao Bao3aec6962018-04-20 09:24:58 -07001437 if (action < 0) {
1438 switch (action) {
1439 case Device::kHighlightUp:
1440 selected = SelectMenu(--selected);
1441 break;
1442 case Device::kHighlightDown:
1443 selected = SelectMenu(++selected);
1444 break;
Alessandro Astone73961412020-10-04 18:11:40 +02001445 case Device::kScrollUp:
1446 selected = ScrollMenu(-1);
1447 break;
1448 case Device::kScrollDown:
1449 selected = ScrollMenu(1);
1450 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001451 case Device::kInvokeItem:
Alessandro Astone4a0de5a2020-03-09 23:17:50 +01001452 if (selected < 0) {
1453 chosen_item = Device::kGoBack;
1454 } else {
1455 chosen_item = selected;
1456 }
Tao Bao3aec6962018-04-20 09:24:58 -07001457 break;
1458 case Device::kNoAction:
1459 break;
Tom Marshall4b921692017-08-24 13:50:01 +00001460 case Device::kGoBack:
1461 chosen_item = Device::kGoBack;
1462 break;
1463 case Device::kGoHome:
1464 chosen_item = Device::kGoHome;
1465 break;
Tom Marshallfdf50332018-12-17 15:57:44 -08001466 case Device::kDoSideload:
1467 chosen_item = Device::kDoSideload;
1468 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001469 }
1470 } else if (!menu_only) {
1471 chosen_item = action;
1472 }
Tom Marshallfdf50332018-12-17 15:57:44 -08001473
1474 if (chosen_item == Device::kGoBack || chosen_item == Device::kGoHome ||
1475 chosen_item == Device::kDoSideload) {
Tom Marshall4b921692017-08-24 13:50:01 +00001476 break;
1477 }
Tao Bao3aec6962018-04-20 09:24:58 -07001478 }
1479
Tianjie Xub99e6062018-10-16 15:13:09 -07001480 menu_.reset();
Tianjie Xub99e6062018-10-16 15:13:09 -07001481
Tao Bao3aec6962018-04-20 09:24:58 -07001482 return chosen_item;
1483}
1484
Tianjie Xub99e6062018-10-16 15:13:09 -07001485size_t ScreenRecoveryUI::ShowMenu(const std::vector<std::string>& headers,
1486 const std::vector<std::string>& items, size_t initial_selection,
1487 bool menu_only,
1488 const std::function<int(int, bool)>& key_handler) {
1489 auto menu = CreateMenu(headers, items, initial_selection);
1490 if (menu == nullptr) {
1491 return initial_selection;
1492 }
1493
Tao Baofa8e02a2019-06-27 09:07:04 -07001494 return ShowMenu(std::move(menu), menu_only, key_handler);
Tianjie Xub99e6062018-10-16 15:13:09 -07001495}
1496
1497size_t ScreenRecoveryUI::ShowPromptWipeDataMenu(const std::vector<std::string>& backup_headers,
1498 const std::vector<std::string>& backup_items,
1499 const std::function<int(int, bool)>& key_handler) {
Tao Baoda409fb2018-10-21 23:36:26 -07001500 auto wipe_data_menu = CreateMenu(wipe_data_menu_header_text_.get(),
1501 { try_again_text_.get(), factory_data_reset_text_.get() },
1502 backup_headers, backup_items, 0);
Tianjie Xub99e6062018-10-16 15:13:09 -07001503 if (wipe_data_menu == nullptr) {
1504 return 0;
1505 }
1506
1507 return ShowMenu(std::move(wipe_data_menu), true, key_handler);
1508}
1509
Tianjie Xu1a0a30a2018-10-25 15:22:07 -07001510size_t ScreenRecoveryUI::ShowPromptWipeDataConfirmationMenu(
1511 const std::vector<std::string>& backup_headers, const std::vector<std::string>& backup_items,
1512 const std::function<int(int, bool)>& key_handler) {
1513 auto confirmation_menu =
1514 CreateMenu(wipe_data_confirmation_text_.get(),
1515 { cancel_wipe_data_text_.get(), factory_data_reset_text_.get() }, backup_headers,
1516 backup_items, 0);
1517 if (confirmation_menu == nullptr) {
1518 return 0;
1519 }
1520
1521 return ShowMenu(std::move(confirmation_menu), true, key_handler);
1522}
1523
Elliott Hughes8de52072015-04-08 20:06:50 -07001524bool ScreenRecoveryUI::IsTextVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001525 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001526 int visible = show_text;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001527 return visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001528}
1529
Elliott Hughes8de52072015-04-08 20:06:50 -07001530bool ScreenRecoveryUI::WasTextEverVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001531 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001532 int ever_visible = show_text_ever;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001533 return ever_visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001534}
1535
Elliott Hughes8de52072015-04-08 20:06:50 -07001536void ScreenRecoveryUI::ShowText(bool visible) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001537 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001538 show_text = visible;
1539 if (show_text) show_text_ever = true;
1540 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001541}
Doug Zongkerc0441d12013-07-31 11:28:24 -07001542
Elliott Hughes8de52072015-04-08 20:06:50 -07001543void ScreenRecoveryUI::Redraw() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001544 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001545 update_screen_locked();
Doug Zongkerc0441d12013-07-31 11:28:24 -07001546}
Elliott Hughes642aaa72015-04-10 12:47:46 -07001547
1548void ScreenRecoveryUI::KeyLongPress(int) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001549 // Redraw so that if we're in the menu, the highlight
1550 // will change color to indicate a successful long press.
1551 Redraw();
Elliott Hughes642aaa72015-04-10 12:47:46 -07001552}
Tao Baoefb49ad2017-01-31 23:03:10 -08001553
1554void ScreenRecoveryUI::SetLocale(const std::string& new_locale) {
1555 locale_ = new_locale;
1556 rtl_locale_ = false;
1557
1558 if (!new_locale.empty()) {
Tao Bao347a6592018-05-08 15:58:29 -07001559 size_t separator = new_locale.find('-');
1560 // lang has the language prefix prior to the separator, or full string if none exists.
1561 std::string lang = new_locale.substr(0, separator);
Tao Baoefb49ad2017-01-31 23:03:10 -08001562
1563 // A bit cheesy: keep an explicit list of supported RTL languages.
1564 if (lang == "ar" || // Arabic
1565 lang == "fa" || // Persian (Farsi)
1566 lang == "he" || // Hebrew (new language code)
1567 lang == "iw" || // Hebrew (old language code)
1568 lang == "ur") { // Urdu
1569 rtl_locale_ = true;
1570 }
1571 }
1572}
fredchiouf2f5aa22022-02-11 16:39:53 +08001573
1574int ScreenRecoveryUI::SetSwCallback(int code, int value) {
1575 if (!is_graphics_available) { return -1; }
1576 if (code > SW_MAX) { return -1; }
1577 if (code != SW_LID) { return 0; }
1578
1579 /* detect dual display */
1580 if (!gr_has_multiple_connectors()) { return -1; }
1581
1582 /* turn off all screen */
1583 gr_fb_blank(true, DirectRenderManager::DRM_INNER);
1584 gr_fb_blank(true, DirectRenderManager::DRM_OUTER);
1585 gr_color(0, 0, 0, 255);
1586 gr_clear();
1587
1588 /* turn on the screen */
1589 gr_fb_blank(false, value);
1590 gr_flip();
1591
1592 /* set the retation */
1593 std::string rotation_str;
1594 if (value == DirectRenderManager::DRM_OUTER) {
1595 rotation_str =
1596 android::base::GetProperty("ro.minui.second_rotation", "ROTATION_NONE");
1597 } else {
1598 rotation_str =
1599 android::base::GetProperty("ro.minui.default_rotation", "ROTATION_NONE");
1600 }
1601
1602 if (rotation_str == "ROTATION_RIGHT") {
1603 gr_rotate(GRRotation::RIGHT);
1604 } else if (rotation_str == "ROTATION_DOWN") {
1605 gr_rotate(GRRotation::DOWN);
1606 } else if (rotation_str == "ROTATION_LEFT") {
1607 gr_rotate(GRRotation::LEFT);
1608 } else { // "ROTATION_NONE" or unknown string
1609 gr_rotate(GRRotation::NONE);
1610 }
1611 Redraw();
1612
1613 return 0;
1614}