blob: 82d51f9d6322b3496fdc0895ef7c67d12c85c04b [file] [log] [blame]
Doug Zongker211aebc2011-10-28 15:13:10 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Tianjie Xu8f397302018-08-20 13:40:47 -070017#include "recovery_ui/screen_ui.h"
Tao Baoefb49ad2017-01-31 23:03:10 -080018
Elliott Hughes498cda62016-04-14 16:49:04 -070019#include <dirent.h>
Doug Zongker211aebc2011-10-28 15:13:10 -070020#include <errno.h>
21#include <fcntl.h>
Doug Zongker211aebc2011-10-28 15:13:10 -070022#include <stdarg.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/stat.h>
27#include <sys/time.h>
28#include <sys/types.h>
29#include <time.h>
30#include <unistd.h>
31
Tao Bao1fe1afe2018-05-01 15:56:05 -070032#include <algorithm>
Tao Bao26ea9592018-05-09 16:32:02 -070033#include <chrono>
Tianjie Xu29d55752017-09-20 17:53:46 -070034#include <memory>
Tao Bao736d59c2017-01-03 10:15:33 -080035#include <string>
Tao Bao26ea9592018-05-09 16:32:02 -070036#include <thread>
Tianjie Xu29d55752017-09-20 17:53:46 -070037#include <unordered_map>
Elliott Hughes95fc63e2015-04-10 19:12:01 -070038#include <vector>
39
Mark Salyzyn30017e72020-05-13 12:39:12 -070040#include <android-base/chrono_utils.h>
Tianjie Xuc21edd42016-08-05 18:00:04 -070041#include <android-base/logging.h>
Elliott Hughescb220402016-09-23 15:30:55 -070042#include <android-base/properties.h>
Elliott Hughes4b166f02015-12-04 15:30:20 -080043#include <android-base/stringprintf.h>
Tao Baocb5524c2017-09-08 21:25:32 -070044#include <android-base/strings.h>
Tao Baob6918c72015-05-19 17:02:16 -070045
Tao Bao6cd81682018-05-03 21:53:11 -070046#include "minui/minui.h"
47#include "otautil/paths.h"
Tianjie Xu8f397302018-08-20 13:40:47 -070048#include "recovery_ui/device.h"
49#include "recovery_ui/ui.h"
Doug Zongker211aebc2011-10-28 15:13:10 -070050
fredchiouf2f5aa22022-02-11 16:39:53 +080051enum DirectRenderManager {
52 DRM_INNER,
53 DRM_OUTER,
54};
55
Doug Zongker211aebc2011-10-28 15:13:10 -070056// Return the current time as a double (including fractions of a second).
57static double now() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -070058 struct timeval tv;
59 gettimeofday(&tv, nullptr);
60 return tv.tv_sec + tv.tv_usec / 1000000.0;
Doug Zongker211aebc2011-10-28 15:13:10 -070061}
62
Tianjie Xu66dbf632018-10-11 16:54:50 -070063Menu::Menu(size_t initial_selection, const DrawInterface& draw_func)
64 : selection_(initial_selection), draw_funcs_(draw_func) {}
65
Alessandro Astone5a57a942020-03-09 23:17:50 +010066int Menu::selection() const {
Tianjie Xu66dbf632018-10-11 16:54:50 -070067 return selection_;
68}
69
Alessandro Astone5a57a942020-03-09 23:17:50 +010070TextMenu::TextMenu(bool wrappable, size_t max_items, size_t max_length,
Tianjie Xu66dbf632018-10-11 16:54:50 -070071 const std::vector<std::string>& headers, const std::vector<std::string>& items,
72 size_t initial_selection, int char_height, const DrawInterface& draw_funcs)
73 : Menu(initial_selection, draw_funcs),
Alessandro Astone5a57a942020-03-09 23:17:50 +010074 wrappable_(wrappable),
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070075 max_display_items_(max_items),
76 max_item_length_(max_length),
Tao Baoe02a5b22018-05-02 15:46:11 -070077 text_headers_(headers),
Tianjie Xu66dbf632018-10-11 16:54:50 -070078 char_height_(char_height) {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070079 CHECK_LE(max_items, static_cast<size_t>(std::numeric_limits<int>::max()));
Tao Baoe02a5b22018-05-02 15:46:11 -070080
Alessandro Astone5a57a942020-03-09 23:17:50 +010081 size_t items_count = items.size();
Tao Bao1fe1afe2018-05-01 15:56:05 -070082 for (size_t i = 0; i < items_count; ++i) {
83 text_items_.emplace_back(items[i].substr(0, max_item_length_));
Tao Baoe02a5b22018-05-02 15:46:11 -070084 }
Alessandro Astone5a57a942020-03-09 23:17:50 +010085 menu_start_ = std::max(0, (int)selection_ - (int)max_display_items_ + 1);
Tao Baoe02a5b22018-05-02 15:46:11 -070086
87 CHECK(!text_items_.empty());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070088}
89
Tianjie Xu66dbf632018-10-11 16:54:50 -070090const std::vector<std::string>& TextMenu::text_headers() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070091 return text_headers_;
92}
93
Tianjie Xu66dbf632018-10-11 16:54:50 -070094std::string TextMenu::TextItem(size_t index) const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070095 CHECK_LT(index, text_items_.size());
96
97 return text_items_[index];
98}
99
Tianjie Xu66dbf632018-10-11 16:54:50 -0700100size_t TextMenu::MenuStart() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700101 return menu_start_;
102}
103
Tianjie Xu66dbf632018-10-11 16:54:50 -0700104size_t TextMenu::MenuEnd() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700105 return std::min(ItemsCount(), menu_start_ + max_display_items_);
106}
107
Tianjie Xu66dbf632018-10-11 16:54:50 -0700108size_t TextMenu::ItemsCount() const {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700109 return text_items_.size();
110}
111
Tianjie Xu66dbf632018-10-11 16:54:50 -0700112bool TextMenu::ItemsOverflow(std::string* cur_selection_str) const {
Alessandro Astone5a57a942020-03-09 23:17:50 +0100113 if (ItemsCount() <= max_display_items_) {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700114 return false;
115 }
116
117 *cur_selection_str =
Alessandro Astone5a57a942020-03-09 23:17:50 +0100118 android::base::StringPrintf("Current item: %d/%zu", selection_ + 1, ItemsCount());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700119 return true;
120}
121
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700122// TODO(xunchang) modify the function parameters to button up & down.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700123int TextMenu::Select(int sel) {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700124 CHECK_LE(ItemsCount(), static_cast<size_t>(std::numeric_limits<int>::max()));
125 int count = ItemsCount();
126
Alessandro Astone5a57a942020-03-09 23:17:50 +0100127 int min = IsMain() ? 0 : -1; // -1 is back arrow
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700128
Alessandro Astone5a57a942020-03-09 23:17:50 +0100129 if (sel < min) {
130 selection_ = wrappable() ? count - 1 : min;
131 } else if (sel >= count) {
132 selection_ = wrappable() ? min : count - 1;
133 } else {
134 selection_ = sel;
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700135 }
136
Alessandro Astone5a57a942020-03-09 23:17:50 +0100137 if (selection_ >= 0) {
138 if (selection_ < menu_start_) {
139 menu_start_ = selection_;
140 } else if (static_cast<size_t>(selection_) >= MenuEnd()) {
141 menu_start_ = selection_ - max_display_items_ + 1;
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700142 }
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700143 }
144
145 return selection_;
146}
147
Tianjie Xu66dbf632018-10-11 16:54:50 -0700148int TextMenu::DrawHeader(int x, int y) const {
149 int offset = 0;
150
151 draw_funcs_.SetColor(UIElement::HEADER);
Alessandro Astone5a57a942020-03-09 23:17:50 +0100152 offset += draw_funcs_.DrawWrappedTextLines(x, y + offset, text_headers());
Tianjie Xu66dbf632018-10-11 16:54:50 -0700153
154 return offset;
155}
156
157int TextMenu::DrawItems(int x, int y, int screen_width, bool long_press) const {
158 int offset = 0;
Alessandro Astone5a57a942020-03-09 23:17:50 +0100159 int padding = draw_funcs_.MenuItemPadding();
Tianjie Xu66dbf632018-10-11 16:54:50 -0700160
161 draw_funcs_.SetColor(UIElement::MENU);
Alessandro Astone5a57a942020-03-09 23:17:50 +0100162 offset += draw_funcs_.DrawHorizontalRule(y + offset) + 4;
163
164 int item_container_offset = offset; // store it for drawing scrollbar on most top
165
Tianjie Xu66dbf632018-10-11 16:54:50 -0700166 for (size_t i = MenuStart(); i < MenuEnd(); ++i) {
167 bool bold = false;
168 if (i == selection()) {
169 // Draw the highlight bar.
170 draw_funcs_.SetColor(long_press ? UIElement::MENU_SEL_BG_ACTIVE : UIElement::MENU_SEL_BG);
171
Alessandro Astone5a57a942020-03-09 23:17:50 +0100172 int bar_height = padding + char_height_ + padding;
173 draw_funcs_.DrawHighlightBar(0, y + offset, screen_width, bar_height);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700174
175 // Bold white text for the selected item.
176 draw_funcs_.SetColor(UIElement::MENU_SEL_FG);
177 bold = true;
178 }
179 offset += draw_funcs_.DrawTextLine(x, y + offset, TextItem(i), bold);
180
181 draw_funcs_.SetColor(UIElement::MENU);
182 }
183 offset += draw_funcs_.DrawHorizontalRule(y + offset);
184
Alessandro Astone5a57a942020-03-09 23:17:50 +0100185 std::string unused;
186 if (ItemsOverflow(&unused)) {
187 int container_height = max_display_items_ * (2 * padding + char_height_);
188 int bar_height = container_height / (text_items_.size() - max_display_items_ + 1);
189 int start_y = y + item_container_offset + bar_height * menu_start_;
190 draw_funcs_.SetColor(UIElement::SCROLLBAR);
191 draw_funcs_.DrawScrollBar(start_y, bar_height);
192 }
193
Tianjie Xu66dbf632018-10-11 16:54:50 -0700194 return offset;
195}
196
Tao Baoda409fb2018-10-21 23:36:26 -0700197GraphicMenu::GraphicMenu(const GRSurface* graphic_headers,
198 const std::vector<const GRSurface*>& graphic_items,
Tianjie Xub99e6062018-10-16 15:13:09 -0700199 size_t initial_selection, const DrawInterface& draw_funcs)
Tao Baoda409fb2018-10-21 23:36:26 -0700200 : Menu(initial_selection, draw_funcs) {
201 graphic_headers_ = graphic_headers->Clone();
202 graphic_items_.reserve(graphic_items.size());
203 for (const auto& item : graphic_items) {
204 graphic_items_.emplace_back(item->Clone());
205 }
206}
Tianjie Xu66dbf632018-10-11 16:54:50 -0700207
208int GraphicMenu::Select(int sel) {
209 CHECK_LE(graphic_items_.size(), static_cast<size_t>(std::numeric_limits<int>::max()));
210 int count = graphic_items_.size();
211
212 // Wraps the selection at boundary if the menu is not scrollable.
213 if (sel < 0) {
214 selection_ = count - 1;
215 } else if (sel >= count) {
216 selection_ = 0;
217 } else {
218 selection_ = sel;
219 }
220
221 return selection_;
222}
223
224int GraphicMenu::DrawHeader(int x, int y) const {
Tianjie Xub99e6062018-10-16 15:13:09 -0700225 draw_funcs_.SetColor(UIElement::HEADER);
Tao Baoda409fb2018-10-21 23:36:26 -0700226 draw_funcs_.DrawTextIcon(x, y, graphic_headers_.get());
Tianjie Xu66dbf632018-10-11 16:54:50 -0700227 return graphic_headers_->height;
228}
229
230int GraphicMenu::DrawItems(int x, int y, int screen_width, bool long_press) const {
231 int offset = 0;
232
233 draw_funcs_.SetColor(UIElement::MENU);
234 offset += draw_funcs_.DrawHorizontalRule(y + offset) + 4;
235
236 for (size_t i = 0; i < graphic_items_.size(); i++) {
237 auto& item = graphic_items_[i];
238 if (i == selection_) {
239 draw_funcs_.SetColor(long_press ? UIElement::MENU_SEL_BG_ACTIVE : UIElement::MENU_SEL_BG);
240
241 int bar_height = item->height + 4;
242 draw_funcs_.DrawHighlightBar(0, y + offset - 2, screen_width, bar_height);
243
244 // Bold white text for the selected item.
245 draw_funcs_.SetColor(UIElement::MENU_SEL_FG);
246 }
Tao Baoda409fb2018-10-21 23:36:26 -0700247 draw_funcs_.DrawTextIcon(x, y + offset, item.get());
Tianjie Xu66dbf632018-10-11 16:54:50 -0700248 offset += item->height;
249
250 draw_funcs_.SetColor(UIElement::MENU);
251 }
xunchangc7dbc732018-12-20 11:31:18 -0800252 offset += draw_funcs_.DrawHorizontalRule(y + offset);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700253
254 return offset;
255}
256
Tao Baoda409fb2018-10-21 23:36:26 -0700257bool GraphicMenu::Validate(size_t max_width, size_t max_height, const GRSurface* graphic_headers,
258 const std::vector<const GRSurface*>& graphic_items) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700259 int offset = 0;
Tianjie Xub99e6062018-10-16 15:13:09 -0700260 if (!ValidateGraphicSurface(max_width, max_height, offset, graphic_headers)) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700261 return false;
262 }
Tianjie Xub99e6062018-10-16 15:13:09 -0700263 offset += graphic_headers->height;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700264
Tianjie Xub99e6062018-10-16 15:13:09 -0700265 for (const auto& item : graphic_items) {
266 if (!ValidateGraphicSurface(max_width, max_height, offset, item)) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700267 return false;
268 }
269 offset += item->height;
270 }
271
272 return true;
273}
274
Tianjie Xub99e6062018-10-16 15:13:09 -0700275bool GraphicMenu::ValidateGraphicSurface(size_t max_width, size_t max_height, int y,
276 const GRSurface* surface) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700277 if (!surface) {
Tao Baoa00b4492019-01-16 09:29:47 -0800278 fprintf(stderr, "Graphic surface can not be null\n");
Tianjie Xu66dbf632018-10-11 16:54:50 -0700279 return false;
280 }
281
282 if (surface->pixel_bytes != 1 || surface->width != surface->row_bytes) {
Tao Baoa00b4492019-01-16 09:29:47 -0800283 fprintf(stderr, "Invalid graphic surface, pixel bytes: %zu, width: %zu row_bytes: %zu\n",
Tianjie Xu66dbf632018-10-11 16:54:50 -0700284 surface->pixel_bytes, surface->width, surface->row_bytes);
285 return false;
286 }
287
Tianjie Xub99e6062018-10-16 15:13:09 -0700288 if (surface->width > max_width || surface->height > max_height - y) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700289 fprintf(stderr,
Tao Baodd789822018-11-26 16:28:07 -0800290 "Graphic surface doesn't fit into the screen. width: %zu, height: %zu, max_width: %zu,"
Tianjie Xu66dbf632018-10-11 16:54:50 -0700291 " max_height: %zu, vertical offset: %d\n",
Tianjie Xub99e6062018-10-16 15:13:09 -0700292 surface->width, surface->height, max_width, max_height, y);
Tianjie Xu66dbf632018-10-11 16:54:50 -0700293 return false;
294 }
295
296 return true;
297}
298
Alessandro Astone5a57a942020-03-09 23:17:50 +0100299MenuDrawFunctions::MenuDrawFunctions(const DrawInterface& wrappee)
300 : wrappee_(wrappee) {
301}
302
303int MenuDrawFunctions::DrawTextLine(int x, int y, const std::string& line, bool bold) const {
304 gr_text(gr_menu_font(), x, y + MenuItemPadding(), line.c_str(), bold);
305 return 2 * MenuItemPadding() + MenuCharHeight();
306}
307
308int MenuDrawFunctions::DrawTextLines(int x, int y, const std::vector<std::string>& lines) const {
309 int offset = 0;
310 for (const auto& line : lines) {
311 offset += DrawTextLine(x, y + offset, line, false);
312 }
313 return offset;
314}
315
316int MenuDrawFunctions::DrawWrappedTextLines(int x, int y, const std::vector<std::string>& lines) const {
317 // Keep symmetrical margins based on the given offset (i.e. x).
318 size_t text_cols = (gr_fb_width() - x * 2) / MenuCharWidth();
319 int offset = 0;
320 for (const auto& line : lines) {
321 size_t next_start = 0;
322 while (next_start < line.size()) {
323 std::string sub = line.substr(next_start, text_cols + 1);
324 if (sub.size() <= text_cols) {
325 next_start += sub.size();
326 } else {
327 // Line too long and must be wrapped to text_cols columns.
328 size_t last_space = sub.find_last_of(" \t\n");
329 if (last_space == std::string::npos) {
330 // No space found, just draw as much as we can.
331 sub.resize(text_cols);
332 next_start += text_cols;
333 } else {
334 sub.resize(last_space);
335 next_start += last_space + 1;
336 }
337 }
338 offset += DrawTextLine(x, y + offset, sub, false);
339 }
340 }
341 return offset;
342}
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700343
Tao Bao0bc88de2018-07-31 14:53:16 -0700344constexpr int kDefaultMarginHeight = 0;
345constexpr int kDefaultMarginWidth = 0;
346constexpr int kDefaultAnimationFps = 30;
347
Alessandro Astone5a57a942020-03-09 23:17:50 +0100348ScreenRecoveryUI::ScreenRecoveryUI()
Tao Bao0bc88de2018-07-31 14:53:16 -0700349 : margin_width_(
350 android::base::GetIntProperty("ro.recovery.ui.margin_width", kDefaultMarginWidth)),
351 margin_height_(
352 android::base::GetIntProperty("ro.recovery.ui.margin_height", kDefaultMarginHeight)),
353 animation_fps_(
354 android::base::GetIntProperty("ro.recovery.ui.animation_fps", kDefaultAnimationFps)),
355 density_(static_cast<float>(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f),
Michael Bestas19630862019-03-23 17:28:22 +0200356 blank_unblank_on_init_(
357 android::base::GetBoolProperty("ro.recovery.ui.blank_unblank_on_init", false)),
Tao Baoda409fb2018-10-21 23:36:26 -0700358 current_icon_(NONE),
359 current_frame_(0),
360 intro_done_(false),
Tao Bao736d59c2017-01-03 10:15:33 -0800361 progressBarType(EMPTY),
362 progressScopeStart(0),
363 progressScopeSize(0),
364 progress(0),
365 pagesIdentical(false),
366 text_cols_(0),
367 text_rows_(0),
368 text_(nullptr),
369 text_col_(0),
370 text_row_(0),
Tao Bao736d59c2017-01-03 10:15:33 -0800371 show_text(false),
372 show_text_ever(false),
Tao Bao736d59c2017-01-03 10:15:33 -0800373 file_viewer_text_(nullptr),
Tao Bao736d59c2017-01-03 10:15:33 -0800374 stage(-1),
375 max_stage(-1),
Tao Baoefb49ad2017-01-31 23:03:10 -0800376 locale_(""),
fredchiouf2f5aa22022-02-11 16:39:53 +0800377 rtl_locale_(false),
378 is_graphics_available(false) {}
Doug Zongker211aebc2011-10-28 15:13:10 -0700379
Tao Bao26ea9592018-05-09 16:32:02 -0700380ScreenRecoveryUI::~ScreenRecoveryUI() {
381 progress_thread_stopped_ = true;
Tao Bao94371fd2018-06-06 07:38:54 -0700382 if (progress_thread_.joinable()) {
383 progress_thread_.join();
384 }
Tao Bao60ac6222018-06-13 14:33:51 -0700385 // No-op if gr_init() (via Init()) was not called or had failed.
386 gr_exit();
Tao Bao26ea9592018-05-09 16:32:02 -0700387}
388
Tao Baoda409fb2018-10-21 23:36:26 -0700389const GRSurface* ScreenRecoveryUI::GetCurrentFrame() const {
390 if (current_icon_ == INSTALLING_UPDATE || current_icon_ == ERASING) {
391 return intro_done_ ? loop_frames_[current_frame_].get() : intro_frames_[current_frame_].get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700392 }
Tao Baoda409fb2018-10-21 23:36:26 -0700393 return error_icon_.get();
Elliott Hughes498cda62016-04-14 16:49:04 -0700394}
395
Tao Baoda409fb2018-10-21 23:36:26 -0700396const GRSurface* ScreenRecoveryUI::GetCurrentText() const {
397 switch (current_icon_) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700398 case ERASING:
Tao Baoda409fb2018-10-21 23:36:26 -0700399 return erasing_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700400 case ERROR:
Tao Baoda409fb2018-10-21 23:36:26 -0700401 return error_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700402 case INSTALLING_UPDATE:
Tao Baoda409fb2018-10-21 23:36:26 -0700403 return installing_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700404 case NO_COMMAND:
Tao Baoda409fb2018-10-21 23:36:26 -0700405 return no_command_text_.get();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700406 case NONE:
407 abort();
408 }
Elliott Hughes498cda62016-04-14 16:49:04 -0700409}
410
Mikhail Lappob49767c2017-03-23 21:44:26 +0100411int ScreenRecoveryUI::PixelsFromDp(int dp) const {
Tao Bao0bc88de2018-07-31 14:53:16 -0700412 return dp * density_;
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700413}
414
415// Here's the intended layout:
416
Elliott Hughes6d089a92016-07-08 17:23:41 -0700417// | portrait large landscape large
418// ---------+-------------------------------------------------
Tao Bao3250f722017-06-29 14:32:05 -0700419// gap |
Elliott Hughes6d089a92016-07-08 17:23:41 -0700420// icon | (200dp)
421// gap | 68dp 68dp 56dp 112dp
422// text | (14sp)
423// gap | 32dp 32dp 26dp 52dp
424// progress | (2dp)
Tao Bao3250f722017-06-29 14:32:05 -0700425// gap |
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700426
Tao Bao3250f722017-06-29 14:32:05 -0700427// Note that "baseline" is actually the *top* of each icon (because that's how our drawing routines
428// 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 -0700429
Elliott Hughes6d089a92016-07-08 17:23:41 -0700430enum Layout { PORTRAIT = 0, PORTRAIT_LARGE = 1, LANDSCAPE = 2, LANDSCAPE_LARGE = 3, LAYOUT_MAX };
Tao Bao3250f722017-06-29 14:32:05 -0700431enum Dimension { TEXT = 0, ICON = 1, DIMENSION_MAX };
Elliott Hughes6d089a92016-07-08 17:23:41 -0700432static constexpr int kLayouts[LAYOUT_MAX][DIMENSION_MAX] = {
Tianjie Xu8f397302018-08-20 13:40:47 -0700433 { 32, 68 }, // PORTRAIT
434 { 32, 68 }, // PORTRAIT_LARGE
435 { 26, 56 }, // LANDSCAPE
436 { 52, 112 }, // LANDSCAPE_LARGE
Elliott Hughes6d089a92016-07-08 17:23:41 -0700437};
438
Tao Bao99b2d772017-06-23 22:47:03 -0700439int ScreenRecoveryUI::GetAnimationBaseline() const {
Tao Baoda409fb2018-10-21 23:36:26 -0700440 return GetTextBaseline() - PixelsFromDp(kLayouts[layout_][ICON]) -
441 gr_get_height(loop_frames_[0].get());
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700442}
443
Tao Bao99b2d772017-06-23 22:47:03 -0700444int ScreenRecoveryUI::GetTextBaseline() const {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700445 return GetProgressBaseline() - PixelsFromDp(kLayouts[layout_][TEXT]) -
Tao Baoda409fb2018-10-21 23:36:26 -0700446 gr_get_height(installing_text_.get());
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700447}
448
Tao Bao99b2d772017-06-23 22:47:03 -0700449int ScreenRecoveryUI::GetProgressBaseline() const {
Tao Baoda409fb2018-10-21 23:36:26 -0700450 int elements_sum = gr_get_height(loop_frames_[0].get()) + PixelsFromDp(kLayouts[layout_][ICON]) +
451 gr_get_height(installing_text_.get()) + PixelsFromDp(kLayouts[layout_][TEXT]) +
452 gr_get_height(progress_bar_fill_.get());
Luke Song92eda4d2017-09-19 10:51:35 -0700453 int bottom_gap = (ScreenHeight() - elements_sum) / 2;
Tao Baoda409fb2018-10-21 23:36:26 -0700454 return ScreenHeight() - bottom_gap - gr_get_height(progress_bar_fill_.get());
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700455}
456
Doug Zongker211aebc2011-10-28 15:13:10 -0700457// Clear the screen and draw the currently selected background icon (if any).
458// Should only be called with updateMutex locked.
Elliott Hughes498cda62016-04-14 16:49:04 -0700459void ScreenRecoveryUI::draw_background_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700460 pagesIdentical = false;
461 gr_color(0, 0, 0, 255);
462 gr_clear();
Tao Baoda409fb2018-10-21 23:36:26 -0700463 if (current_icon_ != NONE) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700464 if (max_stage != -1) {
Tao Baoda409fb2018-10-21 23:36:26 -0700465 int stage_height = gr_get_height(stage_marker_empty_.get());
466 int stage_width = gr_get_width(stage_marker_empty_.get());
467 int x = (ScreenWidth() - max_stage * gr_get_width(stage_marker_empty_.get())) / 2;
Tao Bao0bc88de2018-07-31 14:53:16 -0700468 int y = ScreenHeight() - stage_height - margin_height_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700469 for (int i = 0; i < max_stage; ++i) {
Tao Baoda409fb2018-10-21 23:36:26 -0700470 const auto& stage_surface = (i < stage) ? stage_marker_fill_ : stage_marker_empty_;
471 DrawSurface(stage_surface.get(), 0, 0, stage_width, stage_height, x, y);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700472 x += stage_width;
473 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700474 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700475
Tao Baoda409fb2018-10-21 23:36:26 -0700476 const auto& text_surface = GetCurrentText();
Luke Song92eda4d2017-09-19 10:51:35 -0700477 int text_x = (ScreenWidth() - gr_get_width(text_surface)) / 2;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700478 int text_y = GetTextBaseline();
479 gr_color(255, 255, 255, 255);
Luke Song92eda4d2017-09-19 10:51:35 -0700480 DrawTextIcon(text_x, text_y, text_surface);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700481 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700482}
483
Tao Baoea78d862017-06-28 14:52:17 -0700484// Draws the animation and progress bar (if any) on the screen. Does not flip pages. Should only be
485// called with updateMutex locked.
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700486void ScreenRecoveryUI::draw_foreground_locked() {
Tao Baoda409fb2018-10-21 23:36:26 -0700487 if (current_icon_ != NONE) {
488 const auto& frame = GetCurrentFrame();
Tao Bao736d59c2017-01-03 10:15:33 -0800489 int frame_width = gr_get_width(frame);
490 int frame_height = gr_get_height(frame);
Luke Song92eda4d2017-09-19 10:51:35 -0700491 int frame_x = (ScreenWidth() - frame_width) / 2;
Tao Bao736d59c2017-01-03 10:15:33 -0800492 int frame_y = GetAnimationBaseline();
zhang sanshan0d81b892019-08-07 16:21:10 +0800493 if (frame_x >= 0 && frame_y >= 0 && (frame_x + frame_width) < ScreenWidth() &&
494 (frame_y + frame_height) < ScreenHeight())
495 DrawSurface(frame, 0, 0, frame_width, frame_height, frame_x, frame_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800496 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700497
Tao Bao736d59c2017-01-03 10:15:33 -0800498 if (progressBarType != EMPTY) {
Tao Baoda409fb2018-10-21 23:36:26 -0700499 int width = gr_get_width(progress_bar_empty_.get());
500 int height = gr_get_height(progress_bar_empty_.get());
Doug Zongker211aebc2011-10-28 15:13:10 -0700501
Luke Song92eda4d2017-09-19 10:51:35 -0700502 int progress_x = (ScreenWidth() - width) / 2;
Tao Bao736d59c2017-01-03 10:15:33 -0800503 int progress_y = GetProgressBaseline();
Doug Zongker211aebc2011-10-28 15:13:10 -0700504
Tao Bao736d59c2017-01-03 10:15:33 -0800505 // Erase behind the progress bar (in case this was a progress-only update)
506 gr_color(0, 0, 0, 255);
Luke Song92eda4d2017-09-19 10:51:35 -0700507 DrawFill(progress_x, progress_y, width, height);
Doug Zongker211aebc2011-10-28 15:13:10 -0700508
Tao Bao736d59c2017-01-03 10:15:33 -0800509 if (progressBarType == DETERMINATE) {
510 float p = progressScopeStart + progress * progressScopeSize;
511 int pos = static_cast<int>(p * width);
Doug Zongker211aebc2011-10-28 15:13:10 -0700512
Tao Bao736d59c2017-01-03 10:15:33 -0800513 if (rtl_locale_) {
514 // Fill the progress bar from right to left.
515 if (pos > 0) {
Tao Baoda409fb2018-10-21 23:36:26 -0700516 DrawSurface(progress_bar_fill_.get(), width - pos, 0, pos, height,
517 progress_x + width - pos, progress_y);
Doug Zongker211aebc2011-10-28 15:13:10 -0700518 }
Tao Bao736d59c2017-01-03 10:15:33 -0800519 if (pos < width - 1) {
Tao Baoda409fb2018-10-21 23:36:26 -0700520 DrawSurface(progress_bar_empty_.get(), 0, 0, width - pos, height, progress_x, progress_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800521 }
522 } else {
523 // Fill the progress bar from left to right.
524 if (pos > 0) {
Tao Baoda409fb2018-10-21 23:36:26 -0700525 DrawSurface(progress_bar_fill_.get(), 0, 0, pos, height, progress_x, progress_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800526 }
527 if (pos < width - 1) {
Tao Baoda409fb2018-10-21 23:36:26 -0700528 DrawSurface(progress_bar_empty_.get(), pos, 0, width - pos, height, progress_x + pos,
529 progress_y);
Tao Bao736d59c2017-01-03 10:15:33 -0800530 }
531 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700532 }
Tao Bao736d59c2017-01-03 10:15:33 -0800533 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700534}
535
Alessandro Astone5a57a942020-03-09 23:17:50 +0100536/* Lineage teal: #167c80 */
Tao Bao99b2d772017-06-23 22:47:03 -0700537void ScreenRecoveryUI::SetColor(UIElement e) const {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700538 switch (e) {
Tianjie Xu66dbf632018-10-11 16:54:50 -0700539 case UIElement::INFO:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700540 gr_color(249, 194, 0, 255);
541 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700542 case UIElement::HEADER:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700543 gr_color(247, 0, 6, 255);
544 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700545 case UIElement::MENU:
546 case UIElement::MENU_SEL_BG:
Alessandro Astone5a57a942020-03-09 23:17:50 +0100547 gr_color(0xd8, 0xd8, 0xd8, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700548 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700549 case UIElement::MENU_SEL_BG_ACTIVE:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700550 gr_color(0, 156, 100, 255);
551 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700552 case UIElement::MENU_SEL_FG:
Alessandro Astone5a57a942020-03-09 23:17:50 +0100553 case UIElement::SCROLLBAR:
554 gr_color(0x16, 0x7c, 0x80, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700555 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700556 case UIElement::LOG:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700557 gr_color(196, 196, 196, 255);
558 break;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700559 case UIElement::TEXT_FILL:
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700560 gr_color(0, 0, 0, 160);
561 break;
562 default:
563 gr_color(255, 255, 255, 255);
564 break;
565 }
Doug Zongkerc0441d12013-07-31 11:28:24 -0700566}
Doug Zongker211aebc2011-10-28 15:13:10 -0700567
Tianjie Xu29d55752017-09-20 17:53:46 -0700568void ScreenRecoveryUI::SelectAndShowBackgroundText(const std::vector<std::string>& locales_entries,
569 size_t sel) {
570 SetLocale(locales_entries[sel]);
571 std::vector<std::string> text_name = { "erasing_text", "error_text", "installing_text",
572 "installing_security_text", "no_command_text" };
Tao Baoda409fb2018-10-21 23:36:26 -0700573 std::unordered_map<std::string, std::unique_ptr<GRSurface>> surfaces;
Tianjie Xu29d55752017-09-20 17:53:46 -0700574 for (const auto& name : text_name) {
Tao Baoda409fb2018-10-21 23:36:26 -0700575 auto text_image = LoadLocalizedBitmap(name);
Tianjie Xu29d55752017-09-20 17:53:46 -0700576 if (!text_image) {
577 Print("Failed to load %s\n", name.c_str());
578 return;
579 }
Tao Baoda409fb2018-10-21 23:36:26 -0700580 surfaces.emplace(name, std::move(text_image));
Tianjie Xu29d55752017-09-20 17:53:46 -0700581 }
582
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700583 std::lock_guard<std::mutex> lg(updateMutex);
Tianjie Xu29d55752017-09-20 17:53:46 -0700584 gr_color(0, 0, 0, 255);
585 gr_clear();
586
Tao Bao0bc88de2018-07-31 14:53:16 -0700587 int text_y = margin_height_;
588 int text_x = margin_width_;
Tianjie Xu29d55752017-09-20 17:53:46 -0700589 int line_spacing = gr_sys_font()->char_height; // Put some extra space between images.
590 // Write the header and descriptive texts.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700591 SetColor(UIElement::INFO);
Tianjie Xu29d55752017-09-20 17:53:46 -0700592 std::string header = "Show background text image";
Tao Bao93e46ad2018-05-02 14:57:21 -0700593 text_y += DrawTextLine(text_x, text_y, header, true);
Tianjie Xu29d55752017-09-20 17:53:46 -0700594 std::string locale_selection = android::base::StringPrintf(
Tao Bao39c49182018-05-07 22:50:33 -0700595 "Current locale: %s, %zu/%zu", locales_entries[sel].c_str(), sel + 1, locales_entries.size());
Tao Bao93e46ad2018-05-02 14:57:21 -0700596 // clang-format off
597 std::vector<std::string> instruction = {
598 locale_selection,
599 "Use volume up/down to switch locales and power to exit."
600 };
601 // clang-format on
Tianjie Xu29d55752017-09-20 17:53:46 -0700602 text_y += DrawWrappedTextLines(text_x, text_y, instruction);
603
604 // Iterate through the text images and display them in order for the current locale.
605 for (const auto& p : surfaces) {
606 text_y += line_spacing;
Tianjie Xu66dbf632018-10-11 16:54:50 -0700607 SetColor(UIElement::LOG);
Tao Bao93e46ad2018-05-02 14:57:21 -0700608 text_y += DrawTextLine(text_x, text_y, p.first, false);
Tianjie Xu29d55752017-09-20 17:53:46 -0700609 gr_color(255, 255, 255, 255);
610 gr_texticon(text_x, text_y, p.second.get());
611 text_y += gr_get_height(p.second.get());
612 }
613 // Update the whole screen.
614 gr_flip();
Tianjie Xu29d55752017-09-20 17:53:46 -0700615}
616
Tao Bao39c49182018-05-07 22:50:33 -0700617void ScreenRecoveryUI::CheckBackgroundTextImages() {
Tianjie Xu29d55752017-09-20 17:53:46 -0700618 // Load a list of locales embedded in one of the resource files.
619 std::vector<std::string> locales_entries = get_locales_in_png("installing_text");
620 if (locales_entries.empty()) {
621 Print("Failed to load locales from the resource files\n");
622 return;
623 }
Tao Bao39c49182018-05-07 22:50:33 -0700624 std::string saved_locale = locale_;
Tianjie Xu29d55752017-09-20 17:53:46 -0700625 size_t selected = 0;
626 SelectAndShowBackgroundText(locales_entries, selected);
627
628 FlushKeys();
629 while (true) {
630 int key = WaitKey();
Jerry Zhangb76af932018-05-22 12:08:35 -0700631 if (key == static_cast<int>(KeyError::INTERRUPTED)) break;
Tianjie Xu29d55752017-09-20 17:53:46 -0700632 if (key == KEY_POWER || key == KEY_ENTER) {
633 break;
634 } else if (key == KEY_UP || key == KEY_VOLUMEUP) {
635 selected = (selected == 0) ? locales_entries.size() - 1 : selected - 1;
636 SelectAndShowBackgroundText(locales_entries, selected);
637 } else if (key == KEY_DOWN || key == KEY_VOLUMEDOWN) {
638 selected = (selected == locales_entries.size() - 1) ? 0 : selected + 1;
639 SelectAndShowBackgroundText(locales_entries, selected);
640 }
641 }
642
643 SetLocale(saved_locale);
644}
645
Luke Song92eda4d2017-09-19 10:51:35 -0700646int ScreenRecoveryUI::ScreenWidth() const {
647 return gr_fb_width();
648}
649
650int ScreenRecoveryUI::ScreenHeight() const {
651 return gr_fb_height();
652}
653
Tao Bao65815b62018-10-23 10:54:02 -0700654void ScreenRecoveryUI::DrawSurface(const GRSurface* surface, int sx, int sy, int w, int h, int dx,
Luke Song92eda4d2017-09-19 10:51:35 -0700655 int dy) const {
656 gr_blit(surface, sx, sy, w, h, dx, dy);
657}
658
Tao Baoea78d862017-06-28 14:52:17 -0700659int ScreenRecoveryUI::DrawHorizontalRule(int y) const {
Luke Song92eda4d2017-09-19 10:51:35 -0700660 gr_fill(0, y + 4, ScreenWidth(), y + 6);
Tao Baoea78d862017-06-28 14:52:17 -0700661 return 8;
Elliott Hughes95fc63e2015-04-10 19:12:01 -0700662}
663
Luke Songe2bd8762017-06-12 16:08:33 -0700664void ScreenRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const {
Alessandro Astone5a57a942020-03-09 23:17:50 +0100665 if (y + height > ScreenHeight())
666 height = ScreenHeight() - y;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700667 gr_fill(x, y, x + width, y + height);
Luke Songe2bd8762017-06-12 16:08:33 -0700668}
669
Alessandro Astone5a57a942020-03-09 23:17:50 +0100670void ScreenRecoveryUI::DrawScrollBar(int y, int height) const {
671 int x = ScreenWidth() - margin_width_;
672 int width = 8;
673 gr_fill(x - width, y, x, y + height);
674}
675
Luke Song92eda4d2017-09-19 10:51:35 -0700676void ScreenRecoveryUI::DrawFill(int x, int y, int w, int h) const {
677 gr_fill(x, y, w, h);
678}
679
Tao Bao65815b62018-10-23 10:54:02 -0700680void ScreenRecoveryUI::DrawTextIcon(int x, int y, const GRSurface* surface) const {
Luke Song92eda4d2017-09-19 10:51:35 -0700681 gr_texticon(x, y, surface);
682}
683
Tao Bao93e46ad2018-05-02 14:57:21 -0700684int ScreenRecoveryUI::DrawTextLine(int x, int y, const std::string& line, bool bold) const {
685 gr_text(gr_sys_font(), x, y, line.c_str(), bold);
Tao Baoea78d862017-06-28 14:52:17 -0700686 return char_height_ + 4;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700687}
688
Tao Bao93e46ad2018-05-02 14:57:21 -0700689int ScreenRecoveryUI::DrawTextLines(int x, int y, const std::vector<std::string>& lines) const {
Tao Baoea78d862017-06-28 14:52:17 -0700690 int offset = 0;
Tao Bao93e46ad2018-05-02 14:57:21 -0700691 for (const auto& line : lines) {
692 offset += DrawTextLine(x, y + offset, line, false);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700693 }
Tao Baoea78d862017-06-28 14:52:17 -0700694 return offset;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700695}
696
Tao Bao93e46ad2018-05-02 14:57:21 -0700697int ScreenRecoveryUI::DrawWrappedTextLines(int x, int y,
698 const std::vector<std::string>& lines) const {
Tao Bao452b4872018-05-09 11:52:09 -0700699 // Keep symmetrical margins based on the given offset (i.e. x).
700 size_t text_cols = (ScreenWidth() - x * 2) / char_width_;
Tao Bao2bbc6d62017-08-13 23:48:55 -0700701 int offset = 0;
Tao Bao93e46ad2018-05-02 14:57:21 -0700702 for (const auto& line : lines) {
Tao Bao2bbc6d62017-08-13 23:48:55 -0700703 size_t next_start = 0;
704 while (next_start < line.size()) {
Tao Bao452b4872018-05-09 11:52:09 -0700705 std::string sub = line.substr(next_start, text_cols + 1);
706 if (sub.size() <= text_cols) {
Tao Bao2bbc6d62017-08-13 23:48:55 -0700707 next_start += sub.size();
708 } else {
Tao Bao452b4872018-05-09 11:52:09 -0700709 // Line too long and must be wrapped to text_cols columns.
Tao Bao2bbc6d62017-08-13 23:48:55 -0700710 size_t last_space = sub.find_last_of(" \t\n");
711 if (last_space == std::string::npos) {
Tao Bao93e46ad2018-05-02 14:57:21 -0700712 // No space found, just draw as much as we can.
Tao Bao452b4872018-05-09 11:52:09 -0700713 sub.resize(text_cols);
714 next_start += text_cols;
Tao Bao2bbc6d62017-08-13 23:48:55 -0700715 } else {
716 sub.resize(last_space);
717 next_start += last_space + 1;
718 }
719 }
Tao Bao93e46ad2018-05-02 14:57:21 -0700720 offset += DrawTextLine(x, y + offset, sub, false);
Tao Bao2bbc6d62017-08-13 23:48:55 -0700721 }
722 }
723 return offset;
724}
725
Jerry Zhang0e577ee2018-05-07 11:21:10 -0700726void ScreenRecoveryUI::SetTitle(const std::vector<std::string>& lines) {
727 title_lines_ = lines;
728}
729
Tianjie Xue5032212019-07-23 13:23:29 -0700730std::vector<std::string> ScreenRecoveryUI::GetMenuHelpMessage() const {
731 // clang-format off
732 static std::vector<std::string> REGULAR_HELP{
733 "Use volume up/down and power.",
734 };
735 static std::vector<std::string> LONG_PRESS_HELP{
736 "Any button cycles highlight.",
737 "Long-press activates.",
738 };
739 // clang-format on
740 return HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP;
741}
742
Tao Bao171b4c42017-06-19 23:10:44 -0700743// Redraws everything on the screen. Does not flip pages. Should only be called with updateMutex
744// locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700745void ScreenRecoveryUI::draw_screen_locked() {
Tao Bao171b4c42017-06-19 23:10:44 -0700746 if (!show_text) {
747 draw_background_locked();
748 draw_foreground_locked();
749 return;
750 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700751
Tao Bao171b4c42017-06-19 23:10:44 -0700752 gr_color(0, 0, 0, 255);
753 gr_clear();
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700754
Tianjie Xue5032212019-07-23 13:23:29 -0700755 draw_menu_and_text_buffer_locked(GetMenuHelpMessage());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700756}
757
758// Draws the menu and text buffer on the screen. Should only be called with updateMutex locked.
Tao Bao93e46ad2018-05-02 14:57:21 -0700759void ScreenRecoveryUI::draw_menu_and_text_buffer_locked(
760 const std::vector<std::string>& help_message) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700761 int y = margin_height_;
David Anderson983e2d52019-01-02 11:35:38 -0800762
763 if (fastbootd_logo_ && fastbootd_logo_enabled_) {
764 // Try to get this centered on screen.
765 auto width = gr_get_width(fastbootd_logo_.get());
766 auto height = gr_get_height(fastbootd_logo_.get());
767 auto centered_x = ScreenWidth() / 2 - width / 2;
768 DrawSurface(fastbootd_logo_.get(), 0, 0, width, height, centered_x, y);
769 y += height;
770 }
771
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700772 if (menu_) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700773 int x = margin_width_ + kMenuIndent;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700774
Tianjie Xu66dbf632018-10-11 16:54:50 -0700775 SetColor(UIElement::INFO);
Jerry Zhang0e577ee2018-05-07 11:21:10 -0700776
Alessandro Astone5a57a942020-03-09 23:17:50 +0100777 if (lineage_logo_ && back_icon_) {
778 auto logo_width = gr_get_width(lineage_logo_.get());
779 auto logo_height = gr_get_height(lineage_logo_.get());
780 auto centered_x = ScreenWidth() / 2 - logo_width / 2;
781 DrawSurface(lineage_logo_.get(), 0, 0, logo_width, logo_height, centered_x, y);
782 y += logo_height;
Tao Bao171b4c42017-06-19 23:10:44 -0700783
Alessandro Astone5a57a942020-03-09 23:17:50 +0100784 if (!menu_->IsMain()) {
785 auto icon_w = gr_get_width(back_icon_.get());
786 auto icon_h = gr_get_height(back_icon_.get());
787 auto icon_x = centered_x / 2 - icon_w / 2;
788 auto icon_y = y - logo_height / 2 - icon_h / 2;
789 gr_blit(back_icon_sel_ && menu_->selection() == -1 ? back_icon_sel_.get() : back_icon_.get(),
790 0, 0, icon_w, icon_h, icon_x, icon_y);
791 }
792 } else {
793 for (size_t i = 0; i < title_lines_.size(); i++) {
794 y += DrawTextLine(x, y, title_lines_[i], i == 0);
795 }
796 y += DrawTextLines(x, y, help_message);
797 }
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700798
Tianjie Xu66dbf632018-10-11 16:54:50 -0700799 y += menu_->DrawHeader(x, y);
800 y += menu_->DrawItems(x, y, ScreenWidth(), IsLongPress());
Tao Bao171b4c42017-06-19 23:10:44 -0700801 }
802
803 // Display from the bottom up, until we hit the top of the screen, the bottom of the menu, or
804 // we've displayed the entire text buffer.
Tianjie Xu66dbf632018-10-11 16:54:50 -0700805 SetColor(UIElement::LOG);
Tao Baocb5524c2017-09-08 21:25:32 -0700806 int row = text_row_;
Tao Bao171b4c42017-06-19 23:10:44 -0700807 size_t count = 0;
Tao Bao0bc88de2018-07-31 14:53:16 -0700808 for (int ty = ScreenHeight() - margin_height_ - char_height_; ty >= y && count < text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700809 ty -= char_height_, ++count) {
Tao Bao0bc88de2018-07-31 14:53:16 -0700810 DrawTextLine(margin_width_, ty, text_[row], false);
Tao Bao171b4c42017-06-19 23:10:44 -0700811 --row;
812 if (row < 0) row = text_rows_ - 1;
813 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700814}
815
816// Redraw everything on the screen and flip the screen (make it visible).
817// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700818void ScreenRecoveryUI::update_screen_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700819 draw_screen_locked();
820 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700821}
822
823// Updates only the progress bar, if possible, otherwise redraws the screen.
824// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700825void ScreenRecoveryUI::update_progress_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700826 if (show_text || !pagesIdentical) {
827 draw_screen_locked(); // Must redraw the whole screen
828 pagesIdentical = true;
829 } else {
830 draw_foreground_locked(); // Draw only the progress bar and overlays
831 }
832 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700833}
834
Elliott Hughes985022a2015-04-13 13:04:32 -0700835void ScreenRecoveryUI::ProgressThreadLoop() {
Tao Bao0bc88de2018-07-31 14:53:16 -0700836 double interval = 1.0 / animation_fps_;
Tao Bao26ea9592018-05-09 16:32:02 -0700837 while (!progress_thread_stopped_) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700838 double start = now();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700839 bool redraw = false;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700840 {
841 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker211aebc2011-10-28 15:13:10 -0700842
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700843 // update the installation animation, if active
844 // skip this if we have a text overlay (too expensive to update)
Tao Baoda409fb2018-10-21 23:36:26 -0700845 if ((current_icon_ == INSTALLING_UPDATE || current_icon_ == ERASING) && !show_text) {
846 if (!intro_done_) {
847 if (current_frame_ == intro_frames_.size() - 1) {
848 intro_done_ = true;
849 current_frame_ = 0;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700850 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700851 ++current_frame_;
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700852 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700853 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700854 current_frame_ = (current_frame_ + 1) % loop_frames_.size();
Doug Zongker211aebc2011-10-28 15:13:10 -0700855 }
856
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700857 redraw = true;
858 }
Jerry Zhangb31f9ce2018-05-21 16:04:57 -0700859
860 // move the progress bar forward on timed intervals, if configured
861 int duration = progressScopeDuration;
862 if (progressBarType == DETERMINATE && duration > 0) {
863 double elapsed = now() - progressScopeTime;
864 float p = 1.0 * elapsed / duration;
865 if (p > 1.0) p = 1.0;
866 if (p > progress) {
867 progress = p;
868 redraw = true;
869 }
870 }
871
872 if (redraw) update_progress_locked();
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700873 }
874
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700875 double end = now();
876 // minimum of 20ms delay between frames
877 double delay = interval - (end - start);
878 if (delay < 0.02) delay = 0.02;
879 usleep(static_cast<useconds_t>(delay * 1000000));
880 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700881}
882
Tao Baoda409fb2018-10-21 23:36:26 -0700883std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadBitmap(const std::string& filename) {
884 GRSurface* surface;
885 if (auto result = res_create_display_surface(filename.c_str(), &surface); result < 0) {
886 LOG(ERROR) << "Failed to load bitmap " << filename << " (error " << result << ")";
887 return nullptr;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700888 }
Tao Baoda409fb2018-10-21 23:36:26 -0700889 return std::unique_ptr<GRSurface>(surface);
Doug Zongkereac881c2014-03-07 09:21:25 -0800890}
891
Tao Baoda409fb2018-10-21 23:36:26 -0700892std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadLocalizedBitmap(const std::string& filename) {
893 GRSurface* surface;
xunchang9d05c8a2019-04-16 12:07:42 -0700894 auto result = res_create_localized_alpha_surface(filename.c_str(), locale_.c_str(), &surface);
895 if (result == 0) {
896 return std::unique_ptr<GRSurface>(surface);
Tao Bao736d59c2017-01-03 10:15:33 -0800897 }
xunchang9d05c8a2019-04-16 12:07:42 -0700898 // TODO(xunchang) create a error code enum to refine the retry condition.
899 LOG(WARNING) << "Failed to load bitmap " << filename << " for locale " << locale_ << " (error "
900 << result << "). Falling back to use default locale.";
901
902 result = res_create_localized_alpha_surface(filename.c_str(), DEFAULT_LOCALE, &surface);
903 if (result == 0) {
904 return std::unique_ptr<GRSurface>(surface);
905 }
906
907 LOG(ERROR) << "Failed to load bitmap " << filename << " for locale " << DEFAULT_LOCALE
908 << " (error " << result << ")";
909 return nullptr;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700910}
911
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700912static char** Alloc2d(size_t rows, size_t cols) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700913 char** result = new char*[rows];
914 for (size_t i = 0; i < rows; ++i) {
915 result[i] = new char[cols];
916 memset(result[i], 0, cols);
917 }
918 return result;
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700919}
920
Tianjie Xu35926c42016-04-28 18:06:26 -0700921// Choose the right background string to display during update.
922void ScreenRecoveryUI::SetSystemUpdateText(bool security_update) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700923 if (security_update) {
Tao Baoda409fb2018-10-21 23:36:26 -0700924 installing_text_ = LoadLocalizedBitmap("installing_security_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700925 } else {
Tao Baoda409fb2018-10-21 23:36:26 -0700926 installing_text_ = LoadLocalizedBitmap("installing_text");
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700927 }
928 Redraw();
Tianjie Xu35926c42016-04-28 18:06:26 -0700929}
930
Sen Jiangd5304492016-12-09 16:20:49 -0800931bool ScreenRecoveryUI::InitTextParams() {
Tao Baoba4edb32018-06-13 11:22:50 -0700932 // gr_init() would return successfully on font initialization failure.
933 if (gr_sys_font() == nullptr) {
Tao Bao171b4c42017-06-19 23:10:44 -0700934 return false;
935 }
Tao Bao171b4c42017-06-19 23:10:44 -0700936 gr_font_size(gr_sys_font(), &char_width_, &char_height_);
Alessandro Astone5a57a942020-03-09 23:17:50 +0100937 gr_font_size(gr_menu_font(), &menu_char_width_, &menu_char_height_);
Tao Bao0bc88de2018-07-31 14:53:16 -0700938 text_rows_ = (ScreenHeight() - margin_height_ * 2) / char_height_;
939 text_cols_ = (ScreenWidth() - margin_width_ * 2) / char_width_;
Tao Bao171b4c42017-06-19 23:10:44 -0700940 return true;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -0700941}
942
Tianjie Xub99e6062018-10-16 15:13:09 -0700943bool ScreenRecoveryUI::LoadWipeDataMenuText() {
Tianjie Xu1a0a30a2018-10-25 15:22:07 -0700944 // Ignores the errors since the member variables will stay as nullptr.
945 cancel_wipe_data_text_ = LoadLocalizedBitmap("cancel_wipe_data_text");
946 factory_data_reset_text_ = LoadLocalizedBitmap("factory_data_reset_text");
947 try_again_text_ = LoadLocalizedBitmap("try_again_text");
948 wipe_data_confirmation_text_ = LoadLocalizedBitmap("wipe_data_confirmation_text");
949 wipe_data_menu_header_text_ = LoadLocalizedBitmap("wipe_data_menu_header_text");
Tianjie Xub99e6062018-10-16 15:13:09 -0700950 return true;
951}
952
Mark Salyzyn30017e72020-05-13 12:39:12 -0700953static bool InitGraphics() {
954 // Timeout is same as init wait for file default of 5 seconds and is arbitrary
955 const unsigned timeout = 500; // 10ms increments
956 for (auto retry = timeout; retry > 0; --retry) {
957 if (gr_init() == 0) {
958 if (retry < timeout) {
959 // Log message like init wait for file completion log for consistency.
960 LOG(WARNING) << "wait for 'graphics' took " << ((timeout - retry) * 10) << "ms";
961 }
962 return true;
963 }
964 std::this_thread::sleep_for(10ms);
965 }
966 // Log message like init wait for file timeout log for consistency.
967 LOG(ERROR) << "timeout wait for 'graphics' took " << (timeout * 10) << "ms";
968 return false;
969}
970
Tao Bao736d59c2017-01-03 10:15:33 -0800971bool ScreenRecoveryUI::Init(const std::string& locale) {
972 RecoveryUI::Init(locale);
Tao Baoefb49ad2017-01-31 23:03:10 -0800973
Mark Salyzyn30017e72020-05-13 12:39:12 -0700974 if (!InitGraphics()) {
Tao Baoba4edb32018-06-13 11:22:50 -0700975 return false;
976 }
fredchiouf2f5aa22022-02-11 16:39:53 +0800977 is_graphics_available = true;
Tao Baoba4edb32018-06-13 11:22:50 -0700978
Tao Bao736d59c2017-01-03 10:15:33 -0800979 if (!InitTextParams()) {
980 return false;
981 }
Alessandro Astone5a57a942020-03-09 23:17:50 +0100982 menu_draw_funcs_ = std::make_unique<MenuDrawFunctions>(*this);
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -0700983
Michael Bestas19630862019-03-23 17:28:22 +0200984 if (blank_unblank_on_init_) {
985 gr_fb_blank(true);
986 gr_fb_blank(false);
987 }
988
Tao Bao736d59c2017-01-03 10:15:33 -0800989 // Are we portrait or landscape?
990 layout_ = (gr_fb_width() > gr_fb_height()) ? LANDSCAPE : PORTRAIT;
991 // Are we the large variant of our base layout?
992 if (gr_fb_height() > PixelsFromDp(800)) ++layout_;
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700993
Tao Bao736d59c2017-01-03 10:15:33 -0800994 text_ = Alloc2d(text_rows_, text_cols_ + 1);
995 file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
Doug Zongker55a36ac2013-03-04 15:49:02 -0800996
Tao Bao736d59c2017-01-03 10:15:33 -0800997 text_col_ = text_row_ = 0;
Doug Zongker211aebc2011-10-28 15:13:10 -0700998
Tao Baoefb49ad2017-01-31 23:03:10 -0800999 // Set up the locale info.
1000 SetLocale(locale);
1001
Tao Baoda409fb2018-10-21 23:36:26 -07001002 error_icon_ = LoadBitmap("icon_error");
Doug Zongker02ec6b82012-08-22 17:26:40 -07001003
Tao Baoda409fb2018-10-21 23:36:26 -07001004 progress_bar_empty_ = LoadBitmap("progress_empty");
1005 progress_bar_fill_ = LoadBitmap("progress_fill");
1006 stage_marker_empty_ = LoadBitmap("stage_empty");
1007 stage_marker_fill_ = LoadBitmap("stage_fill");
Elliott Hughes498cda62016-04-14 16:49:04 -07001008
Tao Baoda409fb2018-10-21 23:36:26 -07001009 erasing_text_ = LoadLocalizedBitmap("erasing_text");
1010 no_command_text_ = LoadLocalizedBitmap("no_command_text");
1011 error_text_ = LoadLocalizedBitmap("error_text");
Doug Zongker211aebc2011-10-28 15:13:10 -07001012
Alessandro Astone5a57a942020-03-09 23:17:50 +01001013 lineage_logo_ = LoadBitmap("logo_image");
1014 back_icon_ = LoadBitmap("ic_back");
1015 back_icon_sel_ = LoadBitmap("ic_back_sel");
Alessandro Astone957d98e2020-02-26 17:25:54 +01001016 if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) ||
1017 android::base::GetBoolProperty("ro.fastbootd.available", false)) {
David Anderson983e2d52019-01-02 11:35:38 -08001018 fastbootd_logo_ = LoadBitmap("fastbootd");
1019 }
1020
Tao Baoda409fb2018-10-21 23:36:26 -07001021 // Background text for "installing_update" could be "installing update" or
1022 // "installing security update". It will be set after Init() according to the commands in BCB.
1023 installing_text_.reset();
Elliott Hughes498cda62016-04-14 16:49:04 -07001024
Tianjie Xub99e6062018-10-16 15:13:09 -07001025 LoadWipeDataMenuText();
1026
Tao Bao736d59c2017-01-03 10:15:33 -08001027 LoadAnimation();
Doug Zongker02ec6b82012-08-22 17:26:40 -07001028
Tao Bao26ea9592018-05-09 16:32:02 -07001029 // Keep the progress bar updated, even when the process is otherwise busy.
1030 progress_thread_ = std::thread(&ScreenRecoveryUI::ProgressThreadLoop, this);
Sen Jiangd5304492016-12-09 16:20:49 -08001031
fredchiouf2f5aa22022-02-11 16:39:53 +08001032 // set the callback for hall sensor event
1033 (void)ev_sync_sw_state([this](auto&& a, auto&& b) { return this->SetSwCallback(a, b);});
1034
Tao Bao736d59c2017-01-03 10:15:33 -08001035 return true;
Doug Zongker211aebc2011-10-28 15:13:10 -07001036}
1037
Tao Bao551d2c32018-05-09 20:53:13 -07001038std::string ScreenRecoveryUI::GetLocale() const {
Jerry Zhang2dea53e2018-05-02 17:15:03 -07001039 return locale_;
1040}
1041
Elliott Hughes498cda62016-04-14 16:49:04 -07001042void ScreenRecoveryUI::LoadAnimation() {
Tao Bao6cd81682018-05-03 21:53:11 -07001043 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(Paths::Get().resource_dir().c_str()),
1044 closedir);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001045 dirent* de;
1046 std::vector<std::string> intro_frame_names;
Joshua Lambert94a83be2019-12-09 20:24:18 +00001047 std::vector<std::string> loop_frame_names;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001048
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001049 while ((de = readdir(dir.get())) != nullptr) {
1050 int value, num_chars;
1051 if (sscanf(de->d_name, "intro%d%n.png", &value, &num_chars) == 1) {
1052 intro_frame_names.emplace_back(de->d_name, num_chars);
1053 } else if (sscanf(de->d_name, "loop%d%n.png", &value, &num_chars) == 1) {
Joshua Lambert94a83be2019-12-09 20:24:18 +00001054 loop_frame_names.emplace_back(de->d_name, num_chars);
Elliott Hughes498cda62016-04-14 16:49:04 -07001055 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001056 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001057
Tao Baoda409fb2018-10-21 23:36:26 -07001058 size_t intro_frames = intro_frame_names.size();
1059 size_t loop_frames = loop_frame_names.size();
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001060
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001061 // It's okay to not have an intro.
Tao Baoda409fb2018-10-21 23:36:26 -07001062 if (intro_frames == 0) intro_done_ = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001063 // But you must have an animation.
1064 if (loop_frames == 0) abort();
Elliott Hughes498cda62016-04-14 16:49:04 -07001065
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001066 std::sort(intro_frame_names.begin(), intro_frame_names.end());
1067 std::sort(loop_frame_names.begin(), loop_frame_names.end());
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -07001068
Tao Baoda409fb2018-10-21 23:36:26 -07001069 intro_frames_.clear();
1070 intro_frames_.reserve(intro_frames);
1071 for (const auto& frame_name : intro_frame_names) {
1072 intro_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001073 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001074
Tao Baoda409fb2018-10-21 23:36:26 -07001075 loop_frames_.clear();
1076 loop_frames_.reserve(loop_frames);
1077 for (const auto& frame_name : loop_frame_names) {
1078 loop_frames_.emplace_back(LoadBitmap(frame_name));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001079 }
Elliott Hughes498cda62016-04-14 16:49:04 -07001080}
1081
Elliott Hughes8de52072015-04-08 20:06:50 -07001082void ScreenRecoveryUI::SetBackground(Icon icon) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001083 std::lock_guard<std::mutex> lg(updateMutex);
Doug Zongker02ec6b82012-08-22 17:26:40 -07001084
Tao Baoda409fb2018-10-21 23:36:26 -07001085 current_icon_ = icon;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001086 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001087}
1088
Elliott Hughes8de52072015-04-08 20:06:50 -07001089void ScreenRecoveryUI::SetProgressType(ProgressType type) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001090 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001091 if (progressBarType != type) {
1092 progressBarType = type;
1093 }
1094 progressScopeStart = 0;
1095 progressScopeSize = 0;
1096 progress = 0;
1097 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001098}
1099
Elliott Hughes8de52072015-04-08 20:06:50 -07001100void ScreenRecoveryUI::ShowProgress(float portion, float seconds) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001101 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001102 progressBarType = DETERMINATE;
1103 progressScopeStart += progressScopeSize;
1104 progressScopeSize = portion;
1105 progressScopeTime = now();
1106 progressScopeDuration = seconds;
1107 progress = 0;
1108 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001109}
1110
Elliott Hughes8de52072015-04-08 20:06:50 -07001111void ScreenRecoveryUI::SetProgress(float fraction) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001112 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001113 if (fraction < 0.0) fraction = 0.0;
1114 if (fraction > 1.0) fraction = 1.0;
1115 if (progressBarType == DETERMINATE && fraction > progress) {
1116 // Skip updates that aren't visibly different.
Tao Baoda409fb2018-10-21 23:36:26 -07001117 int width = gr_get_width(progress_bar_empty_.get());
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001118 float scale = width * progressScopeSize;
1119 if ((int)(progress * scale) != (int)(fraction * scale)) {
1120 progress = fraction;
1121 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001122 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001123 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001124}
1125
Doug Zongkerc87bab12013-11-25 13:53:25 -08001126void ScreenRecoveryUI::SetStage(int current, int max) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001127 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001128 stage = current;
1129 max_stage = max;
Doug Zongkerc87bab12013-11-25 13:53:25 -08001130}
1131
Tao Baob6918c72015-05-19 17:02:16 -07001132void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001133 std::string str;
1134 android::base::StringAppendV(&str, fmt, ap);
Doug Zongker211aebc2011-10-28 15:13:10 -07001135
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001136 if (copy_to_stdout) {
1137 fputs(str.c_str(), stdout);
1138 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001139
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001140 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001141 if (text_rows_ > 0 && text_cols_ > 0) {
1142 for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
1143 if (*ptr == '\n' || text_col_ >= text_cols_) {
Elliott Hughesc0491632015-05-06 12:40:05 -07001144 text_[text_row_][text_col_] = '\0';
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001145 text_col_ = 0;
1146 text_row_ = (text_row_ + 1) % text_rows_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001147 }
1148 if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
Doug Zongker211aebc2011-10-28 15:13:10 -07001149 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001150 text_[text_row_][text_col_] = '\0';
1151 update_screen_locked();
1152 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001153}
1154
Tao Baob6918c72015-05-19 17:02:16 -07001155void ScreenRecoveryUI::Print(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001156 va_list ap;
1157 va_start(ap, fmt);
1158 PrintV(fmt, true, ap);
1159 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001160}
1161
Tianjie Xu8f397302018-08-20 13:40:47 -07001162void ScreenRecoveryUI::PrintOnScreenOnly(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001163 va_list ap;
1164 va_start(ap, fmt);
1165 PrintV(fmt, false, ap);
1166 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001167}
1168
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001169void ScreenRecoveryUI::PutChar(char ch) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001170 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001171 if (ch != '\n') text_[text_row_][text_col_++] = ch;
1172 if (ch == '\n' || text_col_ >= text_cols_) {
1173 text_col_ = 0;
1174 ++text_row_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001175 }
Elliott Hughes8de52072015-04-08 20:06:50 -07001176}
1177
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001178void ScreenRecoveryUI::ClearText() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001179 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001180 text_col_ = 0;
1181 text_row_ = 0;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001182 for (size_t i = 0; i < text_rows_; ++i) {
1183 memset(text_[i], 0, text_cols_ + 1);
1184 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001185}
1186
1187void ScreenRecoveryUI::ShowFile(FILE* fp) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001188 std::vector<off_t> offsets;
1189 offsets.push_back(ftello(fp));
1190 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001191
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001192 struct stat sb;
1193 fstat(fileno(fp), &sb);
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001194
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001195 bool show_prompt = false;
1196 while (true) {
1197 if (show_prompt) {
1198 PrintOnScreenOnly("--(%d%% of %d bytes)--",
1199 static_cast<int>(100 * (double(ftello(fp)) / double(sb.st_size))),
1200 static_cast<int>(sb.st_size));
1201 Redraw();
1202 while (show_prompt) {
1203 show_prompt = false;
1204 int key = WaitKey();
Jerry Zhangb76af932018-05-22 12:08:35 -07001205 if (key == static_cast<int>(KeyError::INTERRUPTED)) return;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001206 if (key == KEY_POWER || key == KEY_ENTER) {
1207 return;
1208 } else if (key == KEY_UP || key == KEY_VOLUMEUP) {
1209 if (offsets.size() <= 1) {
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001210 show_prompt = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001211 } else {
1212 offsets.pop_back();
1213 fseek(fp, offsets.back(), SEEK_SET);
1214 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001215 } else {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001216 if (feof(fp)) {
1217 return;
1218 }
1219 offsets.push_back(ftello(fp));
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001220 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001221 }
1222 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001223 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001224
1225 int ch = getc(fp);
1226 if (ch == EOF) {
1227 while (text_row_ < text_rows_ - 1) PutChar('\n');
1228 show_prompt = true;
1229 } else {
1230 PutChar(ch);
1231 if (text_col_ == 0 && text_row_ >= text_rows_ - 1) {
1232 show_prompt = true;
1233 }
1234 }
1235 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001236}
1237
Tao Bao1d156b92018-05-02 12:43:18 -07001238void ScreenRecoveryUI::ShowFile(const std::string& filename) {
1239 std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(filename.c_str(), "re"), fclose);
1240 if (!fp) {
1241 Print(" Unable to open %s: %s\n", filename.c_str(), strerror(errno));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001242 return;
1243 }
Elliott Hughesc0491632015-05-06 12:40:05 -07001244
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001245 char** old_text = text_;
1246 size_t old_text_col = text_col_;
1247 size_t old_text_row = text_row_;
Elliott Hughesc0491632015-05-06 12:40:05 -07001248
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001249 // Swap in the alternate screen and clear it.
1250 text_ = file_viewer_text_;
1251 ClearText();
Elliott Hughesc0491632015-05-06 12:40:05 -07001252
Tao Bao1d156b92018-05-02 12:43:18 -07001253 ShowFile(fp.get());
Elliott Hughesc0491632015-05-06 12:40:05 -07001254
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001255 text_ = old_text;
1256 text_col_ = old_text_col;
1257 text_row_ = old_text_row;
Elliott Hughes8de52072015-04-08 20:06:50 -07001258}
1259
Tao Baoda409fb2018-10-21 23:36:26 -07001260std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(
1261 const GRSurface* graphic_header, const std::vector<const GRSurface*>& graphic_items,
1262 const std::vector<std::string>& text_headers, const std::vector<std::string>& text_items,
1263 size_t initial_selection) const {
Tianjie Xub99e6062018-10-16 15:13:09 -07001264 // horizontal unusable area: margin width + menu indent
1265 size_t max_width = ScreenWidth() - margin_width_ - kMenuIndent;
1266 // vertical unusable area: margin height + title lines + helper message + high light bar.
1267 // It is safe to reserve more space.
1268 size_t max_height = ScreenHeight() - margin_height_ - char_height_ * (title_lines_.size() + 3);
1269 if (GraphicMenu::Validate(max_width, max_height, graphic_header, graphic_items)) {
1270 return std::make_unique<GraphicMenu>(graphic_header, graphic_items, initial_selection, *this);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001271 }
Tianjie Xub99e6062018-10-16 15:13:09 -07001272
1273 fprintf(stderr, "Failed to initialize graphic menu, falling back to use the text menu.\n");
1274
1275 return CreateMenu(text_headers, text_items, initial_selection);
1276}
1277
1278std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(const std::vector<std::string>& text_headers,
1279 const std::vector<std::string>& text_items,
1280 size_t initial_selection) const {
Alessandro Astone5a57a942020-03-09 23:17:50 +01001281 int menu_char_width = MenuCharWidth();
1282 int menu_char_height = MenuCharHeight();
1283 int menu_item_padding = MenuItemPadding();
1284 int menu_rows = (ScreenHeight() - margin_height_*2 - gr_get_height(lineage_logo_.get()))
1285 / (menu_char_height + 2 * menu_item_padding) - text_headers.size();
1286 int menu_cols = (ScreenWidth() - margin_width_*2 - kMenuIndent) / menu_char_width;
1287 bool wrap_selection = !HasThreeButtons() && !HasTouchScreen();
1288 return std::make_unique<TextMenu>(wrap_selection, menu_rows, menu_cols, text_headers, text_items,
1289 initial_selection, menu_char_height, *menu_draw_funcs_);
Doug Zongker211aebc2011-10-28 15:13:10 -07001290}
1291
1292int ScreenRecoveryUI::SelectMenu(int sel) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001293 std::lock_guard<std::mutex> lg(updateMutex);
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001294 if (menu_) {
1295 int old_sel = menu_->selection();
1296 sel = menu_->Select(sel);
Elliott Hughesfc06f872015-03-23 13:45:31 -07001297
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001298 if (sel != old_sel) {
1299 update_screen_locked();
1300 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001301 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001302 return sel;
Doug Zongker211aebc2011-10-28 15:13:10 -07001303}
1304
Tianjie Xub99e6062018-10-16 15:13:09 -07001305size_t ScreenRecoveryUI::ShowMenu(std::unique_ptr<Menu>&& menu, bool menu_only,
Tao Bao1fe1afe2018-05-01 15:56:05 -07001306 const std::function<int(int, bool)>& key_handler) {
Tao Bao3aec6962018-04-20 09:24:58 -07001307 // Throw away keys pressed previously, so user doesn't accidentally trigger menu items.
1308 FlushKeys();
1309
Jerry Zhangb76af932018-05-22 12:08:35 -07001310 // If there is a key interrupt in progress, return KeyError::INTERRUPTED without starting the
1311 // menu.
1312 if (IsKeyInterrupted()) return static_cast<size_t>(KeyError::INTERRUPTED);
1313
Tianjie Xub99e6062018-10-16 15:13:09 -07001314 CHECK(menu != nullptr);
Tao Bao3aec6962018-04-20 09:24:58 -07001315
Tianjie Xub99e6062018-10-16 15:13:09 -07001316 // Starts and displays the menu
1317 menu_ = std::move(menu);
1318 Redraw();
1319
1320 int selected = menu_->selection();
Tao Bao3aec6962018-04-20 09:24:58 -07001321 int chosen_item = -1;
1322 while (chosen_item < 0) {
1323 int key = WaitKey();
Jerry Zhangb76af932018-05-22 12:08:35 -07001324 if (key == static_cast<int>(KeyError::INTERRUPTED)) { // WaitKey() was interrupted.
1325 return static_cast<size_t>(KeyError::INTERRUPTED);
1326 }
1327 if (key == static_cast<int>(KeyError::TIMED_OUT)) { // WaitKey() timed out.
Tao Bao3aec6962018-04-20 09:24:58 -07001328 if (WasTextEverVisible()) {
1329 continue;
1330 } else {
1331 LOG(INFO) << "Timed out waiting for key input; rebooting.";
Tianjie Xub99e6062018-10-16 15:13:09 -07001332 menu_.reset();
1333 Redraw();
Jerry Zhangb76af932018-05-22 12:08:35 -07001334 return static_cast<size_t>(KeyError::TIMED_OUT);
Tao Bao3aec6962018-04-20 09:24:58 -07001335 }
1336 }
1337
1338 bool visible = IsTextVisible();
1339 int action = key_handler(key, visible);
1340 if (action < 0) {
1341 switch (action) {
1342 case Device::kHighlightUp:
1343 selected = SelectMenu(--selected);
1344 break;
1345 case Device::kHighlightDown:
1346 selected = SelectMenu(++selected);
1347 break;
1348 case Device::kInvokeItem:
Alessandro Astone5a57a942020-03-09 23:17:50 +01001349 if (selected < 0) {
1350 chosen_item = Device::kGoBack;
1351 } else {
1352 chosen_item = selected;
1353 }
Tao Bao3aec6962018-04-20 09:24:58 -07001354 break;
1355 case Device::kNoAction:
1356 break;
Tom Marshallf20b3e32017-08-24 13:50:01 +00001357 case Device::kGoBack:
1358 chosen_item = Device::kGoBack;
1359 break;
1360 case Device::kGoHome:
1361 chosen_item = Device::kGoHome;
1362 break;
Tom Marshall9f2f3282018-12-17 15:57:44 -08001363 case Device::kDoSideload:
1364 chosen_item = Device::kDoSideload;
1365 break;
Tao Bao3aec6962018-04-20 09:24:58 -07001366 }
1367 } else if (!menu_only) {
1368 chosen_item = action;
1369 }
Tom Marshall9f2f3282018-12-17 15:57:44 -08001370
1371 if (chosen_item == Device::kGoBack || chosen_item == Device::kGoHome ||
1372 chosen_item == Device::kDoSideload) {
Tom Marshallf20b3e32017-08-24 13:50:01 +00001373 break;
1374 }
Tao Bao3aec6962018-04-20 09:24:58 -07001375 }
1376
Tianjie Xub99e6062018-10-16 15:13:09 -07001377 menu_.reset();
1378 Redraw();
1379
Tao Bao3aec6962018-04-20 09:24:58 -07001380 return chosen_item;
1381}
1382
Tianjie Xub99e6062018-10-16 15:13:09 -07001383size_t ScreenRecoveryUI::ShowMenu(const std::vector<std::string>& headers,
1384 const std::vector<std::string>& items, size_t initial_selection,
1385 bool menu_only,
1386 const std::function<int(int, bool)>& key_handler) {
1387 auto menu = CreateMenu(headers, items, initial_selection);
1388 if (menu == nullptr) {
1389 return initial_selection;
1390 }
1391
Tao Baofa8e02a2019-06-27 09:07:04 -07001392 return ShowMenu(std::move(menu), menu_only, key_handler);
Tianjie Xub99e6062018-10-16 15:13:09 -07001393}
1394
1395size_t ScreenRecoveryUI::ShowPromptWipeDataMenu(const std::vector<std::string>& backup_headers,
1396 const std::vector<std::string>& backup_items,
1397 const std::function<int(int, bool)>& key_handler) {
Tao Baoda409fb2018-10-21 23:36:26 -07001398 auto wipe_data_menu = CreateMenu(wipe_data_menu_header_text_.get(),
1399 { try_again_text_.get(), factory_data_reset_text_.get() },
1400 backup_headers, backup_items, 0);
Tianjie Xub99e6062018-10-16 15:13:09 -07001401 if (wipe_data_menu == nullptr) {
1402 return 0;
1403 }
1404
1405 return ShowMenu(std::move(wipe_data_menu), true, key_handler);
1406}
1407
Tianjie Xu1a0a30a2018-10-25 15:22:07 -07001408size_t ScreenRecoveryUI::ShowPromptWipeDataConfirmationMenu(
1409 const std::vector<std::string>& backup_headers, const std::vector<std::string>& backup_items,
1410 const std::function<int(int, bool)>& key_handler) {
1411 auto confirmation_menu =
1412 CreateMenu(wipe_data_confirmation_text_.get(),
1413 { cancel_wipe_data_text_.get(), factory_data_reset_text_.get() }, backup_headers,
1414 backup_items, 0);
1415 if (confirmation_menu == nullptr) {
1416 return 0;
1417 }
1418
1419 return ShowMenu(std::move(confirmation_menu), true, key_handler);
1420}
1421
Elliott Hughes8de52072015-04-08 20:06:50 -07001422bool ScreenRecoveryUI::IsTextVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001423 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001424 int visible = show_text;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001425 return visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001426}
1427
Elliott Hughes8de52072015-04-08 20:06:50 -07001428bool ScreenRecoveryUI::WasTextEverVisible() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001429 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001430 int ever_visible = show_text_ever;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001431 return ever_visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001432}
1433
Elliott Hughes8de52072015-04-08 20:06:50 -07001434void ScreenRecoveryUI::ShowText(bool visible) {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001435 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001436 show_text = visible;
1437 if (show_text) show_text_ever = true;
1438 update_screen_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001439}
Doug Zongkerc0441d12013-07-31 11:28:24 -07001440
Elliott Hughes8de52072015-04-08 20:06:50 -07001441void ScreenRecoveryUI::Redraw() {
Jerry Zhangb31f9ce2018-05-21 16:04:57 -07001442 std::lock_guard<std::mutex> lg(updateMutex);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001443 update_screen_locked();
Doug Zongkerc0441d12013-07-31 11:28:24 -07001444}
Elliott Hughes642aaa72015-04-10 12:47:46 -07001445
1446void ScreenRecoveryUI::KeyLongPress(int) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001447 // Redraw so that if we're in the menu, the highlight
1448 // will change color to indicate a successful long press.
1449 Redraw();
Elliott Hughes642aaa72015-04-10 12:47:46 -07001450}
Tao Baoefb49ad2017-01-31 23:03:10 -08001451
1452void ScreenRecoveryUI::SetLocale(const std::string& new_locale) {
1453 locale_ = new_locale;
1454 rtl_locale_ = false;
1455
1456 if (!new_locale.empty()) {
Tao Bao347a6592018-05-08 15:58:29 -07001457 size_t separator = new_locale.find('-');
1458 // lang has the language prefix prior to the separator, or full string if none exists.
1459 std::string lang = new_locale.substr(0, separator);
Tao Baoefb49ad2017-01-31 23:03:10 -08001460
1461 // A bit cheesy: keep an explicit list of supported RTL languages.
1462 if (lang == "ar" || // Arabic
1463 lang == "fa" || // Persian (Farsi)
1464 lang == "he" || // Hebrew (new language code)
1465 lang == "iw" || // Hebrew (old language code)
1466 lang == "ur") { // Urdu
1467 rtl_locale_ = true;
1468 }
1469 }
1470}
fredchiouf2f5aa22022-02-11 16:39:53 +08001471
1472int ScreenRecoveryUI::SetSwCallback(int code, int value) {
1473 if (!is_graphics_available) { return -1; }
1474 if (code > SW_MAX) { return -1; }
1475 if (code != SW_LID) { return 0; }
1476
1477 /* detect dual display */
1478 if (!gr_has_multiple_connectors()) { return -1; }
1479
1480 /* turn off all screen */
1481 gr_fb_blank(true, DirectRenderManager::DRM_INNER);
1482 gr_fb_blank(true, DirectRenderManager::DRM_OUTER);
1483 gr_color(0, 0, 0, 255);
1484 gr_clear();
1485
1486 /* turn on the screen */
1487 gr_fb_blank(false, value);
1488 gr_flip();
1489
1490 /* set the retation */
1491 std::string rotation_str;
1492 if (value == DirectRenderManager::DRM_OUTER) {
1493 rotation_str =
1494 android::base::GetProperty("ro.minui.second_rotation", "ROTATION_NONE");
1495 } else {
1496 rotation_str =
1497 android::base::GetProperty("ro.minui.default_rotation", "ROTATION_NONE");
1498 }
1499
1500 if (rotation_str == "ROTATION_RIGHT") {
1501 gr_rotate(GRRotation::RIGHT);
1502 } else if (rotation_str == "ROTATION_DOWN") {
1503 gr_rotate(GRRotation::DOWN);
1504 } else if (rotation_str == "ROTATION_LEFT") {
1505 gr_rotate(GRRotation::LEFT);
1506 } else { // "ROTATION_NONE" or unknown string
1507 gr_rotate(GRRotation::NONE);
1508 }
1509 Redraw();
1510
1511 return 0;
1512}