Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 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 | |
Yifan Hong | e3ffd1b | 2021-10-20 22:18:16 -0700 | [diff] [blame] | 23 | #if !defined(__ANDROID_VNDK__) |
| 24 | #include "charger.sysprop.h" |
| 25 | #endif |
| 26 | |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 27 | #define LOGE(x...) KLOG_ERROR("charger", x); |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 28 | #define LOGW(x...) KLOG_WARNING("charger", x); |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 29 | #define LOGV(x...) KLOG_DEBUG("charger", x); |
| 30 | |
Yifan Hong | 97eecdc | 2019-07-03 11:07:37 -0700 | [diff] [blame] | 31 | static bool get_split_screen() { |
Yifan Hong | e3ffd1b | 2021-10-20 22:18:16 -0700 | [diff] [blame] | 32 | #if !defined(__ANDROID_VNDK__) |
Yifan Hong | 97eecdc | 2019-07-03 11:07:37 -0700 | [diff] [blame] | 33 | return android::sysprop::ChargerProperties::draw_split_screen().value_or(false); |
Yifan Hong | e3ffd1b | 2021-10-20 22:18:16 -0700 | [diff] [blame] | 34 | #else |
| 35 | return false; |
| 36 | #endif |
Yifan Hong | 97eecdc | 2019-07-03 11:07:37 -0700 | [diff] [blame] | 37 | } |
| 38 | |
| 39 | static int get_split_offset() { |
Yifan Hong | e3ffd1b | 2021-10-20 22:18:16 -0700 | [diff] [blame] | 40 | #if !defined(__ANDROID_VNDK__) |
Yifan Hong | 97eecdc | 2019-07-03 11:07:37 -0700 | [diff] [blame] | 41 | int64_t value = android::sysprop::ChargerProperties::draw_split_offset().value_or(0); |
Yifan Hong | e3ffd1b | 2021-10-20 22:18:16 -0700 | [diff] [blame] | 42 | #else |
| 43 | int64_t value = 0; |
| 44 | #endif |
Yifan Hong | 97eecdc | 2019-07-03 11:07:37 -0700 | [diff] [blame] | 45 | 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 Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 58 | HealthdDraw::HealthdDraw(animation* anim) |
Yifan Hong | 97eecdc | 2019-07-03 11:07:37 -0700 | [diff] [blame] | 59 | : kSplitScreen(get_split_screen()), kSplitOffset(get_split_offset()) { |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 60 | 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 Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 80 | } |
| 81 | |
| 82 | HealthdDraw::~HealthdDraw() {} |
| 83 | |
| 84 | void HealthdDraw::redraw_screen(const animation* batt_anim, GRSurface* surf_unknown) { |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 85 | if (!graphics_available) return; |
| 86 | clear_screen(); |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 87 | |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 88 | /* try to display *something* */ |
Ken Tsou | 6c7ece7 | 2019-02-15 10:50:58 +0800 | [diff] [blame] | 89 | if (batt_anim->cur_status == BATTERY_STATUS_UNKNOWN || batt_anim->cur_level < 0 || |
| 90 | batt_anim->num_frames == 0) |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 91 | draw_unknown(surf_unknown); |
| 92 | else |
| 93 | draw_battery(batt_anim); |
| 94 | gr_flip(); |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 95 | } |
| 96 | |
Jack Wu | c1b1711 | 2021-09-29 22:27:56 +0800 | [diff] [blame] | 97 | void HealthdDraw::blank_screen(bool blank, int drm) { |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 98 | if (!graphics_available) return; |
Jack Wu | c1b1711 | 2021-09-29 22:27:56 +0800 | [diff] [blame] | 99 | gr_fb_blank(blank, drm); |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 100 | } |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 101 | |
Jack Wu | 20b8a01 | 2022-08-29 17:59:09 +0800 | [diff] [blame] | 102 | // support screen rotation for foldable phone |
Jack Wu | 56540a0 | 2021-10-22 17:10:37 +0800 | [diff] [blame] | 103 | void 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 Wu | 20b8a01 | 2022-08-29 17:59:09 +0800 | [diff] [blame] | 111 | // detect dual display |
| 112 | bool HealthdDraw::has_multiple_connectors() { |
| 113 | return graphics_available && gr_has_multiple_connectors(); |
| 114 | } |
| 115 | |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 116 | void HealthdDraw::clear_screen(void) { |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 117 | if (!graphics_available) return; |
| 118 | gr_color(0, 0, 0, 255); |
| 119 | gr_clear(); |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 120 | } |
| 121 | |
| 122 | int HealthdDraw::draw_surface_centered(GRSurface* surface) { |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 123 | if (!graphics_available) return 0; |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 124 | |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 125 | 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 Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 130 | LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y); |
| 131 | gr_blit(surface, 0, 0, w, h, x, y); |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 132 | 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 Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 137 | |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 138 | return y + h; |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 139 | } |
| 140 | |
| 141 | int HealthdDraw::draw_text(const GRFont* font, int x, int y, const char* str) { |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 142 | if (!graphics_available) return 0; |
| 143 | int str_len_px = gr_measure(font, str); |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 144 | |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 145 | 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 Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 149 | |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 150 | return y + char_height_; |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 151 | } |
| 152 | |
| 153 | void HealthdDraw::determine_xy(const animation::text_field& field, |
| 154 | const int length, int* x, int* y) { |
| 155 | *x = field.pos_x; |
Jack Wu | c1b1711 | 2021-09-29 22:27:56 +0800 | [diff] [blame] | 156 | screen_width_ = gr_fb_width() / (kSplitScreen ? 2 : 1); |
| 157 | screen_height_ = gr_fb_height(); |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 158 | |
| 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 | |
| 179 | void HealthdDraw::draw_clock(const animation* anim) { |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 180 | static constexpr char CLOCK_FORMAT[] = "%H:%M"; |
| 181 | static constexpr int CLOCK_LENGTH = 6; |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 182 | |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 183 | const animation::text_field& field = anim->text_clock; |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 184 | |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 185 | if (!graphics_available || field.font == nullptr || field.font->char_width == 0 || |
| 186 | field.font->char_height == 0) |
| 187 | return; |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 188 | |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 189 | time_t rawtime; |
| 190 | time(&rawtime); |
| 191 | tm* time_info = localtime(&rawtime); |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 192 | |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 193 | 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 Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 199 | |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 200 | int x, y; |
| 201 | determine_xy(field, length, &x, &y); |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 202 | |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 203 | 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 Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 206 | } |
| 207 | |
| 208 | void HealthdDraw::draw_percent(const animation* anim) { |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 209 | 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 Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 214 | |
kentsou | ba61ea4 | 2018-07-17 17:49:34 +0800 | [diff] [blame] | 215 | if (cur_level < 0) return; |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 216 | |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 217 | 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 Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 221 | |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 222 | std::string str = base::StringPrintf("%d%%", cur_level); |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 223 | |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 224 | int x, y; |
| 225 | determine_xy(field, str.size(), &x, &y); |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 226 | |
Stefan Berger | af01f59 | 2018-12-10 23:51:26 +0100 | [diff] [blame] | 227 | int batt_height = 0; |
| 228 | // get height of battery image to draw text below |
| 229 | const animation::frame& frame = anim->frames[anim->cur_frame]; |
| 230 | if (anim->num_frames != 0) { |
| 231 | // nothing else should happen actually |
| 232 | batt_height = gr_get_height(frame.surface); |
| 233 | } |
| 234 | // draw it below the battery image |
| 235 | y = (gr_fb_height() + batt_height) / 2 + char_height_ * 2; |
| 236 | |
| 237 | if (cur_level < 15) { |
| 238 | gr_color(255, 0, 0, 255); // red |
| 239 | } else if (cur_level < 50) { |
| 240 | gr_color(255, 128, 0, 255); // orange |
| 241 | } else if (cur_level < 90) { |
| 242 | gr_color(field.color_r, field.color_g, field.color_b, field.color_a); |
| 243 | } else { // cur_level >= 90 |
| 244 | gr_color(0, 255, 0, 255); // green |
| 245 | } |
| 246 | |
| 247 | LOGV("drawing percent %s %d %d\n", str.c_str(), x, y); |
| 248 | draw_text(field.font, x, y, str.c_str()); |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 249 | } |
| 250 | |
| 251 | void HealthdDraw::draw_battery(const animation* anim) { |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 252 | if (!graphics_available) return; |
| 253 | const animation::frame& frame = anim->frames[anim->cur_frame]; |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 254 | |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 255 | if (anim->num_frames != 0) { |
| 256 | draw_surface_centered(frame.surface); |
| 257 | LOGV("drawing frame #%d min_cap=%d time=%d\n", anim->cur_frame, frame.min_level, |
| 258 | frame.disp_time); |
| 259 | } |
| 260 | draw_clock(anim); |
| 261 | draw_percent(anim); |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 262 | } |
| 263 | |
| 264 | void HealthdDraw::draw_unknown(GRSurface* surf_unknown) { |
| 265 | int y; |
| 266 | if (surf_unknown) { |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 267 | draw_surface_centered(surf_unknown); |
| 268 | } else if (sys_font) { |
| 269 | gr_color(0xa4, 0xc6, 0x39, 255); |
| 270 | y = draw_text(sys_font, -1, -1, "Charging!"); |
| 271 | draw_text(sys_font, -1, y + 25, "?\?/100"); |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 272 | } else { |
Todd Poynor | e5d1b62 | 2018-06-01 11:09:58 -0700 | [diff] [blame] | 273 | LOGW("Charging, level unknown\n"); |
Luke Song | 7f386dc | 2017-07-13 15:10:35 -0700 | [diff] [blame] | 274 | } |
| 275 | } |
Xiaohui Niu | a40d872 | 2021-08-31 16:22:25 +0800 | [diff] [blame] | 276 | |
| 277 | std::unique_ptr<HealthdDraw> HealthdDraw::Create(animation *anim) { |
| 278 | if (gr_init() < 0) { |
| 279 | LOGE("gr_init failed\n"); |
| 280 | return nullptr; |
| 281 | } |
| 282 | return std::unique_ptr<HealthdDraw>(new HealthdDraw(anim)); |
| 283 | } |