blob: 9376bdd7a9c95cfd622c4510fbbeac51d1a837fd [file] [log] [blame]
Doug Zongker211aebc2011-10-28 15:13:10 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
Tom Marshall820517b2019-01-14 14:28:55 -08003 * Copyright (C) 2019 The LineageOS Project
Doug Zongker211aebc2011-10-28 15:13:10 -07004 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
Tao Baoefb49ad2017-01-31 23:03:10 -080018#include "screen_ui.h"
19
Elliott Hughes498cda62016-04-14 16:49:04 -070020#include <dirent.h>
Doug Zongker211aebc2011-10-28 15:13:10 -070021#include <errno.h>
22#include <fcntl.h>
23#include <linux/input.h>
24#include <pthread.h>
25#include <stdarg.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <sys/stat.h>
30#include <sys/time.h>
31#include <sys/types.h>
32#include <time.h>
33#include <unistd.h>
34
Tianjie Xu29d55752017-09-20 17:53:46 -070035#include <memory>
Tao Bao736d59c2017-01-03 10:15:33 -080036#include <string>
Tianjie Xu29d55752017-09-20 17:53:46 -070037#include <unordered_map>
Elliott Hughes95fc63e2015-04-10 19:12:01 -070038#include <vector>
39
Tianjie Xuc21edd42016-08-05 18:00:04 -070040#include <android-base/logging.h>
Elliott Hughescb220402016-09-23 15:30:55 -070041#include <android-base/properties.h>
Elliott Hughes4b166f02015-12-04 15:30:20 -080042#include <android-base/stringprintf.h>
Tao Baocb5524c2017-09-08 21:25:32 -070043#include <android-base/strings.h>
Tao Baoefb49ad2017-01-31 23:03:10 -080044#include <minui/minui.h>
Tao Baob6918c72015-05-19 17:02:16 -070045
Tom Marshall820517b2019-01-14 14:28:55 -080046#include <healthd/BatteryMonitor.h>
47
Doug Zongker211aebc2011-10-28 15:13:10 -070048#include "common.h"
Doug Zongkerdaefc1d2011-10-31 09:34:15 -070049#include "device.h"
Doug Zongker32a0a472011-11-01 11:00:20 -070050#include "ui.h"
Doug Zongker211aebc2011-10-28 15:13:10 -070051
Doug Zongker211aebc2011-10-28 15:13:10 -070052// Return the current time as a double (including fractions of a second).
53static double now() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -070054 struct timeval tv;
55 gettimeofday(&tv, nullptr);
56 return tv.tv_sec + tv.tv_usec / 1000000.0;
Doug Zongker211aebc2011-10-28 15:13:10 -070057}
58
Tom Marshall820517b2019-01-14 14:28:55 -080059static void get_battery_status(bool& charged, int& capacity) {
60 struct healthd_config healthd_config = {
61 .batteryStatusPath = android::String8(android::String8::kEmptyString),
62 .batteryHealthPath = android::String8(android::String8::kEmptyString),
63 .batteryPresentPath = android::String8(android::String8::kEmptyString),
64 .batteryCapacityPath = android::String8(android::String8::kEmptyString),
65 .batteryVoltagePath = android::String8(android::String8::kEmptyString),
66 .batteryTemperaturePath = android::String8(android::String8::kEmptyString),
67 .batteryTechnologyPath = android::String8(android::String8::kEmptyString),
68 .batteryCurrentNowPath = android::String8(android::String8::kEmptyString),
69 .batteryCurrentAvgPath = android::String8(android::String8::kEmptyString),
70 .batteryChargeCounterPath = android::String8(android::String8::kEmptyString),
71 .batteryFullChargePath = android::String8(android::String8::kEmptyString),
72 .batteryCycleCountPath = android::String8(android::String8::kEmptyString),
73 .energyCounter = NULL,
74 .boot_min_cap = 0,
75 .screen_on = NULL
76 };
77 healthd_board_init(&healthd_config);
78
79 android::BatteryMonitor monitor;
80 monitor.init(&healthd_config);
81
82 int charge_status = monitor.getChargeStatus();
83 // Treat unknown status as charged.
84 charged = (charge_status != android::BATTERY_STATUS_DISCHARGING &&
85 charge_status != android::BATTERY_STATUS_NOT_CHARGING);
86 android::BatteryProperty prop;
87 android::status_t status = monitor.getProperty(android::BATTERY_PROP_CAPACITY, &prop);
88 // If we can't read battery percentage, it may be a device without battery. In this
89 // situation, use 100 as a fake battery percentage.
90 if (status != 0) {
91 prop.valueInt64 = 100;
92 }
93 capacity = (int)prop.valueInt64;
94}
95
96ScreenMenuItem::~ScreenMenuItem() {
97 if (icon_) {
98 res_free_surface(icon_);
99 }
100 if (icon_sel_) {
101 res_free_surface(icon_sel_);
102 }
103}
104
105GRSurface* ScreenMenuItem::icon() {
106 if (!icon_) {
107 res_create_display_surface(icon_name_.c_str(), &icon_);
108 }
109 return icon_;
110}
111
112GRSurface* ScreenMenuItem::icon_sel() {
113 if (icon_name_sel_.empty()) {
114 return icon();
115 }
116 if (!icon_sel_) {
117 res_create_display_surface(icon_name_sel_.c_str(), &icon_sel_);
118 }
119 return icon_sel_;
120}
121
Tao Bao736d59c2017-01-03 10:15:33 -0800122ScreenRecoveryUI::ScreenRecoveryUI()
Tao Bao4521b702017-06-20 18:11:21 -0700123 : kMarginWidth(RECOVERY_UI_MARGIN_WIDTH),
124 kMarginHeight(RECOVERY_UI_MARGIN_HEIGHT),
Tao Bao0470cee2017-08-02 17:11:04 -0700125 kAnimationFps(RECOVERY_UI_ANIMATION_FPS),
Tao Bao75779652017-09-10 11:28:32 -0700126 kDensity(static_cast<float>(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f),
Tao Bao171b4c42017-06-19 23:10:44 -0700127 currentIcon(NONE),
Tao Bao736d59c2017-01-03 10:15:33 -0800128 progressBarType(EMPTY),
129 progressScopeStart(0),
130 progressScopeSize(0),
131 progress(0),
132 pagesIdentical(false),
133 text_cols_(0),
134 text_rows_(0),
135 text_(nullptr),
136 text_col_(0),
137 text_row_(0),
Tao Bao736d59c2017-01-03 10:15:33 -0800138 show_text(false),
139 show_text_ever(false),
Alessandro Astone20c8d412019-04-01 11:11:33 +0200140 previous_row_ended(false),
141 update_screen_on_print(false),
Tom Marshall820517b2019-01-14 14:28:55 -0800142 menu_is_main_(true),
143 menu_type_(MT_NONE),
Tao Baoe15d7a52017-09-07 13:38:51 -0700144 menu_headers_(nullptr),
Tom Marshall820517b2019-01-14 14:28:55 -0800145 menu_start_y_(0),
Tao Bao736d59c2017-01-03 10:15:33 -0800146 show_menu(false),
Tom Marshall820517b2019-01-14 14:28:55 -0800147 menu_show_start(0),
148 menu_show_count(0),
Tao Bao736d59c2017-01-03 10:15:33 -0800149 menu_sel(0),
150 file_viewer_text_(nullptr),
151 intro_frames(0),
152 loop_frames(0),
153 current_frame(0),
154 intro_done(false),
Tao Bao736d59c2017-01-03 10:15:33 -0800155 stage(-1),
156 max_stage(-1),
Tao Baoefb49ad2017-01-31 23:03:10 -0800157 locale_(""),
158 rtl_locale_(false),
CEnnis910ccad612013-05-02 13:29:53 -0400159 updateMutex(PTHREAD_MUTEX_INITIALIZER),
160 rainbow(false),
161 wrap_count(0) {}
Doug Zongker211aebc2011-10-28 15:13:10 -0700162
Tao Bao99b2d772017-06-23 22:47:03 -0700163GRSurface* ScreenRecoveryUI::GetCurrentFrame() const {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700164 if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
165 return intro_done ? loopFrames[current_frame] : introFrames[current_frame];
166 }
Tom Marshallf780f1c2018-03-01 23:51:51 +0100167 return nullptr;
Elliott Hughes498cda62016-04-14 16:49:04 -0700168}
169
Tao Bao99b2d772017-06-23 22:47:03 -0700170GRSurface* ScreenRecoveryUI::GetCurrentText() const {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700171 switch (currentIcon) {
172 case ERASING:
173 return erasing_text;
174 case ERROR:
175 return error_text;
176 case INSTALLING_UPDATE:
177 return installing_text;
178 case NO_COMMAND:
179 return no_command_text;
180 case NONE:
181 abort();
182 }
Elliott Hughes498cda62016-04-14 16:49:04 -0700183}
184
Mikhail Lappob49767c2017-03-23 21:44:26 +0100185int ScreenRecoveryUI::PixelsFromDp(int dp) const {
Tao Bao75779652017-09-10 11:28:32 -0700186 return dp * kDensity;
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700187}
188
189// Here's the intended layout:
190
Elliott Hughes6d089a92016-07-08 17:23:41 -0700191// | portrait large landscape large
192// ---------+-------------------------------------------------
Tao Bao3250f722017-06-29 14:32:05 -0700193// gap |
Elliott Hughes6d089a92016-07-08 17:23:41 -0700194// icon | (200dp)
195// gap | 68dp 68dp 56dp 112dp
196// text | (14sp)
197// gap | 32dp 32dp 26dp 52dp
198// progress | (2dp)
Tao Bao3250f722017-06-29 14:32:05 -0700199// gap |
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700200
Tao Bao3250f722017-06-29 14:32:05 -0700201// Note that "baseline" is actually the *top* of each icon (because that's how our drawing routines
202// 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 -0700203
Elliott Hughes6d089a92016-07-08 17:23:41 -0700204enum Layout { PORTRAIT = 0, PORTRAIT_LARGE = 1, LANDSCAPE = 2, LANDSCAPE_LARGE = 3, LAYOUT_MAX };
Tao Bao3250f722017-06-29 14:32:05 -0700205enum Dimension { TEXT = 0, ICON = 1, DIMENSION_MAX };
Elliott Hughes6d089a92016-07-08 17:23:41 -0700206static constexpr int kLayouts[LAYOUT_MAX][DIMENSION_MAX] = {
Tao Bao3250f722017-06-29 14:32:05 -0700207 { 32, 68, }, // PORTRAIT
208 { 32, 68, }, // PORTRAIT_LARGE
209 { 26, 56, }, // LANDSCAPE
210 { 52, 112, }, // LANDSCAPE_LARGE
Elliott Hughes6d089a92016-07-08 17:23:41 -0700211};
212
Tao Bao99b2d772017-06-23 22:47:03 -0700213int ScreenRecoveryUI::GetAnimationBaseline() const {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700214 return GetTextBaseline() - PixelsFromDp(kLayouts[layout_][ICON]) - gr_get_height(loopFrames[0]);
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700215}
216
Tao Bao99b2d772017-06-23 22:47:03 -0700217int ScreenRecoveryUI::GetTextBaseline() const {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700218 return GetProgressBaseline() - PixelsFromDp(kLayouts[layout_][TEXT]) -
219 gr_get_height(installing_text);
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700220}
221
Tao Bao99b2d772017-06-23 22:47:03 -0700222int ScreenRecoveryUI::GetProgressBaseline() const {
Tao Bao3250f722017-06-29 14:32:05 -0700223 int elements_sum = gr_get_height(loopFrames[0]) + PixelsFromDp(kLayouts[layout_][ICON]) +
224 gr_get_height(installing_text) + PixelsFromDp(kLayouts[layout_][TEXT]) +
225 gr_get_height(progressBarFill);
Luke Song92eda4d2017-09-19 10:51:35 -0700226 int bottom_gap = (ScreenHeight() - elements_sum) / 2;
227 return ScreenHeight() - bottom_gap - gr_get_height(progressBarFill);
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700228}
229
Tom Marshallf780f1c2018-03-01 23:51:51 +0100230// Draw the currently selected stage icon(s) (if any).
231// Does not flip pages. Should only be called with updateMutex locked.
Elliott Hughes498cda62016-04-14 16:49:04 -0700232void ScreenRecoveryUI::draw_background_locked() {
Tom Marshallf780f1c2018-03-01 23:51:51 +0100233 if (currentIcon != NONE && currentIcon != NO_COMMAND) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700234 if (max_stage != -1) {
235 int stage_height = gr_get_height(stageMarkerEmpty);
236 int stage_width = gr_get_width(stageMarkerEmpty);
Tom Marshallf780f1c2018-03-01 23:51:51 +0100237 int stage_x = kMarginWidth + (ScreenWidth() - max_stage * gr_get_width(stageMarkerEmpty)) / 2;
238 int stage_y = kMarginHeight;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700239 for (int i = 0; i < max_stage; ++i) {
240 GRSurface* stage_surface = (i < stage) ? stageMarkerFill : stageMarkerEmpty;
Tom Marshallf780f1c2018-03-01 23:51:51 +0100241 DrawSurface(stage_surface, 0, 0, stage_width, stage_height, stage_x, stage_y);
242 stage_x += stage_width;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700243 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700244 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700245 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700246}
247
Tom Marshallf780f1c2018-03-01 23:51:51 +0100248// Draws either the animation and progress bar or the currently
249// selected icon and text on the screen.
250// Does not flip pages. Should only be called with updateMutex locked.
251void ScreenRecoveryUI::draw_foreground_locked(int& y) {
252 GRSurface* frame = GetCurrentFrame();
253 if (frame) {
254 // Show animation frame and progress bar
Tao Bao736d59c2017-01-03 10:15:33 -0800255 int frame_width = gr_get_width(frame);
256 int frame_height = gr_get_height(frame);
Tom Marshall84221d82018-02-26 20:46:24 +0100257 int frame_x = kMarginWidth + (ScreenWidth() - frame_width) / 2;
258 int frame_y = kMarginHeight + GetAnimationBaseline();
Luke Song92eda4d2017-09-19 10:51:35 -0700259 DrawSurface(frame, 0, 0, frame_width, frame_height, frame_x, frame_y);
Tom Marshallf780f1c2018-03-01 23:51:51 +0100260 y = frame_y + frame_height;
Doug Zongker211aebc2011-10-28 15:13:10 -0700261
Tom Marshallf780f1c2018-03-01 23:51:51 +0100262 if (progressBarType != EMPTY) {
263 int width = gr_get_width(progressBarEmpty);
264 int height = gr_get_height(progressBarEmpty);
Doug Zongker211aebc2011-10-28 15:13:10 -0700265
Tom Marshallf780f1c2018-03-01 23:51:51 +0100266 int progress_x = kMarginWidth + (ScreenWidth() - width) / 2;
267 int progress_y = kMarginHeight + GetProgressBaseline();
Doug Zongker211aebc2011-10-28 15:13:10 -0700268
Tom Marshallf780f1c2018-03-01 23:51:51 +0100269 // Erase behind the progress bar (in case this was a progress-only update)
270 gr_color(0, 0, 0, 255);
271 DrawFill(progress_x, progress_y, width, height);
Doug Zongker211aebc2011-10-28 15:13:10 -0700272
Tom Marshallf780f1c2018-03-01 23:51:51 +0100273 if (progressBarType == DETERMINATE) {
274 float p = progressScopeStart + progress * progressScopeSize;
275 int pos = static_cast<int>(p * width);
Doug Zongker211aebc2011-10-28 15:13:10 -0700276
Tom Marshallf780f1c2018-03-01 23:51:51 +0100277 if (rtl_locale_) {
278 // Fill the progress bar from right to left.
279 if (pos > 0) {
280 DrawSurface(progressBarFill, width - pos, 0, pos, height, progress_x + width - pos,
281 progress_y);
282 }
283 if (pos < width - 1) {
284 DrawSurface(progressBarEmpty, 0, 0, width - pos, height, progress_x, progress_y);
285 }
286 } else {
287 // Fill the progress bar from left to right.
288 if (pos > 0) {
289 DrawSurface(progressBarFill, 0, 0, pos, height, progress_x, progress_y);
290 }
291 if (pos < width - 1) {
292 DrawSurface(progressBarEmpty, pos, 0, width - pos, height, progress_x + pos,
293 progress_y);
294 }
Tao Bao736d59c2017-01-03 10:15:33 -0800295 }
296 }
Tom Marshallf780f1c2018-03-01 23:51:51 +0100297 y = progress_y + height;
298 }
299 } else {
300 // Show icon and text
301 if (currentIcon != NONE && currentIcon != NO_COMMAND) {
302 GRSurface* icon_surface = error_icon;
303 int icon_width = gr_get_width(icon_surface);
304 int icon_height = gr_get_height(icon_surface);
305 int icon_x = kMarginWidth + (gr_fb_width() - icon_width) / 2;
306 int icon_y = kMarginHeight + GetAnimationBaseline();
307 gr_blit(icon_surface, 0, 0, icon_width, icon_height, icon_x, icon_y);
308
309 GRSurface* text_surface = GetCurrentText();
310 int text_width = gr_get_width(text_surface);
311 int text_height = gr_get_height(text_surface);
312 int text_x = kMarginWidth + (gr_fb_width() - text_width) / 2;
313 int text_y = kMarginHeight + GetTextBaseline();
314 gr_color(255, 255, 255, 255);
315 gr_texticon(text_x, text_y, text_surface);
316
317 y = text_y + text_height;
Doug Zongker211aebc2011-10-28 15:13:10 -0700318 }
Tao Bao736d59c2017-01-03 10:15:33 -0800319 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700320}
321
Tom Marshall820517b2019-01-14 14:28:55 -0800322/* Lineage teal: #167c80 */
Tao Bao99b2d772017-06-23 22:47:03 -0700323void ScreenRecoveryUI::SetColor(UIElement e) const {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700324 switch (e) {
Tom Marshall820517b2019-01-14 14:28:55 -0800325 case STATUSBAR:
326 gr_color(255, 255, 255, 255);
327 break;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700328 case INFO:
329 gr_color(249, 194, 0, 255);
330 break;
331 case HEADER:
332 gr_color(247, 0, 6, 255);
333 break;
334 case MENU:
335 case MENU_SEL_BG:
Tom Marshall820517b2019-01-14 14:28:55 -0800336 gr_color(0xd8, 0xd8, 0xd8, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700337 break;
338 case MENU_SEL_BG_ACTIVE:
Tom Marshall5333a062014-11-24 16:38:37 -0800339 gr_color(138, 135, 134, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700340 break;
341 case MENU_SEL_FG:
Tom Marshall820517b2019-01-14 14:28:55 -0800342 gr_color(0x16, 0x7c, 0x80, 255);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700343 break;
344 case LOG:
345 gr_color(196, 196, 196, 255);
346 break;
347 case TEXT_FILL:
348 gr_color(0, 0, 0, 160);
349 break;
350 default:
351 gr_color(255, 255, 255, 255);
352 break;
353 }
Doug Zongkerc0441d12013-07-31 11:28:24 -0700354}
Doug Zongker211aebc2011-10-28 15:13:10 -0700355
Tianjie Xu29d55752017-09-20 17:53:46 -0700356void ScreenRecoveryUI::SelectAndShowBackgroundText(const std::vector<std::string>& locales_entries,
357 size_t sel) {
358 SetLocale(locales_entries[sel]);
359 std::vector<std::string> text_name = { "erasing_text", "error_text", "installing_text",
360 "installing_security_text", "no_command_text" };
361 std::unordered_map<std::string, std::unique_ptr<GRSurface, decltype(&free)>> surfaces;
362 for (const auto& name : text_name) {
363 GRSurface* text_image = nullptr;
364 LoadLocalizedBitmap(name.c_str(), &text_image);
365 if (!text_image) {
366 Print("Failed to load %s\n", name.c_str());
367 return;
368 }
369 surfaces.emplace(name, std::unique_ptr<GRSurface, decltype(&free)>(text_image, &free));
370 }
371
372 pthread_mutex_lock(&updateMutex);
373 gr_color(0, 0, 0, 255);
374 gr_clear();
375
376 int text_y = kMarginHeight;
377 int text_x = kMarginWidth;
378 int line_spacing = gr_sys_font()->char_height; // Put some extra space between images.
379 // Write the header and descriptive texts.
380 SetColor(INFO);
381 std::string header = "Show background text image";
382 text_y += DrawTextLine(text_x, text_y, header.c_str(), true);
383 std::string locale_selection = android::base::StringPrintf(
384 "Current locale: %s, %zu/%zu", locales_entries[sel].c_str(), sel, locales_entries.size());
385 const char* instruction[] = { locale_selection.c_str(),
386 "Use volume up/down to switch locales and power to exit.",
387 nullptr };
388 text_y += DrawWrappedTextLines(text_x, text_y, instruction);
389
390 // Iterate through the text images and display them in order for the current locale.
391 for (const auto& p : surfaces) {
392 text_y += line_spacing;
393 SetColor(LOG);
394 text_y += DrawTextLine(text_x, text_y, p.first.c_str(), false);
395 gr_color(255, 255, 255, 255);
396 gr_texticon(text_x, text_y, p.second.get());
397 text_y += gr_get_height(p.second.get());
398 }
399 // Update the whole screen.
400 gr_flip();
401 pthread_mutex_unlock(&updateMutex);
402}
403
404void ScreenRecoveryUI::CheckBackgroundTextImages(const std::string& saved_locale) {
405 // Load a list of locales embedded in one of the resource files.
406 std::vector<std::string> locales_entries = get_locales_in_png("installing_text");
407 if (locales_entries.empty()) {
408 Print("Failed to load locales from the resource files\n");
409 return;
410 }
411 size_t selected = 0;
412 SelectAndShowBackgroundText(locales_entries, selected);
413
414 FlushKeys();
415 while (true) {
Tom Marshall820517b2019-01-14 14:28:55 -0800416 RecoveryUI::InputEvent evt = WaitInputEvent();
417 if (evt.type() != RecoveryUI::EVENT_TYPE_KEY) {
Tianjie Xu29d55752017-09-20 17:53:46 -0700418 break;
Tom Marshall820517b2019-01-14 14:28:55 -0800419 }
420 if (evt.key() == KEY_POWER || evt.key() == KEY_ENTER) {
421 break;
422 } else if (evt.key() == KEY_UP || evt.key() == KEY_VOLUMEUP) {
Tianjie Xu29d55752017-09-20 17:53:46 -0700423 selected = (selected == 0) ? locales_entries.size() - 1 : selected - 1;
424 SelectAndShowBackgroundText(locales_entries, selected);
Tom Marshall820517b2019-01-14 14:28:55 -0800425 } else if (evt.key() == KEY_DOWN || evt.key() == KEY_VOLUMEDOWN) {
Tianjie Xu29d55752017-09-20 17:53:46 -0700426 selected = (selected == locales_entries.size() - 1) ? 0 : selected + 1;
427 SelectAndShowBackgroundText(locales_entries, selected);
428 }
429 }
430
431 SetLocale(saved_locale);
432}
433
Luke Song92eda4d2017-09-19 10:51:35 -0700434int ScreenRecoveryUI::ScreenWidth() const {
435 return gr_fb_width();
436}
437
438int ScreenRecoveryUI::ScreenHeight() const {
439 return gr_fb_height();
440}
441
442void ScreenRecoveryUI::DrawSurface(GRSurface* surface, int sx, int sy, int w, int h, int dx,
443 int dy) const {
444 gr_blit(surface, sx, sy, w, h, dx, dy);
445}
446
Tao Baoea78d862017-06-28 14:52:17 -0700447int ScreenRecoveryUI::DrawHorizontalRule(int y) const {
Luke Song92eda4d2017-09-19 10:51:35 -0700448 gr_fill(0, y + 4, ScreenWidth(), y + 6);
Tao Baoea78d862017-06-28 14:52:17 -0700449 return 8;
Elliott Hughes95fc63e2015-04-10 19:12:01 -0700450}
451
Luke Songe2bd8762017-06-12 16:08:33 -0700452void ScreenRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700453 gr_fill(x, y, x + width, y + height);
Luke Songe2bd8762017-06-12 16:08:33 -0700454}
455
Luke Song92eda4d2017-09-19 10:51:35 -0700456void ScreenRecoveryUI::DrawFill(int x, int y, int w, int h) const {
457 gr_fill(x, y, w, h);
458}
459
460void ScreenRecoveryUI::DrawTextIcon(int x, int y, GRSurface* surface) const {
461 gr_texticon(x, y, surface);
462}
463
Tao Baoea78d862017-06-28 14:52:17 -0700464int ScreenRecoveryUI::DrawTextLine(int x, int y, const char* line, bool bold) const {
465 gr_text(gr_sys_font(), x, y, line, bold);
466 return char_height_ + 4;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700467}
468
Tao Baoea78d862017-06-28 14:52:17 -0700469int ScreenRecoveryUI::DrawTextLines(int x, int y, const char* const* lines) const {
470 int offset = 0;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700471 for (size_t i = 0; lines != nullptr && lines[i] != nullptr; ++i) {
Tao Baoea78d862017-06-28 14:52:17 -0700472 offset += DrawTextLine(x, y + offset, lines[i], false);
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700473 }
Tao Baoea78d862017-06-28 14:52:17 -0700474 return offset;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700475}
476
Tao Bao2bbc6d62017-08-13 23:48:55 -0700477int ScreenRecoveryUI::DrawWrappedTextLines(int x, int y, const char* const* lines) const {
478 int offset = 0;
479 for (size_t i = 0; lines != nullptr && lines[i] != nullptr; ++i) {
480 // The line will be wrapped if it exceeds text_cols_.
481 std::string line(lines[i]);
482 size_t next_start = 0;
483 while (next_start < line.size()) {
484 std::string sub = line.substr(next_start, text_cols_ + 1);
485 if (sub.size() <= text_cols_) {
486 next_start += sub.size();
487 } else {
488 // Line too long and must be wrapped to text_cols_ columns.
489 size_t last_space = sub.find_last_of(" \t\n");
490 if (last_space == std::string::npos) {
491 // No space found, just draw as much as we can
492 sub.resize(text_cols_);
493 next_start += text_cols_;
494 } else {
495 sub.resize(last_space);
496 next_start += last_space + 1;
497 }
498 }
Tom Marshall820517b2019-01-14 14:28:55 -0800499 gr_text(gr_menu_font(), x, y + offset, sub.c_str(), false);
500 offset += menu_char_height_ + 4;
Tao Bao2bbc6d62017-08-13 23:48:55 -0700501 }
502 }
503 return offset;
504}
505
Tom Marshall820517b2019-01-14 14:28:55 -0800506void ScreenRecoveryUI::draw_statusbar_locked() {
507 int y = kMarginHeight;
508 int x;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700509
Tom Marshall820517b2019-01-14 14:28:55 -0800510 int icon_x, icon_y, icon_h, icon_w;
511
512 // Battery status
513 bool batt_charged;
514 int batt_capacity;
515 get_battery_status(batt_charged, batt_capacity);
516 char batt_capacity_str[3 + 1 + 1];
517 snprintf(batt_capacity_str, sizeof(batt_capacity_str), "%d%%", batt_capacity);
518
519 // Draw status bar from right to left
520
521 // Battery icon
Tom Marshallbd5a3942018-03-06 20:32:45 +0100522 x = gr_fb_width() - RECOVERY_UI_MARGIN_STATUSBAR;
Tom Marshall820517b2019-01-14 14:28:55 -0800523 x -= 1 * char_width_;
524 SetColor((batt_capacity < 20) ? HEADER : STATUSBAR);
525
526 // Top
527 icon_x = x + char_width_ / 3;
528 icon_y = y;
529 icon_w = char_width_ / 3;
530 icon_h = char_height_ / 12;
531 gr_fill(icon_x, icon_y, icon_x + icon_w, icon_y + icon_h);
532
533 // Main rect
534 icon_x = x;
535 icon_y = y + icon_h;
536 icon_w = char_width_;
537 icon_h = char_height_ - (char_height_ / 12);
538 gr_fill(icon_x, icon_y, icon_x + icon_w, icon_y + icon_h);
539
540 // Capacity
541 icon_x = x + char_width_ / 6;
542 icon_y = y + char_height_ / 12;
543 icon_w = char_width_ - (2 * char_width_ / 6);
544 icon_h = char_height_ - (3 * char_height_ / 12);
545 int cap_h = icon_h * batt_capacity / 100;
546 gr_fill(icon_x, icon_y + icon_h - cap_h, icon_x + icon_w, icon_y + icon_h);
547 gr_color(0, 0, 0, 255);
548 gr_fill(icon_x, icon_y, icon_x + icon_w, icon_y + icon_h - cap_h);
549 SetColor(STATUSBAR);
550
551 x -= char_width_; // Separator
552
553 // Battery text
554 x -= strlen(batt_capacity_str) * char_width_;
555 gr_text(gr_sys_font(), x, y, batt_capacity_str, false);
556}
557
558/*
559 * Header layout:
560 * * 1/32: Status bar
561 * * Header image
562 * * 1/32: Margin
563 */
564void ScreenRecoveryUI::draw_header_locked(int& y) {
565 int h_unit = gr_fb_width() / 9;
566 int v_unit = gr_fb_height() / 16;
567
568 GRSurface* icon;
569 int icon_x, icon_y, icon_h, icon_w;
570
571 y += v_unit / 2; // Margin
572
573 // Draw back icon if not in main menu
574 if (!menu_is_main_) {
575 icon = (menu_sel == -1 ? ic_back_sel : ic_back);
576 icon_w = gr_get_width(icon);
577 icon_h = gr_get_height(icon);
578 icon_x = kMarginWidth + (h_unit / 2) + ((h_unit * 1) - icon_w) / 2;
579 icon_y = y + ((v_unit * 1) - icon_h) / 2;
580 gr_blit(icon, 0, 0, icon_w, icon_h, icon_x, icon_y);
581 }
582 y += v_unit;
583
584 // Draw logo
585 icon = logo_image;
586 icon_w = gr_get_width(icon);
587 icon_h = gr_get_height(icon);
588 icon_x = kMarginWidth + (gr_fb_width() - icon_w) / 2;
589 icon_y = y + ((v_unit * 4) - icon_h) / 2;
590 gr_blit(icon, 0, 0, icon_w, icon_h, icon_x, icon_y);
591 y += v_unit * 4;
592
593 y += v_unit * 1; // Margin
594}
595
596void ScreenRecoveryUI::draw_text_menu_locked(int& y) {
597 static constexpr int kMenuIndent = 4;
598 int x = kMarginWidth + kMenuIndent;
Alessandro Astonefeaa81c2019-03-29 16:42:38 +0100599 // An item should not be displayed if it's shown height would be less than 75% of its true height
600 static const int kMinItemHeight = MenuItemHeight() * 3 / 4;
Tom Marshall820517b2019-01-14 14:28:55 -0800601
602 draw_statusbar_locked();
603 draw_header_locked(y);
604
605 if (menu_headers_) {
606 SetColor(HEADER);
607 // Ignore kMenuIndent, which is not taken into account by text_cols_.
608 y += DrawWrappedTextLines(kMarginWidth, y, menu_headers_);
609
610 SetColor(MENU);
611 y += DrawHorizontalRule(y) + 4;
612 }
613
614 menu_start_y_ = y;
615 int i;
Alessandro Astonefeaa81c2019-03-29 16:42:38 +0100616 for (i = menu_show_start; i < (int)menu_items_.size() && y + kMinItemHeight < gr_fb_height();
617 ++i) {
Tom Marshall820517b2019-01-14 14:28:55 -0800618 const ScreenMenuItem& item = menu_items_.at(i);
619 if (i == menu_sel) {
620 SetColor(MENU_SEL_FG);
621 y += menu_char_height_;
622 gr_text(gr_menu_font(), x, y, item.text().c_str(), false);
623 y += menu_char_height_;
624 y += menu_char_height_;
625 } else {
626 SetColor(MENU);
627 y += menu_char_height_;
628 gr_text(gr_menu_font(), x, y, item.text().c_str(), false);
629 y += menu_char_height_;
630 y += menu_char_height_;
631 }
632 }
633 menu_show_count = i - menu_show_start;
634}
635
636/*
637 * Grid layout.
638 *
639 * Grid item:
640 * Horizontal:
641 * * 3/9 of screen per item.
642 * * 1/9 of screen margin around/between items.
643 * Vertical:
644 * * 3/16 of screen per item.
645 * * No margin between items.
646 *
647 * Within a grid item:
648 * Asher's icons 1/5 of grid both dimensions.
649 * Current icons 2/5 of grid both dimensions.
650 * Horizontal:
651 * * All items centered.
652 * Vertical:
653 * * Icon lower aligned in top 2/3.
654 * * Text upper aligned in low 1/3 plus half line margin.
655 */
656void ScreenRecoveryUI::draw_grid_menu_locked(int& y) {
657 int h_unit = gr_fb_width() / 9;
658 int v_unit = gr_fb_height() / 16;
659
660 int grid_w = h_unit * 3;
661 int grid_h = v_unit * 3;
662
663 draw_statusbar_locked();
664 draw_header_locked(y);
665
666 menu_start_y_ = y;
667 int i;
668 for (i = menu_show_start; i < (int)menu_items_.size() && y + grid_h < gr_fb_height(); ++i) {
669 ScreenMenuItem& item = menu_items_.at(i);
670 int grid_x = kMarginWidth + ((i % 2) ? h_unit * 5 : h_unit * 1);
671 int grid_y = y;
672 if (item.icon()) {
673 GRSurface* icon = (i == menu_sel) ? item.icon_sel() : item.icon();
674 int icon_w = gr_get_width(icon);
675 int icon_h = gr_get_height(icon);
676 int icon_x = grid_x + (grid_w - icon_w) / 2;
677 int icon_y = grid_y + ((grid_h * 2 / 3) - icon_h) / 2;
678 gr_blit(icon, 0, 0, icon_w, icon_h, icon_x, icon_y);
679 }
680 if (!item.text().empty()) {
681 int text_w = item.text().size() * char_width_;
682 int text_x = grid_x + (grid_w - text_w) / 2;
683 int text_y = grid_y + (grid_h * 2 / 3) + (char_height_ / 2);
684 SetColor(i == menu_sel ? MENU_SEL_FG : MENU);
685 gr_text(gr_sys_font(), text_x, text_y, item.text().c_str(), false);
686 }
687 if (i % 2) {
688 y += grid_h;
689 grid_y = y;
690 }
691 }
692 menu_show_count = i - menu_show_start;
693}
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700694
Tao Bao171b4c42017-06-19 23:10:44 -0700695// Redraws everything on the screen. Does not flip pages. Should only be called with updateMutex
696// locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700697void ScreenRecoveryUI::draw_screen_locked() {
Tom Marshallf780f1c2018-03-01 23:51:51 +0100698 pagesIdentical = false;
Tao Bao171b4c42017-06-19 23:10:44 -0700699 gr_color(0, 0, 0, 255);
700 gr_clear();
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700701
Tao Bao4521b702017-06-20 18:11:21 -0700702 int y = kMarginHeight;
Tao Bao171b4c42017-06-19 23:10:44 -0700703 if (show_menu) {
Tom Marshall820517b2019-01-14 14:28:55 -0800704 switch (menu_type_) {
705 case MT_LIST:
706 draw_text_menu_locked(y);
707 break;
708 case MT_GRID:
709 draw_grid_menu_locked(y);
710 break;
711 default:
712 break;
Doug Zongker211aebc2011-10-28 15:13:10 -0700713 }
Tao Bao171b4c42017-06-19 23:10:44 -0700714
Tom Marshall820517b2019-01-14 14:28:55 -0800715 // Draw version info
716 if (menu_is_main_) {
717 int text_x, text_y;
718 text_x = kMarginWidth + (gr_fb_width() - (android_version_.size() * char_width_)) / 2;
719 text_y = gr_fb_height() - 2 * (char_height_ + 4);
720 SetColor(MENU);
721 DrawTextLine(text_x, text_y, android_version_.c_str(), false);
722 text_x = kMarginWidth + (gr_fb_width() - (lineage_version_.size() * char_width_)) / 2;
723 text_y = gr_fb_height() - 1 * (char_height_ + 4);
724 DrawTextLine(text_x, text_y, lineage_version_.c_str(), false);
Tao Bao171b4c42017-06-19 23:10:44 -0700725 }
Tom Marshall820517b2019-01-14 14:28:55 -0800726 } else {
Tom Marshallf780f1c2018-03-01 23:51:51 +0100727 draw_background_locked();
728 draw_foreground_locked(y);
729
730 if (show_text) {
731 // Display from the bottom up, until we hit the top of the screen, the
732 // bottom of the foreground, or we've displayed the entire text buffer.
733 SetColor(LOG);
734 int row = (text_rows_ - 1) % text_rows_;
735 size_t count = 0;
736 for (int ty = gr_fb_height() - kMarginHeight - char_height_; ty >= y && count < text_rows_;
737 ty -= char_height_, ++count) {
738 DrawTextLine(kMarginWidth, ty, text_[row], false);
739 --row;
740 if (row < 0) row = text_rows_ - 1;
741 }
Tom Marshall820517b2019-01-14 14:28:55 -0800742 }
Tao Bao171b4c42017-06-19 23:10:44 -0700743 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700744}
745
746// Redraw everything on the screen and flip the screen (make it visible).
747// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700748void ScreenRecoveryUI::update_screen_locked() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700749 draw_screen_locked();
750 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700751}
752
753// Updates only the progress bar, if possible, otherwise redraws the screen.
754// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700755void ScreenRecoveryUI::update_progress_locked() {
Tom Marshallf780f1c2018-03-01 23:51:51 +0100756 if (!pagesIdentical) {
757 draw_screen_locked();
758 pagesIdentical = true;
759 } else {
760 int y = kMarginHeight;
761 draw_foreground_locked(y);
762 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700763 gr_flip();
Doug Zongker211aebc2011-10-28 15:13:10 -0700764}
765
766// Keeps the progress bar updated, even when the process is otherwise busy.
Elliott Hughes985022a2015-04-13 13:04:32 -0700767void* ScreenRecoveryUI::ProgressThreadStartRoutine(void* data) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700768 reinterpret_cast<ScreenRecoveryUI*>(data)->ProgressThreadLoop();
769 return nullptr;
Doug Zongker32a0a472011-11-01 11:00:20 -0700770}
771
Elliott Hughes985022a2015-04-13 13:04:32 -0700772void ScreenRecoveryUI::ProgressThreadLoop() {
Tao Bao0470cee2017-08-02 17:11:04 -0700773 double interval = 1.0 / kAnimationFps;
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700774 while (true) {
775 double start = now();
776 pthread_mutex_lock(&updateMutex);
Doug Zongker211aebc2011-10-28 15:13:10 -0700777
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700778 bool redraw = false;
Doug Zongker211aebc2011-10-28 15:13:10 -0700779
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700780 // update the installation animation, if active
781 // skip this if we have a text overlay (too expensive to update)
Steve Kondik17b88b02014-09-12 18:52:28 -0700782 if ((currentIcon == INSTALLING_UPDATE || currentIcon == ERASING)) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700783 if (!intro_done) {
784 if (current_frame == intro_frames - 1) {
785 intro_done = true;
786 current_frame = 0;
787 } else {
788 ++current_frame;
Doug Zongker211aebc2011-10-28 15:13:10 -0700789 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700790 } else {
791 current_frame = (current_frame + 1) % loop_frames;
792 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700793
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700794 redraw = true;
Doug Zongker211aebc2011-10-28 15:13:10 -0700795 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700796
797 // move the progress bar forward on timed intervals, if configured
798 int duration = progressScopeDuration;
799 if (progressBarType == DETERMINATE && duration > 0) {
800 double elapsed = now() - progressScopeTime;
801 float p = 1.0 * elapsed / duration;
802 if (p > 1.0) p = 1.0;
803 if (p > progress) {
804 progress = p;
805 redraw = true;
806 }
807 }
808
809 if (redraw) update_progress_locked();
810
811 pthread_mutex_unlock(&updateMutex);
Steve Kondik17b88b02014-09-12 18:52:28 -0700812
813 if (progressBarType == EMPTY) break;
814
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700815 double end = now();
816 // minimum of 20ms delay between frames
817 double delay = interval - (end - start);
818 if (delay < 0.02) delay = 0.02;
819 usleep(static_cast<useconds_t>(delay * 1000000));
820 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700821}
822
Elliott Hughes0a5cb0c2015-04-15 10:58:56 -0700823void ScreenRecoveryUI::LoadBitmap(const char* filename, GRSurface** surface) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700824 int result = res_create_display_surface(filename, surface);
825 if (result < 0) {
826 LOG(ERROR) << "couldn't load bitmap " << filename << " (error " << result << ")";
827 }
Doug Zongkereac881c2014-03-07 09:21:25 -0800828}
829
Tom Marshall820517b2019-01-14 14:28:55 -0800830void ScreenRecoveryUI::FreeBitmap(GRSurface* surface) {
831 res_free_surface(surface);
832}
833
Elliott Hughes0a5cb0c2015-04-15 10:58:56 -0700834void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, GRSurface** surface) {
Tao Bao736d59c2017-01-03 10:15:33 -0800835 int result = res_create_localized_alpha_surface(filename, locale_.c_str(), surface);
836 if (result < 0) {
837 LOG(ERROR) << "couldn't load bitmap " << filename << " (error " << result << ")";
838 }
Doug Zongker02ec6b82012-08-22 17:26:40 -0700839}
840
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700841static char** Alloc2d(size_t rows, size_t cols) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700842 char** result = new char*[rows];
843 for (size_t i = 0; i < rows; ++i) {
844 result[i] = new char[cols];
845 memset(result[i], 0, cols);
846 }
847 return result;
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700848}
849
Tianjie Xu35926c42016-04-28 18:06:26 -0700850// Choose the right background string to display during update.
851void ScreenRecoveryUI::SetSystemUpdateText(bool security_update) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700852 if (security_update) {
853 LoadLocalizedBitmap("installing_security_text", &installing_text);
854 } else {
855 LoadLocalizedBitmap("installing_text", &installing_text);
856 }
Tianjie Xu35926c42016-04-28 18:06:26 -0700857}
858
Sen Jiangd5304492016-12-09 16:20:49 -0800859bool ScreenRecoveryUI::InitTextParams() {
Tao Bao171b4c42017-06-19 23:10:44 -0700860 if (gr_init() < 0) {
861 return false;
862 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700863
Tao Bao171b4c42017-06-19 23:10:44 -0700864 gr_font_size(gr_sys_font(), &char_width_, &char_height_);
Tom Marshall820517b2019-01-14 14:28:55 -0800865 gr_font_size(gr_menu_font(), &menu_char_width_, &menu_char_height_);
Luke Song92eda4d2017-09-19 10:51:35 -0700866 text_rows_ = (ScreenHeight() - kMarginHeight * 2) / char_height_;
867 text_cols_ = (ScreenWidth() - kMarginWidth * 2) / char_width_;
Tao Bao171b4c42017-06-19 23:10:44 -0700868 return true;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -0700869}
870
Tao Bao736d59c2017-01-03 10:15:33 -0800871bool ScreenRecoveryUI::Init(const std::string& locale) {
872 RecoveryUI::Init(locale);
Tao Baoefb49ad2017-01-31 23:03:10 -0800873
Tao Bao736d59c2017-01-03 10:15:33 -0800874 if (!InitTextParams()) {
875 return false;
876 }
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -0700877
Michael Bestas5d5abd82019-03-23 17:28:22 +0200878#ifdef RECOVERY_UI_BLANK_UNBLANK_ON_INIT
879 gr_fb_blank(true);
880 gr_fb_blank(false);
881#endif
882
Tao Bao736d59c2017-01-03 10:15:33 -0800883 // Are we portrait or landscape?
884 layout_ = (gr_fb_width() > gr_fb_height()) ? LANDSCAPE : PORTRAIT;
885 // Are we the large variant of our base layout?
886 if (gr_fb_height() > PixelsFromDp(800)) ++layout_;
Elliott Hughesfaf36e02016-04-20 17:22:16 -0700887
Tao Bao736d59c2017-01-03 10:15:33 -0800888 text_ = Alloc2d(text_rows_, text_cols_ + 1);
889 file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
Doug Zongker55a36ac2013-03-04 15:49:02 -0800890
Alessandro Astone20c8d412019-04-01 11:11:33 +0200891 text_row_ = text_rows_ - 1; // Printed text grows bottom up
892 text_col_ = 0;
Doug Zongker211aebc2011-10-28 15:13:10 -0700893
Tao Baoefb49ad2017-01-31 23:03:10 -0800894 // Set up the locale info.
895 SetLocale(locale);
896
Tom Marshallc9ab1092018-06-21 01:13:19 +0200897 // Load logo and scale it if necessary
898 // Note 2/45 is our standard margin on each side so the maximum image
899 // width is 41/45 of the screen width.
900 GRSurface* image;
901 LoadBitmap("logo_image", &image);
902 if ((int)gr_get_width(image) > gr_fb_width() * 41 / 45) {
903 float scale = (float)gr_fb_width() / (float)gr_get_width(image) * (41.0f / 45.0f);
904 GRSurface* scaled_image;
905 res_create_scaled_surface(&scaled_image, image, scale, scale);
906 logo_image = scaled_image;
907 res_free_surface(image);
908 } else {
909 logo_image = image;
910 }
911
Tom Marshall820517b2019-01-14 14:28:55 -0800912 LoadBitmap("ic_back", &ic_back);
913 LoadBitmap("ic_back_sel", &ic_back_sel);
914
Tao Bao736d59c2017-01-03 10:15:33 -0800915 LoadBitmap("icon_error", &error_icon);
Doug Zongker02ec6b82012-08-22 17:26:40 -0700916
Tao Bao736d59c2017-01-03 10:15:33 -0800917 LoadBitmap("progress_empty", &progressBarEmpty);
918 LoadBitmap("progress_fill", &progressBarFill);
Elliott Hughes498cda62016-04-14 16:49:04 -0700919
Tao Bao736d59c2017-01-03 10:15:33 -0800920 LoadBitmap("stage_empty", &stageMarkerEmpty);
921 LoadBitmap("stage_fill", &stageMarkerFill);
Doug Zongker211aebc2011-10-28 15:13:10 -0700922
Tao Bao736d59c2017-01-03 10:15:33 -0800923 // Background text for "installing_update" could be "installing update"
924 // or "installing security update". It will be set after UI init according
925 // to commands in BCB.
926 installing_text = nullptr;
927 LoadLocalizedBitmap("erasing_text", &erasing_text);
928 LoadLocalizedBitmap("no_command_text", &no_command_text);
929 LoadLocalizedBitmap("error_text", &error_text);
Elliott Hughes498cda62016-04-14 16:49:04 -0700930
Tao Bao736d59c2017-01-03 10:15:33 -0800931 LoadAnimation();
Doug Zongker02ec6b82012-08-22 17:26:40 -0700932
Tao Bao736d59c2017-01-03 10:15:33 -0800933 return true;
Doug Zongker211aebc2011-10-28 15:13:10 -0700934}
935
Tom Marshallcfcafa12017-09-08 19:48:46 +0000936void ScreenRecoveryUI::Stop() {
937 RecoveryUI::Stop();
938 gr_fb_blank(true);
939}
940
Elliott Hughes498cda62016-04-14 16:49:04 -0700941void ScreenRecoveryUI::LoadAnimation() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700942 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir("/res/images"), closedir);
943 dirent* de;
944 std::vector<std::string> intro_frame_names;
945 std::vector<std::string> loop_frame_names;
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -0700946
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700947 while ((de = readdir(dir.get())) != nullptr) {
948 int value, num_chars;
949 if (sscanf(de->d_name, "intro%d%n.png", &value, &num_chars) == 1) {
950 intro_frame_names.emplace_back(de->d_name, num_chars);
951 } else if (sscanf(de->d_name, "loop%d%n.png", &value, &num_chars) == 1) {
952 loop_frame_names.emplace_back(de->d_name, num_chars);
Elliott Hughes498cda62016-04-14 16:49:04 -0700953 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700954 }
Elliott Hughes498cda62016-04-14 16:49:04 -0700955
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700956 intro_frames = intro_frame_names.size();
957 loop_frames = loop_frame_names.size();
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -0700958
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700959 // It's okay to not have an intro.
960 if (intro_frames == 0) intro_done = true;
961 // But you must have an animation.
962 if (loop_frames == 0) abort();
Elliott Hughes498cda62016-04-14 16:49:04 -0700963
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700964 std::sort(intro_frame_names.begin(), intro_frame_names.end());
965 std::sort(loop_frame_names.begin(), loop_frame_names.end());
Damien Bargiacchi5e7cfb92016-08-24 18:28:43 -0700966
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700967 introFrames = new GRSurface*[intro_frames];
968 for (size_t i = 0; i < intro_frames; i++) {
969 LoadBitmap(intro_frame_names.at(i).c_str(), &introFrames[i]);
970 }
Elliott Hughes498cda62016-04-14 16:49:04 -0700971
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700972 loopFrames = new GRSurface*[loop_frames];
973 for (size_t i = 0; i < loop_frames; i++) {
974 LoadBitmap(loop_frame_names.at(i).c_str(), &loopFrames[i]);
975 }
Elliott Hughes498cda62016-04-14 16:49:04 -0700976}
977
Elliott Hughes8de52072015-04-08 20:06:50 -0700978void ScreenRecoveryUI::SetBackground(Icon icon) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700979 pthread_mutex_lock(&updateMutex);
Doug Zongker02ec6b82012-08-22 17:26:40 -0700980
Tom Marshall820517b2019-01-14 14:28:55 -0800981 if (icon != currentIcon) {
982 currentIcon = icon;
Tom Marshall820517b2019-01-14 14:28:55 -0800983 }
Doug Zongker52eeea42012-09-04 14:28:25 -0700984
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700985 pthread_mutex_unlock(&updateMutex);
Doug Zongker211aebc2011-10-28 15:13:10 -0700986}
987
Elliott Hughes8de52072015-04-08 20:06:50 -0700988void ScreenRecoveryUI::SetProgressType(ProgressType type) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700989 pthread_mutex_lock(&updateMutex);
990 if (progressBarType != type) {
991 progressBarType = type;
Tom Marshallf780f1c2018-03-01 23:51:51 +0100992 progressScopeStart = 0;
993 progressScopeSize = 0;
994 progress = 0;
Steve Kondik17b88b02014-09-12 18:52:28 -0700995 if (progressBarType != EMPTY) {
Tom Marshallf780f1c2018-03-01 23:51:51 +0100996 update_screen_locked();
Steve Kondik17b88b02014-09-12 18:52:28 -0700997 pthread_create(&progress_thread_, nullptr, ProgressThreadStartRoutine, this);
998 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -0700999 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001000 pthread_mutex_unlock(&updateMutex);
Doug Zongker211aebc2011-10-28 15:13:10 -07001001}
1002
Elliott Hughes8de52072015-04-08 20:06:50 -07001003void ScreenRecoveryUI::ShowProgress(float portion, float seconds) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001004 pthread_mutex_lock(&updateMutex);
1005 progressBarType = DETERMINATE;
1006 progressScopeStart += progressScopeSize;
1007 progressScopeSize = portion;
1008 progressScopeTime = now();
1009 progressScopeDuration = seconds;
1010 progress = 0;
1011 update_progress_locked();
1012 pthread_mutex_unlock(&updateMutex);
Doug Zongker211aebc2011-10-28 15:13:10 -07001013}
1014
Elliott Hughes8de52072015-04-08 20:06:50 -07001015void ScreenRecoveryUI::SetProgress(float fraction) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001016 pthread_mutex_lock(&updateMutex);
1017 if (fraction < 0.0) fraction = 0.0;
1018 if (fraction > 1.0) fraction = 1.0;
1019 if (progressBarType == DETERMINATE && fraction > progress) {
1020 // Skip updates that aren't visibly different.
1021 int width = gr_get_width(progressBarEmpty);
1022 float scale = width * progressScopeSize;
1023 if ((int)(progress * scale) != (int)(fraction * scale)) {
1024 progress = fraction;
1025 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -07001026 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001027 }
1028 pthread_mutex_unlock(&updateMutex);
Doug Zongker211aebc2011-10-28 15:13:10 -07001029}
1030
Doug Zongkerc87bab12013-11-25 13:53:25 -08001031void ScreenRecoveryUI::SetStage(int current, int max) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001032 pthread_mutex_lock(&updateMutex);
1033 stage = current;
1034 max_stage = max;
1035 pthread_mutex_unlock(&updateMutex);
Doug Zongkerc87bab12013-11-25 13:53:25 -08001036}
1037
Alessandro Astone20c8d412019-04-01 11:11:33 +02001038void ScreenRecoveryUI::NewLine() {
1039 // Shift the rows array up
1040 char* p = text_[0];
1041 for (size_t i = 0; i < text_rows_ - 1; i++) {
1042 text_[i] = text_[i + 1];
1043 }
1044 text_[text_rows_ - 1] = p;
1045 memset(text_[text_rows_ - 1], 0, text_cols_ + 1);
1046
1047 text_col_ = 0;
1048}
1049
Tao Baob6918c72015-05-19 17:02:16 -07001050void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001051 std::string str;
1052 android::base::StringAppendV(&str, fmt, ap);
Doug Zongker211aebc2011-10-28 15:13:10 -07001053
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001054 if (copy_to_stdout) {
1055 fputs(str.c_str(), stdout);
1056 }
Doug Zongker211aebc2011-10-28 15:13:10 -07001057
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001058 pthread_mutex_lock(&updateMutex);
1059 if (text_rows_ > 0 && text_cols_ > 0) {
Alessandro Astone20c8d412019-04-01 11:11:33 +02001060 if (previous_row_ended) {
1061 NewLine();
Doug Zongker211aebc2011-10-28 15:13:10 -07001062 }
Alessandro Astone20c8d412019-04-01 11:11:33 +02001063 previous_row_ended = false;
1064
1065 size_t row = text_rows_ - 1;
1066 for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
1067 if (*ptr == '\n' && *(ptr + 1) == '\0') {
1068 // Scroll on the next print
1069 text_[row][text_col_] = '\0';
1070 previous_row_ended = true;
1071 } else if ((*ptr == '\n' && *(ptr + 1) != '\0') || text_col_ >= text_cols_) {
1072 // We need to keep printing, scroll now
1073 text_[row][text_col_] = '\0';
1074 NewLine();
1075 }
1076 if (*ptr != '\n') text_[row][text_col_++] = *ptr;
1077 }
1078 text_[row][text_col_] = '\0';
1079
1080 if (show_text && update_screen_on_print) {
1081 update_screen_locked();
1082 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001083 }
1084 pthread_mutex_unlock(&updateMutex);
Doug Zongker211aebc2011-10-28 15:13:10 -07001085}
1086
Tao Baob6918c72015-05-19 17:02:16 -07001087void ScreenRecoveryUI::Print(const char* fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001088 va_list ap;
1089 va_start(ap, fmt);
1090 PrintV(fmt, true, ap);
1091 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001092}
1093
1094void ScreenRecoveryUI::PrintOnScreenOnly(const char *fmt, ...) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001095 va_list ap;
1096 va_start(ap, fmt);
1097 PrintV(fmt, false, ap);
1098 va_end(ap);
Tao Baob6918c72015-05-19 17:02:16 -07001099}
1100
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001101void ScreenRecoveryUI::PutChar(char ch) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001102 pthread_mutex_lock(&updateMutex);
1103 if (ch != '\n') text_[text_row_][text_col_++] = ch;
1104 if (ch == '\n' || text_col_ >= text_cols_) {
1105 text_col_ = 0;
1106 ++text_row_;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001107 }
1108 pthread_mutex_unlock(&updateMutex);
Elliott Hughes8de52072015-04-08 20:06:50 -07001109}
1110
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001111void ScreenRecoveryUI::ClearText() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001112 pthread_mutex_lock(&updateMutex);
1113 text_col_ = 0;
1114 text_row_ = 0;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001115 for (size_t i = 0; i < text_rows_; ++i) {
1116 memset(text_[i], 0, text_cols_ + 1);
1117 }
1118 pthread_mutex_unlock(&updateMutex);
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001119}
1120
Tom Marshall820517b2019-01-14 14:28:55 -08001121int ScreenRecoveryUI::ShowFile(FILE* fp) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001122 std::vector<off_t> offsets;
1123 offsets.push_back(ftello(fp));
1124 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001125
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001126 struct stat sb;
1127 fstat(fileno(fp), &sb);
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001128
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001129 bool show_prompt = false;
1130 while (true) {
1131 if (show_prompt) {
1132 PrintOnScreenOnly("--(%d%% of %d bytes)--",
1133 static_cast<int>(100 * (double(ftello(fp)) / double(sb.st_size))),
1134 static_cast<int>(sb.st_size));
1135 Redraw();
1136 while (show_prompt) {
1137 show_prompt = false;
Tom Marshall820517b2019-01-14 14:28:55 -08001138 RecoveryUI::InputEvent evt = WaitInputEvent();
1139 if (evt.type() != RecoveryUI::EVENT_TYPE_KEY) {
1140 show_prompt = true;
1141 continue;
1142 }
1143 if (evt.key() == KEY_POWER || evt.key() == KEY_ENTER || evt.key() == KEY_BACKSPACE ||
1144 evt.key() == KEY_BACK || evt.key() == KEY_HOME || evt.key() == KEY_HOMEPAGE) {
1145 return evt.key();
1146 } else if (evt.key() == KEY_UP || evt.key() == KEY_VOLUMEUP) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001147 if (offsets.size() <= 1) {
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001148 show_prompt = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001149 } else {
1150 offsets.pop_back();
1151 fseek(fp, offsets.back(), SEEK_SET);
1152 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001153 } else {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001154 if (feof(fp)) {
Tom Marshall820517b2019-01-14 14:28:55 -08001155 return -1;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001156 }
1157 offsets.push_back(ftello(fp));
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001158 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001159 }
1160 ClearText();
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001161 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001162
1163 int ch = getc(fp);
1164 if (ch == EOF) {
1165 while (text_row_ < text_rows_ - 1) PutChar('\n');
1166 show_prompt = true;
1167 } else {
1168 PutChar(ch);
1169 if (text_col_ == 0 && text_row_ >= text_rows_ - 1) {
1170 show_prompt = true;
1171 }
1172 }
1173 }
Tom Marshall820517b2019-01-14 14:28:55 -08001174 return -1;
Elliott Hughes95fc63e2015-04-10 19:12:01 -07001175}
1176
Tom Marshall820517b2019-01-14 14:28:55 -08001177int ScreenRecoveryUI::ShowFile(const char* filename) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001178 FILE* fp = fopen_path(filename, "re");
1179 if (fp == nullptr) {
1180 Print(" Unable to open %s: %s\n", filename, strerror(errno));
Tom Marshall820517b2019-01-14 14:28:55 -08001181 return -1;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001182 }
Elliott Hughesc0491632015-05-06 12:40:05 -07001183
Tom Marshallf780f1c2018-03-01 23:51:51 +01001184 Icon oldIcon = currentIcon;
1185 currentIcon = NONE;
1186
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001187 char** old_text = text_;
1188 size_t old_text_col = text_col_;
1189 size_t old_text_row = text_row_;
Elliott Hughesc0491632015-05-06 12:40:05 -07001190
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001191 // Swap in the alternate screen and clear it.
1192 text_ = file_viewer_text_;
1193 ClearText();
Elliott Hughesc0491632015-05-06 12:40:05 -07001194
Tom Marshall820517b2019-01-14 14:28:55 -08001195 int key = ShowFile(fp);
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001196 fclose(fp);
Elliott Hughesc0491632015-05-06 12:40:05 -07001197
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001198 text_ = old_text;
1199 text_col_ = old_text_col;
1200 text_row_ = old_text_row;
Tom Marshallf780f1c2018-03-01 23:51:51 +01001201 currentIcon = oldIcon;
Tom Marshall820517b2019-01-14 14:28:55 -08001202 return key;
Elliott Hughes8de52072015-04-08 20:06:50 -07001203}
1204
Tom Marshall820517b2019-01-14 14:28:55 -08001205void ScreenRecoveryUI::StartMenu(bool is_main, menu_type_t type, const char* const* headers,
1206 const MenuItemVector& items, int initial_selection) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001207 pthread_mutex_lock(&updateMutex);
Tom Marshall820517b2019-01-14 14:28:55 -08001208 menu_is_main_ = is_main;
1209 menu_type_ = type;
1210 menu_headers_ = headers;
1211 for (auto& item : items) {
1212 menu_items_.push_back(ScreenMenuItem(item));
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001213 }
Tom Marshall820517b2019-01-14 14:28:55 -08001214 show_menu = true;
1215 menu_sel = initial_selection;
Alessandro Astone54f55ed2019-03-29 00:16:49 +01001216 menu_show_start = 0;
Tom Marshall820517b2019-01-14 14:28:55 -08001217 draw_screen_locked();
Tom Marshall820517b2019-01-14 14:28:55 -08001218 if (menu_sel >= menu_show_start + menu_show_count) {
1219 menu_show_start = menu_sel - (menu_show_count - 1);
1220 }
1221 update_screen_locked();
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001222 pthread_mutex_unlock(&updateMutex);
Doug Zongker211aebc2011-10-28 15:13:10 -07001223}
1224
1225int ScreenRecoveryUI::SelectMenu(int sel) {
CEnnis910ccad612013-05-02 13:29:53 -04001226 int wrapped = 0;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001227 pthread_mutex_lock(&updateMutex);
1228 if (show_menu) {
Tom Marshall820517b2019-01-14 14:28:55 -08001229 int old_menu_sel = menu_sel;
1230
1231 // Handle wrapping and back item
CEnnis910ccad612013-05-02 13:29:53 -04001232 menu_sel = sel;
Tom Marshall820517b2019-01-14 14:28:55 -08001233 if (sel < 0 && (menu_is_main_ || sel < -1)) {
CEnnis910ccad612013-05-02 13:29:53 -04001234 menu_sel = (int)menu_items_.size() - 1;
1235 wrapped = -1;
Tom Marshall820517b2019-01-14 14:28:55 -08001236 }
1237 if (sel >= (int)menu_items_.size()) {
CEnnis910ccad612013-05-02 13:29:53 -04001238 menu_sel = (menu_is_main_ ? 0 : -1);
1239 wrapped = 1;
Tom Marshall820517b2019-01-14 14:28:55 -08001240 }
Elliott Hughesfc06f872015-03-23 13:45:31 -07001241
Tom Marshall820517b2019-01-14 14:28:55 -08001242 // Scroll
CEnnis910ccad612013-05-02 13:29:53 -04001243 if (menu_sel != -1 && menu_sel < menu_show_start) {
1244 menu_show_start = menu_sel;
Tom Marshall820517b2019-01-14 14:28:55 -08001245 }
CEnnis910ccad612013-05-02 13:29:53 -04001246 if (menu_sel >= menu_show_start + menu_show_count) {
1247 menu_show_start = menu_sel - (menu_show_count - 1);
1248 }
1249
1250 // Rainbows
1251 if (rainbow) {
1252 if (sel > old_menu_sel) {
1253 move_rainbow(-1);
1254 } else if (sel < old_menu_sel) {
1255 move_rainbow(1);
1256 }
1257 }
1258 if (wrapped != 0) {
1259 if (wrap_count / wrapped > 0) {
1260 wrap_count += wrapped;
1261 } else {
1262 wrap_count = wrapped;
1263 }
1264 if (wrap_count / wrapped >= 5) {
1265 wrap_count = 0;
1266 OMGRainbows();
1267 }
Tom Marshall820517b2019-01-14 14:28:55 -08001268 }
Elliott Hughesfc06f872015-03-23 13:45:31 -07001269
Tom Marshall820517b2019-01-14 14:28:55 -08001270 if (menu_sel != old_menu_sel) update_screen_locked();
1271 }
1272 pthread_mutex_unlock(&updateMutex);
CEnnis910ccad612013-05-02 13:29:53 -04001273 return menu_sel;
Tom Marshall820517b2019-01-14 14:28:55 -08001274}
1275
1276int ScreenRecoveryUI::SelectMenu(const Point& point) {
1277 int sel = Device::kNoAction;
1278 int h_unit = gr_fb_width() / 9;
1279 int v_unit = gr_fb_height() / 16;
1280 pthread_mutex_lock(&updateMutex);
1281 if (show_menu) {
1282 if (point.y() < menu_start_y_) {
1283 if (!menu_is_main_ && point.x() >= h_unit / 2 && point.x() < h_unit * 3 / 2 &&
1284 point.y() >= v_unit * 1 / 2 && point.y() < v_unit * 3 / 2) {
1285 sel = Device::kGoBack;
1286 }
1287 } else {
1288 int row = -1, col = -1;
1289 switch (menu_type_) {
1290 case MT_LIST:
Alessandro Astone92a8ed52019-03-28 22:26:18 +01001291 sel = (point.y() - menu_start_y_) / (menu_char_height_ * 3) + menu_show_start;
Tom Marshall820517b2019-01-14 14:28:55 -08001292 break;
1293 case MT_GRID:
1294 row = (point.y() - menu_start_y_) / (gr_fb_height() * 3 / 16);
1295 col = (point.x()) / (gr_fb_width() / 9);
1296 if ((col % 4) != 0) {
1297 sel = row * 2 + ((col - 1) / 4);
1298 }
1299 break;
1300 default:
1301 break;
1302 }
1303 if (sel >= (int)menu_items_.size()) {
1304 sel = Device::kNoAction;
1305 }
1306 }
1307 if (sel != -1 && sel != menu_sel) {
1308 menu_sel = sel;
1309 update_screen_locked();
1310 usleep(100 * 1000);
1311 }
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001312 }
1313 pthread_mutex_unlock(&updateMutex);
1314 return sel;
Doug Zongker211aebc2011-10-28 15:13:10 -07001315}
1316
Alessandro Astoned6eb74e2019-03-28 22:02:02 +01001317int ScreenRecoveryUI::ScrollMenu(int updown) {
1318 pthread_mutex_lock(&updateMutex);
1319 if ((updown > 0 && menu_show_start + menu_show_count < (int)menu_items_.size()) ||
1320 (updown < 0 && menu_show_start > 0)) {
1321 menu_show_start += updown;
1322
1323 /* We can receive a kInvokeItem event from a different source than touch,
1324 like from Power button. For this reason, selection should not get out of
1325 the screen. Constrain it to the first or last visible item of the list */
1326 if (menu_sel < menu_show_start) {
1327 menu_sel = menu_show_start;
1328 } else if (menu_sel >= menu_show_start + menu_show_count) {
1329 menu_sel = menu_show_start + menu_show_count - 1;
1330 }
1331
CEnnis910ccad612013-05-02 13:29:53 -04001332 // Rainbows
1333 int sign = (updown > 0) - (updown < 0);
1334 move_rainbow(sign);
1335
Alessandro Astoned6eb74e2019-03-28 22:02:02 +01001336 update_screen_locked();
1337 }
1338 pthread_mutex_unlock(&updateMutex);
1339 return menu_sel;
1340}
1341
Doug Zongker211aebc2011-10-28 15:13:10 -07001342void ScreenRecoveryUI::EndMenu() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001343 pthread_mutex_lock(&updateMutex);
1344 if (show_menu && text_rows_ > 0 && text_cols_ > 0) {
1345 show_menu = false;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001346 }
Tom Marshall820517b2019-01-14 14:28:55 -08001347 menu_type_ = MT_NONE;
1348 menu_headers_ = nullptr;
1349 menu_items_.clear();
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001350 pthread_mutex_unlock(&updateMutex);
Doug Zongker211aebc2011-10-28 15:13:10 -07001351}
1352
Elliott Hughes8de52072015-04-08 20:06:50 -07001353bool ScreenRecoveryUI::IsTextVisible() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001354 pthread_mutex_lock(&updateMutex);
1355 int visible = show_text;
1356 pthread_mutex_unlock(&updateMutex);
1357 return visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001358}
1359
Elliott Hughes8de52072015-04-08 20:06:50 -07001360bool ScreenRecoveryUI::WasTextEverVisible() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001361 pthread_mutex_lock(&updateMutex);
1362 int ever_visible = show_text_ever;
1363 pthread_mutex_unlock(&updateMutex);
1364 return ever_visible;
Doug Zongker211aebc2011-10-28 15:13:10 -07001365}
1366
Elliott Hughes8de52072015-04-08 20:06:50 -07001367void ScreenRecoveryUI::ShowText(bool visible) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001368 pthread_mutex_lock(&updateMutex);
1369 show_text = visible;
1370 if (show_text) show_text_ever = true;
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001371 pthread_mutex_unlock(&updateMutex);
Doug Zongker211aebc2011-10-28 15:13:10 -07001372}
Doug Zongkerc0441d12013-07-31 11:28:24 -07001373
Elliott Hughes8de52072015-04-08 20:06:50 -07001374void ScreenRecoveryUI::Redraw() {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001375 pthread_mutex_lock(&updateMutex);
1376 update_screen_locked();
1377 pthread_mutex_unlock(&updateMutex);
Doug Zongkerc0441d12013-07-31 11:28:24 -07001378}
Elliott Hughes642aaa72015-04-10 12:47:46 -07001379
1380void ScreenRecoveryUI::KeyLongPress(int) {
Tao Bao5d2e3bd2017-06-23 22:23:50 -07001381 // Redraw so that if we're in the menu, the highlight
1382 // will change color to indicate a successful long press.
1383 Redraw();
Elliott Hughes642aaa72015-04-10 12:47:46 -07001384}
Tao Baoefb49ad2017-01-31 23:03:10 -08001385
1386void ScreenRecoveryUI::SetLocale(const std::string& new_locale) {
1387 locale_ = new_locale;
1388 rtl_locale_ = false;
1389
1390 if (!new_locale.empty()) {
1391 size_t underscore = new_locale.find('_');
1392 // lang has the language prefix prior to '_', or full string if '_' doesn't exist.
1393 std::string lang = new_locale.substr(0, underscore);
1394
1395 // A bit cheesy: keep an explicit list of supported RTL languages.
1396 if (lang == "ar" || // Arabic
1397 lang == "fa" || // Persian (Farsi)
1398 lang == "he" || // Hebrew (new language code)
1399 lang == "iw" || // Hebrew (old language code)
1400 lang == "ur") { // Urdu
1401 rtl_locale_ = true;
1402 }
1403 }
1404}
CEnnis910ccad612013-05-02 13:29:53 -04001405
1406void ScreenRecoveryUI::OMGRainbows() {
1407 rainbow = rainbow ? false : true;
1408 set_rainbow_mode(rainbow);
1409}