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 "charger.sysprop.h"
22 #include "healthd_draw.h"
23
24 #define LOGE(x...) KLOG_ERROR("charger", x);
25 #define LOGW(x...) KLOG_WARNING("charger", x);
26 #define LOGV(x...) KLOG_DEBUG("charger", x);
27
get_split_screen()28 static bool get_split_screen() {
29 return android::sysprop::ChargerProperties::draw_split_screen().value_or(false);
30 }
31
get_split_offset()32 static int get_split_offset() {
33 int64_t value = android::sysprop::ChargerProperties::draw_split_offset().value_or(0);
34 if (value < static_cast<int64_t>(std::numeric_limits<int>::min())) {
35 LOGW("draw_split_offset = %" PRId64 " overflow for an int; resetting to %d.\n", value,
36 std::numeric_limits<int>::min());
37 value = std::numeric_limits<int>::min();
38 }
39 if (value > static_cast<int64_t>(std::numeric_limits<int>::max())) {
40 LOGW("draw_split_offset = %" PRId64 " overflow for an int; resetting to %d.\n", value,
41 std::numeric_limits<int>::max());
42 value = std::numeric_limits<int>::max();
43 }
44 return static_cast<int>(value);
45 }
46
HealthdDraw(animation * anim)47 HealthdDraw::HealthdDraw(animation* anim)
48 : kSplitScreen(get_split_screen()), kSplitOffset(get_split_offset()) {
49 int ret = gr_init();
50
51 if (ret < 0) {
52 LOGE("gr_init failed\n");
53 graphics_available = false;
54 return;
55 }
56
57 graphics_available = true;
58 sys_font = gr_sys_font();
59 if (sys_font == nullptr) {
60 LOGW("No system font, screen fallback text not available\n");
61 } else {
62 gr_font_size(sys_font, &char_width_, &char_height_);
63 }
64
65 screen_width_ = gr_fb_width() / (kSplitScreen ? 2 : 1);
66 screen_height_ = gr_fb_height();
67
68 int res;
69 if (!anim->text_clock.font_file.empty() &&
70 (res = gr_init_font(anim->text_clock.font_file.c_str(), &anim->text_clock.font)) < 0) {
71 LOGE("Could not load time font (%d)\n", res);
72 }
73 if (!anim->text_percent.font_file.empty() &&
74 (res = gr_init_font(anim->text_percent.font_file.c_str(), &anim->text_percent.font)) < 0) {
75 LOGE("Could not load percent font (%d)\n", res);
76 }
77 }
78
~HealthdDraw()79 HealthdDraw::~HealthdDraw() {}
80
redraw_screen(const animation * batt_anim,GRSurface * surf_unknown)81 void HealthdDraw::redraw_screen(const animation* batt_anim, GRSurface* surf_unknown) {
82 if (!graphics_available) return;
83 clear_screen();
84
85 /* try to display *something* */
86 if (batt_anim->cur_status == BATTERY_STATUS_UNKNOWN || batt_anim->cur_level < 0 ||
87 batt_anim->num_frames == 0)
88 draw_unknown(surf_unknown);
89 else
90 draw_battery(batt_anim);
91 gr_flip();
92 }
93
blank_screen(bool blank)94 void HealthdDraw::blank_screen(bool blank) {
95 if (!graphics_available) return;
96 gr_fb_blank(blank);
97 }
98
clear_screen(void)99 void HealthdDraw::clear_screen(void) {
100 if (!graphics_available) return;
101 gr_color(0, 0, 0, 255);
102 gr_clear();
103 }
104
draw_surface_centered(GRSurface * surface)105 int HealthdDraw::draw_surface_centered(GRSurface* surface) {
106 if (!graphics_available) return 0;
107
108 int w = gr_get_width(surface);
109 int h = gr_get_height(surface);
110 int x = (screen_width_ - w) / 2 + kSplitOffset;
111 int y = (screen_height_ - h) / 2;
112
113 LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y);
114 gr_blit(surface, 0, 0, w, h, x, y);
115 if (kSplitScreen) {
116 x += screen_width_ - 2 * kSplitOffset;
117 LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y);
118 gr_blit(surface, 0, 0, w, h, x, y);
119 }
120
121 return y + h;
122 }
123
draw_text(const GRFont * font,int x,int y,const char * str)124 int HealthdDraw::draw_text(const GRFont* font, int x, int y, const char* str) {
125 if (!graphics_available) return 0;
126 int str_len_px = gr_measure(font, str);
127
128 if (x < 0) x = (screen_width_ - str_len_px) / 2;
129 if (y < 0) y = (screen_height_ - char_height_) / 2;
130 gr_text(font, x + kSplitOffset, y, str, false /* bold */);
131 if (kSplitScreen) gr_text(font, x - kSplitOffset + screen_width_, y, str, false /* bold */);
132
133 return y + char_height_;
134 }
135
determine_xy(const animation::text_field & field,const int length,int * x,int * y)136 void HealthdDraw::determine_xy(const animation::text_field& field,
137 const int length, int* x, int* y) {
138 *x = field.pos_x;
139
140 int str_len_px = length * field.font->char_width;
141 if (field.pos_x == CENTER_VAL) {
142 *x = (screen_width_ - str_len_px) / 2;
143 } else if (field.pos_x >= 0) {
144 *x = field.pos_x;
145 } else { // position from max edge
146 *x = screen_width_ + field.pos_x - str_len_px - kSplitOffset;
147 }
148
149 *y = field.pos_y;
150
151 if (field.pos_y == CENTER_VAL) {
152 *y = (screen_height_ - field.font->char_height) / 2;
153 } else if (field.pos_y >= 0) {
154 *y = field.pos_y;
155 } else { // position from max edge
156 *y = screen_height_ + field.pos_y - field.font->char_height;
157 }
158 }
159
draw_clock(const animation * anim)160 void HealthdDraw::draw_clock(const animation* anim) {
161 static constexpr char CLOCK_FORMAT[] = "%H:%M";
162 static constexpr int CLOCK_LENGTH = 6;
163
164 const animation::text_field& field = anim->text_clock;
165
166 if (!graphics_available || field.font == nullptr || field.font->char_width == 0 ||
167 field.font->char_height == 0)
168 return;
169
170 time_t rawtime;
171 time(&rawtime);
172 tm* time_info = localtime(&rawtime);
173
174 char clock_str[CLOCK_LENGTH];
175 size_t length = strftime(clock_str, CLOCK_LENGTH, CLOCK_FORMAT, time_info);
176 if (length != CLOCK_LENGTH - 1) {
177 LOGE("Could not format time\n");
178 return;
179 }
180
181 int x, y;
182 determine_xy(field, length, &x, &y);
183
184 LOGV("drawing clock %s %d %d\n", clock_str, x, y);
185 gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
186 draw_text(field.font, x, y, clock_str);
187 }
188
draw_percent(const animation * anim)189 void HealthdDraw::draw_percent(const animation* anim) {
190 if (!graphics_available) return;
191 int cur_level = anim->cur_level;
192 if (anim->cur_status == BATTERY_STATUS_FULL) {
193 cur_level = 100;
194 }
195
196 if (cur_level < 0) return;
197
198 const animation::text_field& field = anim->text_percent;
199 if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0) {
200 return;
201 }
202
203 std::string str = base::StringPrintf("%d%%", cur_level);
204
205 int x, y;
206 determine_xy(field, str.size(), &x, &y);
207
208 LOGV("drawing percent %s %d %d\n", str.c_str(), x, y);
209 gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
210 draw_text(field.font, x, y, str.c_str());
211 }
212
draw_battery(const animation * anim)213 void HealthdDraw::draw_battery(const animation* anim) {
214 if (!graphics_available) return;
215 const animation::frame& frame = anim->frames[anim->cur_frame];
216
217 if (anim->num_frames != 0) {
218 draw_surface_centered(frame.surface);
219 LOGV("drawing frame #%d min_cap=%d time=%d\n", anim->cur_frame, frame.min_level,
220 frame.disp_time);
221 }
222 draw_clock(anim);
223 draw_percent(anim);
224 }
225
draw_unknown(GRSurface * surf_unknown)226 void HealthdDraw::draw_unknown(GRSurface* surf_unknown) {
227 int y;
228 if (surf_unknown) {
229 draw_surface_centered(surf_unknown);
230 } else if (sys_font) {
231 gr_color(0xa4, 0xc6, 0x39, 255);
232 y = draw_text(sys_font, -1, -1, "Charging!");
233 draw_text(sys_font, -1, y + 25, "?\?/100");
234 } else {
235 LOGW("Charging, level unknown\n");
236 }
237 }
238