text/plain
•
7.58 KB
•
204 lines
#include "player_screen.h"
#include "files_screen.h"
#include "../audio_player.h"
#include "../storage.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
static void format_time(uint32_t ms, char *buf, size_t buf_size) {
uint32_t total_sec = ms / 1000;
uint32_t hours = total_sec / 3600;
uint32_t mins = (total_sec % 3600) / 60;
uint32_t secs = total_sec % 60;
if (hours > 0) {
snprintf(buf, buf_size, "%u:%02u:%02u", (unsigned)hours, (unsigned)mins, (unsigned)secs);
} else {
snprintf(buf, buf_size, "%u:%02u", (unsigned)mins, (unsigned)secs);
}
}
// Navigate back to files screen, restoring directory from last played path
static void navigate_to_files(void) {
char dir[STORAGE_MAX_PATH];
char filename[STORAGE_MAX_NAME];
if (app_state.last_played_path[0] != '\0') {
// Copy path and find last slash
strncpy(dir, app_state.last_played_path, sizeof(dir));
dir[sizeof(dir) - 1] = '\0';
char *last_slash = strrchr(dir, '/');
if (last_slash && last_slash != dir) {
// Extract filename and truncate to get directory
strncpy(filename, last_slash + 1, sizeof(filename));
filename[sizeof(filename) - 1] = '\0';
*last_slash = '\0';
} else if (last_slash == dir) {
// File is in root directory
strncpy(filename, dir + 1, sizeof(filename));
filename[sizeof(filename) - 1] = '\0';
strcpy(dir, "/");
} else {
// Fallback
strcpy(dir, "/");
filename[0] = '\0';
}
} else {
strcpy(dir, "/");
filename[0] = '\0';
}
free_screen();
ui_state = setup_files_screen(dir, filename);
}
static void cancel_event_cb(lv_event_t *e) {
(void)e;
audio_player_stop();
navigate_to_files();
}
static void play_btn_cb(lv_event_t *e) {
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED) {
audio_state_t state = audio_player_get_state();
if (state == AUDIO_STATE_PLAYING) {
audio_player_pause();
} else if (state == AUDIO_STATE_PAUSED) {
audio_player_resume();
}
}
}
static void stop_btn_cb(lv_event_t *e) {
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED) {
audio_player_stop();
navigate_to_files();
}
}
ui_state_t setup_player_screen(void) {
player_screen_state_t *state = calloc(1, sizeof(player_screen_state_t));
// Main container
state->container = lv_obj_create(lv_screen_active());
lv_obj_set_size(state->container, LV_PCT(100), LV_PCT(100));
lv_obj_set_flex_flow(state->container, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(state->container, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_all(state->container, 8, 0);
lv_obj_set_style_pad_gap(state->container, 6, 0);
lv_obj_clear_flag(state->container, LV_OBJ_FLAG_SCROLLABLE);
// Title label (scrolling) - shows "Artist - Title" or filename
state->title_label = lv_label_create(state->container);
const char *title = audio_player_get_title();
lv_label_set_text(state->title_label, title ? title : "Unknown");
lv_label_set_long_mode(state->title_label, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_obj_set_width(state->title_label, LV_PCT(95));
// Time row container (position on left, duration on right)
lv_obj_t *time_cont = lv_obj_create(state->container);
lv_obj_set_size(time_cont, 144, LV_SIZE_CONTENT);
lv_obj_set_flex_flow(time_cont, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(time_cont, LV_FLEX_ALIGN_SPACE_BETWEEN, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_all(time_cont, 0, 0);
lv_obj_set_style_border_width(time_cont, 0, 0);
lv_obj_set_style_bg_opa(time_cont, LV_OPA_TRANSP, 0);
// Position label (left)
state->position_label = lv_label_create(time_cont);
lv_label_set_text(state->position_label, "0:00");
lv_obj_set_style_text_color(state->position_label, lv_color_hex(0xAAAAAA), 0);
// Duration label (right)
state->duration_label = lv_label_create(time_cont);
lv_label_set_text(state->duration_label, "0:00");
lv_obj_set_style_text_color(state->duration_label, lv_color_hex(0xAAAAAA), 0);
// Progress bar
state->progress_bar = lv_bar_create(state->container);
lv_obj_set_size(state->progress_bar, 144, 6);
lv_bar_set_range(state->progress_bar, 0, 100);
lv_bar_set_value(state->progress_bar, 0, LV_ANIM_OFF);
lv_obj_set_style_bg_color(state->progress_bar, lv_color_hex(0x444444), LV_PART_MAIN);
lv_obj_set_style_bg_color(state->progress_bar, lv_color_hex(0xE91E63), LV_PART_INDICATOR);
// Button container
lv_obj_t *btn_cont = lv_obj_create(state->container);
lv_obj_set_size(btn_cont, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_flex_flow(btn_cont, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(btn_cont, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_all(btn_cont, 4, 0);
lv_obj_set_style_border_width(btn_cont, 0, 0);
lv_obj_set_style_bg_opa(btn_cont, LV_OPA_TRANSP, 0);
// Play/Pause button
state->play_btn = lv_button_create(btn_cont);
lv_obj_set_size(state->play_btn, 24, 24);
lv_obj_t *play_label = lv_label_create(state->play_btn);
lv_label_set_text(play_label, LV_SYMBOL_PAUSE);
lv_obj_center(play_label);
lv_obj_add_event_cb(state->play_btn, play_btn_cb, LV_EVENT_CLICKED, state);
lv_obj_add_event_cb(state->play_btn, cancel_event_cb, LV_EVENT_CANCEL, NULL);
lv_group_add_obj(lv_group_get_default(), state->play_btn);
// Stop button
state->stop_btn = lv_button_create(btn_cont);
lv_obj_set_size(state->stop_btn, 24, 24);
lv_obj_t *stop_label = lv_label_create(state->stop_btn);
lv_label_set_text(stop_label, LV_SYMBOL_STOP);
lv_obj_center(stop_label);
lv_obj_add_event_cb(state->stop_btn, stop_btn_cb, LV_EVENT_CLICKED, NULL);
lv_obj_add_event_cb(state->stop_btn, cancel_event_cb, LV_EVENT_CANCEL, NULL);
lv_group_add_obj(lv_group_get_default(), state->stop_btn);
// Focus play button by default
lv_group_focus_obj(state->play_btn);
return (ui_state_t){.type = SCREEN_PLAYER, .player = state};
}
void free_player_screen(player_screen_state_t *state) {
if (!state) return;
lv_group_remove_obj(state->play_btn);
lv_group_remove_obj(state->stop_btn);
lv_obj_delete(state->container);
free(state);
}
void update_player_screen(player_screen_state_t *state) {
if (!state) return;
audio_state_t audio_state = audio_player_get_state();
// Update play/pause button icon
lv_obj_t *play_label = lv_obj_get_child(state->play_btn, 0);
if (audio_state == AUDIO_STATE_PLAYING || audio_state == AUDIO_STATE_PAUSED) {
lv_label_set_text(play_label, audio_state == AUDIO_STATE_PLAYING ? LV_SYMBOL_PAUSE : LV_SYMBOL_PLAY);
} else {
lv_label_set_text(play_label, LV_SYMBOL_PLAY);
}
// Update progress bar and time labels
uint32_t position_ms = audio_player_get_position_ms();
uint32_t duration_ms = audio_player_get_duration_ms();
uint8_t progress = audio_player_get_progress();
lv_bar_set_value(state->progress_bar, progress, LV_ANIM_OFF);
char time_buf[16];
format_time(position_ms, time_buf, sizeof(time_buf));
lv_label_set_text(state->position_label, time_buf);
if (duration_ms > 0) {
format_time(duration_ms, time_buf, sizeof(time_buf));
lv_label_set_text(state->duration_label, time_buf);
} else {
lv_label_set_text(state->duration_label, "--:--");
}
}