1 /*
2  * Copyright (C) 2017 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 <android-base/stringprintf.h>
18 #include <batteryservice/BatteryService.h>
19 #include <cutils/klog.h>
20 
21 #include "healthd_draw.h"
22 
23 #define LOGE(x...) KLOG_ERROR("charger", x);
24 #define LOGV(x...) KLOG_DEBUG("charger", x);
25 
HealthdDraw(animation * anim)26 HealthdDraw::HealthdDraw(animation* anim)
27   : kSplitScreen(HEALTHD_DRAW_SPLIT_SCREEN),
28     kSplitOffset(HEALTHD_DRAW_SPLIT_OFFSET) {
29   gr_init();
30   gr_font_size(gr_sys_font(), &char_width_, &char_height_);
31 
32   screen_width_ = gr_fb_width() / (kSplitScreen ? 2 : 1);
33   screen_height_ = gr_fb_height();
34 
35   int res;
36   if (!anim->text_clock.font_file.empty() &&
37       (res = gr_init_font(anim->text_clock.font_file.c_str(),
38                           &anim->text_clock.font)) < 0) {
39     LOGE("Could not load time font (%d)\n", res);
40   }
41   if (!anim->text_percent.font_file.empty() &&
42       (res = gr_init_font(anim->text_percent.font_file.c_str(),
43                           &anim->text_percent.font)) < 0) {
44     LOGE("Could not load percent font (%d)\n", res);
45   }
46 }
47 
~HealthdDraw()48 HealthdDraw::~HealthdDraw() {}
49 
redraw_screen(const animation * batt_anim,GRSurface * surf_unknown)50 void HealthdDraw::redraw_screen(const animation* batt_anim, GRSurface* surf_unknown) {
51   clear_screen();
52 
53   /* try to display *something* */
54   if (batt_anim->cur_level < 0 || batt_anim->num_frames == 0)
55     draw_unknown(surf_unknown);
56   else
57     draw_battery(batt_anim);
58   gr_flip();
59 }
60 
blank_screen(bool blank)61 void HealthdDraw::blank_screen(bool blank) { gr_fb_blank(blank); }
62 
clear_screen(void)63 void HealthdDraw::clear_screen(void) {
64   gr_color(0, 0, 0, 255);
65   gr_clear();
66 }
67 
draw_surface_centered(GRSurface * surface)68 int HealthdDraw::draw_surface_centered(GRSurface* surface) {
69   int w = gr_get_width(surface);
70   int h = gr_get_height(surface);
71   int x = (screen_width_ - w) / 2 + kSplitOffset;
72   int y = (screen_height_ - h) / 2;
73 
74   LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y);
75   gr_blit(surface, 0, 0, w, h, x, y);
76   if (kSplitScreen) {
77     x += screen_width_ - 2 * kSplitOffset;
78     LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y);
79     gr_blit(surface, 0, 0, w, h, x, y);
80   }
81 
82   return y + h;
83 }
84 
draw_text(const GRFont * font,int x,int y,const char * str)85 int HealthdDraw::draw_text(const GRFont* font, int x, int y, const char* str) {
86   int str_len_px = gr_measure(font, str);
87 
88   if (x < 0) x = (screen_width_ - str_len_px) / 2;
89   if (y < 0) y = (screen_height_ - char_height_) / 2;
90   gr_text(font, x + kSplitOffset, y, str, false /* bold */);
91   if (kSplitScreen)
92     gr_text(font, x - kSplitOffset + screen_width_, y, str, false /* bold */);
93 
94   return y + char_height_;
95 }
96 
determine_xy(const animation::text_field & field,const int length,int * x,int * y)97 void HealthdDraw::determine_xy(const animation::text_field& field,
98                                const int length, int* x, int* y) {
99   *x = field.pos_x;
100 
101   int str_len_px = length * field.font->char_width;
102   if (field.pos_x == CENTER_VAL) {
103     *x = (screen_width_ - str_len_px) / 2;
104   } else if (field.pos_x >= 0) {
105     *x = field.pos_x;
106   } else {  // position from max edge
107     *x = screen_width_ + field.pos_x - str_len_px - kSplitOffset;
108   }
109 
110   *y = field.pos_y;
111 
112   if (field.pos_y == CENTER_VAL) {
113     *y = (screen_height_ - field.font->char_height) / 2;
114   } else if (field.pos_y >= 0) {
115     *y = field.pos_y;
116   } else {  // position from max edge
117     *y = screen_height_ + field.pos_y - field.font->char_height;
118   }
119 }
120 
draw_clock(const animation * anim)121 void HealthdDraw::draw_clock(const animation* anim) {
122   static constexpr char CLOCK_FORMAT[] = "%H:%M";
123   static constexpr int CLOCK_LENGTH = 6;
124 
125   const animation::text_field& field = anim->text_clock;
126 
127   if (field.font == nullptr || field.font->char_width == 0 ||
128       field.font->char_height == 0)
129     return;
130 
131   time_t rawtime;
132   time(&rawtime);
133   tm* time_info = localtime(&rawtime);
134 
135   char clock_str[CLOCK_LENGTH];
136   size_t length = strftime(clock_str, CLOCK_LENGTH, CLOCK_FORMAT, time_info);
137   if (length != CLOCK_LENGTH - 1) {
138     LOGE("Could not format time\n");
139     return;
140   }
141 
142   int x, y;
143   determine_xy(field, length, &x, &y);
144 
145   LOGV("drawing clock %s %d %d\n", clock_str, x, y);
146   gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
147   draw_text(field.font, x, y, clock_str);
148 }
149 
draw_percent(const animation * anim)150 void HealthdDraw::draw_percent(const animation* anim) {
151   int cur_level = anim->cur_level;
152   if (anim->cur_status == BATTERY_STATUS_FULL) {
153     cur_level = 100;
154   }
155 
156   if (cur_level <= 0) return;
157 
158   const animation::text_field& field = anim->text_percent;
159   if (field.font == nullptr || field.font->char_width == 0 ||
160       field.font->char_height == 0) {
161     return;
162   }
163 
164   std::string str = base::StringPrintf("%d%%", cur_level);
165 
166   int x, y;
167   determine_xy(field, str.size(), &x, &y);
168 
169   LOGV("drawing percent %s %d %d\n", str.c_str(), x, y);
170   gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
171   draw_text(field.font, x, y, str.c_str());
172 }
173 
draw_battery(const animation * anim)174 void HealthdDraw::draw_battery(const animation* anim) {
175   const animation::frame& frame = anim->frames[anim->cur_frame];
176 
177   if (anim->num_frames != 0) {
178     draw_surface_centered(frame.surface);
179     LOGV("drawing frame #%d min_cap=%d time=%d\n", anim->cur_frame,
180          frame.min_level, frame.disp_time);
181   }
182   draw_clock(anim);
183   draw_percent(anim);
184 }
185 
draw_unknown(GRSurface * surf_unknown)186 void HealthdDraw::draw_unknown(GRSurface* surf_unknown) {
187   int y;
188   if (surf_unknown) {
189     draw_surface_centered(surf_unknown);
190   } else {
191     gr_color(0xa4, 0xc6, 0x39, 255);
192     y = draw_text(gr_sys_font(), -1, -1, "Charging!");
193     draw_text(gr_sys_font(), -1, y + 25, "?\?/100");
194   }
195 }
196