blob: 7c79319445bf22400f2cb5742167aefea86bdde4 [file] [log] [blame]
Luke Song7f386dc2017-07-13 15:10:35 -07001/*
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
Yifan Honge3ffd1b2021-10-20 22:18:16 -070023#if !defined(__ANDROID_VNDK__)
24#include "charger.sysprop.h"
25#endif
26
Luke Song7f386dc2017-07-13 15:10:35 -070027#define LOGE(x...) KLOG_ERROR("charger", x);
Todd Poynore5d1b622018-06-01 11:09:58 -070028#define LOGW(x...) KLOG_WARNING("charger", x);
Luke Song7f386dc2017-07-13 15:10:35 -070029#define LOGV(x...) KLOG_DEBUG("charger", x);
30
Yifan Hong97eecdc2019-07-03 11:07:37 -070031static bool get_split_screen() {
Yifan Honge3ffd1b2021-10-20 22:18:16 -070032#if !defined(__ANDROID_VNDK__)
Yifan Hong97eecdc2019-07-03 11:07:37 -070033 return android::sysprop::ChargerProperties::draw_split_screen().value_or(false);
Yifan Honge3ffd1b2021-10-20 22:18:16 -070034#else
35 return false;
36#endif
Yifan Hong97eecdc2019-07-03 11:07:37 -070037}
38
39static int get_split_offset() {
Yifan Honge3ffd1b2021-10-20 22:18:16 -070040#if !defined(__ANDROID_VNDK__)
Yifan Hong97eecdc2019-07-03 11:07:37 -070041 int64_t value = android::sysprop::ChargerProperties::draw_split_offset().value_or(0);
Yifan Honge3ffd1b2021-10-20 22:18:16 -070042#else
43 int64_t value = 0;
44#endif
Yifan Hong97eecdc2019-07-03 11:07:37 -070045 if (value < static_cast<int64_t>(std::numeric_limits<int>::min())) {
46 LOGW("draw_split_offset = %" PRId64 " overflow for an int; resetting to %d.\n", value,
47 std::numeric_limits<int>::min());
48 value = std::numeric_limits<int>::min();
49 }
50 if (value > static_cast<int64_t>(std::numeric_limits<int>::max())) {
51 LOGW("draw_split_offset = %" PRId64 " overflow for an int; resetting to %d.\n", value,
52 std::numeric_limits<int>::max());
53 value = std::numeric_limits<int>::max();
54 }
55 return static_cast<int>(value);
56}
57
Luke Song7f386dc2017-07-13 15:10:35 -070058HealthdDraw::HealthdDraw(animation* anim)
Yifan Hong97eecdc2019-07-03 11:07:37 -070059 : kSplitScreen(get_split_screen()), kSplitOffset(get_split_offset()) {
Todd Poynore5d1b622018-06-01 11:09:58 -070060 graphics_available = true;
61 sys_font = gr_sys_font();
62 if (sys_font == nullptr) {
63 LOGW("No system font, screen fallback text not available\n");
64 } else {
65 gr_font_size(sys_font, &char_width_, &char_height_);
66 }
67
68 screen_width_ = gr_fb_width() / (kSplitScreen ? 2 : 1);
69 screen_height_ = gr_fb_height();
70
71 int res;
72 if (!anim->text_clock.font_file.empty() &&
73 (res = gr_init_font(anim->text_clock.font_file.c_str(), &anim->text_clock.font)) < 0) {
74 LOGE("Could not load time font (%d)\n", res);
75 }
76 if (!anim->text_percent.font_file.empty() &&
77 (res = gr_init_font(anim->text_percent.font_file.c_str(), &anim->text_percent.font)) < 0) {
78 LOGE("Could not load percent font (%d)\n", res);
79 }
Luke Song7f386dc2017-07-13 15:10:35 -070080}
81
82HealthdDraw::~HealthdDraw() {}
83
84void HealthdDraw::redraw_screen(const animation* batt_anim, GRSurface* surf_unknown) {
Todd Poynore5d1b622018-06-01 11:09:58 -070085 if (!graphics_available) return;
86 clear_screen();
Luke Song7f386dc2017-07-13 15:10:35 -070087
Todd Poynore5d1b622018-06-01 11:09:58 -070088 /* try to display *something* */
Ken Tsou6c7ece72019-02-15 10:50:58 +080089 if (batt_anim->cur_status == BATTERY_STATUS_UNKNOWN || batt_anim->cur_level < 0 ||
90 batt_anim->num_frames == 0)
Todd Poynore5d1b622018-06-01 11:09:58 -070091 draw_unknown(surf_unknown);
92 else
93 draw_battery(batt_anim);
94 gr_flip();
Luke Song7f386dc2017-07-13 15:10:35 -070095}
96
Jack Wuc1b17112021-09-29 22:27:56 +080097void HealthdDraw::blank_screen(bool blank, int drm) {
Todd Poynore5d1b622018-06-01 11:09:58 -070098 if (!graphics_available) return;
Jack Wuc1b17112021-09-29 22:27:56 +080099 gr_fb_blank(blank, drm);
Todd Poynore5d1b622018-06-01 11:09:58 -0700100}
Luke Song7f386dc2017-07-13 15:10:35 -0700101
Jack Wu20b8a012022-08-29 17:59:09 +0800102// support screen rotation for foldable phone
Jack Wu56540a02021-10-22 17:10:37 +0800103void HealthdDraw::rotate_screen(int drm) {
104 if (!graphics_available) return;
105 if (drm == 0)
106 gr_rotate(GRRotation::RIGHT /* landscape mode */);
107 else
108 gr_rotate(GRRotation::NONE /* Portrait mode */);
109}
110
Jack Wu20b8a012022-08-29 17:59:09 +0800111// detect dual display
112bool HealthdDraw::has_multiple_connectors() {
113 return graphics_available && gr_has_multiple_connectors();
114}
115
Luke Song7f386dc2017-07-13 15:10:35 -0700116void HealthdDraw::clear_screen(void) {
Todd Poynore5d1b622018-06-01 11:09:58 -0700117 if (!graphics_available) return;
118 gr_color(0, 0, 0, 255);
119 gr_clear();
Luke Song7f386dc2017-07-13 15:10:35 -0700120}
121
122int HealthdDraw::draw_surface_centered(GRSurface* surface) {
Todd Poynore5d1b622018-06-01 11:09:58 -0700123 if (!graphics_available) return 0;
Luke Song7f386dc2017-07-13 15:10:35 -0700124
Todd Poynore5d1b622018-06-01 11:09:58 -0700125 int w = gr_get_width(surface);
126 int h = gr_get_height(surface);
127 int x = (screen_width_ - w) / 2 + kSplitOffset;
128 int y = (screen_height_ - h) / 2;
129
Luke Song7f386dc2017-07-13 15:10:35 -0700130 LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y);
131 gr_blit(surface, 0, 0, w, h, x, y);
Todd Poynore5d1b622018-06-01 11:09:58 -0700132 if (kSplitScreen) {
133 x += screen_width_ - 2 * kSplitOffset;
134 LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y);
135 gr_blit(surface, 0, 0, w, h, x, y);
136 }
Luke Song7f386dc2017-07-13 15:10:35 -0700137
Todd Poynore5d1b622018-06-01 11:09:58 -0700138 return y + h;
Luke Song7f386dc2017-07-13 15:10:35 -0700139}
140
141int HealthdDraw::draw_text(const GRFont* font, int x, int y, const char* str) {
Todd Poynore5d1b622018-06-01 11:09:58 -0700142 if (!graphics_available) return 0;
143 int str_len_px = gr_measure(font, str);
Luke Song7f386dc2017-07-13 15:10:35 -0700144
Todd Poynore5d1b622018-06-01 11:09:58 -0700145 if (x < 0) x = (screen_width_ - str_len_px) / 2;
146 if (y < 0) y = (screen_height_ - char_height_) / 2;
147 gr_text(font, x + kSplitOffset, y, str, false /* bold */);
148 if (kSplitScreen) gr_text(font, x - kSplitOffset + screen_width_, y, str, false /* bold */);
Luke Song7f386dc2017-07-13 15:10:35 -0700149
Todd Poynore5d1b622018-06-01 11:09:58 -0700150 return y + char_height_;
Luke Song7f386dc2017-07-13 15:10:35 -0700151}
152
153void HealthdDraw::determine_xy(const animation::text_field& field,
154 const int length, int* x, int* y) {
155 *x = field.pos_x;
Jack Wuc1b17112021-09-29 22:27:56 +0800156 screen_width_ = gr_fb_width() / (kSplitScreen ? 2 : 1);
157 screen_height_ = gr_fb_height();
Luke Song7f386dc2017-07-13 15:10:35 -0700158
159 int str_len_px = length * field.font->char_width;
160 if (field.pos_x == CENTER_VAL) {
161 *x = (screen_width_ - str_len_px) / 2;
162 } else if (field.pos_x >= 0) {
163 *x = field.pos_x;
164 } else { // position from max edge
165 *x = screen_width_ + field.pos_x - str_len_px - kSplitOffset;
166 }
167
168 *y = field.pos_y;
169
170 if (field.pos_y == CENTER_VAL) {
171 *y = (screen_height_ - field.font->char_height) / 2;
172 } else if (field.pos_y >= 0) {
173 *y = field.pos_y;
174 } else { // position from max edge
175 *y = screen_height_ + field.pos_y - field.font->char_height;
176 }
177}
178
179void HealthdDraw::draw_clock(const animation* anim) {
Todd Poynore5d1b622018-06-01 11:09:58 -0700180 static constexpr char CLOCK_FORMAT[] = "%H:%M";
181 static constexpr int CLOCK_LENGTH = 6;
Luke Song7f386dc2017-07-13 15:10:35 -0700182
Todd Poynore5d1b622018-06-01 11:09:58 -0700183 const animation::text_field& field = anim->text_clock;
Luke Song7f386dc2017-07-13 15:10:35 -0700184
Todd Poynore5d1b622018-06-01 11:09:58 -0700185 if (!graphics_available || field.font == nullptr || field.font->char_width == 0 ||
186 field.font->char_height == 0)
187 return;
Luke Song7f386dc2017-07-13 15:10:35 -0700188
Todd Poynore5d1b622018-06-01 11:09:58 -0700189 time_t rawtime;
190 time(&rawtime);
191 tm* time_info = localtime(&rawtime);
Luke Song7f386dc2017-07-13 15:10:35 -0700192
Todd Poynore5d1b622018-06-01 11:09:58 -0700193 char clock_str[CLOCK_LENGTH];
194 size_t length = strftime(clock_str, CLOCK_LENGTH, CLOCK_FORMAT, time_info);
195 if (length != CLOCK_LENGTH - 1) {
196 LOGE("Could not format time\n");
197 return;
198 }
Luke Song7f386dc2017-07-13 15:10:35 -0700199
Todd Poynore5d1b622018-06-01 11:09:58 -0700200 int x, y;
201 determine_xy(field, length, &x, &y);
Luke Song7f386dc2017-07-13 15:10:35 -0700202
Todd Poynore5d1b622018-06-01 11:09:58 -0700203 LOGV("drawing clock %s %d %d\n", clock_str, x, y);
204 gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
205 draw_text(field.font, x, y, clock_str);
Luke Song7f386dc2017-07-13 15:10:35 -0700206}
207
208void HealthdDraw::draw_percent(const animation* anim) {
Todd Poynore5d1b622018-06-01 11:09:58 -0700209 if (!graphics_available) return;
210 int cur_level = anim->cur_level;
211 if (anim->cur_status == BATTERY_STATUS_FULL) {
212 cur_level = 100;
213 }
Luke Song7f386dc2017-07-13 15:10:35 -0700214
kentsouba61ea42018-07-17 17:49:34 +0800215 if (cur_level < 0) return;
Luke Song7f386dc2017-07-13 15:10:35 -0700216
Todd Poynore5d1b622018-06-01 11:09:58 -0700217 const animation::text_field& field = anim->text_percent;
218 if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0) {
219 return;
220 }
Luke Song7f386dc2017-07-13 15:10:35 -0700221
Todd Poynore5d1b622018-06-01 11:09:58 -0700222 std::string str = base::StringPrintf("%d%%", cur_level);
Luke Song7f386dc2017-07-13 15:10:35 -0700223
Todd Poynore5d1b622018-06-01 11:09:58 -0700224 int x, y;
225 determine_xy(field, str.size(), &x, &y);
Luke Song7f386dc2017-07-13 15:10:35 -0700226
Todd Poynore5d1b622018-06-01 11:09:58 -0700227 LOGV("drawing percent %s %d %d\n", str.c_str(), x, y);
228 gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
229 draw_text(field.font, x, y, str.c_str());
Luke Song7f386dc2017-07-13 15:10:35 -0700230}
231
232void HealthdDraw::draw_battery(const animation* anim) {
Todd Poynore5d1b622018-06-01 11:09:58 -0700233 if (!graphics_available) return;
234 const animation::frame& frame = anim->frames[anim->cur_frame];
Luke Song7f386dc2017-07-13 15:10:35 -0700235
Todd Poynore5d1b622018-06-01 11:09:58 -0700236 if (anim->num_frames != 0) {
237 draw_surface_centered(frame.surface);
238 LOGV("drawing frame #%d min_cap=%d time=%d\n", anim->cur_frame, frame.min_level,
239 frame.disp_time);
240 }
241 draw_clock(anim);
242 draw_percent(anim);
Luke Song7f386dc2017-07-13 15:10:35 -0700243}
244
245void HealthdDraw::draw_unknown(GRSurface* surf_unknown) {
246 int y;
247 if (surf_unknown) {
Todd Poynore5d1b622018-06-01 11:09:58 -0700248 draw_surface_centered(surf_unknown);
249 } else if (sys_font) {
250 gr_color(0xa4, 0xc6, 0x39, 255);
251 y = draw_text(sys_font, -1, -1, "Charging!");
252 draw_text(sys_font, -1, y + 25, "?\?/100");
Luke Song7f386dc2017-07-13 15:10:35 -0700253 } else {
Todd Poynore5d1b622018-06-01 11:09:58 -0700254 LOGW("Charging, level unknown\n");
Luke Song7f386dc2017-07-13 15:10:35 -0700255 }
256}
Xiaohui Niua40d8722021-08-31 16:22:25 +0800257
258std::unique_ptr<HealthdDraw> HealthdDraw::Create(animation *anim) {
259 if (gr_init() < 0) {
260 LOGE("gr_init failed\n");
261 return nullptr;
262 }
263 return std::unique_ptr<HealthdDraw>(new HealthdDraw(anim));
264}