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 26HealthdDraw::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 48HealthdDraw::~HealthdDraw() {} 49 50void 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 61void HealthdDraw::blank_screen(bool blank) { gr_fb_blank(blank); } 62 63void HealthdDraw::clear_screen(void) { 64 gr_color(0, 0, 0, 255); 65 gr_clear(); 66} 67 68int 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 85int 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 97void 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 121void 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 150void 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 174void 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 186void 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