• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 "wear_ui.h"
18 
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <stdarg.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/stat.h>
25 #include <sys/time.h>
26 #include <sys/types.h>
27 #include <time.h>
28 #include <unistd.h>
29 
30 #include <string>
31 #include <vector>
32 
33 #include <android-base/properties.h>
34 #include <android-base/strings.h>
35 #include <android-base/stringprintf.h>
36 #include <minui/minui.h>
37 
38 #include "common.h"
39 #include "device.h"
40 
41 // There's only (at most) one of these objects, and global callbacks
42 // (for pthread_create, and the input event system) need to find it,
43 // so use a global variable.
44 static WearRecoveryUI* self = NULL;
45 
46 // Return the current time as a double (including fractions of a second).
now()47 static double now() {
48   struct timeval tv;
49   gettimeofday(&tv, NULL);
50   return tv.tv_sec + tv.tv_usec / 1000000.0;
51 }
52 
WearRecoveryUI()53 WearRecoveryUI::WearRecoveryUI()
54     : kProgressBarBaseline(RECOVERY_UI_PROGRESS_BAR_BASELINE),
55       kMenuUnusableRows(RECOVERY_UI_MENU_UNUSABLE_ROWS) {
56   // TODO: kMenuUnusableRows should be computed based on the lines in draw_screen_locked().
57 
58   // TODO: The following three variables are likely not needed. The first two are detected
59   // automatically in ScreenRecoveryUI::LoadAnimation(), based on the actual files seen on device.
60   intro_frames = 22;
61   loop_frames = 60;
62 
63   touch_screen_allowed_ = true;
64 
65   for (size_t i = 0; i < 5; i++) backgroundIcon[i] = NULL;
66 
67   self = this;
68 }
69 
GetProgressBaseline() const70 int WearRecoveryUI::GetProgressBaseline() const {
71   return kProgressBarBaseline;
72 }
73 
74 // Draw background frame on the screen.  Does not flip pages.
75 // Should only be called with updateMutex locked.
76 // TODO merge drawing routines with screen_ui
draw_background_locked()77 void WearRecoveryUI::draw_background_locked() {
78   pagesIdentical = false;
79   gr_color(0, 0, 0, 255);
80   gr_fill(0, 0, gr_fb_width(), gr_fb_height());
81 
82   if (currentIcon != NONE) {
83     GRSurface* surface;
84     if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
85       if (!intro_done) {
86         surface = introFrames[current_frame];
87       } else {
88         surface = loopFrames[current_frame];
89       }
90     } else {
91       surface = backgroundIcon[currentIcon];
92     }
93 
94     int width = gr_get_width(surface);
95     int height = gr_get_height(surface);
96 
97     int x = (gr_fb_width() - width) / 2;
98     int y = (gr_fb_height() - height) / 2;
99 
100     gr_blit(surface, 0, 0, width, height, x, y);
101   }
102 }
103 
104 static const char* SWIPE_HELP[] = {
105   "Swipe up/down to move.",
106   "Swipe left/right to select.",
107   "",
108   NULL
109 };
110 
111 // TODO merge drawing routines with screen_ui
draw_screen_locked()112 void WearRecoveryUI::draw_screen_locked() {
113   char cur_selection_str[50];
114 
115   draw_background_locked();
116   if (!show_text) {
117     draw_foreground_locked();
118   } else {
119     SetColor(TEXT_FILL);
120     gr_fill(0, 0, gr_fb_width(), gr_fb_height());
121 
122     int y = kMarginHeight;
123     int x = kMarginWidth;
124     if (show_menu) {
125       std::string recovery_fingerprint =
126           android::base::GetProperty("ro.bootimage.build.fingerprint", "");
127       SetColor(HEADER);
128       y += DrawTextLine(x + 4, y, "Android Recovery", true);
129       for (auto& chunk : android::base::Split(recovery_fingerprint, ":")) {
130         y += DrawTextLine(x + 4, y, chunk.c_str(), false);
131       }
132 
133       // This is actually the help strings.
134       y += DrawTextLines(x + 4, y, SWIPE_HELP);
135       SetColor(HEADER);
136       y += DrawTextLines(x + 4, y, menu_headers_);
137 
138       // Show the current menu item number in relation to total number if
139       // items don't fit on the screen.
140       if (menu_items > menu_end - menu_start) {
141         sprintf(cur_selection_str, "Current item: %d/%d", menu_sel + 1, menu_items);
142         gr_text(gr_sys_font(), x + 4, y, cur_selection_str, 1);
143         y += char_height_ + 4;
144       }
145 
146       // Menu begins here
147       SetColor(MENU);
148 
149       for (int i = menu_start; i < menu_end; ++i) {
150         if (i == menu_sel) {
151           // draw the highlight bar
152           SetColor(MENU_SEL_BG);
153           gr_fill(x, y - 2, gr_fb_width() - x, y + char_height_ + 2);
154           // white text of selected item
155           SetColor(MENU_SEL_FG);
156           if (menu_[i][0]) {
157             gr_text(gr_sys_font(), x + 4, y, menu_[i].c_str(), 1);
158           }
159           SetColor(MENU);
160         } else if (menu_[i][0]) {
161           gr_text(gr_sys_font(), x + 4, y, menu_[i].c_str(), 0);
162         }
163         y += char_height_ + 4;
164       }
165       SetColor(MENU);
166       y += 4;
167       gr_fill(0, y, gr_fb_width(), y + 2);
168       y += 4;
169     }
170 
171     SetColor(LOG);
172 
173     // display from the bottom up, until we hit the top of the
174     // screen, the bottom of the menu, or we've displayed the
175     // entire text buffer.
176     int ty;
177     int row = (text_top_ + text_rows_ - 1) % text_rows_;
178     size_t count = 0;
179     for (int ty = gr_fb_height() - char_height_ - kMarginHeight; ty > y + 2 && count < text_rows_;
180          ty -= char_height_, ++count) {
181       gr_text(gr_sys_font(), x + 4, ty, text_[row], 0);
182       --row;
183       if (row < 0) row = text_rows_ - 1;
184     }
185   }
186 }
187 
188 // TODO merge drawing routines with screen_ui
update_progress_locked()189 void WearRecoveryUI::update_progress_locked() {
190   draw_screen_locked();
191   gr_flip();
192 }
193 
InitTextParams()194 bool WearRecoveryUI::InitTextParams() {
195   if (!ScreenRecoveryUI::InitTextParams()) {
196     return false;
197   }
198 
199   text_cols_ = (gr_fb_width() - (kMarginWidth * 2)) / char_width_;
200 
201   if (text_rows_ > kMaxRows) text_rows_ = kMaxRows;
202   if (text_cols_ > kMaxCols) text_cols_ = kMaxCols;
203 
204   visible_text_rows = (gr_fb_height() - (kMarginHeight * 2)) / char_height_;
205   return true;
206 }
207 
Init(const std::string & locale)208 bool WearRecoveryUI::Init(const std::string& locale) {
209   if (!ScreenRecoveryUI::Init(locale)) {
210     return false;
211   }
212 
213   LoadBitmap("icon_error", &backgroundIcon[ERROR]);
214   backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR];
215 
216   // This leaves backgroundIcon[INSTALLING_UPDATE] and backgroundIcon[ERASING]
217   // as NULL which is fine since draw_background_locked() doesn't use them.
218 
219   return true;
220 }
221 
SetStage(int current,int max)222 void WearRecoveryUI::SetStage(int current, int max) {
223 }
224 
Print(const char * fmt,...)225 void WearRecoveryUI::Print(const char* fmt, ...) {
226   char buf[256];
227   va_list ap;
228   va_start(ap, fmt);
229   vsnprintf(buf, 256, fmt, ap);
230   va_end(ap);
231 
232   fputs(buf, stdout);
233 
234   // This can get called before ui_init(), so be careful.
235   pthread_mutex_lock(&updateMutex);
236   if (text_rows_ > 0 && text_cols_ > 0) {
237     char* ptr;
238     for (ptr = buf; *ptr != '\0'; ++ptr) {
239       if (*ptr == '\n' || text_col_ >= text_cols_) {
240         text_[text_row_][text_col_] = '\0';
241         text_col_ = 0;
242         text_row_ = (text_row_ + 1) % text_rows_;
243         if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_;
244       }
245       if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
246     }
247     text_[text_row_][text_col_] = '\0';
248     update_screen_locked();
249   }
250   pthread_mutex_unlock(&updateMutex);
251 }
252 
StartMenu(const char * const * headers,const char * const * items,int initial_selection)253 void WearRecoveryUI::StartMenu(const char* const* headers, const char* const* items,
254                                int initial_selection) {
255   pthread_mutex_lock(&updateMutex);
256   if (text_rows_ > 0 && text_cols_ > 0) {
257     menu_headers_ = headers;
258     menu_.clear();
259     // "i < text_rows_" is removed from the loop termination condition,
260     // which is different from the one in ScreenRecoveryUI::StartMenu().
261     // Because WearRecoveryUI supports scrollable menu, it's fine to have
262     // more entries than text_rows_. The menu may be truncated otherwise.
263     // Bug: 23752519
264     for (size_t i = 0; items[i] != nullptr; i++) {
265       menu_.emplace_back(std::string(items[i], strnlen(items[i], text_cols_ - 1)));
266     }
267     menu_items = static_cast<int>(menu_.size());
268     show_menu = true;
269     menu_sel = initial_selection;
270     menu_start = 0;
271     menu_end = visible_text_rows - 1 - kMenuUnusableRows;
272     if (menu_items <= menu_end) menu_end = menu_items;
273     update_screen_locked();
274   }
275   pthread_mutex_unlock(&updateMutex);
276 }
277 
SelectMenu(int sel)278 int WearRecoveryUI::SelectMenu(int sel) {
279   int old_sel;
280   pthread_mutex_lock(&updateMutex);
281   if (show_menu) {
282     old_sel = menu_sel;
283     menu_sel = sel;
284     if (menu_sel < 0) menu_sel = 0;
285     if (menu_sel >= menu_items) menu_sel = menu_items - 1;
286     if (menu_sel < menu_start) {
287       menu_start--;
288       menu_end--;
289     } else if (menu_sel >= menu_end && menu_sel < menu_items) {
290       menu_end++;
291       menu_start++;
292     }
293     sel = menu_sel;
294     if (menu_sel != old_sel) update_screen_locked();
295   }
296   pthread_mutex_unlock(&updateMutex);
297   return sel;
298 }
299 
ShowFile(FILE * fp)300 void WearRecoveryUI::ShowFile(FILE* fp) {
301   std::vector<off_t> offsets;
302   offsets.push_back(ftello(fp));
303   ClearText();
304 
305   struct stat sb;
306   fstat(fileno(fp), &sb);
307 
308   bool show_prompt = false;
309   while (true) {
310     if (show_prompt) {
311       Print("--(%d%% of %d bytes)--",
312             static_cast<int>(100 * (double(ftello(fp)) / double(sb.st_size))),
313             static_cast<int>(sb.st_size));
314       Redraw();
315       while (show_prompt) {
316         show_prompt = false;
317         int key = WaitKey();
318         if (key == KEY_POWER || key == KEY_ENTER) {
319           return;
320         } else if (key == KEY_UP || key == KEY_VOLUMEUP) {
321           if (offsets.size() <= 1) {
322             show_prompt = true;
323           } else {
324             offsets.pop_back();
325             fseek(fp, offsets.back(), SEEK_SET);
326           }
327         } else {
328           if (feof(fp)) {
329             return;
330           }
331           offsets.push_back(ftello(fp));
332         }
333       }
334       ClearText();
335     }
336 
337     int ch = getc(fp);
338     if (ch == EOF) {
339       text_row_ = text_top_ = text_rows_ - 2;
340       show_prompt = true;
341     } else {
342       PutChar(ch);
343       if (text_col_ == 0 && text_row_ >= text_rows_ - 2) {
344         text_top_ = text_row_;
345         show_prompt = true;
346       }
347     }
348   }
349 }
350 
PutChar(char ch)351 void WearRecoveryUI::PutChar(char ch) {
352   pthread_mutex_lock(&updateMutex);
353   if (ch != '\n') text_[text_row_][text_col_++] = ch;
354   if (ch == '\n' || text_col_ >= text_cols_) {
355     text_col_ = 0;
356     ++text_row_;
357   }
358   pthread_mutex_unlock(&updateMutex);
359 }
360 
ShowFile(const char * filename)361 void WearRecoveryUI::ShowFile(const char* filename) {
362   FILE* fp = fopen_path(filename, "re");
363   if (fp == nullptr) {
364     Print("  Unable to open %s: %s\n", filename, strerror(errno));
365     return;
366   }
367   ShowFile(fp);
368   fclose(fp);
369 }
370 
PrintOnScreenOnly(const char * fmt,...)371 void WearRecoveryUI::PrintOnScreenOnly(const char *fmt, ...) {
372   va_list ap;
373   va_start(ap, fmt);
374   PrintV(fmt, false, ap);
375   va_end(ap);
376 }
377 
PrintV(const char * fmt,bool copy_to_stdout,va_list ap)378 void WearRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) {
379   std::string str;
380   android::base::StringAppendV(&str, fmt, ap);
381 
382   if (copy_to_stdout) {
383     fputs(str.c_str(), stdout);
384   }
385 
386   pthread_mutex_lock(&updateMutex);
387   if (text_rows_ > 0 && text_cols_ > 0) {
388     for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
389       if (*ptr == '\n' || text_col_ >= text_cols_) {
390         text_[text_row_][text_col_] = '\0';
391         text_col_ = 0;
392         text_row_ = (text_row_ + 1) % text_rows_;
393         if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_;
394       }
395       if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
396     }
397     text_[text_row_][text_col_] = '\0';
398     update_screen_locked();
399   }
400   pthread_mutex_unlock(&updateMutex);
401 }
402