feat(ImmichElias): add person selector like Bible books, 4 persons Elias/Grace/Alicia/Adolfo, picker UI with toolbar, player bottom-only, random pagination, cache, fullscreen
This commit is contained in:
@@ -38,6 +38,20 @@ static const char* TAG = "ImmichElias";
|
|||||||
#define FALLBACK_W 320
|
#define FALLBACK_W 320
|
||||||
#define FALLBACK_H 240
|
#define FALLBACK_H 240
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char id[64];
|
||||||
|
char name[32];
|
||||||
|
char count_str[16];
|
||||||
|
} PersonInfo;
|
||||||
|
|
||||||
|
static const PersonInfo PERSONS[] = {
|
||||||
|
{"cbece6a9-720f-482a-a945-c9ba5ca41fd4", "Elias", "10739"},
|
||||||
|
{"ad40f024-c34b-4ec6-a19e-2fa157b259e5", "Grace", ""},
|
||||||
|
{"6d9e4ae6-143d-43c9-bd42-437e3edfe2df", "Alicia", ""},
|
||||||
|
{"3ddbc731-e857-473c-8226-8af97e5cb86e", "Adolfo", ""},
|
||||||
|
};
|
||||||
|
#define PERSONS_COUNT (sizeof(PERSONS)/sizeof(PERSONS[0]))
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char id[64];
|
char id[64];
|
||||||
char thumbnail_url[256];
|
char thumbnail_url[256];
|
||||||
@@ -150,7 +164,8 @@ static void shuffle_photos(void) {
|
|||||||
ESP_LOGI(TAG, "Photos shuffled random, count %d", photo_count);
|
ESP_LOGI(TAG, "Photos shuffled random, count %d", photo_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fetch_person_assets(void) {
|
static int fetch_person_assets(const char* person_id) {
|
||||||
|
if (!person_id || person_id[0]=='\0') person_id = PERSONS[0].id;
|
||||||
char* json_buf = heap_caps_malloc(JSON_BUF_SIZE, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
char* json_buf = heap_caps_malloc(JSON_BUF_SIZE, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||||
if (!json_buf) json_buf = heap_caps_malloc(JSON_BUF_SIZE, MALLOC_CAP_DEFAULT | MALLOC_CAP_8BIT);
|
if (!json_buf) json_buf = heap_caps_malloc(JSON_BUF_SIZE, MALLOC_CAP_DEFAULT | MALLOC_CAP_8BIT);
|
||||||
if (!json_buf) return -1;
|
if (!json_buf) return -1;
|
||||||
@@ -158,14 +173,21 @@ static int fetch_person_assets(void) {
|
|||||||
char url[256];
|
char url[256];
|
||||||
snprintf(url, sizeof(url), "%s/api/search/metadata", IMMICH_BASE_URL);
|
snprintf(url, sizeof(url), "%s/api/search/metadata", IMMICH_BASE_URL);
|
||||||
// Pick random page to get variety from 10k+ photos
|
// Pick random page to get variety from 10k+ photos
|
||||||
// Estimate total photos 10739, with MAX_PHOTOS 100 => ~107 pages
|
|
||||||
int total_estimate = 10739;
|
int total_estimate = 10739;
|
||||||
|
// Try to use count from PERSONS if available
|
||||||
|
for (int pi=0; pi<PERSONS_COUNT; pi++) {
|
||||||
|
if (strcmp(PERSONS[pi].id, person_id)==0 && PERSONS[pi].count_str[0]!='\0') {
|
||||||
|
total_estimate = atoi(PERSONS[pi].count_str);
|
||||||
|
if (total_estimate<=0) total_estimate=10739;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
int max_page = total_estimate / MAX_PHOTOS;
|
int max_page = total_estimate / MAX_PHOTOS;
|
||||||
if (max_page < 1) max_page = 1;
|
if (max_page < 1) max_page = 1;
|
||||||
int random_page = (esp_random() % max_page) + 1;
|
int random_page = (esp_random() % max_page) + 1;
|
||||||
char body[320];
|
char body[320];
|
||||||
snprintf(body, sizeof(body), "{\"personIds\":[\"%s\"],\"size\":%d,\"page\":%d}", IMMICH_PERSON_ID, MAX_PHOTOS, random_page);
|
snprintf(body, sizeof(body), "{\"personIds\":[\"%s\"],\"size\":%d,\"page\":%d}", person_id, MAX_PHOTOS, random_page);
|
||||||
ESP_LOGI(TAG, "Fetching person assets page %d/%d size %d", random_page, max_page, MAX_PHOTOS);
|
ESP_LOGI(TAG, "Fetching person %s assets page %d/%d size %d", person_id, random_page, max_page, MAX_PHOTOS);
|
||||||
int status = http_post_json(url, body, &hb);
|
int status = http_post_json(url, body, &hb);
|
||||||
if (status != 200) { heap_caps_free(json_buf); return -1; }
|
if (status != 200) { heap_caps_free(json_buf); return -1; }
|
||||||
if (hb.len >= JSON_BUF_SIZE) hb.len = JSON_BUF_SIZE - 1;
|
if (hb.len >= JSON_BUF_SIZE) hb.len = JSON_BUF_SIZE - 1;
|
||||||
@@ -247,6 +269,10 @@ typedef struct {
|
|||||||
AppHandle app;
|
AppHandle app;
|
||||||
volatile bool visible;
|
volatile bool visible;
|
||||||
volatile bool stop_requested;
|
volatile bool stop_requested;
|
||||||
|
// Picker (persons) like Bible book selector
|
||||||
|
lv_obj_t* picker_wrapper;
|
||||||
|
lv_obj_t* lst_persons;
|
||||||
|
// Player (photos)
|
||||||
lv_obj_t* player_wrapper;
|
lv_obj_t* player_wrapper;
|
||||||
lv_obj_t* header_bar;
|
lv_obj_t* header_bar;
|
||||||
lv_obj_t* ctrl_bar;
|
lv_obj_t* ctrl_bar;
|
||||||
@@ -275,6 +301,9 @@ typedef struct {
|
|||||||
int disp_h;
|
int disp_h;
|
||||||
int img_w;
|
int img_w;
|
||||||
int img_h;
|
int img_h;
|
||||||
|
// Current person
|
||||||
|
char current_person_id[64];
|
||||||
|
char current_person_name[32];
|
||||||
} AppCtx;
|
} AppCtx;
|
||||||
|
|
||||||
static bool get_cache_dir_path(AppCtx* ctx, char* out_dir, size_t out_size) {
|
static bool get_cache_dir_path(AppCtx* ctx, char* out_dir, size_t out_size) {
|
||||||
@@ -624,9 +653,11 @@ static void fetch_task_fn(void* arg) {
|
|||||||
if (!ctx) { vTaskDelete(NULL); return; }
|
if (!ctx) { vTaskDelete(NULL); return; }
|
||||||
if (ctx->stop_requested) { ctx->fetch_task=NULL; ctx->fetch_in_progress=false; vTaskDelete(NULL); return; }
|
if (ctx->stop_requested) { ctx->fetch_task=NULL; ctx->fetch_in_progress=false; vTaskDelete(NULL); return; }
|
||||||
ctx->fetch_in_progress=true;
|
ctx->fetch_in_progress=true;
|
||||||
snprintf(ctx->status_text, sizeof(ctx->status_text), "Fetching %s photos...", PERSON_NAME);
|
const char* fetch_name = ctx->current_person_name[0] ? ctx->current_person_name : PERSON_NAME;
|
||||||
|
const char* fetch_id = ctx->current_person_id[0] ? ctx->current_person_id : PERSONS[0].id;
|
||||||
|
snprintf(ctx->status_text, sizeof(ctx->status_text), "Fetching %s photos...", fetch_name);
|
||||||
ui_update_all(ctx);
|
ui_update_all(ctx);
|
||||||
int n = fetch_person_assets();
|
int n = fetch_person_assets(fetch_id);
|
||||||
if (ctx->stop_requested) { ctx->fetch_task=NULL; ctx->fetch_in_progress=false; vTaskDelete(NULL); return; }
|
if (ctx->stop_requested) { ctx->fetch_task=NULL; ctx->fetch_in_progress=false; vTaskDelete(NULL); return; }
|
||||||
if (n>0) {
|
if (n>0) {
|
||||||
ctx->current_idx=0;
|
ctx->current_idx=0;
|
||||||
@@ -720,13 +751,32 @@ static void reload_event_cb(lv_event_t* e) {
|
|||||||
snprintf(ctx->status_text, sizeof(ctx->status_text), "Reloading %dx%d...", ctx->img_w, ctx->img_h);
|
snprintf(ctx->status_text, sizeof(ctx->status_text), "Reloading %dx%d...", ctx->img_w, ctx->img_h);
|
||||||
ui_update_all(ctx); show_chrome(ctx); start_fetch_task(ctx);
|
ui_update_all(ctx); show_chrome(ctx); start_fetch_task(ctx);
|
||||||
}
|
}
|
||||||
static void back_event_cb(lv_event_t* e) { (void)e; tt_app_stop(); }
|
static void show_picker(AppCtx* ctx);
|
||||||
|
static void show_player(AppCtx* ctx);
|
||||||
|
|
||||||
|
static void back_event_cb(lv_event_t* e) {
|
||||||
|
AppCtx* ctx = (AppCtx*)lv_event_get_user_data(e);
|
||||||
|
if (!ctx) { tt_app_stop(); return; }
|
||||||
|
// If in player, go back to person picker like Bible book selector
|
||||||
|
if (ctx->picker_wrapper && ctx->player_wrapper) {
|
||||||
|
if (!tt_lvgl_lock(pdMS_TO_TICKS(500))) { tt_app_stop(); return; }
|
||||||
|
bool player_visible = !lv_obj_has_flag(ctx->player_wrapper, LV_OBJ_FLAG_HIDDEN);
|
||||||
|
tt_lvgl_unlock();
|
||||||
|
if (player_visible) {
|
||||||
|
show_picker(ctx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tt_app_stop();
|
||||||
|
}
|
||||||
|
|
||||||
static void* create_data(void) {
|
static void* create_data(void) {
|
||||||
AppCtx* ctx = calloc(1, sizeof(AppCtx));
|
AppCtx* ctx = calloc(1, sizeof(AppCtx));
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
ctx->current_idx=0; ctx->disp_w=FALLBACK_W; ctx->disp_h=FALLBACK_H; ctx->img_w=FALLBACK_W; ctx->img_h=FALLBACK_H;
|
ctx->current_idx=0; ctx->disp_w=FALLBACK_W; ctx->disp_h=FALLBACK_H; ctx->img_w=FALLBACK_W; ctx->img_h=FALLBACK_H;
|
||||||
snprintf(ctx->status_text, sizeof(ctx->status_text), "Init %s proxy %s (random)", IMMICH_BASE_URL, RESIZE_PROXY_URL);
|
strncpy(ctx->current_person_id, PERSONS[0].id, sizeof(ctx->current_person_id)-1);
|
||||||
|
strncpy(ctx->current_person_name, PERSONS[0].name, sizeof(ctx->current_person_name)-1);
|
||||||
|
snprintf(ctx->status_text, sizeof(ctx->status_text), "Init %s proxy %s (random) person %s", IMMICH_BASE_URL, RESIZE_PROXY_URL, ctx->current_person_name);
|
||||||
ctx->thumb_buf = heap_caps_malloc(THUMB_BUF_SIZE, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
ctx->thumb_buf = heap_caps_malloc(THUMB_BUF_SIZE, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||||
if (!ctx->thumb_buf) ctx->thumb_buf = heap_caps_malloc(THUMB_BUF_SIZE, MALLOC_CAP_DEFAULT | MALLOC_CAP_8BIT);
|
if (!ctx->thumb_buf) ctx->thumb_buf = heap_caps_malloc(THUMB_BUF_SIZE, MALLOC_CAP_DEFAULT | MALLOC_CAP_8BIT);
|
||||||
}
|
}
|
||||||
@@ -742,9 +792,73 @@ static void destroy_data(void* data) {
|
|||||||
if (ctx->thumb_buf) { heap_caps_free(ctx->thumb_buf); ctx->thumb_buf=NULL; }
|
if (ctx->thumb_buf) { heap_caps_free(ctx->thumb_buf); ctx->thumb_buf=NULL; }
|
||||||
free(ctx);
|
free(ctx);
|
||||||
}
|
}
|
||||||
static void on_create(AppHandle app, void* data) { AppCtx* ctx=(AppCtx*)data; if(ctx) ctx->app=app; ESP_LOGI(TAG,"onCreate proxy=%s random %dx%d", RESIZE_PROXY_URL, ctx?ctx->img_w:0, ctx?ctx->img_h:0); }
|
static void on_create(AppHandle app, void* data) { AppCtx* ctx=(AppCtx*)data; if(ctx) ctx->app=app; ESP_LOGI(TAG,"onCreate proxy=%s random %dx%d person %s", RESIZE_PROXY_URL, ctx?ctx->img_w:0, ctx?ctx->img_h:0, ctx?ctx->current_person_name:"?"); }
|
||||||
static void on_destroy(AppHandle app, void* data) { (void)app;(void)data; }
|
static void on_destroy(AppHandle app, void* data) { (void)app;(void)data; }
|
||||||
|
|
||||||
|
static void person_selected_cb(lv_event_t* e) {
|
||||||
|
AppCtx* ctx = (AppCtx*)lv_event_get_user_data(e);
|
||||||
|
if (!ctx) return;
|
||||||
|
int idx = (int)(intptr_t)lv_obj_get_user_data(lv_event_get_target(e));
|
||||||
|
if (idx < 0 || idx >= PERSONS_COUNT) return;
|
||||||
|
const PersonInfo* p = &PERSONS[idx];
|
||||||
|
strncpy(ctx->current_person_id, p->id, sizeof(ctx->current_person_id)-1);
|
||||||
|
strncpy(ctx->current_person_name, p->name, sizeof(ctx->current_person_name)-1);
|
||||||
|
ESP_LOGI(TAG, "Person selected %s (%s)", p->name, p->id);
|
||||||
|
photo_count = 0;
|
||||||
|
ctx->current_idx = 0;
|
||||||
|
ctx->thumb_len = 0;
|
||||||
|
ctx->has_image = false;
|
||||||
|
show_player(ctx);
|
||||||
|
start_fetch_task(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void build_person_picker(AppCtx* ctx, lv_obj_t* parent) {
|
||||||
|
ctx->picker_wrapper = lv_obj_create(parent);
|
||||||
|
lv_obj_set_size(ctx->picker_wrapper, LV_PCT(100), LV_PCT(100));
|
||||||
|
lv_obj_set_style_border_width(ctx->picker_wrapper, 0, 0);
|
||||||
|
lv_obj_set_style_pad_all(ctx->picker_wrapper, 0, 0);
|
||||||
|
lv_obj_set_style_bg_color(ctx->picker_wrapper, lv_color_hex(0x121212), LV_PART_MAIN);
|
||||||
|
lv_obj_set_style_bg_opa(ctx->picker_wrapper, LV_OPA_COVER, LV_PART_MAIN);
|
||||||
|
|
||||||
|
lv_obj_t* toolbar = tt_lvgl_toolbar_create_for_app(ctx->picker_wrapper, ctx->app);
|
||||||
|
|
||||||
|
ctx->lst_persons = lv_list_create(ctx->picker_wrapper);
|
||||||
|
lv_obj_set_width(ctx->lst_persons, LV_PCT(100));
|
||||||
|
lv_obj_align_to(ctx->lst_persons, toolbar, LV_ALIGN_OUT_BOTTOM_MID, 0, 0);
|
||||||
|
// Calculate height
|
||||||
|
int32_t th = 44;
|
||||||
|
// Use parent height minus toolbar
|
||||||
|
lv_obj_set_height(ctx->lst_persons, LV_PCT(90));
|
||||||
|
lv_obj_set_style_bg_color(ctx->lst_persons, lv_color_hex(0x121212), LV_PART_MAIN);
|
||||||
|
|
||||||
|
for (int i=0;i<PERSONS_COUNT;i++) {
|
||||||
|
const PersonInfo* person = &PERSONS[i];
|
||||||
|
char label[64];
|
||||||
|
if (person->count_str[0]) snprintf(label, sizeof(label), "%s (%s)", person->name, person->count_str);
|
||||||
|
else snprintf(label, sizeof(label), "%s", person->name);
|
||||||
|
lv_obj_t* btn = lv_list_add_button(ctx->lst_persons, LV_SYMBOL_DIRECTORY, label);
|
||||||
|
lv_obj_set_user_data(btn, (void*)(intptr_t)i);
|
||||||
|
lv_obj_add_event_cb(btn, person_selected_cb, LV_EVENT_CLICKED, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void show_picker(AppCtx* ctx) {
|
||||||
|
if (!ctx) return;
|
||||||
|
if (!tt_lvgl_lock(pdMS_TO_TICKS(500))) return;
|
||||||
|
if (ctx->player_wrapper) lv_obj_add_flag(ctx->player_wrapper, LV_OBJ_FLAG_HIDDEN);
|
||||||
|
if (ctx->picker_wrapper) lv_obj_remove_flag(ctx->picker_wrapper, LV_OBJ_FLAG_HIDDEN);
|
||||||
|
tt_lvgl_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void show_player(AppCtx* ctx) {
|
||||||
|
if (!ctx) return;
|
||||||
|
if (!tt_lvgl_lock(pdMS_TO_TICKS(500))) return;
|
||||||
|
if (ctx->picker_wrapper) lv_obj_add_flag(ctx->picker_wrapper, LV_OBJ_FLAG_HIDDEN);
|
||||||
|
if (ctx->player_wrapper) lv_obj_remove_flag(ctx->player_wrapper, LV_OBJ_FLAG_HIDDEN);
|
||||||
|
tt_lvgl_unlock();
|
||||||
|
show_chrome(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
static void on_show(AppHandle app, void* data, lv_obj_t* parent) {
|
static void on_show(AppHandle app, void* data, lv_obj_t* parent) {
|
||||||
AppCtx* ctx=(AppCtx*)data; if(!ctx) return;
|
AppCtx* ctx=(AppCtx*)data; if(!ctx) return;
|
||||||
ctx->visible=true; ctx->stop_requested=false; ctx->app=app;
|
ctx->visible=true; ctx->stop_requested=false; ctx->app=app;
|
||||||
@@ -756,6 +870,9 @@ static void on_show(AppHandle app, void* data, lv_obj_t* parent) {
|
|||||||
lv_obj_set_style_bg_color(parent, lv_color_hex(0x121212), LV_PART_MAIN);
|
lv_obj_set_style_bg_color(parent, lv_color_hex(0x121212), LV_PART_MAIN);
|
||||||
lv_obj_set_style_bg_opa(parent, LV_OPA_COVER, LV_PART_MAIN);
|
lv_obj_set_style_bg_opa(parent, LV_OPA_COVER, LV_PART_MAIN);
|
||||||
|
|
||||||
|
// Picker for persons (like Bible book selector)
|
||||||
|
build_person_picker(ctx, parent);
|
||||||
|
|
||||||
// Fullscreen player like Bible app - image background, only bottom controls
|
// Fullscreen player like Bible app - image background, only bottom controls
|
||||||
ctx->player_wrapper = lv_obj_create(parent);
|
ctx->player_wrapper = lv_obj_create(parent);
|
||||||
lv_obj_set_size(ctx->player_wrapper, LV_PCT(100), LV_PCT(100));
|
lv_obj_set_size(ctx->player_wrapper, LV_PCT(100), LV_PCT(100));
|
||||||
@@ -765,6 +882,7 @@ static void on_show(AppHandle app, void* data, lv_obj_t* parent) {
|
|||||||
lv_obj_set_style_bg_color(ctx->player_wrapper, lv_color_hex(0x121212), LV_PART_MAIN);
|
lv_obj_set_style_bg_color(ctx->player_wrapper, lv_color_hex(0x121212), LV_PART_MAIN);
|
||||||
lv_obj_set_style_bg_opa(ctx->player_wrapper, LV_OPA_COVER, LV_PART_MAIN);
|
lv_obj_set_style_bg_opa(ctx->player_wrapper, LV_OPA_COVER, LV_PART_MAIN);
|
||||||
lv_obj_remove_flag(ctx->player_wrapper, LV_OBJ_FLAG_SCROLLABLE);
|
lv_obj_remove_flag(ctx->player_wrapper, LV_OBJ_FLAG_SCROLLABLE);
|
||||||
|
lv_obj_add_flag(ctx->player_wrapper, LV_OBJ_FLAG_HIDDEN);
|
||||||
|
|
||||||
// Image covers entire background
|
// Image covers entire background
|
||||||
ctx->img_page = lv_image_create(ctx->player_wrapper);
|
ctx->img_page = lv_image_create(ctx->player_wrapper);
|
||||||
@@ -777,7 +895,6 @@ static void on_show(AppHandle app, void* data, lv_obj_t* parent) {
|
|||||||
lv_obj_add_flag(ctx->img_page, LV_OBJ_FLAG_CLICKABLE);
|
lv_obj_add_flag(ctx->img_page, LV_OBJ_FLAG_CLICKABLE);
|
||||||
lv_obj_add_event_cb(ctx->img_page, on_image_click, LV_EVENT_CLICKED, ctx);
|
lv_obj_add_event_cb(ctx->img_page, on_image_click, LV_EVENT_CLICKED, ctx);
|
||||||
|
|
||||||
// Bottom-only controls like Bible app - no top header, no info/status text
|
|
||||||
ctx->header_bar = NULL;
|
ctx->header_bar = NULL;
|
||||||
ctx->lbl_book_title = NULL;
|
ctx->lbl_book_title = NULL;
|
||||||
ctx->lbl_indicator = NULL;
|
ctx->lbl_indicator = NULL;
|
||||||
@@ -798,12 +915,11 @@ static void on_show(AppHandle app, void* data, lv_obj_t* parent) {
|
|||||||
lv_obj_set_flex_align(ctx->ctrl_bar, LV_FLEX_ALIGN_SPACE_BETWEEN, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
|
lv_obj_set_flex_align(ctx->ctrl_bar, LV_FLEX_ALIGN_SPACE_BETWEEN, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
|
||||||
lv_obj_remove_flag(ctx->ctrl_bar, LV_OBJ_FLAG_SCROLLABLE);
|
lv_obj_remove_flag(ctx->ctrl_bar, LV_OBJ_FLAG_SCROLLABLE);
|
||||||
|
|
||||||
// Left close button (like Bible app X)
|
|
||||||
ctx->btn_back = lv_btn_create(ctx->ctrl_bar);
|
ctx->btn_back = lv_btn_create(ctx->ctrl_bar);
|
||||||
lv_obj_set_size(ctx->btn_back, 44, 32);
|
lv_obj_set_size(ctx->btn_back, 44, 32);
|
||||||
lv_obj_set_style_radius(ctx->btn_back, 8, 0);
|
lv_obj_set_style_radius(ctx->btn_back, 8, 0);
|
||||||
lv_obj_set_style_bg_color(ctx->btn_back, lv_color_hex(0x252538), LV_PART_MAIN);
|
lv_obj_set_style_bg_color(ctx->btn_back, lv_color_hex(0x252538), LV_PART_MAIN);
|
||||||
lv_obj_t* lbl_back = lv_label_create(ctx->btn_back); lv_label_set_text(lbl_back, LV_SYMBOL_CLOSE); lv_obj_center(lbl_back);
|
lv_obj_t* lbl_back = lv_label_create(ctx->btn_back); lv_label_set_text(lbl_back, LV_SYMBOL_LEFT); lv_obj_center(lbl_back);
|
||||||
lv_obj_add_event_cb(ctx->btn_back, back_event_cb, LV_EVENT_CLICKED, ctx);
|
lv_obj_add_event_cb(ctx->btn_back, back_event_cb, LV_EVENT_CLICKED, ctx);
|
||||||
|
|
||||||
ctx->btn_prev = lv_btn_create(ctx->ctrl_bar);
|
ctx->btn_prev = lv_btn_create(ctx->ctrl_bar);
|
||||||
@@ -828,24 +944,25 @@ static void on_show(AppHandle app, void* data, lv_obj_t* parent) {
|
|||||||
lv_obj_t* ln = lv_label_create(ctx->btn_next); lv_label_set_text(ln, LV_SYMBOL_NEXT); lv_obj_center(ln);
|
lv_obj_t* ln = lv_label_create(ctx->btn_next); lv_label_set_text(ln, LV_SYMBOL_NEXT); lv_obj_center(ln);
|
||||||
lv_obj_add_event_cb(ctx->btn_next, next_event_cb, LV_EVENT_CLICKED, ctx);
|
lv_obj_add_event_cb(ctx->btn_next, next_event_cb, LV_EVENT_CLICKED, ctx);
|
||||||
|
|
||||||
// Timers: slideshow every 60s, autohide chrome after 3s
|
|
||||||
// Only slideshow timer (60s), no autohide for stability - chrome toggles on tap like BookPlayer
|
|
||||||
if (tt_lvgl_lock(pdMS_TO_TICKS(500))) {
|
if (tt_lvgl_lock(pdMS_TO_TICKS(500))) {
|
||||||
if (ctx->slideshow_timer) { lv_timer_delete(ctx->slideshow_timer); ctx->slideshow_timer=NULL; }
|
if (ctx->slideshow_timer) { lv_timer_delete(ctx->slideshow_timer); ctx->slideshow_timer=NULL; }
|
||||||
ctx->slideshow_timer = lv_timer_create(slideshow_timer_cb, 60000, ctx);
|
ctx->slideshow_timer = lv_timer_create(slideshow_timer_cb, 60000, ctx);
|
||||||
if (ctx->autohide_timer) { lv_timer_delete(ctx->autohide_timer); ctx->autohide_timer=NULL; }
|
if (ctx->autohide_timer) { lv_timer_delete(ctx->autohide_timer); ctx->autohide_timer=NULL; }
|
||||||
// Autohide disabled - user taps to hide/show like BookPlayer
|
|
||||||
ctx->autohide_timer = NULL;
|
ctx->autohide_timer = NULL;
|
||||||
tt_lvgl_unlock();
|
tt_lvgl_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (photo_count>0) {
|
// Always show person picker first like Bible book selector
|
||||||
if(ctx->current_idx>=photo_count) ctx->current_idx=0;
|
show_picker(ctx);
|
||||||
snprintf(ctx->status_text,sizeof(ctx->status_text),"Cached %d random %dx%d",photo_count, ctx->img_w, ctx->img_h);
|
// If we already have photos for current person, user can still see them after selecting
|
||||||
ui_update_all(ctx); show_chrome(ctx); start_thumb_task(ctx);
|
// Don't auto-start fetch here; wait for person selection to fetch fresh random page
|
||||||
|
// But if we have cached photos from previous session for current person, we could show player directly
|
||||||
|
// For now, just show picker and let user choose person
|
||||||
|
if (photo_count>0 && ctx->current_person_id[0]!='\0') {
|
||||||
|
// Optionally prefetch in background
|
||||||
|
snprintf(ctx->status_text,sizeof(ctx->status_text),"Cached %d for %s - select person", photo_count, ctx->current_person_name);
|
||||||
} else {
|
} else {
|
||||||
snprintf(ctx->status_text,sizeof(ctx->status_text),"Fetching %s random %dx%d via %s disp %dx%d",PERSON_NAME, ctx->img_w, ctx->img_h, RESIZE_PROXY_URL, ctx->disp_w, ctx->disp_h);
|
snprintf(ctx->status_text,sizeof(ctx->status_text),"Select a person");
|
||||||
ui_update_all(ctx); show_chrome(ctx); start_fetch_task(ctx);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user