blob: 3614e7a832383f489249448caf291dc3ed263534 [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
17#include <errno.h>
18#include <fcntl.h>
19#include <linux/input.h>
20#include <pthread.h>
21#include <stdarg.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <sys/stat.h>
26#include <sys/time.h>
27#include <sys/types.h>
28#include <time.h>
29#include <unistd.h>
30
Elliott Hughes95fc63e2015-04-10 19:12:01 -070031#include <vector>
32
Elliott Hughes4b166f02015-12-04 15:30:20 -080033#include <android-base/strings.h>
34#include <android-base/stringprintf.h>
Tao Baob6918c72015-05-19 17:02:16 -070035#include <cutils/properties.h>
36
Doug Zongker211aebc2011-10-28 15:13:10 -070037#include "common.h"
Doug Zongkerdaefc1d2011-10-31 09:34:15 -070038#include "device.h"
Doug Zongker32a0a472011-11-01 11:00:20 -070039#include "minui/minui.h"
40#include "screen_ui.h"
41#include "ui.h"
Doug Zongker211aebc2011-10-28 15:13:10 -070042
Prashant Malani7a491222016-03-11 10:00:55 -080043#define TEXT_INDENT 4
Doug Zongker211aebc2011-10-28 15:13:10 -070044
Doug Zongker211aebc2011-10-28 15:13:10 -070045// Return the current time as a double (including fractions of a second).
46static double now() {
47 struct timeval tv;
Elliott Hughesaa0d6af2015-04-08 12:42:50 -070048 gettimeofday(&tv, nullptr);
Doug Zongker211aebc2011-10-28 15:13:10 -070049 return tv.tv_sec + tv.tv_usec / 1000000.0;
50}
51
52ScreenRecoveryUI::ScreenRecoveryUI() :
Prashant Malanif7f9e502016-03-10 03:40:20 +000053 currentIcon(NONE),
Doug Zongker211aebc2011-10-28 15:13:10 -070054 installingFrame(0),
Elliott Hughesaa0d6af2015-04-08 12:42:50 -070055 locale(nullptr),
Doug Zongker211aebc2011-10-28 15:13:10 -070056 progressBarType(EMPTY),
57 progressScopeStart(0),
58 progressScopeSize(0),
59 progress(0),
60 pagesIdentical(false),
Elliott Hughesc0491632015-05-06 12:40:05 -070061 text_cols_(0),
62 text_rows_(0),
63 text_(nullptr),
64 text_col_(0),
65 text_row_(0),
66 text_top_(0),
Doug Zongker211aebc2011-10-28 15:13:10 -070067 show_text(false),
68 show_text_ever(false),
Elliott Hughesc0491632015-05-06 12:40:05 -070069 menu_(nullptr),
Doug Zongker211aebc2011-10-28 15:13:10 -070070 show_menu(false),
Doug Zongker211aebc2011-10-28 15:13:10 -070071 menu_items(0),
72 menu_sel(0),
Elliott Hughesc0491632015-05-06 12:40:05 -070073 file_viewer_text_(nullptr),
Tao Baob723f4f2015-12-11 15:18:51 -080074 animation_fps(-1),
Doug Zongkereac881c2014-03-07 09:21:25 -080075 installing_frames(-1),
Doug Zongkerc87bab12013-11-25 13:53:25 -080076 stage(-1),
Prashant Malani0ba21cf2016-03-10 14:51:25 -080077 max_stage(-1),
78 rtl_locale(false) {
yetta_wu5b468fc2013-06-25 15:03:11 +080079
Elliott Hughesaa0d6af2015-04-08 12:42:50 -070080 for (int i = 0; i < 5; i++) {
81 backgroundIcon[i] = nullptr;
82 }
83 pthread_mutex_init(&updateMutex, nullptr);
Doug Zongker211aebc2011-10-28 15:13:10 -070084}
85
Doug Zongker211aebc2011-10-28 15:13:10 -070086// Clear the screen and draw the currently selected background icon (if any).
87// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -070088void ScreenRecoveryUI::draw_background_locked(Icon icon) {
Doug Zongker211aebc2011-10-28 15:13:10 -070089 pagesIdentical = false;
Doug Zongker5b5f6c22014-06-03 10:50:13 -070090 gr_color(0, 0, 0, 255);
Doug Zongker39cf4172014-03-06 16:16:05 -080091 gr_clear();
Doug Zongker211aebc2011-10-28 15:13:10 -070092
93 if (icon) {
Elliott Hughes0a5cb0c2015-04-15 10:58:56 -070094 GRSurface* surface = backgroundIcon[icon];
Doug Zongkereac881c2014-03-07 09:21:25 -080095 if (icon == INSTALLING_UPDATE || icon == ERASING) {
96 surface = installation[installingFrame];
97 }
Elliott Hughes0a5cb0c2015-04-15 10:58:56 -070098 GRSurface* text_surface = backgroundText[icon];
Doug Zongker02ec6b82012-08-22 17:26:40 -070099
Doug Zongker211aebc2011-10-28 15:13:10 -0700100 int iconWidth = gr_get_width(surface);
101 int iconHeight = gr_get_height(surface);
Doug Zongker02ec6b82012-08-22 17:26:40 -0700102 int textWidth = gr_get_width(text_surface);
103 int textHeight = gr_get_height(text_surface);
Doug Zongkerc87bab12013-11-25 13:53:25 -0800104 int stageHeight = gr_get_height(stageMarkerEmpty);
105
106 int sh = (max_stage >= 0) ? stageHeight : 0;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700107
Doug Zongkereac881c2014-03-07 09:21:25 -0800108 iconX = (gr_fb_width() - iconWidth) / 2;
109 iconY = (gr_fb_height() - (iconHeight+textHeight+40+sh)) / 2;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700110
111 int textX = (gr_fb_width() - textWidth) / 2;
Doug Zongkerc87bab12013-11-25 13:53:25 -0800112 int textY = ((gr_fb_height() - (iconHeight+textHeight+40+sh)) / 2) + iconHeight + 40;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700113
Doug Zongker211aebc2011-10-28 15:13:10 -0700114 gr_blit(surface, 0, 0, iconWidth, iconHeight, iconX, iconY);
Doug Zongkerc87bab12013-11-25 13:53:25 -0800115 if (stageHeight > 0) {
116 int sw = gr_get_width(stageMarkerEmpty);
117 int x = (gr_fb_width() - max_stage * gr_get_width(stageMarkerEmpty)) / 2;
118 int y = iconY + iconHeight + 20;
119 for (int i = 0; i < max_stage; ++i) {
120 gr_blit((i < stage) ? stageMarkerFill : stageMarkerEmpty,
121 0, 0, sw, stageHeight, x, y);
122 x += sw;
123 }
124 }
125
Doug Zongker5b5f6c22014-06-03 10:50:13 -0700126 gr_color(255, 255, 255, 255);
Doug Zongker02ec6b82012-08-22 17:26:40 -0700127 gr_texticon(textX, textY, text_surface);
Doug Zongker211aebc2011-10-28 15:13:10 -0700128 }
129}
130
131// Draw the progress bar (if any) on the screen. Does not flip pages.
132// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700133void ScreenRecoveryUI::draw_progress_locked() {
Doug Zongker69f4b672012-04-26 14:37:53 -0700134 if (currentIcon == ERROR) return;
135
Doug Zongker02ec6b82012-08-22 17:26:40 -0700136 if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
Elliott Hughes0a5cb0c2015-04-15 10:58:56 -0700137 GRSurface* icon = installation[installingFrame];
Doug Zongkereac881c2014-03-07 09:21:25 -0800138 gr_blit(icon, 0, 0, gr_get_width(icon), gr_get_height(icon), iconX, iconY);
Doug Zongker211aebc2011-10-28 15:13:10 -0700139 }
140
141 if (progressBarType != EMPTY) {
Doug Zongker02ec6b82012-08-22 17:26:40 -0700142 int iconHeight = gr_get_height(backgroundIcon[INSTALLING_UPDATE]);
Doug Zongker211aebc2011-10-28 15:13:10 -0700143 int width = gr_get_width(progressBarEmpty);
144 int height = gr_get_height(progressBarEmpty);
145
146 int dx = (gr_fb_width() - width)/2;
147 int dy = (3*gr_fb_height() + iconHeight - 2*height)/4;
148
149 // Erase behind the progress bar (in case this was a progress-only update)
Doug Zongker5b5f6c22014-06-03 10:50:13 -0700150 gr_color(0, 0, 0, 255);
Doug Zongker211aebc2011-10-28 15:13:10 -0700151 gr_fill(dx, dy, width, height);
152
153 if (progressBarType == DETERMINATE) {
154 float p = progressScopeStart + progress * progressScopeSize;
155 int pos = (int) (p * width);
156
Doug Zongker5fa8c232012-09-18 12:37:02 -0700157 if (rtl_locale) {
158 // Fill the progress bar from right to left.
159 if (pos > 0) {
160 gr_blit(progressBarFill, width-pos, 0, pos, height, dx+width-pos, dy);
161 }
162 if (pos < width-1) {
163 gr_blit(progressBarEmpty, 0, 0, width-pos, height, dx, dy);
164 }
165 } else {
166 // Fill the progress bar from left to right.
167 if (pos > 0) {
168 gr_blit(progressBarFill, 0, 0, pos, height, dx, dy);
169 }
170 if (pos < width-1) {
171 gr_blit(progressBarEmpty, pos, 0, width-pos, height, dx+pos, dy);
172 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700173 }
174 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700175 }
176}
177
Doug Zongkerc0441d12013-07-31 11:28:24 -0700178void ScreenRecoveryUI::SetColor(UIElement e) {
179 switch (e) {
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700180 case INFO:
181 gr_color(249, 194, 0, 255);
182 break;
Doug Zongkerc0441d12013-07-31 11:28:24 -0700183 case HEADER:
Doug Zongker5b5f6c22014-06-03 10:50:13 -0700184 gr_color(247, 0, 6, 255);
Doug Zongkerc0441d12013-07-31 11:28:24 -0700185 break;
186 case MENU:
187 case MENU_SEL_BG:
Doug Zongker5b5f6c22014-06-03 10:50:13 -0700188 gr_color(0, 106, 157, 255);
Doug Zongkerc0441d12013-07-31 11:28:24 -0700189 break;
Elliott Hughes642aaa72015-04-10 12:47:46 -0700190 case MENU_SEL_BG_ACTIVE:
191 gr_color(0, 156, 100, 255);
192 break;
Doug Zongkerc0441d12013-07-31 11:28:24 -0700193 case MENU_SEL_FG:
194 gr_color(255, 255, 255, 255);
195 break;
196 case LOG:
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700197 gr_color(196, 196, 196, 255);
Doug Zongker5b5f6c22014-06-03 10:50:13 -0700198 break;
199 case TEXT_FILL:
200 gr_color(0, 0, 0, 160);
Doug Zongkerc0441d12013-07-31 11:28:24 -0700201 break;
202 default:
203 gr_color(255, 255, 255, 255);
204 break;
205 }
206}
Doug Zongker211aebc2011-10-28 15:13:10 -0700207
Elliott Hughes95fc63e2015-04-10 19:12:01 -0700208void ScreenRecoveryUI::DrawHorizontalRule(int* y) {
209 SetColor(MENU);
210 *y += 4;
211 gr_fill(0, *y, gr_fb_width(), *y + 2);
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700212 *y += 4;
Elliott Hughes95fc63e2015-04-10 19:12:01 -0700213}
214
Prashant Malani7a491222016-03-11 10:00:55 -0800215void ScreenRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) {
216 gr_text(x, *y, line, bold);
217 *y += char_height_ + 4;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700218}
219
Prashant Malani7a491222016-03-11 10:00:55 -0800220void ScreenRecoveryUI::DrawTextLines(int x, int* y, const char* const* lines) {
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700221 for (size_t i = 0; lines != nullptr && lines[i] != nullptr; ++i) {
Prashant Malani7a491222016-03-11 10:00:55 -0800222 DrawTextLine(x, y, lines[i], false);
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700223 }
224}
225
226static const char* REGULAR_HELP[] = {
227 "Use volume up/down and power.",
228 NULL
229};
230
231static const char* LONG_PRESS_HELP[] = {
232 "Any button cycles highlight.",
233 "Long-press activates.",
234 NULL
235};
236
Doug Zongker211aebc2011-10-28 15:13:10 -0700237// Redraw everything on the screen. Does not flip pages.
238// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700239void ScreenRecoveryUI::draw_screen_locked() {
Doug Zongker39cf4172014-03-06 16:16:05 -0800240 if (!show_text) {
241 draw_background_locked(currentIcon);
242 draw_progress_locked();
243 } else {
Doug Zongker5b5f6c22014-06-03 10:50:13 -0700244 gr_color(0, 0, 0, 255);
Doug Zongker39cf4172014-03-06 16:16:05 -0800245 gr_clear();
Doug Zongker211aebc2011-10-28 15:13:10 -0700246
Doug Zongker6fd59ac2013-03-06 15:01:11 -0800247 int y = 0;
Doug Zongker211aebc2011-10-28 15:13:10 -0700248 if (show_menu) {
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700249 char recovery_fingerprint[PROPERTY_VALUE_MAX];
250 property_get("ro.bootimage.build.fingerprint", recovery_fingerprint, "");
251
252 SetColor(INFO);
Prashant Malani7a491222016-03-11 10:00:55 -0800253 DrawTextLine(TEXT_INDENT, &y, "Android Recovery", true);
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700254 for (auto& chunk : android::base::Split(recovery_fingerprint, ":")) {
Prashant Malani7a491222016-03-11 10:00:55 -0800255 DrawTextLine(TEXT_INDENT, &y, chunk.c_str(), false);
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700256 }
Prashant Malani7a491222016-03-11 10:00:55 -0800257 DrawTextLines(TEXT_INDENT, &y,
258 HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP);
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700259
Doug Zongkerc0441d12013-07-31 11:28:24 -0700260 SetColor(HEADER);
Prashant Malani7a491222016-03-11 10:00:55 -0800261 DrawTextLines(TEXT_INDENT, &y, menu_headers_);
Doug Zongker211aebc2011-10-28 15:13:10 -0700262
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700263 SetColor(MENU);
264 DrawHorizontalRule(&y);
265 y += 4;
266 for (int i = 0; i < menu_items; ++i) {
267 if (i == menu_sel) {
268 // Draw the highlight bar.
Elliott Hughes642aaa72015-04-10 12:47:46 -0700269 SetColor(IsLongPress() ? MENU_SEL_BG_ACTIVE : MENU_SEL_BG);
Prashant Malani7a491222016-03-11 10:00:55 -0800270 gr_fill(0, y - 2, gr_fb_width(), y + char_height_ + 2);
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700271 // Bold white text for the selected item.
Doug Zongkerc0441d12013-07-31 11:28:24 -0700272 SetColor(MENU_SEL_FG);
Elliott Hughesc0491632015-05-06 12:40:05 -0700273 gr_text(4, y, menu_[i], true);
Doug Zongkerc0441d12013-07-31 11:28:24 -0700274 SetColor(MENU);
Doug Zongker211aebc2011-10-28 15:13:10 -0700275 } else {
Elliott Hughesc0491632015-05-06 12:40:05 -0700276 gr_text(4, y, menu_[i], false);
Doug Zongker211aebc2011-10-28 15:13:10 -0700277 }
Prashant Malani7a491222016-03-11 10:00:55 -0800278 y += char_height_ + 4;
Doug Zongker211aebc2011-10-28 15:13:10 -0700279 }
Elliott Hughes95fc63e2015-04-10 19:12:01 -0700280 DrawHorizontalRule(&y);
Doug Zongker211aebc2011-10-28 15:13:10 -0700281 }
282
Doug Zongker6fd59ac2013-03-06 15:01:11 -0800283 // display from the bottom up, until we hit the top of the
284 // screen, the bottom of the menu, or we've displayed the
285 // entire text buffer.
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700286 SetColor(LOG);
Elliott Hughesc0491632015-05-06 12:40:05 -0700287 int row = (text_top_ + text_rows_ - 1) % text_rows_;
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700288 size_t count = 0;
Prashant Malani7a491222016-03-11 10:00:55 -0800289 for (int ty = gr_fb_height() - char_height_;
Elliott Hughesc0491632015-05-06 12:40:05 -0700290 ty >= y && count < text_rows_;
Prashant Malani7a491222016-03-11 10:00:55 -0800291 ty -= char_height_, ++count) {
Elliott Hughesc0491632015-05-06 12:40:05 -0700292 gr_text(0, ty, text_[row], false);
Doug Zongker6fd59ac2013-03-06 15:01:11 -0800293 --row;
Elliott Hughesc0491632015-05-06 12:40:05 -0700294 if (row < 0) row = text_rows_ - 1;
Doug Zongker211aebc2011-10-28 15:13:10 -0700295 }
296 }
297}
298
299// Redraw everything on the screen and flip the screen (make it visible).
300// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700301void ScreenRecoveryUI::update_screen_locked() {
Doug Zongker211aebc2011-10-28 15:13:10 -0700302 draw_screen_locked();
303 gr_flip();
304}
305
306// Updates only the progress bar, if possible, otherwise redraws the screen.
307// Should only be called with updateMutex locked.
Elliott Hughes8de52072015-04-08 20:06:50 -0700308void ScreenRecoveryUI::update_progress_locked() {
Doug Zongker211aebc2011-10-28 15:13:10 -0700309 if (show_text || !pagesIdentical) {
310 draw_screen_locked(); // Must redraw the whole screen
311 pagesIdentical = true;
312 } else {
313 draw_progress_locked(); // Draw only the progress bar and overlays
314 }
315 gr_flip();
316}
317
318// Keeps the progress bar updated, even when the process is otherwise busy.
Elliott Hughes985022a2015-04-13 13:04:32 -0700319void* ScreenRecoveryUI::ProgressThreadStartRoutine(void* data) {
320 reinterpret_cast<ScreenRecoveryUI*>(data)->ProgressThreadLoop();
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700321 return nullptr;
Doug Zongker32a0a472011-11-01 11:00:20 -0700322}
323
Elliott Hughes985022a2015-04-13 13:04:32 -0700324void ScreenRecoveryUI::ProgressThreadLoop() {
Doug Zongker32a0a472011-11-01 11:00:20 -0700325 double interval = 1.0 / animation_fps;
Elliott Hughes985022a2015-04-13 13:04:32 -0700326 while (true) {
Doug Zongker211aebc2011-10-28 15:13:10 -0700327 double start = now();
Doug Zongker32a0a472011-11-01 11:00:20 -0700328 pthread_mutex_lock(&updateMutex);
Doug Zongker211aebc2011-10-28 15:13:10 -0700329
330 int redraw = 0;
331
332 // update the installation animation, if active
333 // skip this if we have a text overlay (too expensive to update)
Doug Zongker02ec6b82012-08-22 17:26:40 -0700334 if ((currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) &&
335 installing_frames > 0 && !show_text) {
Doug Zongker32a0a472011-11-01 11:00:20 -0700336 installingFrame = (installingFrame + 1) % installing_frames;
Doug Zongker211aebc2011-10-28 15:13:10 -0700337 redraw = 1;
338 }
339
Doug Zongker211aebc2011-10-28 15:13:10 -0700340 // move the progress bar forward on timed intervals, if configured
Doug Zongker32a0a472011-11-01 11:00:20 -0700341 int duration = progressScopeDuration;
342 if (progressBarType == DETERMINATE && duration > 0) {
343 double elapsed = now() - progressScopeTime;
Doug Zongker69f4b672012-04-26 14:37:53 -0700344 float p = 1.0 * elapsed / duration;
345 if (p > 1.0) p = 1.0;
346 if (p > progress) {
347 progress = p;
Doug Zongker211aebc2011-10-28 15:13:10 -0700348 redraw = 1;
349 }
350 }
351
Doug Zongker32a0a472011-11-01 11:00:20 -0700352 if (redraw) update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -0700353
Doug Zongker32a0a472011-11-01 11:00:20 -0700354 pthread_mutex_unlock(&updateMutex);
Doug Zongker211aebc2011-10-28 15:13:10 -0700355 double end = now();
356 // minimum of 20ms delay between frames
357 double delay = interval - (end-start);
358 if (delay < 0.02) delay = 0.02;
359 usleep((long)(delay * 1000000));
360 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700361}
362
Elliott Hughes0a5cb0c2015-04-15 10:58:56 -0700363void ScreenRecoveryUI::LoadBitmap(const char* filename, GRSurface** surface) {
Doug Zongkera418aa72014-03-17 12:10:02 -0700364 int result = res_create_display_surface(filename, surface);
Doug Zongker211aebc2011-10-28 15:13:10 -0700365 if (result < 0) {
366 LOGE("missing bitmap %s\n(Code %d)\n", filename, result);
367 }
368}
369
Tao Baob723f4f2015-12-11 15:18:51 -0800370void ScreenRecoveryUI::LoadBitmapArray(const char* filename, int* frames, int* fps,
371 GRSurface*** surface) {
372 int result = res_create_multi_display_surface(filename, frames, fps, surface);
Doug Zongkereac881c2014-03-07 09:21:25 -0800373 if (result < 0) {
374 LOGE("missing bitmap %s\n(Code %d)\n", filename, result);
375 }
376}
377
Elliott Hughes0a5cb0c2015-04-15 10:58:56 -0700378void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, GRSurface** surface) {
Doug Zongkera418aa72014-03-17 12:10:02 -0700379 int result = res_create_localized_alpha_surface(filename, locale, surface);
Doug Zongker02ec6b82012-08-22 17:26:40 -0700380 if (result < 0) {
381 LOGE("missing bitmap %s\n(Code %d)\n", filename, result);
382 }
383}
384
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700385static char** Alloc2d(size_t rows, size_t cols) {
386 char** result = new char*[rows];
387 for (size_t i = 0; i < rows; ++i) {
388 result[i] = new char[cols];
389 memset(result[i], 0, cols);
390 }
391 return result;
392}
393
Elliott Hughes8de52072015-04-08 20:06:50 -0700394void ScreenRecoveryUI::Init() {
Doug Zongker211aebc2011-10-28 15:13:10 -0700395 gr_init();
Doug Zongker211aebc2011-10-28 15:13:10 -0700396
Prashant Malani7a491222016-03-11 10:00:55 -0800397 gr_font_size(&char_width_, &char_height_);
398 text_rows_ = gr_fb_height() / char_height_;
399 text_cols_ = gr_fb_width() / char_width_;
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700400
Elliott Hughesc0491632015-05-06 12:40:05 -0700401 text_ = Alloc2d(text_rows_, text_cols_ + 1);
402 file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
403 menu_ = Alloc2d(text_rows_, text_cols_ + 1);
Doug Zongker55a36ac2013-03-04 15:49:02 -0800404
Elliott Hughesc0491632015-05-06 12:40:05 -0700405 text_col_ = text_row_ = 0;
406 text_top_ = 1;
Doug Zongker211aebc2011-10-28 15:13:10 -0700407
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700408 backgroundIcon[NONE] = nullptr;
Tao Baob723f4f2015-12-11 15:18:51 -0800409 LoadBitmapArray("icon_installing", &installing_frames, &animation_fps, &installation);
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700410 backgroundIcon[INSTALLING_UPDATE] = installing_frames ? installation[0] : nullptr;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700411 backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE];
Doug Zongker211aebc2011-10-28 15:13:10 -0700412 LoadBitmap("icon_error", &backgroundIcon[ERROR]);
Doug Zongker02ec6b82012-08-22 17:26:40 -0700413 backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR];
414
Doug Zongker211aebc2011-10-28 15:13:10 -0700415 LoadBitmap("progress_empty", &progressBarEmpty);
416 LoadBitmap("progress_fill", &progressBarFill);
Doug Zongkerc87bab12013-11-25 13:53:25 -0800417 LoadBitmap("stage_empty", &stageMarkerEmpty);
418 LoadBitmap("stage_fill", &stageMarkerFill);
Doug Zongker211aebc2011-10-28 15:13:10 -0700419
Doug Zongker02ec6b82012-08-22 17:26:40 -0700420 LoadLocalizedBitmap("installing_text", &backgroundText[INSTALLING_UPDATE]);
421 LoadLocalizedBitmap("erasing_text", &backgroundText[ERASING]);
422 LoadLocalizedBitmap("no_command_text", &backgroundText[NO_COMMAND]);
423 LoadLocalizedBitmap("error_text", &backgroundText[ERROR]);
424
Elliott Hughes985022a2015-04-13 13:04:32 -0700425 pthread_create(&progress_thread_, nullptr, ProgressThreadStartRoutine, this);
Doug Zongker32a0a472011-11-01 11:00:20 -0700426
427 RecoveryUI::Init();
Doug Zongker211aebc2011-10-28 15:13:10 -0700428}
429
Doug Zongkera418aa72014-03-17 12:10:02 -0700430void ScreenRecoveryUI::SetLocale(const char* new_locale) {
431 if (new_locale) {
432 this->locale = new_locale;
Doug Zongker5fa8c232012-09-18 12:37:02 -0700433 char* lang = strdup(locale);
434 for (char* p = lang; *p; ++p) {
435 if (*p == '_') {
436 *p = '\0';
437 break;
438 }
439 }
440
441 // A bit cheesy: keep an explicit list of supported languages
442 // that are RTL.
443 if (strcmp(lang, "ar") == 0 || // Arabic
444 strcmp(lang, "fa") == 0 || // Persian (Farsi)
445 strcmp(lang, "he") == 0 || // Hebrew (new language code)
Doug Zongkerb66cb692012-09-18 14:52:18 -0700446 strcmp(lang, "iw") == 0 || // Hebrew (old language code)
447 strcmp(lang, "ur") == 0) { // Urdu
Doug Zongker5fa8c232012-09-18 12:37:02 -0700448 rtl_locale = true;
449 }
450 free(lang);
Doug Zongkera418aa72014-03-17 12:10:02 -0700451 } else {
Elliott Hughesaa0d6af2015-04-08 12:42:50 -0700452 new_locale = nullptr;
Doug Zongker5fa8c232012-09-18 12:37:02 -0700453 }
454}
455
Elliott Hughes8de52072015-04-08 20:06:50 -0700456void ScreenRecoveryUI::SetBackground(Icon icon) {
Doug Zongker211aebc2011-10-28 15:13:10 -0700457 pthread_mutex_lock(&updateMutex);
Doug Zongker02ec6b82012-08-22 17:26:40 -0700458
Doug Zongker52eeea4f2012-09-04 14:28:25 -0700459 currentIcon = icon;
460 update_screen_locked();
461
Doug Zongker211aebc2011-10-28 15:13:10 -0700462 pthread_mutex_unlock(&updateMutex);
463}
464
Elliott Hughes8de52072015-04-08 20:06:50 -0700465void ScreenRecoveryUI::SetProgressType(ProgressType type) {
Doug Zongker211aebc2011-10-28 15:13:10 -0700466 pthread_mutex_lock(&updateMutex);
467 if (progressBarType != type) {
468 progressBarType = type;
Doug Zongker211aebc2011-10-28 15:13:10 -0700469 }
Doug Zongker69f4b672012-04-26 14:37:53 -0700470 progressScopeStart = 0;
Doug Zongker239ac6a2013-08-20 16:03:25 -0700471 progressScopeSize = 0;
Doug Zongker69f4b672012-04-26 14:37:53 -0700472 progress = 0;
Doug Zongker239ac6a2013-08-20 16:03:25 -0700473 update_progress_locked();
Doug Zongker211aebc2011-10-28 15:13:10 -0700474 pthread_mutex_unlock(&updateMutex);
475}
476
Elliott Hughes8de52072015-04-08 20:06:50 -0700477void ScreenRecoveryUI::ShowProgress(float portion, float seconds) {
Doug Zongker211aebc2011-10-28 15:13:10 -0700478 pthread_mutex_lock(&updateMutex);
479 progressBarType = DETERMINATE;
480 progressScopeStart += progressScopeSize;
481 progressScopeSize = portion;
482 progressScopeTime = now();
483 progressScopeDuration = seconds;
484 progress = 0;
485 update_progress_locked();
486 pthread_mutex_unlock(&updateMutex);
487}
488
Elliott Hughes8de52072015-04-08 20:06:50 -0700489void ScreenRecoveryUI::SetProgress(float fraction) {
Doug Zongker211aebc2011-10-28 15:13:10 -0700490 pthread_mutex_lock(&updateMutex);
491 if (fraction < 0.0) fraction = 0.0;
492 if (fraction > 1.0) fraction = 1.0;
493 if (progressBarType == DETERMINATE && fraction > progress) {
494 // Skip updates that aren't visibly different.
Doug Zongkereac881c2014-03-07 09:21:25 -0800495 int width = gr_get_width(progressBarEmpty);
Doug Zongker211aebc2011-10-28 15:13:10 -0700496 float scale = width * progressScopeSize;
497 if ((int) (progress * scale) != (int) (fraction * scale)) {
498 progress = fraction;
499 update_progress_locked();
500 }
501 }
502 pthread_mutex_unlock(&updateMutex);
503}
504
Doug Zongkerc87bab12013-11-25 13:53:25 -0800505void ScreenRecoveryUI::SetStage(int current, int max) {
506 pthread_mutex_lock(&updateMutex);
507 stage = current;
508 max_stage = max;
509 pthread_mutex_unlock(&updateMutex);
510}
511
Tao Baob6918c72015-05-19 17:02:16 -0700512void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) {
513 std::string str;
514 android::base::StringAppendV(&str, fmt, ap);
Doug Zongker211aebc2011-10-28 15:13:10 -0700515
Tao Baob6918c72015-05-19 17:02:16 -0700516 if (copy_to_stdout) {
517 fputs(str.c_str(), stdout);
518 }
Doug Zongker211aebc2011-10-28 15:13:10 -0700519
Doug Zongker211aebc2011-10-28 15:13:10 -0700520 pthread_mutex_lock(&updateMutex);
Elliott Hughesc0491632015-05-06 12:40:05 -0700521 if (text_rows_ > 0 && text_cols_ > 0) {
Tao Baob6918c72015-05-19 17:02:16 -0700522 for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
Elliott Hughesc0491632015-05-06 12:40:05 -0700523 if (*ptr == '\n' || text_col_ >= text_cols_) {
524 text_[text_row_][text_col_] = '\0';
525 text_col_ = 0;
526 text_row_ = (text_row_ + 1) % text_rows_;
527 if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_;
Doug Zongker211aebc2011-10-28 15:13:10 -0700528 }
Elliott Hughesc0491632015-05-06 12:40:05 -0700529 if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
Doug Zongker211aebc2011-10-28 15:13:10 -0700530 }
Elliott Hughesc0491632015-05-06 12:40:05 -0700531 text_[text_row_][text_col_] = '\0';
Doug Zongker211aebc2011-10-28 15:13:10 -0700532 update_screen_locked();
533 }
534 pthread_mutex_unlock(&updateMutex);
535}
536
Tao Baob6918c72015-05-19 17:02:16 -0700537void ScreenRecoveryUI::Print(const char* fmt, ...) {
538 va_list ap;
539 va_start(ap, fmt);
540 PrintV(fmt, true, ap);
541 va_end(ap);
542}
543
544void ScreenRecoveryUI::PrintOnScreenOnly(const char *fmt, ...) {
545 va_list ap;
546 va_start(ap, fmt);
547 PrintV(fmt, false, ap);
548 va_end(ap);
549}
550
Elliott Hughes95fc63e2015-04-10 19:12:01 -0700551void ScreenRecoveryUI::PutChar(char ch) {
Elliott Hughes8de52072015-04-08 20:06:50 -0700552 pthread_mutex_lock(&updateMutex);
Elliott Hughesc0491632015-05-06 12:40:05 -0700553 if (ch != '\n') text_[text_row_][text_col_++] = ch;
554 if (ch == '\n' || text_col_ >= text_cols_) {
555 text_col_ = 0;
556 ++text_row_;
557
558 if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_;
Elliott Hughes8de52072015-04-08 20:06:50 -0700559 }
560 pthread_mutex_unlock(&updateMutex);
561}
562
Elliott Hughes95fc63e2015-04-10 19:12:01 -0700563void ScreenRecoveryUI::ClearText() {
564 pthread_mutex_lock(&updateMutex);
Elliott Hughesc0491632015-05-06 12:40:05 -0700565 text_col_ = 0;
566 text_row_ = 0;
567 text_top_ = 1;
568 for (size_t i = 0; i < text_rows_; ++i) {
569 memset(text_[i], 0, text_cols_ + 1);
Elliott Hughes95fc63e2015-04-10 19:12:01 -0700570 }
571 pthread_mutex_unlock(&updateMutex);
572}
573
574void ScreenRecoveryUI::ShowFile(FILE* fp) {
575 std::vector<long> offsets;
576 offsets.push_back(ftell(fp));
577 ClearText();
578
579 struct stat sb;
580 fstat(fileno(fp), &sb);
581
582 bool show_prompt = false;
583 while (true) {
584 if (show_prompt) {
Tao Bao9a7fd802015-09-10 13:33:58 -0700585 PrintOnScreenOnly("--(%d%% of %d bytes)--",
Elliott Hughes95fc63e2015-04-10 19:12:01 -0700586 static_cast<int>(100 * (double(ftell(fp)) / double(sb.st_size))),
587 static_cast<int>(sb.st_size));
588 Redraw();
589 while (show_prompt) {
590 show_prompt = false;
591 int key = WaitKey();
Elliott Hughes300ed082015-04-13 10:47:59 -0700592 if (key == KEY_POWER || key == KEY_ENTER) {
Elliott Hughes95fc63e2015-04-10 19:12:01 -0700593 return;
594 } else if (key == KEY_UP || key == KEY_VOLUMEUP) {
595 if (offsets.size() <= 1) {
596 show_prompt = true;
597 } else {
598 offsets.pop_back();
599 fseek(fp, offsets.back(), SEEK_SET);
600 }
601 } else {
602 if (feof(fp)) {
603 return;
604 }
605 offsets.push_back(ftell(fp));
606 }
607 }
608 ClearText();
609 }
610
611 int ch = getc(fp);
612 if (ch == EOF) {
Elliott Hughesc0491632015-05-06 12:40:05 -0700613 while (text_row_ < text_rows_ - 1) PutChar('\n');
Elliott Hughes95fc63e2015-04-10 19:12:01 -0700614 show_prompt = true;
615 } else {
616 PutChar(ch);
Elliott Hughesc0491632015-05-06 12:40:05 -0700617 if (text_col_ == 0 && text_row_ >= text_rows_ - 1) {
Elliott Hughes95fc63e2015-04-10 19:12:01 -0700618 show_prompt = true;
619 }
620 }
621 }
622}
623
Elliott Hughes8de52072015-04-08 20:06:50 -0700624void ScreenRecoveryUI::ShowFile(const char* filename) {
625 FILE* fp = fopen_path(filename, "re");
626 if (fp == nullptr) {
627 Print(" Unable to open %s: %s\n", filename, strerror(errno));
628 return;
629 }
Elliott Hughesc0491632015-05-06 12:40:05 -0700630
631 char** old_text = text_;
632 size_t old_text_col = text_col_;
633 size_t old_text_row = text_row_;
634 size_t old_text_top = text_top_;
635
636 // Swap in the alternate screen and clear it.
637 text_ = file_viewer_text_;
638 ClearText();
639
Elliott Hughes95fc63e2015-04-10 19:12:01 -0700640 ShowFile(fp);
Elliott Hughes8de52072015-04-08 20:06:50 -0700641 fclose(fp);
Elliott Hughesc0491632015-05-06 12:40:05 -0700642
643 text_ = old_text;
644 text_col_ = old_text_col;
645 text_row_ = old_text_row;
646 text_top_ = old_text_top;
Elliott Hughes8de52072015-04-08 20:06:50 -0700647}
648
Doug Zongker211aebc2011-10-28 15:13:10 -0700649void ScreenRecoveryUI::StartMenu(const char* const * headers, const char* const * items,
650 int initial_selection) {
Doug Zongker211aebc2011-10-28 15:13:10 -0700651 pthread_mutex_lock(&updateMutex);
Elliott Hughesc0491632015-05-06 12:40:05 -0700652 if (text_rows_ > 0 && text_cols_ > 0) {
653 menu_headers_ = headers;
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700654 size_t i = 0;
Elliott Hughesc0491632015-05-06 12:40:05 -0700655 for (; i < text_rows_ && items[i] != nullptr; ++i) {
656 strncpy(menu_[i], items[i], text_cols_ - 1);
657 menu_[i][text_cols_ - 1] = '\0';
Doug Zongker211aebc2011-10-28 15:13:10 -0700658 }
Elliott Hughes8fd86d72015-04-13 14:36:02 -0700659 menu_items = i;
Elliott Hughes95fc63e2015-04-10 19:12:01 -0700660 show_menu = true;
Doug Zongker211aebc2011-10-28 15:13:10 -0700661 menu_sel = initial_selection;
662 update_screen_locked();
663 }
664 pthread_mutex_unlock(&updateMutex);
665}
666
667int ScreenRecoveryUI::SelectMenu(int sel) {
Doug Zongker211aebc2011-10-28 15:13:10 -0700668 pthread_mutex_lock(&updateMutex);
Elliott Hughes95fc63e2015-04-10 19:12:01 -0700669 if (show_menu) {
Elliott Hughes01a4d082015-03-24 15:21:48 -0700670 int old_sel = menu_sel;
Doug Zongker211aebc2011-10-28 15:13:10 -0700671 menu_sel = sel;
Elliott Hughesfc06f872015-03-23 13:45:31 -0700672
673 // Wrap at top and bottom.
674 if (menu_sel < 0) menu_sel = menu_items - 1;
675 if (menu_sel >= menu_items) menu_sel = 0;
676
Doug Zongker211aebc2011-10-28 15:13:10 -0700677 sel = menu_sel;
678 if (menu_sel != old_sel) update_screen_locked();
679 }
680 pthread_mutex_unlock(&updateMutex);
681 return sel;
682}
683
684void ScreenRecoveryUI::EndMenu() {
Doug Zongker211aebc2011-10-28 15:13:10 -0700685 pthread_mutex_lock(&updateMutex);
Elliott Hughesc0491632015-05-06 12:40:05 -0700686 if (show_menu && text_rows_ > 0 && text_cols_ > 0) {
Elliott Hughes95fc63e2015-04-10 19:12:01 -0700687 show_menu = false;
Doug Zongker211aebc2011-10-28 15:13:10 -0700688 update_screen_locked();
689 }
690 pthread_mutex_unlock(&updateMutex);
691}
692
Elliott Hughes8de52072015-04-08 20:06:50 -0700693bool ScreenRecoveryUI::IsTextVisible() {
Doug Zongker211aebc2011-10-28 15:13:10 -0700694 pthread_mutex_lock(&updateMutex);
695 int visible = show_text;
696 pthread_mutex_unlock(&updateMutex);
697 return visible;
698}
699
Elliott Hughes8de52072015-04-08 20:06:50 -0700700bool ScreenRecoveryUI::WasTextEverVisible() {
Doug Zongker211aebc2011-10-28 15:13:10 -0700701 pthread_mutex_lock(&updateMutex);
702 int ever_visible = show_text_ever;
703 pthread_mutex_unlock(&updateMutex);
704 return ever_visible;
705}
706
Elliott Hughes8de52072015-04-08 20:06:50 -0700707void ScreenRecoveryUI::ShowText(bool visible) {
Doug Zongker211aebc2011-10-28 15:13:10 -0700708 pthread_mutex_lock(&updateMutex);
709 show_text = visible;
Elliott Hughes8de52072015-04-08 20:06:50 -0700710 if (show_text) show_text_ever = true;
Doug Zongker211aebc2011-10-28 15:13:10 -0700711 update_screen_locked();
712 pthread_mutex_unlock(&updateMutex);
713}
Doug Zongkerc0441d12013-07-31 11:28:24 -0700714
Elliott Hughes8de52072015-04-08 20:06:50 -0700715void ScreenRecoveryUI::Redraw() {
Doug Zongkerc0441d12013-07-31 11:28:24 -0700716 pthread_mutex_lock(&updateMutex);
717 update_screen_locked();
718 pthread_mutex_unlock(&updateMutex);
719}
Elliott Hughes642aaa72015-04-10 12:47:46 -0700720
721void ScreenRecoveryUI::KeyLongPress(int) {
722 // Redraw so that if we're in the menu, the highlight
723 // will change color to indicate a successful long press.
724 Redraw();
725}