text/plain
•
4.95 KB
•
157 lines
#include "files_screen.h"
#include "../storage.h"
#include "../audio_player.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define MAX_ENTRIES 32
// Case-insensitive extension check helper
static bool ext_match(const char *ext, const char *pattern) {
while (*pattern) {
char c = *ext++;
char p = *pattern++;
// Convert to lowercase for comparison
if (c >= 'A' && c <= 'Z') c += 32;
if (p >= 'A' && p <= 'Z') p += 32;
if (c != p) return false;
}
return true;
}
// Check if filename is a supported audio file
static bool is_audio_file(const char *name) {
size_t len = strlen(name);
if (len < 4) return false;
// Find last dot
const char *dot = strrchr(name, '.');
if (!dot || dot == name) return false;
// Check supported extensions
return ext_match(dot, ".mp3") ||
ext_match(dot, ".wav") ||
ext_match(dot, ".aac") ||
ext_match(dot, ".m4a") ||
ext_match(dot, ".flac") ||
ext_match(dot, ".ogg");
}
static void cancel_event_cb(lv_event_t *e) {
(void)e;
navigate_to(SCREEN_HOME);
}
static void back_event_cb(lv_event_t *e) {
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED) {
navigate_to(SCREEN_HOME);
}
}
static void file_click_cb(lv_event_t *e) {
lv_event_code_t code = lv_event_get_code(e);
if (code != LV_EVENT_CLICKED) return;
lv_obj_t *btn = lv_event_get_target(e);
// Get the label child (second child after the icon)
lv_obj_t *label = lv_obj_get_child(btn, 1);
if (!label) return;
const char *filename = lv_label_get_text(label);
if (!filename) return;
if (is_audio_file(filename)) {
// Build path (files are in root directory)
char path[STORAGE_MAX_PATH];
snprintf(path, sizeof(path), "/%s", filename);
if (audio_player_play(path)) {
navigate_to(SCREEN_PLAYER);
}
}
}
ui_state_t setup_files_screen(void) {
files_screen_state_t *state = calloc(1, sizeof(files_screen_state_t));
// Create a list that fills the screen
state->list = lv_list_create(lv_screen_active());
lv_obj_set_size(state->list, LV_PCT(100), LV_PCT(100));
lv_obj_center(state->list);
// Add list header with styled background
lv_obj_t *header = lv_list_add_text(state->list, "Files");
lv_obj_set_style_bg_color(header, lv_color_hex(0x0288D1), 0);
lv_obj_set_style_bg_opa(header, LV_OPA_COVER, 0);
lv_obj_set_style_text_color(header, lv_color_hex(0xFFFFFF), 0);
lv_obj_t *first_btn = NULL;
if (!storage_is_mounted()) {
lv_obj_t *error_label = lv_list_add_text(state->list, "SD card not mounted");
(void)error_label;
} else {
// List root directory contents
storage_entry_t entries[MAX_ENTRIES];
int count = storage_list_dir("/", entries, MAX_ENTRIES);
if (count < 0) {
lv_obj_t *error_label = lv_list_add_text(state->list, "Error reading directory");
(void)error_label;
} else if (count == 0) {
lv_obj_t *empty_label = lv_list_add_text(state->list, "(empty)");
(void)empty_label;
} else {
for (int i = 0; i < count; i++) {
const char *icon;
if (entries[i].type == STORAGE_TYPE_DIR) {
icon = LV_SYMBOL_DIRECTORY;
} else if (is_audio_file(entries[i].name)) {
icon = LV_SYMBOL_AUDIO;
} else {
icon = LV_SYMBOL_FILE;
}
lv_obj_t *btn = lv_list_add_button(state->list, icon, entries[i].name);
lv_obj_add_event_cb(btn, cancel_event_cb, LV_EVENT_CANCEL, NULL);
lv_obj_add_event_cb(btn, file_click_cb, LV_EVENT_CLICKED, NULL);
lv_group_add_obj(lv_group_get_default(), btn);
if (!first_btn) {
first_btn = btn;
}
}
}
}
// Add Back button
lv_obj_t *back_btn = lv_list_add_button(state->list, LV_SYMBOL_LEFT, "Back");
lv_obj_add_event_cb(back_btn, back_event_cb, LV_EVENT_CLICKED, NULL);
lv_obj_add_event_cb(back_btn, cancel_event_cb, LV_EVENT_CANCEL, NULL);
lv_group_add_obj(lv_group_get_default(), back_btn);
// Focus first file entry if available, otherwise back button
lv_group_focus_obj(first_btn ? first_btn : back_btn);
return (ui_state_t){.type = SCREEN_FILES, .files = state};
}
void free_files_screen(files_screen_state_t *state) {
if (!state) return;
// Get all children from list and remove from group
uint32_t child_count = lv_obj_get_child_count(state->list);
for (uint32_t i = 0; i < child_count; i++) {
lv_obj_t *child = lv_obj_get_child(state->list, i);
lv_group_remove_obj(child);
}
lv_obj_delete(state->list);
free(state);
}
void update_files_screen(files_screen_state_t *state) {
(void)state; // LVGL handles updates via timer
}