• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
31 #include "common.h"
32 #include "device.h"
33 #include "minui/minui.h"
34 #include "screen_ui.h"
35 #include "ui.h"
36 
37 #define CHAR_WIDTH 10
38 #define CHAR_HEIGHT 18
39 
40 // There's only (at most) one of these objects, and global callbacks
41 // (for pthread_create, and the input event system) need to find it,
42 // so use a global variable.
43 static ScreenRecoveryUI* self = NULL;
44 
45 // Return the current time as a double (including fractions of a second).
now()46 static double now() {
47     struct timeval tv;
48     gettimeofday(&tv, NULL);
49     return tv.tv_sec + tv.tv_usec / 1000000.0;
50 }
51 
ScreenRecoveryUI()52 ScreenRecoveryUI::ScreenRecoveryUI() :
53     currentIcon(NONE),
54     installingFrame(0),
55     progressBarType(EMPTY),
56     progressScopeStart(0),
57     progressScopeSize(0),
58     progress(0),
59     pagesIdentical(false),
60     text_cols(0),
61     text_rows(0),
62     text_col(0),
63     text_row(0),
64     text_top(0),
65     show_text(false),
66     show_text_ever(false),
67     show_menu(false),
68     menu_top(0),
69     menu_items(0),
70     menu_sel(0),
71 
72     // These values are correct for the default image resources
73     // provided with the android platform.  Devices which use
74     // different resources should have a subclass of ScreenRecoveryUI
75     // that overrides Init() to set these values appropriately and
76     // then call the superclass Init().
77     animation_fps(20),
78     indeterminate_frames(6),
79     installing_frames(7),
80     install_overlay_offset_x(13),
81     install_overlay_offset_y(190) {
82     pthread_mutex_init(&updateMutex, NULL);
83     self = this;
84 }
85 
86 // Draw the given frame over the installation overlay animation.  The
87 // background is not cleared or draw with the base icon first; we
88 // assume that the frame already contains some other frame of the
89 // animation.  Does nothing if no overlay animation is defined.
90 // Should only be called with updateMutex locked.
draw_install_overlay_locked(int frame)91 void ScreenRecoveryUI::draw_install_overlay_locked(int frame) {
92     if (installationOverlay == NULL) return;
93     gr_surface surface = installationOverlay[frame];
94     int iconWidth = gr_get_width(surface);
95     int iconHeight = gr_get_height(surface);
96     gr_blit(surface, 0, 0, iconWidth, iconHeight,
97             install_overlay_offset_x, install_overlay_offset_y);
98 }
99 
100 // Clear the screen and draw the currently selected background icon (if any).
101 // Should only be called with updateMutex locked.
draw_background_locked(Icon icon)102 void ScreenRecoveryUI::draw_background_locked(Icon icon)
103 {
104     pagesIdentical = false;
105     gr_color(0, 0, 0, 255);
106     gr_fill(0, 0, gr_fb_width(), gr_fb_height());
107 
108     if (icon) {
109         gr_surface surface = backgroundIcon[icon];
110         int iconWidth = gr_get_width(surface);
111         int iconHeight = gr_get_height(surface);
112         int iconX = (gr_fb_width() - iconWidth) / 2;
113         int iconY = (gr_fb_height() - iconHeight) / 2;
114         gr_blit(surface, 0, 0, iconWidth, iconHeight, iconX, iconY);
115         if (icon == INSTALLING) {
116             draw_install_overlay_locked(installingFrame);
117         }
118     }
119 }
120 
121 // Draw the progress bar (if any) on the screen.  Does not flip pages.
122 // Should only be called with updateMutex locked.
draw_progress_locked()123 void ScreenRecoveryUI::draw_progress_locked()
124 {
125     if (currentIcon == ERROR) return;
126 
127     if (currentIcon == INSTALLING) {
128         draw_install_overlay_locked(installingFrame);
129     }
130 
131     if (progressBarType != EMPTY) {
132         int iconHeight = gr_get_height(backgroundIcon[INSTALLING]);
133         int width = gr_get_width(progressBarEmpty);
134         int height = gr_get_height(progressBarEmpty);
135 
136         int dx = (gr_fb_width() - width)/2;
137         int dy = (3*gr_fb_height() + iconHeight - 2*height)/4;
138 
139         // Erase behind the progress bar (in case this was a progress-only update)
140         gr_color(0, 0, 0, 255);
141         gr_fill(dx, dy, width, height);
142 
143         if (progressBarType == DETERMINATE) {
144             float p = progressScopeStart + progress * progressScopeSize;
145             int pos = (int) (p * width);
146 
147             if (pos > 0) {
148                 gr_blit(progressBarFill, 0, 0, pos, height, dx, dy);
149             }
150             if (pos < width-1) {
151                 gr_blit(progressBarEmpty, pos, 0, width-pos, height, dx+pos, dy);
152             }
153         }
154 
155         if (progressBarType == INDETERMINATE) {
156             static int frame = 0;
157             gr_blit(progressBarIndeterminate[frame], 0, 0, width, height, dx, dy);
158             frame = (frame + 1) % indeterminate_frames;
159         }
160     }
161 }
162 
draw_text_line(int row,const char * t)163 void ScreenRecoveryUI::draw_text_line(int row, const char* t) {
164   if (t[0] != '\0') {
165     gr_text(0, (row+1)*CHAR_HEIGHT-1, t);
166   }
167 }
168 
169 // Redraw everything on the screen.  Does not flip pages.
170 // Should only be called with updateMutex locked.
draw_screen_locked()171 void ScreenRecoveryUI::draw_screen_locked()
172 {
173     draw_background_locked(currentIcon);
174     draw_progress_locked();
175 
176     if (show_text) {
177         gr_color(0, 0, 0, 160);
178         gr_fill(0, 0, gr_fb_width(), gr_fb_height());
179 
180         int i = 0;
181         if (show_menu) {
182             gr_color(64, 96, 255, 255);
183             gr_fill(0, (menu_top+menu_sel) * CHAR_HEIGHT,
184                     gr_fb_width(), (menu_top+menu_sel+1)*CHAR_HEIGHT+1);
185 
186             for (; i < menu_top + menu_items; ++i) {
187                 if (i == menu_top + menu_sel) {
188                     gr_color(255, 255, 255, 255);
189                     draw_text_line(i, menu[i]);
190                     gr_color(64, 96, 255, 255);
191                 } else {
192                     draw_text_line(i, menu[i]);
193                 }
194             }
195             gr_fill(0, i*CHAR_HEIGHT+CHAR_HEIGHT/2-1,
196                     gr_fb_width(), i*CHAR_HEIGHT+CHAR_HEIGHT/2+1);
197             ++i;
198         }
199 
200         gr_color(255, 255, 0, 255);
201 
202         for (; i < text_rows; ++i) {
203             draw_text_line(i, text[(i+text_top) % text_rows]);
204         }
205     }
206 }
207 
208 // Redraw everything on the screen and flip the screen (make it visible).
209 // Should only be called with updateMutex locked.
update_screen_locked()210 void ScreenRecoveryUI::update_screen_locked()
211 {
212     draw_screen_locked();
213     gr_flip();
214 }
215 
216 // Updates only the progress bar, if possible, otherwise redraws the screen.
217 // Should only be called with updateMutex locked.
update_progress_locked()218 void ScreenRecoveryUI::update_progress_locked()
219 {
220     if (show_text || !pagesIdentical) {
221         draw_screen_locked();    // Must redraw the whole screen
222         pagesIdentical = true;
223     } else {
224         draw_progress_locked();  // Draw only the progress bar and overlays
225     }
226     gr_flip();
227 }
228 
229 // Keeps the progress bar updated, even when the process is otherwise busy.
progress_thread(void * cookie)230 void* ScreenRecoveryUI::progress_thread(void *cookie) {
231     self->progress_loop();
232     return NULL;
233 }
234 
progress_loop()235 void ScreenRecoveryUI::progress_loop() {
236     double interval = 1.0 / animation_fps;
237     for (;;) {
238         double start = now();
239         pthread_mutex_lock(&updateMutex);
240 
241         int redraw = 0;
242 
243         // update the installation animation, if active
244         // skip this if we have a text overlay (too expensive to update)
245         if (currentIcon == INSTALLING && installing_frames > 0 && !show_text) {
246             installingFrame = (installingFrame + 1) % installing_frames;
247             redraw = 1;
248         }
249 
250         // update the progress bar animation, if active
251         // skip this if we have a text overlay (too expensive to update)
252         if (progressBarType == INDETERMINATE && !show_text) {
253             redraw = 1;
254         }
255 
256         // move the progress bar forward on timed intervals, if configured
257         int duration = progressScopeDuration;
258         if (progressBarType == DETERMINATE && duration > 0) {
259             double elapsed = now() - progressScopeTime;
260             float p = 1.0 * elapsed / duration;
261             if (p > 1.0) p = 1.0;
262             if (p > progress) {
263                 progress = p;
264                 redraw = 1;
265             }
266         }
267 
268         if (redraw) update_progress_locked();
269 
270         pthread_mutex_unlock(&updateMutex);
271         double end = now();
272         // minimum of 20ms delay between frames
273         double delay = interval - (end-start);
274         if (delay < 0.02) delay = 0.02;
275         usleep((long)(delay * 1000000));
276     }
277 }
278 
LoadBitmap(const char * filename,gr_surface * surface)279 void ScreenRecoveryUI::LoadBitmap(const char* filename, gr_surface* surface) {
280     int result = res_create_surface(filename, surface);
281     if (result < 0) {
282         LOGE("missing bitmap %s\n(Code %d)\n", filename, result);
283     }
284 }
285 
Init()286 void ScreenRecoveryUI::Init()
287 {
288     gr_init();
289 
290     text_col = text_row = 0;
291     text_rows = gr_fb_height() / CHAR_HEIGHT;
292     if (text_rows > kMaxRows) text_rows = kMaxRows;
293     text_top = 1;
294 
295     text_cols = gr_fb_width() / CHAR_WIDTH;
296     if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1;
297 
298     LoadBitmap("icon_installing", &backgroundIcon[INSTALLING]);
299     LoadBitmap("icon_error", &backgroundIcon[ERROR]);
300     LoadBitmap("progress_empty", &progressBarEmpty);
301     LoadBitmap("progress_fill", &progressBarFill);
302 
303     int i;
304 
305     progressBarIndeterminate = (gr_surface*)malloc(indeterminate_frames *
306                                                     sizeof(gr_surface));
307     for (i = 0; i < indeterminate_frames; ++i) {
308         char filename[40];
309         // "indeterminate01.png", "indeterminate02.png", ...
310         sprintf(filename, "indeterminate%02d", i+1);
311         LoadBitmap(filename, progressBarIndeterminate+i);
312     }
313 
314     if (installing_frames > 0) {
315         installationOverlay = (gr_surface*)malloc(installing_frames *
316                                                    sizeof(gr_surface));
317         for (i = 0; i < installing_frames; ++i) {
318             char filename[40];
319             // "icon_installing_overlay01.png",
320             // "icon_installing_overlay02.png", ...
321             sprintf(filename, "icon_installing_overlay%02d", i+1);
322             LoadBitmap(filename, installationOverlay+i);
323         }
324 
325         // Adjust the offset to account for the positioning of the
326         // base image on the screen.
327         if (backgroundIcon[INSTALLING] != NULL) {
328             gr_surface bg = backgroundIcon[INSTALLING];
329             install_overlay_offset_x += (gr_fb_width() - gr_get_width(bg)) / 2;
330             install_overlay_offset_y += (gr_fb_height() - gr_get_height(bg)) / 2;
331         }
332     } else {
333         installationOverlay = NULL;
334     }
335 
336     pthread_create(&progress_t, NULL, progress_thread, NULL);
337 
338     RecoveryUI::Init();
339 }
340 
SetBackground(Icon icon)341 void ScreenRecoveryUI::SetBackground(Icon icon)
342 {
343     pthread_mutex_lock(&updateMutex);
344     currentIcon = icon;
345     update_screen_locked();
346     pthread_mutex_unlock(&updateMutex);
347 }
348 
SetProgressType(ProgressType type)349 void ScreenRecoveryUI::SetProgressType(ProgressType type)
350 {
351     pthread_mutex_lock(&updateMutex);
352     if (progressBarType != type) {
353         progressBarType = type;
354         update_progress_locked();
355     }
356     progressScopeStart = 0;
357     progress = 0;
358     pthread_mutex_unlock(&updateMutex);
359 }
360 
ShowProgress(float portion,float seconds)361 void ScreenRecoveryUI::ShowProgress(float portion, float seconds)
362 {
363     pthread_mutex_lock(&updateMutex);
364     progressBarType = DETERMINATE;
365     progressScopeStart += progressScopeSize;
366     progressScopeSize = portion;
367     progressScopeTime = now();
368     progressScopeDuration = seconds;
369     progress = 0;
370     update_progress_locked();
371     pthread_mutex_unlock(&updateMutex);
372 }
373 
SetProgress(float fraction)374 void ScreenRecoveryUI::SetProgress(float fraction)
375 {
376     pthread_mutex_lock(&updateMutex);
377     if (fraction < 0.0) fraction = 0.0;
378     if (fraction > 1.0) fraction = 1.0;
379     if (progressBarType == DETERMINATE && fraction > progress) {
380         // Skip updates that aren't visibly different.
381         int width = gr_get_width(progressBarIndeterminate[0]);
382         float scale = width * progressScopeSize;
383         if ((int) (progress * scale) != (int) (fraction * scale)) {
384             progress = fraction;
385             update_progress_locked();
386         }
387     }
388     pthread_mutex_unlock(&updateMutex);
389 }
390 
Print(const char * fmt,...)391 void ScreenRecoveryUI::Print(const char *fmt, ...)
392 {
393     char buf[256];
394     va_list ap;
395     va_start(ap, fmt);
396     vsnprintf(buf, 256, fmt, ap);
397     va_end(ap);
398 
399     fputs(buf, stdout);
400 
401     // This can get called before ui_init(), so be careful.
402     pthread_mutex_lock(&updateMutex);
403     if (text_rows > 0 && text_cols > 0) {
404         char *ptr;
405         for (ptr = buf; *ptr != '\0'; ++ptr) {
406             if (*ptr == '\n' || text_col >= text_cols) {
407                 text[text_row][text_col] = '\0';
408                 text_col = 0;
409                 text_row = (text_row + 1) % text_rows;
410                 if (text_row == text_top) text_top = (text_top + 1) % text_rows;
411             }
412             if (*ptr != '\n') text[text_row][text_col++] = *ptr;
413         }
414         text[text_row][text_col] = '\0';
415         update_screen_locked();
416     }
417     pthread_mutex_unlock(&updateMutex);
418 }
419 
StartMenu(const char * const * headers,const char * const * items,int initial_selection)420 void ScreenRecoveryUI::StartMenu(const char* const * headers, const char* const * items,
421                                  int initial_selection) {
422     int i;
423     pthread_mutex_lock(&updateMutex);
424     if (text_rows > 0 && text_cols > 0) {
425         for (i = 0; i < text_rows; ++i) {
426             if (headers[i] == NULL) break;
427             strncpy(menu[i], headers[i], text_cols-1);
428             menu[i][text_cols-1] = '\0';
429         }
430         menu_top = i;
431         for (; i < text_rows; ++i) {
432             if (items[i-menu_top] == NULL) break;
433             strncpy(menu[i], items[i-menu_top], text_cols-1);
434             menu[i][text_cols-1] = '\0';
435         }
436         menu_items = i - menu_top;
437         show_menu = 1;
438         menu_sel = initial_selection;
439         update_screen_locked();
440     }
441     pthread_mutex_unlock(&updateMutex);
442 }
443 
SelectMenu(int sel)444 int ScreenRecoveryUI::SelectMenu(int sel) {
445     int old_sel;
446     pthread_mutex_lock(&updateMutex);
447     if (show_menu > 0) {
448         old_sel = menu_sel;
449         menu_sel = sel;
450         if (menu_sel < 0) menu_sel = 0;
451         if (menu_sel >= menu_items) menu_sel = menu_items-1;
452         sel = menu_sel;
453         if (menu_sel != old_sel) update_screen_locked();
454     }
455     pthread_mutex_unlock(&updateMutex);
456     return sel;
457 }
458 
EndMenu()459 void ScreenRecoveryUI::EndMenu() {
460     int i;
461     pthread_mutex_lock(&updateMutex);
462     if (show_menu > 0 && text_rows > 0 && text_cols > 0) {
463         show_menu = 0;
464         update_screen_locked();
465     }
466     pthread_mutex_unlock(&updateMutex);
467 }
468 
IsTextVisible()469 bool ScreenRecoveryUI::IsTextVisible()
470 {
471     pthread_mutex_lock(&updateMutex);
472     int visible = show_text;
473     pthread_mutex_unlock(&updateMutex);
474     return visible;
475 }
476 
WasTextEverVisible()477 bool ScreenRecoveryUI::WasTextEverVisible()
478 {
479     pthread_mutex_lock(&updateMutex);
480     int ever_visible = show_text_ever;
481     pthread_mutex_unlock(&updateMutex);
482     return ever_visible;
483 }
484 
ShowText(bool visible)485 void ScreenRecoveryUI::ShowText(bool visible)
486 {
487     pthread_mutex_lock(&updateMutex);
488     show_text = visible;
489     if (show_text) show_text_ever = 1;
490     update_screen_locked();
491     pthread_mutex_unlock(&updateMutex);
492 }
493