2017-07-14 00:10:35 +02:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2017 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <android-base/stringprintf.h>
|
|
|
|
#include <batteryservice/BatteryService.h>
|
|
|
|
#include <cutils/klog.h>
|
|
|
|
|
|
|
|
#include "healthd_draw.h"
|
|
|
|
|
2021-10-21 07:18:16 +02:00
|
|
|
#if !defined(__ANDROID_VNDK__)
|
|
|
|
#include "charger.sysprop.h"
|
|
|
|
#endif
|
|
|
|
|
2017-07-14 00:10:35 +02:00
|
|
|
#define LOGE(x...) KLOG_ERROR("charger", x);
|
2018-06-01 20:09:58 +02:00
|
|
|
#define LOGW(x...) KLOG_WARNING("charger", x);
|
2017-07-14 00:10:35 +02:00
|
|
|
#define LOGV(x...) KLOG_DEBUG("charger", x);
|
|
|
|
|
2019-07-03 20:07:37 +02:00
|
|
|
static bool get_split_screen() {
|
2021-10-21 07:18:16 +02:00
|
|
|
#if !defined(__ANDROID_VNDK__)
|
2019-07-03 20:07:37 +02:00
|
|
|
return android::sysprop::ChargerProperties::draw_split_screen().value_or(false);
|
2021-10-21 07:18:16 +02:00
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
2019-07-03 20:07:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int get_split_offset() {
|
2021-10-21 07:18:16 +02:00
|
|
|
#if !defined(__ANDROID_VNDK__)
|
2019-07-03 20:07:37 +02:00
|
|
|
int64_t value = android::sysprop::ChargerProperties::draw_split_offset().value_or(0);
|
2021-10-21 07:18:16 +02:00
|
|
|
#else
|
|
|
|
int64_t value = 0;
|
|
|
|
#endif
|
2019-07-03 20:07:37 +02:00
|
|
|
if (value < static_cast<int64_t>(std::numeric_limits<int>::min())) {
|
|
|
|
LOGW("draw_split_offset = %" PRId64 " overflow for an int; resetting to %d.\n", value,
|
|
|
|
std::numeric_limits<int>::min());
|
|
|
|
value = std::numeric_limits<int>::min();
|
|
|
|
}
|
|
|
|
if (value > static_cast<int64_t>(std::numeric_limits<int>::max())) {
|
|
|
|
LOGW("draw_split_offset = %" PRId64 " overflow for an int; resetting to %d.\n", value,
|
|
|
|
std::numeric_limits<int>::max());
|
|
|
|
value = std::numeric_limits<int>::max();
|
|
|
|
}
|
|
|
|
return static_cast<int>(value);
|
|
|
|
}
|
|
|
|
|
2017-07-14 00:10:35 +02:00
|
|
|
HealthdDraw::HealthdDraw(animation* anim)
|
2019-07-03 20:07:37 +02:00
|
|
|
: kSplitScreen(get_split_screen()), kSplitOffset(get_split_offset()) {
|
2018-06-01 20:09:58 +02:00
|
|
|
graphics_available = true;
|
|
|
|
sys_font = gr_sys_font();
|
|
|
|
if (sys_font == nullptr) {
|
|
|
|
LOGW("No system font, screen fallback text not available\n");
|
|
|
|
} else {
|
|
|
|
gr_font_size(sys_font, &char_width_, &char_height_);
|
|
|
|
}
|
|
|
|
|
|
|
|
screen_width_ = gr_fb_width() / (kSplitScreen ? 2 : 1);
|
|
|
|
screen_height_ = gr_fb_height();
|
|
|
|
|
|
|
|
int res;
|
|
|
|
if (!anim->text_clock.font_file.empty() &&
|
|
|
|
(res = gr_init_font(anim->text_clock.font_file.c_str(), &anim->text_clock.font)) < 0) {
|
|
|
|
LOGE("Could not load time font (%d)\n", res);
|
|
|
|
}
|
|
|
|
if (!anim->text_percent.font_file.empty() &&
|
|
|
|
(res = gr_init_font(anim->text_percent.font_file.c_str(), &anim->text_percent.font)) < 0) {
|
|
|
|
LOGE("Could not load percent font (%d)\n", res);
|
|
|
|
}
|
2017-07-14 00:10:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
HealthdDraw::~HealthdDraw() {}
|
|
|
|
|
|
|
|
void HealthdDraw::redraw_screen(const animation* batt_anim, GRSurface* surf_unknown) {
|
2018-06-01 20:09:58 +02:00
|
|
|
if (!graphics_available) return;
|
|
|
|
clear_screen();
|
|
|
|
|
|
|
|
/* try to display *something* */
|
2019-02-15 03:50:58 +01:00
|
|
|
if (batt_anim->cur_status == BATTERY_STATUS_UNKNOWN || batt_anim->cur_level < 0 ||
|
|
|
|
batt_anim->num_frames == 0)
|
2018-06-01 20:09:58 +02:00
|
|
|
draw_unknown(surf_unknown);
|
|
|
|
else
|
|
|
|
draw_battery(batt_anim);
|
|
|
|
gr_flip();
|
2017-07-14 00:10:35 +02:00
|
|
|
}
|
|
|
|
|
2021-09-29 16:27:56 +02:00
|
|
|
void HealthdDraw::blank_screen(bool blank, int drm) {
|
2018-06-01 20:09:58 +02:00
|
|
|
if (!graphics_available) return;
|
2021-09-29 16:27:56 +02:00
|
|
|
gr_fb_blank(blank, drm);
|
2018-06-01 20:09:58 +02:00
|
|
|
}
|
2017-07-14 00:10:35 +02:00
|
|
|
|
2022-08-29 11:59:09 +02:00
|
|
|
// support screen rotation for foldable phone
|
2021-10-22 11:10:37 +02:00
|
|
|
void HealthdDraw::rotate_screen(int drm) {
|
|
|
|
if (!graphics_available) return;
|
|
|
|
if (drm == 0)
|
|
|
|
gr_rotate(GRRotation::RIGHT /* landscape mode */);
|
|
|
|
else
|
|
|
|
gr_rotate(GRRotation::NONE /* Portrait mode */);
|
|
|
|
}
|
|
|
|
|
2022-08-29 11:59:09 +02:00
|
|
|
// detect dual display
|
|
|
|
bool HealthdDraw::has_multiple_connectors() {
|
|
|
|
return graphics_available && gr_has_multiple_connectors();
|
|
|
|
}
|
|
|
|
|
2017-07-14 00:10:35 +02:00
|
|
|
void HealthdDraw::clear_screen(void) {
|
2018-06-01 20:09:58 +02:00
|
|
|
if (!graphics_available) return;
|
|
|
|
gr_color(0, 0, 0, 255);
|
|
|
|
gr_clear();
|
2017-07-14 00:10:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int HealthdDraw::draw_surface_centered(GRSurface* surface) {
|
2018-06-01 20:09:58 +02:00
|
|
|
if (!graphics_available) return 0;
|
|
|
|
|
|
|
|
int w = gr_get_width(surface);
|
|
|
|
int h = gr_get_height(surface);
|
|
|
|
int x = (screen_width_ - w) / 2 + kSplitOffset;
|
|
|
|
int y = (screen_height_ - h) / 2;
|
|
|
|
|
2017-07-14 00:10:35 +02:00
|
|
|
LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y);
|
|
|
|
gr_blit(surface, 0, 0, w, h, x, y);
|
2018-06-01 20:09:58 +02:00
|
|
|
if (kSplitScreen) {
|
|
|
|
x += screen_width_ - 2 * kSplitOffset;
|
|
|
|
LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y);
|
|
|
|
gr_blit(surface, 0, 0, w, h, x, y);
|
|
|
|
}
|
2017-07-14 00:10:35 +02:00
|
|
|
|
2018-06-01 20:09:58 +02:00
|
|
|
return y + h;
|
2017-07-14 00:10:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int HealthdDraw::draw_text(const GRFont* font, int x, int y, const char* str) {
|
2018-06-01 20:09:58 +02:00
|
|
|
if (!graphics_available) return 0;
|
|
|
|
int str_len_px = gr_measure(font, str);
|
2017-07-14 00:10:35 +02:00
|
|
|
|
2018-06-01 20:09:58 +02:00
|
|
|
if (x < 0) x = (screen_width_ - str_len_px) / 2;
|
|
|
|
if (y < 0) y = (screen_height_ - char_height_) / 2;
|
|
|
|
gr_text(font, x + kSplitOffset, y, str, false /* bold */);
|
|
|
|
if (kSplitScreen) gr_text(font, x - kSplitOffset + screen_width_, y, str, false /* bold */);
|
2017-07-14 00:10:35 +02:00
|
|
|
|
2018-06-01 20:09:58 +02:00
|
|
|
return y + char_height_;
|
2017-07-14 00:10:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void HealthdDraw::determine_xy(const animation::text_field& field,
|
|
|
|
const int length, int* x, int* y) {
|
|
|
|
*x = field.pos_x;
|
2021-09-29 16:27:56 +02:00
|
|
|
screen_width_ = gr_fb_width() / (kSplitScreen ? 2 : 1);
|
|
|
|
screen_height_ = gr_fb_height();
|
2017-07-14 00:10:35 +02:00
|
|
|
|
|
|
|
int str_len_px = length * field.font->char_width;
|
|
|
|
if (field.pos_x == CENTER_VAL) {
|
|
|
|
*x = (screen_width_ - str_len_px) / 2;
|
|
|
|
} else if (field.pos_x >= 0) {
|
|
|
|
*x = field.pos_x;
|
|
|
|
} else { // position from max edge
|
|
|
|
*x = screen_width_ + field.pos_x - str_len_px - kSplitOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
*y = field.pos_y;
|
|
|
|
|
|
|
|
if (field.pos_y == CENTER_VAL) {
|
|
|
|
*y = (screen_height_ - field.font->char_height) / 2;
|
|
|
|
} else if (field.pos_y >= 0) {
|
|
|
|
*y = field.pos_y;
|
|
|
|
} else { // position from max edge
|
|
|
|
*y = screen_height_ + field.pos_y - field.font->char_height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HealthdDraw::draw_clock(const animation* anim) {
|
2018-06-01 20:09:58 +02:00
|
|
|
static constexpr char CLOCK_FORMAT[] = "%H:%M";
|
|
|
|
static constexpr int CLOCK_LENGTH = 6;
|
2017-07-14 00:10:35 +02:00
|
|
|
|
2018-06-01 20:09:58 +02:00
|
|
|
const animation::text_field& field = anim->text_clock;
|
2017-07-14 00:10:35 +02:00
|
|
|
|
2018-06-01 20:09:58 +02:00
|
|
|
if (!graphics_available || field.font == nullptr || field.font->char_width == 0 ||
|
|
|
|
field.font->char_height == 0)
|
|
|
|
return;
|
2017-07-14 00:10:35 +02:00
|
|
|
|
2018-06-01 20:09:58 +02:00
|
|
|
time_t rawtime;
|
|
|
|
time(&rawtime);
|
|
|
|
tm* time_info = localtime(&rawtime);
|
2017-07-14 00:10:35 +02:00
|
|
|
|
2018-06-01 20:09:58 +02:00
|
|
|
char clock_str[CLOCK_LENGTH];
|
|
|
|
size_t length = strftime(clock_str, CLOCK_LENGTH, CLOCK_FORMAT, time_info);
|
|
|
|
if (length != CLOCK_LENGTH - 1) {
|
|
|
|
LOGE("Could not format time\n");
|
|
|
|
return;
|
|
|
|
}
|
2017-07-14 00:10:35 +02:00
|
|
|
|
2018-06-01 20:09:58 +02:00
|
|
|
int x, y;
|
|
|
|
determine_xy(field, length, &x, &y);
|
2017-07-14 00:10:35 +02:00
|
|
|
|
2018-06-01 20:09:58 +02:00
|
|
|
LOGV("drawing clock %s %d %d\n", clock_str, x, y);
|
|
|
|
gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
|
|
|
|
draw_text(field.font, x, y, clock_str);
|
2017-07-14 00:10:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void HealthdDraw::draw_percent(const animation* anim) {
|
2018-06-01 20:09:58 +02:00
|
|
|
if (!graphics_available) return;
|
|
|
|
int cur_level = anim->cur_level;
|
|
|
|
if (anim->cur_status == BATTERY_STATUS_FULL) {
|
|
|
|
cur_level = 100;
|
|
|
|
}
|
2017-07-14 00:10:35 +02:00
|
|
|
|
2018-07-17 11:49:34 +02:00
|
|
|
if (cur_level < 0) return;
|
2017-07-14 00:10:35 +02:00
|
|
|
|
2018-06-01 20:09:58 +02:00
|
|
|
const animation::text_field& field = anim->text_percent;
|
|
|
|
if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0) {
|
|
|
|
return;
|
|
|
|
}
|
2017-07-14 00:10:35 +02:00
|
|
|
|
2018-06-01 20:09:58 +02:00
|
|
|
std::string str = base::StringPrintf("%d%%", cur_level);
|
2017-07-14 00:10:35 +02:00
|
|
|
|
2018-06-01 20:09:58 +02:00
|
|
|
int x, y;
|
|
|
|
determine_xy(field, str.size(), &x, &y);
|
2017-07-14 00:10:35 +02:00
|
|
|
|
2018-06-01 20:09:58 +02:00
|
|
|
LOGV("drawing percent %s %d %d\n", str.c_str(), x, y);
|
|
|
|
gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
|
|
|
|
draw_text(field.font, x, y, str.c_str());
|
2017-07-14 00:10:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void HealthdDraw::draw_battery(const animation* anim) {
|
2018-06-01 20:09:58 +02:00
|
|
|
if (!graphics_available) return;
|
|
|
|
const animation::frame& frame = anim->frames[anim->cur_frame];
|
|
|
|
|
|
|
|
if (anim->num_frames != 0) {
|
|
|
|
draw_surface_centered(frame.surface);
|
|
|
|
LOGV("drawing frame #%d min_cap=%d time=%d\n", anim->cur_frame, frame.min_level,
|
|
|
|
frame.disp_time);
|
|
|
|
}
|
|
|
|
draw_clock(anim);
|
|
|
|
draw_percent(anim);
|
2017-07-14 00:10:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void HealthdDraw::draw_unknown(GRSurface* surf_unknown) {
|
|
|
|
int y;
|
|
|
|
if (surf_unknown) {
|
2018-06-01 20:09:58 +02:00
|
|
|
draw_surface_centered(surf_unknown);
|
|
|
|
} else if (sys_font) {
|
|
|
|
gr_color(0xa4, 0xc6, 0x39, 255);
|
|
|
|
y = draw_text(sys_font, -1, -1, "Charging!");
|
|
|
|
draw_text(sys_font, -1, y + 25, "?\?/100");
|
2017-07-14 00:10:35 +02:00
|
|
|
} else {
|
2018-06-01 20:09:58 +02:00
|
|
|
LOGW("Charging, level unknown\n");
|
2017-07-14 00:10:35 +02:00
|
|
|
}
|
|
|
|
}
|
2021-08-31 10:22:25 +02:00
|
|
|
|
|
|
|
std::unique_ptr<HealthdDraw> HealthdDraw::Create(animation *anim) {
|
|
|
|
if (gr_init() < 0) {
|
|
|
|
LOGE("gr_init failed\n");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return std::unique_ptr<HealthdDraw>(new HealthdDraw(anim));
|
|
|
|
}
|