diff --git a/Makefile b/Makefile index 8834cba6..a48ed613 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ PROJECT_ROOT := $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST))))) include $(PROJECT_ROOT)/make/git.mk include $(PROJECT_ROOT)/assets/copro.mk +include $(PROJECT_ROOT)/assets/splash.mk PROJECT_SOURCE_DIRECTORIES := \ $(PROJECT_ROOT)/applications \ @@ -103,7 +104,8 @@ updater_package: firmware_all updater assets_manifest --radio $(COPRO_STACK_BIN_PATH) \ --radiotype $(COPRO_STACK_TYPE) \ $(COPRO_DISCLAIMER) \ - --obdata $(PROJECT_ROOT)/scripts/$(COPRO_OB_DATA) + --obdata $(PROJECT_ROOT)/scripts/$(COPRO_OB_DATA) \ + --splash $(UPDATER_SPLASH_DIR) .PHONY: assets_manifest assets_manifest: diff --git a/applications/desktop/desktop.c b/applications/desktop/desktop.c index 34c169e0..52091753 100644 --- a/applications/desktop/desktop.c +++ b/applications/desktop/desktop.c @@ -158,11 +158,11 @@ Desktop* desktop_alloc() { desktop->lock_menu = desktop_lock_menu_alloc(); desktop->debug_view = desktop_debug_alloc(); - desktop->first_start_view = desktop_first_start_alloc(); desktop->hw_mismatch_popup = popup_alloc(); desktop->locked_view = desktop_view_locked_alloc(); desktop->pin_input_view = desktop_view_pin_input_alloc(); desktop->pin_timeout_view = desktop_view_pin_timeout_alloc(); + desktop->slideshow_view = desktop_view_slideshow_alloc(); desktop->main_view_stack = view_stack_alloc(); desktop->main_view = desktop_main_alloc(); @@ -193,10 +193,6 @@ Desktop* desktop_alloc() { desktop_lock_menu_get_view(desktop->lock_menu)); view_dispatcher_add_view( desktop->view_dispatcher, DesktopViewIdDebug, desktop_debug_get_view(desktop->debug_view)); - view_dispatcher_add_view( - desktop->view_dispatcher, - DesktopViewIdFirstStart, - desktop_first_start_get_view(desktop->first_start_view)); view_dispatcher_add_view( desktop->view_dispatcher, DesktopViewIdHwMismatch, @@ -209,6 +205,10 @@ Desktop* desktop_alloc() { desktop->view_dispatcher, DesktopViewIdPinInput, desktop_view_pin_input_get_view(desktop->pin_input_view)); + view_dispatcher_add_view( + desktop->view_dispatcher, + DesktopViewIdSlideshow, + desktop_view_slideshow_get_view(desktop->slideshow_view)); // Lock icon desktop->lock_viewport = view_port_alloc(); @@ -258,7 +258,6 @@ void desktop_free(Desktop* desktop) { view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLockMenu); view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLocked); view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdDebug); - view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdFirstStart); view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdHwMismatch); view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdPinInput); view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdPinTimeout); @@ -274,7 +273,6 @@ void desktop_free(Desktop* desktop) { desktop_lock_menu_free(desktop->lock_menu); desktop_view_locked_free(desktop->locked_view); desktop_debug_free(desktop->debug_view); - desktop_first_start_free(desktop->first_start_view); popup_free(desktop->hw_mismatch_popup); desktop_view_pin_timeout_free(desktop->pin_timeout_view); @@ -290,9 +288,9 @@ void desktop_free(Desktop* desktop) { free(desktop); } -static bool desktop_is_first_start() { +static bool desktop_check_file_flag(const char* flag_path) { Storage* storage = furi_record_open("storage"); - bool exists = storage_common_stat(storage, "/int/first_start", NULL) == FSE_OK; + bool exists = storage_common_stat(storage, flag_path, NULL) == FSE_OK; furi_record_close("storage"); return exists; @@ -320,8 +318,8 @@ int32_t desktop_srv(void* p) { desktop_lock(desktop); } - if(desktop_is_first_start()) { - scene_manager_next_scene(desktop->scene_manager, DesktopSceneFirstStart); + if(desktop_check_file_flag("/int/slideshow")) { + scene_manager_next_scene(desktop->scene_manager, DesktopSceneSlideshow); } if(!furi_hal_version_do_i_belong_here()) { diff --git a/applications/desktop/desktop_i.h b/applications/desktop/desktop_i.h index 2888f099..8dc92e84 100644 --- a/applications/desktop/desktop_i.h +++ b/applications/desktop/desktop_i.h @@ -6,9 +6,9 @@ #include "views/desktop_view_pin_input.h" #include "views/desktop_view_locked.h" #include "views/desktop_view_main.h" -#include "views/desktop_view_first_start.h" #include "views/desktop_view_lock_menu.h" #include "views/desktop_view_debug.h" +#include "views/desktop_view_slideshow.h" #include "desktop/desktop_settings/desktop_settings.h" #include @@ -28,10 +28,10 @@ typedef enum { DesktopViewIdLockMenu, DesktopViewIdLocked, DesktopViewIdDebug, - DesktopViewIdFirstStart, DesktopViewIdHwMismatch, DesktopViewIdPinInput, DesktopViewIdPinTimeout, + DesktopViewIdSlideshow, DesktopViewIdTotal, } DesktopViewId; @@ -43,13 +43,13 @@ struct Desktop { ViewDispatcher* view_dispatcher; SceneManager* scene_manager; - DesktopFirstStartView* first_start_view; Popup* hw_mismatch_popup; DesktopLockMenuView* lock_menu; DesktopDebugView* debug_view; DesktopViewLocked* locked_view; DesktopMainView* main_view; DesktopViewPinTimeout* pin_timeout_view; + DesktopSlideshowView* slideshow_view; ViewStack* main_view_stack; ViewStack* locked_view_stack; diff --git a/applications/desktop/helpers/slideshow.c b/applications/desktop/helpers/slideshow.c new file mode 100644 index 00000000..37f66cf3 --- /dev/null +++ b/applications/desktop/helpers/slideshow.c @@ -0,0 +1,115 @@ +#include "slideshow.h" + +#include +#include +#include +#include +#include + +#define SLIDESHOW_MAGIC 0x72676468 +#define SLIDESHOW_MAX_SUPPORTED_VERSION 1 + +struct Slideshow { + Icon icon; + uint32_t current_frame; +}; + +#pragma pack(push, 1) + +typedef struct { + uint32_t magic; + uint8_t version; + uint8_t width; + uint8_t height; + uint8_t frame_count; +} SlideshowFileHeader; +_Static_assert(sizeof(SlideshowFileHeader) == 8, "Incorrect SlideshowFileHeader size"); + +typedef struct { + uint16_t size; +} SlideshowFrameHeader; +_Static_assert(sizeof(SlideshowFrameHeader) == 2, "Incorrect SlideshowFrameHeader size"); + +#pragma pack(pop) + +Slideshow* slideshow_alloc() { + Slideshow* ret = malloc(sizeof(Slideshow)); + return ret; +} + +void slideshow_free(Slideshow* slideshow) { + Icon* icon = &slideshow->icon; + if(icon) { + for(int frame_idx = 0; frame_idx < icon->frame_count; ++frame_idx) { + uint8_t* frame_data = (uint8_t*)icon->frames[frame_idx]; + free(frame_data); + } + free((uint8_t**)icon->frames); + } + free(slideshow); +} + +bool slideshow_load(Slideshow* slideshow, const char* fspath) { + Storage* storage = furi_record_open("storage"); + File* slideshow_file = storage_file_alloc(storage); + bool load_success = false; + do { + if(!storage_file_open(slideshow_file, fspath, FSAM_READ, FSOM_OPEN_EXISTING)) { + break; + } + SlideshowFileHeader header; + if((storage_file_read(slideshow_file, &header, sizeof(header)) != sizeof(header)) || + (header.magic != SLIDESHOW_MAGIC) || + (header.version > SLIDESHOW_MAX_SUPPORTED_VERSION)) { + break; + } + Icon* icon = &slideshow->icon; + FURI_CONST_ASSIGN(icon->frame_count, header.frame_count); + FURI_CONST_ASSIGN(icon->width, header.width); + FURI_CONST_ASSIGN(icon->height, header.height); + icon->frames = malloc(header.frame_count * sizeof(uint8_t*)); + for(int frame_idx = 0; frame_idx < header.frame_count; ++frame_idx) { + SlideshowFrameHeader frame_header; + if(storage_file_read(slideshow_file, &frame_header, sizeof(frame_header)) != + sizeof(frame_header)) { + break; + } + FURI_CONST_ASSIGN_PTR(icon->frames[frame_idx], malloc(frame_header.size)); + uint8_t* frame_data = (uint8_t*)icon->frames[frame_idx]; + if(storage_file_read(slideshow_file, frame_data, frame_header.size) != + frame_header.size) { + break; + } + load_success = (frame_idx + 1) == header.frame_count; + } + } while(false); + storage_file_free(slideshow_file); + furi_record_close("storage"); + return load_success; +} + +bool slideshow_advance(Slideshow* slideshow) { + uint8_t next_frame = slideshow->current_frame + 1; + if(next_frame < slideshow->icon.frame_count) { + slideshow->current_frame = next_frame; + return true; + } + return false; +} + +void slideshow_goback(Slideshow* slideshow) { + if(slideshow->current_frame > 0) { + slideshow->current_frame--; + } +} + +void slideshow_draw(Slideshow* slideshow, Canvas* canvas, uint8_t x, uint8_t y) { + furi_assert(slideshow->current_frame < slideshow->icon.frame_count); + canvas_draw_bitmap( + canvas, + x, + y, + slideshow->icon.width, + slideshow->icon.height, + slideshow->icon.frames[slideshow->current_frame]); +} \ No newline at end of file diff --git a/applications/desktop/helpers/slideshow.h b/applications/desktop/helpers/slideshow.h new file mode 100644 index 00000000..db19779a --- /dev/null +++ b/applications/desktop/helpers/slideshow.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +typedef struct Slideshow Slideshow; + +Slideshow* slideshow_alloc(); + +void slideshow_free(Slideshow* slideshow); +bool slideshow_load(Slideshow* slideshow, const char* fspath); +void slideshow_goback(Slideshow* slideshow); +bool slideshow_advance(Slideshow* slideshow); +void slideshow_draw(Slideshow* slideshow, Canvas* canvas, uint8_t x, uint8_t y); diff --git a/applications/desktop/scenes/desktop_scene_config.h b/applications/desktop/scenes/desktop_scene_config.h index c84d6ff8..c153972b 100644 --- a/applications/desktop/scenes/desktop_scene_config.h +++ b/applications/desktop/scenes/desktop_scene_config.h @@ -1,9 +1,9 @@ ADD_SCENE(desktop, main, Main) ADD_SCENE(desktop, lock_menu, LockMenu) ADD_SCENE(desktop, debug, Debug) -ADD_SCENE(desktop, first_start, FirstStart) ADD_SCENE(desktop, hw_mismatch, HwMismatch) ADD_SCENE(desktop, fault, Fault) ADD_SCENE(desktop, locked, Locked) ADD_SCENE(desktop, pin_input, PinInput) ADD_SCENE(desktop, pin_timeout, PinTimeout) +ADD_SCENE(desktop, slideshow, Slideshow) diff --git a/applications/desktop/scenes/desktop_scene_first_start.c b/applications/desktop/scenes/desktop_scene_first_start.c deleted file mode 100644 index dbdf8919..00000000 --- a/applications/desktop/scenes/desktop_scene_first_start.c +++ /dev/null @@ -1,54 +0,0 @@ -#include -#include - -#include "../desktop_i.h" -#include "../views/desktop_view_first_start.h" -#include "../views/desktop_events.h" - -void desktop_scene_first_start_callback(DesktopEvent event, void* context) { - Desktop* desktop = (Desktop*)context; - view_dispatcher_send_custom_event(desktop->view_dispatcher, event); -} - -void desktop_scene_first_start_on_enter(void* context) { - Desktop* desktop = (Desktop*)context; - DesktopFirstStartView* first_start_view = desktop->first_start_view; - - desktop_first_start_set_callback( - first_start_view, desktop_scene_first_start_callback, desktop); - - view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdFirstStart); -} - -bool desktop_scene_first_start_on_event(void* context, SceneManagerEvent event) { - Desktop* desktop = (Desktop*)context; - bool consumed = false; - Storage* storage = NULL; - Power* power = NULL; - - if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case DesktopFirstStartCompleted: - storage = furi_record_open("storage"); - storage_common_remove(storage, "/int/first_start"); - furi_record_close("storage"); - scene_manager_previous_scene(desktop->scene_manager); - consumed = true; - break; - case DesktopFirstStartPoweroff: - power = furi_record_open("power"); - power_off(power); - furi_record_close("power"); - consumed = true; - break; - - default: - break; - } - } - return consumed; -} - -void desktop_scene_first_start_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/desktop/scenes/desktop_scene_slideshow.c b/applications/desktop/scenes/desktop_scene_slideshow.c new file mode 100644 index 00000000..70080127 --- /dev/null +++ b/applications/desktop/scenes/desktop_scene_slideshow.c @@ -0,0 +1,45 @@ +#include + +#include "../desktop_i.h" +#include "../views/desktop_view_slideshow.h" +#include "../views/desktop_events.h" + +void desktop_scene_slideshow_callback(DesktopEvent event, void* context) { + Desktop* desktop = (Desktop*)context; + view_dispatcher_send_custom_event(desktop->view_dispatcher, event); +} + +void desktop_scene_slideshow_on_enter(void* context) { + Desktop* desktop = (Desktop*)context; + DesktopSlideshowView* slideshow_view = desktop->slideshow_view; + + desktop_view_slideshow_set_callback(slideshow_view, desktop_scene_slideshow_callback, desktop); + + view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdSlideshow); +} + +bool desktop_scene_slideshow_on_event(void* context, SceneManagerEvent event) { + Desktop* desktop = (Desktop*)context; + bool consumed = false; + Storage* storage = NULL; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case DesktopSlideshowCompleted: + storage = furi_record_open("storage"); + storage_common_remove(storage, "/int/slideshow"); + furi_record_close("storage"); + scene_manager_previous_scene(desktop->scene_manager); + consumed = true; + break; + + default: + break; + } + } + return consumed; +} + +void desktop_scene_slideshow_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/desktop/views/desktop_events.h b/applications/desktop/views/desktop_events.h index bbb6ff62..4ff5f795 100644 --- a/applications/desktop/views/desktop_events.h +++ b/applications/desktop/views/desktop_events.h @@ -26,9 +26,6 @@ typedef enum { DesktopDebugEventSaveState, DesktopDebugEventExit, - DesktopFirstStartCompleted, - DesktopFirstStartPoweroff, - DesktopLockMenuEventLock, DesktopLockMenuEventPinLock, DesktopLockMenuEventExit, @@ -37,6 +34,8 @@ typedef enum { DesktopAnimationEventNewIdleAnimation, DesktopAnimationEventInteractAnimation, + DesktopSlideshowCompleted, + // Global events DesktopGlobalBeforeAppStarted, DesktopGlobalAfterAppFinished, diff --git a/applications/desktop/views/desktop_view_first_start.c b/applications/desktop/views/desktop_view_first_start.c deleted file mode 100644 index 87c07ad2..00000000 --- a/applications/desktop/views/desktop_view_first_start.c +++ /dev/null @@ -1,166 +0,0 @@ -#include -#include -#include - -#include "../desktop_i.h" -#include "desktop_view_first_start.h" - -#define DESKTOP_FIRST_START_POWEROFF_SHORT 5000 -#define DESKTOP_FIRST_START_POWEROFF_LONG (60 * 60 * 1000) - -struct DesktopFirstStartView { - View* view; - DesktopFirstStartViewCallback callback; - void* context; - osTimerId_t timer; -}; - -typedef struct { - uint8_t page; -} DesktopFirstStartViewModel; - -static void desktop_first_start_draw(Canvas* canvas, void* model) { - DesktopFirstStartViewModel* m = model; - - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontSecondary); - uint8_t width = canvas_width(canvas); - uint8_t height = canvas_height(canvas); - const char* my_name = furi_hal_version_get_name_ptr(); - if(m->page == 0) { - canvas_draw_icon(canvas, 0, height - 51, &I_DolphinFirstStart0_70x53); - elements_multiline_text_framed( - canvas, 75, 16 + STATUS_BAR_Y_SHIFT, "Hey m8,\npress > to\ncontinue"); - } else if(m->page == 1) { - canvas_draw_icon(canvas, 0, height - 51, &I_DolphinFirstStart1_59x53); - elements_multiline_text_framed( - canvas, 64, 16 + STATUS_BAR_Y_SHIFT, "First Of All,\n... >"); - } else if(m->page == 2) { - canvas_draw_icon(canvas, 0, height - 51, &I_DolphinFirstStart2_59x51); - elements_multiline_text_framed( - canvas, 64, 16 + STATUS_BAR_Y_SHIFT, "Thank you\nfor your\nsupport! >"); - } else if(m->page == 3) { - canvas_draw_icon(canvas, width - 57, height - 45, &I_DolphinFirstStart3_57x48); - elements_multiline_text_framed( - canvas, 0, 16 + STATUS_BAR_Y_SHIFT, "Kickstarter\ncampaign\nwas INSANE! >"); - } else if(m->page == 4) { - canvas_draw_icon(canvas, width - 67, height - 51, &I_DolphinFirstStart4_67x53); - elements_multiline_text_framed( - canvas, 0, 13 + STATUS_BAR_Y_SHIFT, "Now\nallow me\nto introduce\nmyself >"); - } else if(m->page == 5) { - char buf[64]; - snprintf( - buf, - 64, - "%s %s%s", - "I am", - my_name ? my_name : "Unknown", - ",\ncyberdolphin\nliving in your\npocket >"); - canvas_draw_icon(canvas, 0, height - 49, &I_DolphinFirstStart5_54x49); - elements_multiline_text_framed(canvas, 60, 13 + STATUS_BAR_Y_SHIFT, buf); - } else if(m->page == 6) { - canvas_draw_icon(canvas, 0, height - 51, &I_DolphinFirstStart6_58x54); - elements_multiline_text_framed( - canvas, - 63, - 13 + STATUS_BAR_Y_SHIFT, - "I can grow\nsmart'n'cool\nif you use me\noften >"); - } else if(m->page == 7) { - canvas_draw_icon(canvas, width - 61, height - 51, &I_DolphinFirstStart7_61x51); - elements_multiline_text_framed( - canvas, 0, 13 + STATUS_BAR_Y_SHIFT, "As long as\nyou read, write\nand emulate >"); - } else if(m->page == 8) { - canvas_draw_icon(canvas, width - 56, height - 51, &I_DolphinFirstStart8_56x51); - elements_multiline_text_framed( - canvas, - 0, - 13 + STATUS_BAR_Y_SHIFT, - "You can check\nmy level and\nmood in the\nPassport menu"); - } -} - -static bool desktop_first_start_input(InputEvent* event, void* context) { - furi_assert(event); - DesktopFirstStartView* instance = context; - - if(event->type == InputTypeShort) { - DesktopFirstStartViewModel* model = view_get_model(instance->view); - if(event->key == InputKeyLeft) { - if(model->page > 0) model->page--; - } else if(event->key == InputKeyRight) { - uint32_t page = ++model->page; - if(page > 8) { - instance->callback(DesktopFirstStartCompleted, instance->context); - } - } - view_commit_model(instance->view, true); - } - - if(event->key == InputKeyOk) { - if(event->type == InputTypePress) { - osTimerStart(instance->timer, DESKTOP_FIRST_START_POWEROFF_SHORT); - } else if(event->type == InputTypeRelease) { - osTimerStop(instance->timer); - } - } - - return true; -} - -static void desktop_first_start_timer_callback(void* context) { - DesktopFirstStartView* instance = context; - instance->callback(DesktopFirstStartPoweroff, instance->context); -} - -static void desktop_first_start_enter(void* context) { - DesktopFirstStartView* instance = context; - - furi_assert(instance->timer == NULL); - instance->timer = osTimerNew(desktop_first_start_timer_callback, osTimerOnce, instance, NULL); - - osTimerStart(instance->timer, DESKTOP_FIRST_START_POWEROFF_LONG); -} - -static void desktop_first_start_exit(void* context) { - DesktopFirstStartView* instance = context; - - osTimerStop(instance->timer); - osTimerDelete(instance->timer); - instance->timer = NULL; -} - -DesktopFirstStartView* desktop_first_start_alloc() { - DesktopFirstStartView* instance = malloc(sizeof(DesktopFirstStartView)); - instance->view = view_alloc(); - view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(DesktopFirstStartViewModel)); - view_set_context(instance->view, instance); - view_set_draw_callback(instance->view, (ViewDrawCallback)desktop_first_start_draw); - view_set_input_callback(instance->view, desktop_first_start_input); - view_set_enter_callback(instance->view, desktop_first_start_enter); - view_set_exit_callback(instance->view, desktop_first_start_exit); - - return instance; -} - -void desktop_first_start_free(DesktopFirstStartView* instance) { - furi_assert(instance); - - view_free(instance->view); - free(instance); -} - -View* desktop_first_start_get_view(DesktopFirstStartView* instance) { - furi_assert(instance); - return instance->view; -} - -void desktop_first_start_set_callback( - DesktopFirstStartView* instance, - DesktopFirstStartViewCallback callback, - void* context) { - furi_assert(instance); - furi_assert(callback); - instance->callback = callback; - instance->context = context; -} diff --git a/applications/desktop/views/desktop_view_first_start.h b/applications/desktop/views/desktop_view_first_start.h deleted file mode 100644 index 9b7b3c93..00000000 --- a/applications/desktop/views/desktop_view_first_start.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include - -#include "desktop_events.h" - -typedef struct DesktopFirstStartView DesktopFirstStartView; - -typedef void (*DesktopFirstStartViewCallback)(DesktopEvent event, void* context); - -DesktopFirstStartView* desktop_first_start_alloc(); - -void desktop_first_start_free(DesktopFirstStartView* main_view); - -View* desktop_first_start_get_view(DesktopFirstStartView* main_view); - -void desktop_first_start_set_callback( - DesktopFirstStartView* main_view, - DesktopFirstStartViewCallback callback, - void* context); diff --git a/applications/desktop/views/desktop_view_slideshow.c b/applications/desktop/views/desktop_view_slideshow.c new file mode 100644 index 00000000..97f779b5 --- /dev/null +++ b/applications/desktop/views/desktop_view_slideshow.c @@ -0,0 +1,108 @@ +#include +#include +#include + +#include "../desktop_i.h" +#include "desktop_view_slideshow.h" +#include "../helpers/slideshow.h" + +struct DesktopSlideshowView { + View* view; + DesktopSlideshowViewCallback callback; + void* context; +}; + +typedef struct { + uint8_t page; + Slideshow* slideshow; +} DesktopSlideshowViewModel; + +static void desktop_view_slideshow_draw(Canvas* canvas, void* model) { + DesktopSlideshowViewModel* m = model; + + canvas_clear(canvas); + slideshow_draw(m->slideshow, canvas, 0, 0); +} + +static bool desktop_view_slideshow_input(InputEvent* event, void* context) { + furi_assert(event); + DesktopSlideshowView* instance = context; + + if(event->type == InputTypeShort) { + DesktopSlideshowViewModel* model = view_get_model(instance->view); + bool end_slideshow = false; + switch(event->key) { + case InputKeyLeft: + slideshow_goback(model->slideshow); + break; + case InputKeyRight: + case InputKeyOk: + end_slideshow = !slideshow_advance(model->slideshow); + break; + case InputKeyBack: + end_slideshow = true; + default: + break; + } + if(end_slideshow) { + instance->callback(DesktopSlideshowCompleted, instance->context); + } + view_commit_model(instance->view, true); + } + + return true; +} + +static void desktop_view_slideshow_enter(void* context) { + DesktopSlideshowView* instance = context; + + DesktopSlideshowViewModel* model = view_get_model(instance->view); + model->slideshow = slideshow_alloc(); + if(!slideshow_load(model->slideshow, "/int/slideshow")) { + instance->callback(DesktopSlideshowCompleted, instance->context); + } + view_commit_model(instance->view, false); +} + +static void desktop_view_slideshow_exit(void* context) { + DesktopSlideshowView* instance = context; + + DesktopSlideshowViewModel* model = view_get_model(instance->view); + slideshow_free(model->slideshow); + view_commit_model(instance->view, false); +} + +DesktopSlideshowView* desktop_view_slideshow_alloc() { + DesktopSlideshowView* instance = malloc(sizeof(DesktopSlideshowView)); + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(DesktopSlideshowViewModel)); + view_set_context(instance->view, instance); + view_set_draw_callback(instance->view, (ViewDrawCallback)desktop_view_slideshow_draw); + view_set_input_callback(instance->view, desktop_view_slideshow_input); + view_set_enter_callback(instance->view, desktop_view_slideshow_enter); + view_set_exit_callback(instance->view, desktop_view_slideshow_exit); + + return instance; +} + +void desktop_view_slideshow_free(DesktopSlideshowView* instance) { + furi_assert(instance); + + view_free(instance->view); + free(instance); +} + +View* desktop_view_slideshow_get_view(DesktopSlideshowView* instance) { + furi_assert(instance); + return instance->view; +} + +void desktop_view_slideshow_set_callback( + DesktopSlideshowView* instance, + DesktopSlideshowViewCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} \ No newline at end of file diff --git a/applications/desktop/views/desktop_view_slideshow.h b/applications/desktop/views/desktop_view_slideshow.h new file mode 100644 index 00000000..5b45a6b7 --- /dev/null +++ b/applications/desktop/views/desktop_view_slideshow.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "desktop_events.h" + +typedef struct DesktopSlideshowView DesktopSlideshowView; + +typedef void (*DesktopSlideshowViewCallback)(DesktopEvent event, void* context); + +DesktopSlideshowView* desktop_view_slideshow_alloc(); + +void desktop_view_slideshow_free(DesktopSlideshowView* main_view); + +View* desktop_view_slideshow_get_view(DesktopSlideshowView* main_view); + +void desktop_view_slideshow_set_callback( + DesktopSlideshowView* main_view, + DesktopSlideshowViewCallback callback, + void* context); diff --git a/applications/updater/util/update_task.c b/applications/updater/util/update_task.c index 58b0c975..316fe680 100644 --- a/applications/updater/util/update_task.c +++ b/applications/updater/util/update_task.c @@ -24,6 +24,7 @@ static const char* update_task_stage_descr[] = { [UpdateTaskStageLfsBackup] = "Backing up LFS", [UpdateTaskStageLfsRestore] = "Restoring LFS", [UpdateTaskStageResourcesUpdate] = "Updating resources", + [UpdateTaskStageSplashscreenInstall] = "Installing splashscreen", [UpdateTaskStageCompleted] = "Restarting...", [UpdateTaskStageError] = "Error", [UpdateTaskStageOBError] = "OB, report", @@ -41,13 +42,13 @@ static const UpdateTaskStageGroupMap update_task_stage_progress[] = { [UpdateTaskStageProgress] = STAGE_DEF(UpdateTaskStageGroupMisc, 0), [UpdateTaskStageReadManifest] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 5), - [UpdateTaskStageLfsBackup] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 30), + [UpdateTaskStageLfsBackup] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 15), - [UpdateTaskStageRadioImageValidate] = STAGE_DEF(UpdateTaskStageGroupRadio, 30), + [UpdateTaskStageRadioImageValidate] = STAGE_DEF(UpdateTaskStageGroupRadio, 10), [UpdateTaskStageRadioErase] = STAGE_DEF(UpdateTaskStageGroupRadio, 50), - [UpdateTaskStageRadioWrite] = STAGE_DEF(UpdateTaskStageGroupRadio, 100), - [UpdateTaskStageRadioInstall] = STAGE_DEF(UpdateTaskStageGroupRadio, 5), - [UpdateTaskStageRadioBusy] = STAGE_DEF(UpdateTaskStageGroupRadio, 70), + [UpdateTaskStageRadioWrite] = STAGE_DEF(UpdateTaskStageGroupRadio, 90), + [UpdateTaskStageRadioInstall] = STAGE_DEF(UpdateTaskStageGroupRadio, 15), + [UpdateTaskStageRadioBusy] = STAGE_DEF(UpdateTaskStageGroupRadio, 60), [UpdateTaskStageOBValidation] = STAGE_DEF(UpdateTaskStageGroupOptionBytes, 10), @@ -58,6 +59,7 @@ static const UpdateTaskStageGroupMap update_task_stage_progress[] = { [UpdateTaskStageLfsRestore] = STAGE_DEF(UpdateTaskStageGroupPostUpdate, 30), [UpdateTaskStageResourcesUpdate] = STAGE_DEF(UpdateTaskStageGroupResources, 255), + [UpdateTaskStageSplashscreenInstall] = STAGE_DEF(UpdateTaskStageGroupSplashscreen, 5), [UpdateTaskStageCompleted] = STAGE_DEF(UpdateTaskStageGroupMisc, 1), [UpdateTaskStageError] = STAGE_DEF(UpdateTaskStageGroupMisc, 1), @@ -79,6 +81,9 @@ static UpdateTaskStageGroup update_task_get_task_groups(UpdateTask* update_task) if(!string_empty_p(manifest->resource_bundle)) { ret |= UpdateTaskStageGroupResources; } + if(!string_empty_p(manifest->splash_file)) { + ret |= UpdateTaskStageGroupSplashscreen; + } return ret; } diff --git a/applications/updater/util/update_task.h b/applications/updater/util/update_task.h index ac9dadd6..1f291556 100644 --- a/applications/updater/util/update_task.h +++ b/applications/updater/util/update_task.h @@ -33,6 +33,7 @@ typedef enum { UpdateTaskStageLfsRestore, UpdateTaskStageResourcesUpdate, + UpdateTaskStageSplashscreenInstall, UpdateTaskStageCompleted, UpdateTaskStageError, @@ -52,6 +53,7 @@ typedef enum { UpdateTaskStageGroupRadio = 1 << 4, UpdateTaskStageGroupPostUpdate = 1 << 5, UpdateTaskStageGroupResources = 1 << 6, + UpdateTaskStageGroupSplashscreen = 1 << 7, } UpdateTaskStageGroup; typedef struct { diff --git a/applications/updater/util/update_task_worker_backup.c b/applications/updater/util/update_task_worker_backup.c index 011510ed..f1ce5281 100644 --- a/applications/updater/util/update_task_worker_backup.c +++ b/applications/updater/util/update_task_worker_backup.c @@ -93,6 +93,19 @@ static bool update_task_post_update(UpdateTask* update_task) { CHECK_RESULT(tar_archive_unpack_to(archive, EXT_PATH)); } } + + if(update_task->state.groups & UpdateTaskStageGroupSplashscreen) { + update_task_set_progress(update_task, UpdateTaskStageSplashscreenInstall, 0); + string_t tmp_path; + string_init_set(tmp_path, update_task->update_path); + path_append(tmp_path, string_get_cstr(update_task->manifest->splash_file)); + if(storage_common_copy( + update_task->storage, string_get_cstr(tmp_path), "/int/slideshow") != FSE_OK) { + // actually, not critical + } + string_clear(tmp_path); + update_task_set_progress(update_task, UpdateTaskStageSplashscreenInstall, 100); + } success = true; } while(false); diff --git a/assets/slideshow/update_default/frame_00.png b/assets/slideshow/update_default/frame_00.png new file mode 100644 index 00000000..385fbbe0 Binary files /dev/null and b/assets/slideshow/update_default/frame_00.png differ diff --git a/assets/splash.mk b/assets/splash.mk new file mode 100644 index 00000000..6b01b5c3 --- /dev/null +++ b/assets/splash.mk @@ -0,0 +1,3 @@ +ASSETS_DIR ?= $(PROJECT_ROOT)/assets +UPDATER_SPLASH ?= update_default +UPDATER_SPLASH_DIR := $(ASSETS_DIR)/slideshow/$(UPDATER_SPLASH) diff --git a/lib/update_util/update_manifest.c b/lib/update_util/update_manifest.c index 2fe0a1ef..d1ac0d7d 100644 --- a/lib/update_util/update_manifest.c +++ b/lib/update_util/update_manifest.c @@ -17,6 +17,7 @@ #define MANIFEST_KEY_OB_REFERENCE "OB reference" #define MANIFEST_KEY_OB_MASK "OB mask" #define MANIFEST_KEY_OB_WRITE_MASK "OB write mask" +#define MANIFEST_KEY_SPLASH_FILE "Splashscreen" UpdateManifest* update_manifest_alloc() { UpdateManifest* update_manifest = malloc(sizeof(UpdateManifest)); @@ -25,6 +26,7 @@ UpdateManifest* update_manifest_alloc() { string_init(update_manifest->radio_image); string_init(update_manifest->staged_loader_file); string_init(update_manifest->resource_bundle); + string_init(update_manifest->splash_file); update_manifest->target = 0; update_manifest->manifest_version = 0; memset(update_manifest->ob_reference.bytes, 0, FURI_HAL_FLASH_OB_RAW_SIZE_BYTES); @@ -41,6 +43,7 @@ void update_manifest_free(UpdateManifest* update_manifest) { string_clear(update_manifest->radio_image); string_clear(update_manifest->staged_loader_file); string_clear(update_manifest->resource_bundle); + string_clear(update_manifest->splash_file); free(update_manifest); } @@ -107,6 +110,9 @@ static bool update_manifest->ob_write_mask.bytes, FURI_HAL_FLASH_OB_RAW_SIZE_BYTES); + flipper_format_read_string( + flipper_file, MANIFEST_KEY_SPLASH_FILE, update_manifest->splash_file); + update_manifest->valid = (!string_empty_p(update_manifest->firmware_dfu_image) || !string_empty_p(update_manifest->radio_image) || diff --git a/lib/update_util/update_manifest.h b/lib/update_util/update_manifest.h index 2b1e6857..2b0c9179 100644 --- a/lib/update_util/update_manifest.h +++ b/lib/update_util/update_manifest.h @@ -42,6 +42,7 @@ typedef struct { FuriHalFlashRawOptionByteData ob_reference; FuriHalFlashRawOptionByteData ob_compare_mask; FuriHalFlashRawOptionByteData ob_write_mask; + string_t splash_file; bool valid; } UpdateManifest; diff --git a/scripts/bin2dfu.py b/scripts/bin2dfu.py index 62554f69..d27b44aa 100755 --- a/scripts/bin2dfu.py +++ b/scripts/bin2dfu.py @@ -38,9 +38,9 @@ class Main(App): return 1 with open(self.args.input, mode="rb") as file: - bin = file.read() + bindata = file.read() - data = struct.pack("