[FL-1995] New dolphin animations (part 1) (#835)
* Desktop Animation (part 1): Ugly naked ohmygod architecture
* fix butthurt, fix locked scene
* Change SD icons, fixes
* Fix level update animation
* Fixes, correct butthurt
* Clean up code
* furi_assert(0) -> furi_crash("msg")
* Gui: rename none layer to desktop, update docs.
Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
@@ -1,5 +1,17 @@
|
||||
#include "assets_icons.h"
|
||||
#include "cmsis_os2.h"
|
||||
#include "desktop/desktop.h"
|
||||
#include "desktop_i.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
#include <furi/pubsub.h>
|
||||
#include <furi/record.h>
|
||||
#include "portmacro.h"
|
||||
#include "storage/filesystem-api-defines.h"
|
||||
#include "storage/storage.h"
|
||||
#include <furi-hal-lock.h>
|
||||
#include <stdint.h>
|
||||
#include <power/power_service/power.h>
|
||||
#include "helpers/desktop_animation.h"
|
||||
|
||||
static void desktop_lock_icon_callback(Canvas* canvas, void* context) {
|
||||
furi_assert(canvas);
|
||||
@@ -25,10 +37,11 @@ Desktop* desktop_alloc() {
|
||||
desktop->scene_thread = furi_thread_alloc();
|
||||
desktop->view_dispatcher = view_dispatcher_alloc();
|
||||
desktop->scene_manager = scene_manager_alloc(&desktop_scene_handlers, desktop);
|
||||
desktop->animation = desktop_animation_alloc();
|
||||
|
||||
view_dispatcher_enable_queue(desktop->view_dispatcher);
|
||||
view_dispatcher_attach_to_gui(
|
||||
desktop->view_dispatcher, desktop->gui, ViewDispatcherTypeWindow);
|
||||
desktop->view_dispatcher, desktop->gui, ViewDispatcherTypeDesktop);
|
||||
|
||||
view_dispatcher_set_event_callback_context(desktop->view_dispatcher, desktop);
|
||||
view_dispatcher_set_custom_event_callback(
|
||||
@@ -79,6 +92,7 @@ Desktop* desktop_alloc() {
|
||||
void desktop_free(Desktop* desktop) {
|
||||
furi_assert(desktop);
|
||||
|
||||
desktop_animation_free(desktop->animation);
|
||||
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewMain);
|
||||
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewLockMenu);
|
||||
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewLocked);
|
||||
@@ -116,8 +130,29 @@ static bool desktop_is_first_start() {
|
||||
return exists;
|
||||
}
|
||||
|
||||
static void desktop_dolphin_state_changed_callback(const void* message, void* context) {
|
||||
Desktop* desktop = context;
|
||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopMainEventUpdateAnimation);
|
||||
}
|
||||
|
||||
static void desktop_storage_state_changed_callback(const void* message, void* context) {
|
||||
Desktop* desktop = context;
|
||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopMainEventUpdateAnimation);
|
||||
}
|
||||
|
||||
int32_t desktop_srv(void* p) {
|
||||
Desktop* desktop = desktop_alloc();
|
||||
|
||||
Dolphin* dolphin = furi_record_open("dolphin");
|
||||
FuriPubSub* dolphin_pubsub = dolphin_get_pubsub(dolphin);
|
||||
FuriPubSubSubscription* dolphin_subscription =
|
||||
furi_pubsub_subscribe(dolphin_pubsub, desktop_dolphin_state_changed_callback, desktop);
|
||||
|
||||
Storage* storage = furi_record_open("storage");
|
||||
FuriPubSub* storage_pubsub = storage_get_pubsub(storage);
|
||||
FuriPubSubSubscription* storage_subscription =
|
||||
furi_pubsub_subscribe(storage_pubsub, desktop_storage_state_changed_callback, desktop);
|
||||
|
||||
bool loaded = LOAD_DESKTOP_SETTINGS(&desktop->settings);
|
||||
if(!loaded) {
|
||||
furi_hal_lock_set(false);
|
||||
@@ -143,6 +178,8 @@ int32_t desktop_srv(void* p) {
|
||||
}
|
||||
|
||||
view_dispatcher_run(desktop->view_dispatcher);
|
||||
furi_pubsub_unsubscribe(dolphin_pubsub, dolphin_subscription);
|
||||
furi_pubsub_unsubscribe(storage_pubsub, storage_subscription);
|
||||
desktop_free(desktop);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "scenes/desktop_scene.h"
|
||||
#include "helpers/desktop_animation.h"
|
||||
#include "desktop/desktop_settings/desktop_settings.h"
|
||||
#include <gui/icon.h>
|
||||
|
||||
typedef enum {
|
||||
DesktopViewMain,
|
||||
@@ -43,6 +44,7 @@ struct Desktop {
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
|
||||
DesktopAnimation* animation;
|
||||
DesktopFirstStartView* first_start_view;
|
||||
Popup* hw_mismatch_popup;
|
||||
DesktopMainView* main_view;
|
||||
|
||||
@@ -1,28 +1,365 @@
|
||||
#include "desktop_animation.h"
|
||||
#include "desktop/helpers/desktop_animation.h"
|
||||
#include "assets_icons.h"
|
||||
#include "desktop_animation_i.h"
|
||||
#include "cmsis_os2.h"
|
||||
#include "furi/common_defines.h"
|
||||
#include "furi/record.h"
|
||||
#include "storage/filesystem-api-defines.h"
|
||||
#include <power/power_service/power.h>
|
||||
#include <m-list.h>
|
||||
#include <storage/storage.h>
|
||||
#include <desktop/desktop.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
#define TAG "DesktopAnimation"
|
||||
LIST_DEF(AnimationList, const PairedAnimation*, M_PTR_OPLIST)
|
||||
#define M_OPL_AnimationList_t() LIST_OPLIST(AnimationList)
|
||||
|
||||
static const Icon* idle_scenes[] = {&A_Wink_128x64, &A_WatchingTV_128x64};
|
||||
#define PUSH_BACK_ANIMATIONS(listname, animations, butthurt) \
|
||||
for(int i = 0; i < COUNT_OF(animations); ++i) { \
|
||||
if(!(animations)[i].basic->butthurt_level_mask || \
|
||||
((animations)[i].basic->butthurt_level_mask & BUTTHURT_LEVEL(butthurt))) { \
|
||||
AnimationList_push_back(animation_list, &(animations)[i]); \
|
||||
} \
|
||||
}
|
||||
|
||||
const Icon* desktop_get_icon() {
|
||||
uint8_t new = 0;
|
||||
#define IS_BLOCKING_ANIMATION(x) \
|
||||
(((x) != DesktopAnimationStateBasic) && ((x) != DesktopAnimationStateActive))
|
||||
#define IS_ONESHOT_ANIMATION(x) ((x) == DesktopAnimationStateLevelUpIsPending)
|
||||
|
||||
#if 0
|
||||
// checking dolphin state here to choose appropriate animation
|
||||
static void desktop_animation_timer_callback(void* context);
|
||||
|
||||
struct DesktopAnimation {
|
||||
bool sd_shown_error_db;
|
||||
bool sd_shown_error_card_bad;
|
||||
osTimerId_t timer;
|
||||
const PairedAnimation* current;
|
||||
const Icon* current_blocking_icon;
|
||||
const Icon** current_one_shot_icons;
|
||||
uint8_t one_shot_animation_counter;
|
||||
uint8_t one_shot_animation_size;
|
||||
DesktopAnimationState state;
|
||||
TickType_t basic_started_at;
|
||||
TickType_t active_finished_at;
|
||||
AnimationChangedCallback animation_changed_callback;
|
||||
void* animation_changed_callback_context;
|
||||
};
|
||||
|
||||
DesktopAnimation* desktop_animation_alloc(void) {
|
||||
DesktopAnimation* animation = furi_alloc(sizeof(DesktopAnimation));
|
||||
|
||||
animation->timer = osTimerNew(
|
||||
desktop_animation_timer_callback, osTimerPeriodic /* osTimerOnce */, animation, NULL);
|
||||
animation->active_finished_at = (TickType_t)(-30);
|
||||
animation->basic_started_at = 0;
|
||||
animation->animation_changed_callback = NULL;
|
||||
animation->animation_changed_callback_context = NULL;
|
||||
desktop_start_new_idle_animation(animation);
|
||||
|
||||
return animation;
|
||||
}
|
||||
|
||||
void desktop_animation_free(DesktopAnimation* animation) {
|
||||
furi_assert(animation);
|
||||
|
||||
osTimerDelete(animation->timer);
|
||||
free(animation);
|
||||
}
|
||||
|
||||
void desktop_animation_set_animation_changed_callback(
|
||||
DesktopAnimation* animation,
|
||||
AnimationChangedCallback callback,
|
||||
void* context) {
|
||||
furi_assert(animation);
|
||||
|
||||
animation->animation_changed_callback = callback;
|
||||
animation->animation_changed_callback_context = context;
|
||||
}
|
||||
|
||||
void desktop_start_new_idle_animation(DesktopAnimation* animation) {
|
||||
Dolphin* dolphin = furi_record_open("dolphin");
|
||||
DolphinStats stats = dolphin_stats(dolphin);
|
||||
furi_record_close("dolphin");
|
||||
|
||||
furi_assert((stats.level >= 1) && (stats.level <= 3));
|
||||
uint8_t level = stats.level;
|
||||
|
||||
AnimationList_t animation_list;
|
||||
AnimationList_init(animation_list);
|
||||
|
||||
PUSH_BACK_ANIMATIONS(animation_list, mad_animation, stats.butthurt);
|
||||
PUSH_BACK_ANIMATIONS(animation_list, calm_animation, stats.butthurt);
|
||||
switch(level) {
|
||||
case 1:
|
||||
PUSH_BACK_ANIMATIONS(animation_list, level_1_animation, stats.butthurt);
|
||||
break;
|
||||
case 2:
|
||||
PUSH_BACK_ANIMATIONS(animation_list, level_2_animation, stats.butthurt);
|
||||
break;
|
||||
case 3:
|
||||
PUSH_BACK_ANIMATIONS(animation_list, level_3_animation, stats.butthurt);
|
||||
break;
|
||||
default:
|
||||
furi_crash("Dolphin level is out of bounds");
|
||||
}
|
||||
|
||||
Power* power = furi_record_open("power");
|
||||
PowerInfo info;
|
||||
power_get_info(power, &info);
|
||||
|
||||
if(!power_is_battery_well(&info)) {
|
||||
PUSH_BACK_ANIMATIONS(animation_list, check_battery_animation, stats.butthurt);
|
||||
}
|
||||
|
||||
Storage* storage = furi_record_open("storage");
|
||||
FS_Error sd_status = storage_sd_status(storage);
|
||||
animation->current = NULL;
|
||||
|
||||
if(sd_status == FSE_NOT_READY) {
|
||||
PUSH_BACK_ANIMATIONS(animation_list, no_sd_animation, stats.butthurt);
|
||||
animation->sd_shown_error_card_bad = false;
|
||||
animation->sd_shown_error_db = false;
|
||||
}
|
||||
|
||||
uint32_t whole_weight = 0;
|
||||
for
|
||||
M_EACH(item, animation_list, AnimationList_t) {
|
||||
whole_weight += (*item)->basic->weight;
|
||||
}
|
||||
|
||||
uint32_t lucky_number = random() % whole_weight;
|
||||
uint32_t weight = 0;
|
||||
|
||||
const PairedAnimation* selected = NULL;
|
||||
for
|
||||
M_EACH(item, animation_list, AnimationList_t) {
|
||||
if(lucky_number < weight) {
|
||||
break;
|
||||
}
|
||||
weight += (*item)->basic->weight;
|
||||
selected = *item;
|
||||
}
|
||||
animation->basic_started_at = osKernelGetTickCount();
|
||||
animation->current = selected;
|
||||
osTimerStart(animation->timer, animation->current->basic->duration * 1000);
|
||||
animation->state = DesktopAnimationStateBasic;
|
||||
furi_assert(selected);
|
||||
AnimationList_clear(animation_list);
|
||||
}
|
||||
|
||||
static void desktop_animation_timer_callback(void* context) {
|
||||
furi_assert(context);
|
||||
DesktopAnimation* animation = context;
|
||||
TickType_t now_ms = osKernelGetTickCount();
|
||||
AnimationList_t animation_list;
|
||||
AnimationList_init(animation_list);
|
||||
bool new_basic_animation = false;
|
||||
|
||||
if(animation->state == DesktopAnimationStateActive) {
|
||||
animation->state = DesktopAnimationStateBasic;
|
||||
TickType_t basic_lasts_ms = now_ms - animation->basic_started_at;
|
||||
animation->active_finished_at = now_ms;
|
||||
TickType_t basic_duration_ms = animation->current->basic->duration * 1000;
|
||||
if(basic_lasts_ms > basic_duration_ms) {
|
||||
// if active animation finished, and basic duration came to an end
|
||||
// select new idle animation
|
||||
new_basic_animation = true;
|
||||
} else {
|
||||
// if active animation finished, but basic duration is not finished
|
||||
// play current animation for the rest of time
|
||||
furi_assert(basic_duration_ms != basic_lasts_ms);
|
||||
osTimerStart(animation->timer, basic_duration_ms - basic_lasts_ms);
|
||||
}
|
||||
} else if(animation->state == DesktopAnimationStateBasic) {
|
||||
// if basic animation finished
|
||||
// select new idle animation
|
||||
new_basic_animation = true;
|
||||
}
|
||||
|
||||
if(new_basic_animation) {
|
||||
animation->basic_started_at = now_ms;
|
||||
desktop_start_new_idle_animation(animation);
|
||||
}
|
||||
|
||||
// for oneshot generate events every time
|
||||
if(animation->animation_changed_callback) {
|
||||
animation->animation_changed_callback(animation->animation_changed_callback_context);
|
||||
}
|
||||
}
|
||||
|
||||
void desktop_animation_activate(DesktopAnimation* animation) {
|
||||
furi_assert(animation);
|
||||
|
||||
if(animation->state != DesktopAnimationStateBasic) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(animation->state == DesktopAnimationStateActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!animation->current->active) {
|
||||
return;
|
||||
}
|
||||
|
||||
TickType_t now = osKernelGetTickCount();
|
||||
TickType_t time_since_last_active = now - animation->active_finished_at;
|
||||
|
||||
if(time_since_last_active > (animation->current->basic->active_cooldown * 1000)) {
|
||||
animation->state = DesktopAnimationStateActive;
|
||||
furi_assert(animation->current->active->duration > 0);
|
||||
osTimerStart(animation->timer, animation->current->active->duration * 1000);
|
||||
if(animation->animation_changed_callback) {
|
||||
animation->animation_changed_callback(animation->animation_changed_callback_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const Icon* desktop_animation_get_current_idle_animation(DesktopAnimation* animation) {
|
||||
const Icon* active_icon = animation->current->active->icon;
|
||||
const Icon* basic_icon = animation->current->basic->icon;
|
||||
return (animation->state == DesktopAnimationStateActive && active_icon) ? active_icon :
|
||||
basic_icon;
|
||||
}
|
||||
|
||||
// Every time somebody starts 'desktop_animation_get_animation()'
|
||||
// 1) check if there is a new level
|
||||
// 2) check if there is SD card corruption
|
||||
// 3) check if the SD card is empty
|
||||
// 4) if all false - get idle animation
|
||||
|
||||
const Icon* desktop_animation_get_animation(DesktopAnimation* animation) {
|
||||
Dolphin* dolphin = furi_record_open("dolphin");
|
||||
Storage* storage = furi_record_open("storage");
|
||||
const Icon* icon = NULL;
|
||||
furi_assert(animation);
|
||||
FS_Error sd_status = storage_sd_status(storage);
|
||||
|
||||
if(IS_BLOCKING_ANIMATION(animation->state)) {
|
||||
// don't give new animation till blocked animation
|
||||
// is reseted
|
||||
icon = animation->current_blocking_icon;
|
||||
}
|
||||
|
||||
if(!icon) {
|
||||
if(sd_status == FSE_INTERNAL) {
|
||||
osTimerStop(animation->timer);
|
||||
icon = &A_CardBad_128x51;
|
||||
animation->current_blocking_icon = icon;
|
||||
animation->state = DesktopAnimationStateSDCorrupted;
|
||||
animation->sd_shown_error_card_bad = true;
|
||||
animation->sd_shown_error_db = false;
|
||||
} else if(sd_status == FSE_NOT_READY) {
|
||||
animation->sd_shown_error_card_bad = false;
|
||||
animation->sd_shown_error_db = false;
|
||||
} else if(sd_status == FSE_OK) {
|
||||
bool db_exists = storage_common_stat(storage, "/ext/manifest.txt", NULL) == FSE_OK;
|
||||
if(db_exists && !animation->sd_shown_error_db) {
|
||||
osTimerStop(animation->timer);
|
||||
icon = &A_CardNoDB_128x51;
|
||||
animation->current_blocking_icon = icon;
|
||||
animation->state = DesktopAnimationStateSDEmpty;
|
||||
animation->sd_shown_error_db = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DolphinStats stats = dolphin_stats(dolphin);
|
||||
if(!icon && stats.level_up_is_pending) {
|
||||
osTimerStop(animation->timer);
|
||||
icon = &A_LevelUpPending_128x51;
|
||||
animation->current_blocking_icon = icon;
|
||||
animation->state = DesktopAnimationStateLevelUpIsPending;
|
||||
}
|
||||
|
||||
if(!icon) {
|
||||
icon = desktop_animation_get_current_idle_animation(animation);
|
||||
}
|
||||
|
||||
furi_record_close("storage");
|
||||
furi_record_close("dolphin");
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
DesktopAnimationState desktop_animation_handle_right(DesktopAnimation* animation) {
|
||||
furi_assert(animation);
|
||||
|
||||
bool reset_animation = false;
|
||||
bool update_animation = false;
|
||||
|
||||
switch(animation->state) {
|
||||
case DesktopAnimationStateActive:
|
||||
case DesktopAnimationStateBasic:
|
||||
/* nothing */
|
||||
break;
|
||||
case DesktopAnimationStateLevelUpIsPending:
|
||||
/* do nothing, main scene should change itself */
|
||||
break;
|
||||
case DesktopAnimationStateSDCorrupted:
|
||||
reset_animation = true;
|
||||
break;
|
||||
case DesktopAnimationStateSDEmpty:
|
||||
animation->state = DesktopAnimationStateSDEmptyURL;
|
||||
animation->current_blocking_icon = &A_CardNoDBUrl_128x51;
|
||||
update_animation = true;
|
||||
break;
|
||||
case DesktopAnimationStateSDEmptyURL:
|
||||
reset_animation = true;
|
||||
break;
|
||||
default:
|
||||
furi_crash("Unhandled desktop animation state");
|
||||
}
|
||||
|
||||
if(reset_animation) {
|
||||
desktop_start_new_idle_animation(animation);
|
||||
update_animation = true;
|
||||
}
|
||||
|
||||
if(update_animation) {
|
||||
if(animation->animation_changed_callback) {
|
||||
animation->animation_changed_callback(animation->animation_changed_callback_context);
|
||||
}
|
||||
}
|
||||
|
||||
return animation->state;
|
||||
}
|
||||
|
||||
#define LEVELUP_FRAME_RATE (0.2)
|
||||
|
||||
void desktop_animation_start_oneshot_levelup(DesktopAnimation* animation) {
|
||||
animation->one_shot_animation_counter = 0;
|
||||
animation->state = DesktopAnimationStateLevelUpIsPending;
|
||||
|
||||
Dolphin* dolphin = furi_record_open("dolphin");
|
||||
DolphinStats stats = dolphin_stats(dolphin);
|
||||
float timediff = fabs(difftime(stats.timestamp, dolphin_state_timestamp()));
|
||||
furi_record_close("dolphin");
|
||||
furi_assert(stats.level_up_is_pending);
|
||||
if(stats.level == 1) {
|
||||
animation->current_one_shot_icons = animation_level2up;
|
||||
animation->one_shot_animation_size = COUNT_OF(animation_level2up);
|
||||
} else if(stats.level == 2) {
|
||||
animation->current_one_shot_icons = animation_level3up;
|
||||
animation->one_shot_animation_size = COUNT_OF(animation_level3up);
|
||||
} else {
|
||||
furi_crash("Dolphin level is out of bounds");
|
||||
}
|
||||
osTimerStart(animation->timer, LEVELUP_FRAME_RATE * 1000);
|
||||
}
|
||||
|
||||
FURI_LOG_I(TAG, "background change");
|
||||
FURI_LOG_I(TAG, "icounter: %d", stats.icounter);
|
||||
FURI_LOG_I(TAG, "butthurt: %d", stats.butthurt);
|
||||
FURI_LOG_I(TAG, "time since deeed: %.0f", timediff);
|
||||
#endif
|
||||
const Icon* desktop_animation_get_oneshot_frame(DesktopAnimation* animation) {
|
||||
furi_assert(IS_ONESHOT_ANIMATION(animation->state));
|
||||
furi_assert(animation->one_shot_animation_size > 0);
|
||||
const Icon* icon = NULL;
|
||||
|
||||
if((random() % 100) > 50) { // temp rnd selection
|
||||
new = random() % COUNT_OF(idle_scenes);
|
||||
if(animation->one_shot_animation_counter < animation->one_shot_animation_size) {
|
||||
icon = animation->current_one_shot_icons[animation->one_shot_animation_counter];
|
||||
++animation->one_shot_animation_counter;
|
||||
} else {
|
||||
animation->state = DesktopAnimationStateBasic;
|
||||
animation->one_shot_animation_size = 0;
|
||||
osTimerStop(animation->timer);
|
||||
icon = NULL;
|
||||
}
|
||||
|
||||
return idle_scenes[new];
|
||||
return icon;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <math.h>
|
||||
#include <assets_icons.h>
|
||||
#include "dolphin/dolphin.h"
|
||||
#include "dolphin/helpers/dolphin_state.h"
|
||||
#include "time.h"
|
||||
#include <stdint.h>
|
||||
#include <gui/icon.h>
|
||||
|
||||
const Icon* desktop_get_icon();
|
||||
typedef struct DesktopAnimation DesktopAnimation;
|
||||
|
||||
typedef struct ActiveAnimation ActiveAnimation;
|
||||
typedef struct BasicAnimation BasicAnimation;
|
||||
|
||||
typedef enum {
|
||||
DesktopAnimationStateBasic,
|
||||
DesktopAnimationStateActive,
|
||||
DesktopAnimationStateLevelUpIsPending,
|
||||
DesktopAnimationStateSDEmpty,
|
||||
DesktopAnimationStateSDEmptyURL,
|
||||
DesktopAnimationStateSDCorrupted,
|
||||
} DesktopAnimationState;
|
||||
|
||||
struct BasicAnimation {
|
||||
const Icon* icon;
|
||||
uint16_t duration; // sec
|
||||
uint16_t active_cooldown;
|
||||
uint8_t weight;
|
||||
uint16_t butthurt_level_mask;
|
||||
};
|
||||
|
||||
struct ActiveAnimation {
|
||||
const Icon* icon;
|
||||
uint16_t duration; // sec
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const BasicAnimation* basic;
|
||||
const ActiveAnimation* active;
|
||||
} PairedAnimation;
|
||||
|
||||
typedef void (*AnimationChangedCallback)(void*);
|
||||
|
||||
DesktopAnimation* desktop_animation_alloc(void);
|
||||
void desktop_animation_free(DesktopAnimation*);
|
||||
void desktop_animation_activate(DesktopAnimation* instance);
|
||||
void desktop_animation_set_animation_changed_callback(
|
||||
DesktopAnimation* instance,
|
||||
AnimationChangedCallback callback,
|
||||
void* context);
|
||||
|
||||
DesktopAnimationState desktop_animation_handle_right(DesktopAnimation* animation);
|
||||
|
||||
void desktop_animation_start_oneshot_levelup(DesktopAnimation* animation);
|
||||
|
||||
const Icon* desktop_animation_get_animation(DesktopAnimation* animation);
|
||||
const Icon* desktop_animation_get_oneshot_frame(DesktopAnimation* animation);
|
||||
|
||||
void desktop_start_new_idle_animation(DesktopAnimation* animation);
|
||||
|
||||
@@ -0,0 +1,330 @@
|
||||
#include <assets_icons.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <gui/icon.h>
|
||||
#include "desktop_animation.h"
|
||||
|
||||
// Calm/Mad Basic Idle Animations
|
||||
|
||||
#define COMMON_BASIC_DURATION (60 * 60)
|
||||
#define COMMON_ACTIVE_CYCLES 7
|
||||
#define COMMON_ACTIVE_COOLDOWN 30
|
||||
#define COMMON_WEIGHT 3
|
||||
|
||||
#define BUTTHURT_LEVEL(x) (1UL << (x))
|
||||
#define BUTTHURT_LEVEL_0 0
|
||||
|
||||
// frames * cycles / frame_rate
|
||||
#define COMMON_ACTIVE_DURATION(x) ((x)*COMMON_ACTIVE_CYCLES / 2)
|
||||
|
||||
static const BasicAnimation animation_TV = {
|
||||
.icon = &A_TV_128x51,
|
||||
.duration = COMMON_BASIC_DURATION,
|
||||
.weight = COMMON_WEIGHT,
|
||||
.active_cooldown = COMMON_ACTIVE_COOLDOWN,
|
||||
.butthurt_level_mask = BUTTHURT_LEVEL(0) | BUTTHURT_LEVEL(1) | BUTTHURT_LEVEL(2) |
|
||||
BUTTHURT_LEVEL(3) | BUTTHURT_LEVEL(4) | BUTTHURT_LEVEL(5) |
|
||||
BUTTHURT_LEVEL(6) | BUTTHURT_LEVEL(7)};
|
||||
|
||||
static const ActiveAnimation animation_TV_active = {
|
||||
.icon = &A_TVActive_128x51,
|
||||
.duration = COMMON_ACTIVE_DURATION(2),
|
||||
};
|
||||
|
||||
static const BasicAnimation animation_sleep = {
|
||||
.icon = &A_Sleep_128x51,
|
||||
.duration = COMMON_BASIC_DURATION,
|
||||
.weight = COMMON_WEIGHT,
|
||||
.active_cooldown = COMMON_ACTIVE_COOLDOWN,
|
||||
.butthurt_level_mask = BUTTHURT_LEVEL(0) | BUTTHURT_LEVEL(1) | BUTTHURT_LEVEL(2) |
|
||||
BUTTHURT_LEVEL(3) | BUTTHURT_LEVEL(4) | BUTTHURT_LEVEL(5) |
|
||||
BUTTHURT_LEVEL(6) | BUTTHURT_LEVEL(7) | BUTTHURT_LEVEL(8) |
|
||||
BUTTHURT_LEVEL(9) | BUTTHURT_LEVEL(10)};
|
||||
|
||||
static const ActiveAnimation animation_sleep_active = {
|
||||
.icon = &A_SleepActive_128x51,
|
||||
.duration = COMMON_ACTIVE_DURATION(2),
|
||||
};
|
||||
|
||||
static const BasicAnimation animation_leaving = {
|
||||
.icon = &A_Leaving_128x51,
|
||||
.duration = COMMON_BASIC_DURATION,
|
||||
.weight = COMMON_WEIGHT,
|
||||
.active_cooldown = COMMON_ACTIVE_COOLDOWN,
|
||||
.butthurt_level_mask = BUTTHURT_LEVEL(13) | BUTTHURT_LEVEL(14),
|
||||
};
|
||||
|
||||
static const ActiveAnimation animation_leaving_active = {
|
||||
.icon = &A_LeavingActive_128x51,
|
||||
.duration = COMMON_ACTIVE_DURATION(2),
|
||||
};
|
||||
|
||||
static const BasicAnimation animation_laptop = {
|
||||
.icon = &A_Laptop_128x51,
|
||||
.duration = COMMON_BASIC_DURATION,
|
||||
.weight = COMMON_WEIGHT,
|
||||
.active_cooldown = COMMON_ACTIVE_COOLDOWN,
|
||||
.butthurt_level_mask = BUTTHURT_LEVEL(0) | BUTTHURT_LEVEL(1) | BUTTHURT_LEVEL(2) |
|
||||
BUTTHURT_LEVEL(3) | BUTTHURT_LEVEL(4) | BUTTHURT_LEVEL(5)};
|
||||
|
||||
static const ActiveAnimation animation_laptop_active = {
|
||||
.icon = &A_LaptopActive_128x51,
|
||||
.duration = COMMON_ACTIVE_DURATION(2),
|
||||
};
|
||||
|
||||
static const BasicAnimation animation_knife = {
|
||||
.icon = &A_Knife_128x51,
|
||||
.duration = COMMON_BASIC_DURATION,
|
||||
.weight = COMMON_WEIGHT,
|
||||
.active_cooldown = COMMON_ACTIVE_COOLDOWN,
|
||||
.butthurt_level_mask = BUTTHURT_LEVEL(5) | BUTTHURT_LEVEL(6) | BUTTHURT_LEVEL(7) |
|
||||
BUTTHURT_LEVEL(8) | BUTTHURT_LEVEL(9) | BUTTHURT_LEVEL(10) |
|
||||
BUTTHURT_LEVEL(11) | BUTTHURT_LEVEL(12) | BUTTHURT_LEVEL(13)};
|
||||
|
||||
static const ActiveAnimation animation_knife_active = {
|
||||
.icon = &A_KnifeActive_128x51,
|
||||
.duration = COMMON_ACTIVE_DURATION(2),
|
||||
};
|
||||
|
||||
static const BasicAnimation animation_cry = {
|
||||
.icon = &A_Cry_128x51,
|
||||
.duration = COMMON_BASIC_DURATION,
|
||||
.weight = COMMON_WEIGHT,
|
||||
.active_cooldown = COMMON_ACTIVE_COOLDOWN,
|
||||
.butthurt_level_mask = BUTTHURT_LEVEL(3) | BUTTHURT_LEVEL(4) | BUTTHURT_LEVEL(5) |
|
||||
BUTTHURT_LEVEL(6) | BUTTHURT_LEVEL(7) | BUTTHURT_LEVEL(8) |
|
||||
BUTTHURT_LEVEL(9) | BUTTHURT_LEVEL(10) | BUTTHURT_LEVEL(11) |
|
||||
BUTTHURT_LEVEL(12) | BUTTHURT_LEVEL(13)};
|
||||
|
||||
static const ActiveAnimation animation_cry_active = {
|
||||
.icon = &A_CryActive_128x51,
|
||||
.duration = COMMON_ACTIVE_DURATION(3),
|
||||
};
|
||||
|
||||
static const BasicAnimation animation_box = {
|
||||
.icon = &A_Box_128x51,
|
||||
.duration = COMMON_BASIC_DURATION,
|
||||
.weight = COMMON_WEIGHT,
|
||||
.active_cooldown = COMMON_ACTIVE_COOLDOWN,
|
||||
.butthurt_level_mask = BUTTHURT_LEVEL(7) | BUTTHURT_LEVEL(8) | BUTTHURT_LEVEL(9) |
|
||||
BUTTHURT_LEVEL(10) | BUTTHURT_LEVEL(11) | BUTTHURT_LEVEL(12) |
|
||||
BUTTHURT_LEVEL(13)};
|
||||
|
||||
static const ActiveAnimation animation_box_active = {
|
||||
.icon = &A_BoxActive_128x51,
|
||||
.duration = COMMON_ACTIVE_DURATION(2),
|
||||
};
|
||||
|
||||
static const BasicAnimation animation_waves = {
|
||||
.icon = &A_Waves_128x51,
|
||||
.duration = COMMON_BASIC_DURATION,
|
||||
.weight = COMMON_WEIGHT,
|
||||
.active_cooldown = COMMON_ACTIVE_COOLDOWN,
|
||||
.butthurt_level_mask = BUTTHURT_LEVEL(0) | BUTTHURT_LEVEL(1) | BUTTHURT_LEVEL(2)};
|
||||
|
||||
static const ActiveAnimation animation_waves_active = {
|
||||
.icon = &A_WavesActive_128x51,
|
||||
.duration = COMMON_ACTIVE_DURATION(2),
|
||||
};
|
||||
|
||||
// Level Idle Animations
|
||||
|
||||
static const BasicAnimation animation_level1furippa = {
|
||||
.icon = &A_Level1Furippa_128x51,
|
||||
.duration = COMMON_BASIC_DURATION,
|
||||
.weight = COMMON_WEIGHT,
|
||||
.active_cooldown = COMMON_ACTIVE_COOLDOWN,
|
||||
.butthurt_level_mask = BUTTHURT_LEVEL(0) | BUTTHURT_LEVEL(1) | BUTTHURT_LEVEL(2) |
|
||||
BUTTHURT_LEVEL(3) | BUTTHURT_LEVEL(4) | BUTTHURT_LEVEL(5) |
|
||||
BUTTHURT_LEVEL(6) | BUTTHURT_LEVEL(7)};
|
||||
|
||||
static const ActiveAnimation animation_level1furippa_active = {
|
||||
.icon = &A_Level1FurippaActive_128x51,
|
||||
.duration = COMMON_ACTIVE_DURATION(6),
|
||||
};
|
||||
|
||||
static const BasicAnimation animation_level1read = {
|
||||
.icon = &A_Level1Read_128x51,
|
||||
.duration = COMMON_BASIC_DURATION,
|
||||
.weight = COMMON_WEIGHT,
|
||||
.active_cooldown = COMMON_ACTIVE_COOLDOWN,
|
||||
.butthurt_level_mask = BUTTHURT_LEVEL(0) | BUTTHURT_LEVEL(1) | BUTTHURT_LEVEL(2)};
|
||||
|
||||
static const ActiveAnimation animation_level1read_active = {
|
||||
.icon = &A_Level1ReadActive_128x51,
|
||||
.duration = COMMON_ACTIVE_DURATION(2),
|
||||
};
|
||||
|
||||
static const BasicAnimation animation_level1toys = {
|
||||
.icon = &A_Level1Toys_128x51,
|
||||
.duration = COMMON_BASIC_DURATION,
|
||||
.weight = COMMON_WEIGHT,
|
||||
.active_cooldown = COMMON_ACTIVE_COOLDOWN,
|
||||
.butthurt_level_mask = BUTTHURT_LEVEL(0) | BUTTHURT_LEVEL(1) | BUTTHURT_LEVEL(2) |
|
||||
BUTTHURT_LEVEL(3) | BUTTHURT_LEVEL(4) | BUTTHURT_LEVEL(5) |
|
||||
BUTTHURT_LEVEL(6) | BUTTHURT_LEVEL(7) | BUTTHURT_LEVEL(8)};
|
||||
|
||||
static const ActiveAnimation animation_level1toys_active = {
|
||||
.icon = &A_Level1ToysActive_128x51,
|
||||
.duration = COMMON_ACTIVE_DURATION(2),
|
||||
};
|
||||
|
||||
static const BasicAnimation animation_level2furippa = {
|
||||
.icon = &A_Level2Furippa_128x51,
|
||||
.duration = COMMON_BASIC_DURATION,
|
||||
.weight = COMMON_WEIGHT,
|
||||
.active_cooldown = COMMON_ACTIVE_COOLDOWN,
|
||||
.butthurt_level_mask = BUTTHURT_LEVEL(0) | BUTTHURT_LEVEL(1) | BUTTHURT_LEVEL(2) |
|
||||
BUTTHURT_LEVEL(3) | BUTTHURT_LEVEL(4) | BUTTHURT_LEVEL(5) |
|
||||
BUTTHURT_LEVEL(6) | BUTTHURT_LEVEL(7)};
|
||||
|
||||
static const ActiveAnimation animation_level2furippa_active = {
|
||||
.icon = &A_Level2FurippaActive_128x51,
|
||||
.duration = COMMON_ACTIVE_DURATION(6),
|
||||
};
|
||||
|
||||
static const BasicAnimation animation_level2soldering = {
|
||||
.icon = &A_Level2Soldering_128x51,
|
||||
.duration = COMMON_BASIC_DURATION,
|
||||
.weight = COMMON_WEIGHT,
|
||||
.active_cooldown = COMMON_ACTIVE_COOLDOWN,
|
||||
.butthurt_level_mask = BUTTHURT_LEVEL(0) | BUTTHURT_LEVEL(1) | BUTTHURT_LEVEL(2) |
|
||||
BUTTHURT_LEVEL(3) | BUTTHURT_LEVEL(4) | BUTTHURT_LEVEL(5) |
|
||||
BUTTHURT_LEVEL(6) | BUTTHURT_LEVEL(7) | BUTTHURT_LEVEL(8) |
|
||||
BUTTHURT_LEVEL(9)};
|
||||
|
||||
static const ActiveAnimation animation_level2soldering_active = {
|
||||
.icon = &A_Level2SolderingActive_128x51,
|
||||
.duration = COMMON_ACTIVE_DURATION(2),
|
||||
};
|
||||
|
||||
static const BasicAnimation animation_level2hack = {
|
||||
.icon = &A_Level2Hack_128x51,
|
||||
.duration = COMMON_BASIC_DURATION,
|
||||
.weight = COMMON_WEIGHT,
|
||||
.active_cooldown = COMMON_ACTIVE_COOLDOWN,
|
||||
.butthurt_level_mask = BUTTHURT_LEVEL(0) | BUTTHURT_LEVEL(1) | BUTTHURT_LEVEL(2) |
|
||||
BUTTHURT_LEVEL(3) | BUTTHURT_LEVEL(4) | BUTTHURT_LEVEL(5) |
|
||||
BUTTHURT_LEVEL(6) | BUTTHURT_LEVEL(7) | BUTTHURT_LEVEL(8)};
|
||||
|
||||
static const ActiveAnimation animation_level2hack_active = {
|
||||
.icon = &A_Level2HackActive_128x51,
|
||||
.duration = COMMON_ACTIVE_DURATION(2),
|
||||
};
|
||||
|
||||
static const BasicAnimation animation_level3furippa = {
|
||||
.icon = &A_Level3Furippa_128x51,
|
||||
.duration = COMMON_BASIC_DURATION,
|
||||
.weight = COMMON_WEIGHT,
|
||||
.active_cooldown = COMMON_ACTIVE_COOLDOWN,
|
||||
.butthurt_level_mask = BUTTHURT_LEVEL(0) | BUTTHURT_LEVEL(1) | BUTTHURT_LEVEL(2) |
|
||||
BUTTHURT_LEVEL(3) | BUTTHURT_LEVEL(4) | BUTTHURT_LEVEL(5) |
|
||||
BUTTHURT_LEVEL(6) | BUTTHURT_LEVEL(7)};
|
||||
|
||||
static const ActiveAnimation animation_level3furippa_active = {
|
||||
.icon = &A_Level3FurippaActive_128x51,
|
||||
.duration = COMMON_ACTIVE_DURATION(6),
|
||||
};
|
||||
|
||||
static const BasicAnimation animation_level3hijack = {
|
||||
.icon = &A_Level3Hijack_128x51,
|
||||
.duration = COMMON_BASIC_DURATION,
|
||||
.weight = COMMON_WEIGHT,
|
||||
.active_cooldown = COMMON_ACTIVE_COOLDOWN,
|
||||
.butthurt_level_mask = BUTTHURT_LEVEL(0) | BUTTHURT_LEVEL(1) | BUTTHURT_LEVEL(2) |
|
||||
BUTTHURT_LEVEL(3) | BUTTHURT_LEVEL(4) | BUTTHURT_LEVEL(5) |
|
||||
BUTTHURT_LEVEL(6) | BUTTHURT_LEVEL(7) | BUTTHURT_LEVEL(8) |
|
||||
BUTTHURT_LEVEL(9)};
|
||||
|
||||
static const ActiveAnimation animation_level3hijack_active = {
|
||||
.icon = &A_Level3HijackActive_128x51,
|
||||
.duration = COMMON_ACTIVE_DURATION(2),
|
||||
};
|
||||
|
||||
static const BasicAnimation animation_level3lab = {
|
||||
.icon = &A_Level3Lab_128x51,
|
||||
.duration = COMMON_BASIC_DURATION,
|
||||
.weight = COMMON_WEIGHT,
|
||||
.active_cooldown = COMMON_ACTIVE_COOLDOWN,
|
||||
.butthurt_level_mask = BUTTHURT_LEVEL(0) | BUTTHURT_LEVEL(1) | BUTTHURT_LEVEL(2) |
|
||||
BUTTHURT_LEVEL(3) | BUTTHURT_LEVEL(4) | BUTTHURT_LEVEL(5) |
|
||||
BUTTHURT_LEVEL(6) | BUTTHURT_LEVEL(7) | BUTTHURT_LEVEL(8)};
|
||||
|
||||
static const ActiveAnimation animation_level3lab_active = {
|
||||
.icon = &A_Level3LabActive_128x51,
|
||||
.duration = COMMON_ACTIVE_DURATION(2),
|
||||
};
|
||||
|
||||
// System Idle Animations
|
||||
|
||||
static const BasicAnimation animation_bad_battery = {
|
||||
.icon = &A_BadBattery_128x51,
|
||||
.duration = COMMON_BASIC_DURATION,
|
||||
.weight = 7,
|
||||
};
|
||||
|
||||
static const BasicAnimation animation_no_sd_card = {
|
||||
.icon = &A_NoSdCard_128x51,
|
||||
.duration = COMMON_BASIC_DURATION,
|
||||
.weight = 7,
|
||||
};
|
||||
|
||||
const Icon* animation_level2up[] = {
|
||||
&I_LevelUp2_01,
|
||||
&I_LevelUp2_02,
|
||||
&I_LevelUp2_03,
|
||||
&I_LevelUp2_04,
|
||||
&I_LevelUp2_05,
|
||||
&I_LevelUp2_06,
|
||||
&I_LevelUp2_07};
|
||||
|
||||
const Icon* animation_level3up[] = {
|
||||
&I_LevelUp3_01,
|
||||
&I_LevelUp3_02,
|
||||
&I_LevelUp3_03,
|
||||
&I_LevelUp3_04,
|
||||
&I_LevelUp3_05,
|
||||
&I_LevelUp3_06,
|
||||
&I_LevelUp3_07};
|
||||
|
||||
// Blocking Idle Animations & One shot Animations represented as naked Icon
|
||||
|
||||
static const PairedAnimation calm_animation[] = {
|
||||
{.basic = &animation_TV, .active = &animation_TV_active},
|
||||
{.basic = &animation_waves, .active = &animation_waves_active},
|
||||
{.basic = &animation_sleep, .active = &animation_sleep_active},
|
||||
{.basic = &animation_laptop, .active = &animation_laptop_active},
|
||||
};
|
||||
|
||||
static const PairedAnimation mad_animation[] = {
|
||||
{.basic = &animation_cry, .active = &animation_cry_active},
|
||||
{.basic = &animation_knife, .active = &animation_knife_active},
|
||||
{.basic = &animation_box, .active = &animation_box_active},
|
||||
{.basic = &animation_leaving, .active = &animation_leaving_active},
|
||||
};
|
||||
|
||||
static const PairedAnimation level_1_animation[] = {
|
||||
{.basic = &animation_level1furippa, .active = &animation_level1furippa_active},
|
||||
{.basic = &animation_level1read, .active = &animation_level1read_active},
|
||||
{.basic = &animation_level1toys, .active = &animation_level1toys_active},
|
||||
};
|
||||
|
||||
static const PairedAnimation level_2_animation[] = {
|
||||
{.basic = &animation_level2furippa, .active = &animation_level2furippa_active},
|
||||
{.basic = &animation_level2soldering, .active = &animation_level2soldering_active},
|
||||
{.basic = &animation_level2hack, .active = &animation_level2hack_active},
|
||||
};
|
||||
|
||||
static const PairedAnimation level_3_animation[] = {
|
||||
{.basic = &animation_level3furippa, .active = &animation_level3furippa_active},
|
||||
{.basic = &animation_level3hijack, .active = &animation_level3hijack_active},
|
||||
{.basic = &animation_level3lab, .active = &animation_level3lab_active},
|
||||
};
|
||||
|
||||
static const PairedAnimation no_sd_animation[] = {
|
||||
{.basic = &animation_no_sd_card, .active = NULL},
|
||||
};
|
||||
|
||||
static const PairedAnimation check_battery_animation[] = {
|
||||
{.basic = &animation_bad_battery, .active = NULL},
|
||||
};
|
||||
@@ -5,3 +5,4 @@ ADD_SCENE(desktop, debug, Debug)
|
||||
ADD_SCENE(desktop, first_start, FirstStart)
|
||||
ADD_SCENE(desktop, hw_mismatch, HwMismatch)
|
||||
ADD_SCENE(desktop, pinsetup, PinSetup)
|
||||
ADD_SCENE(desktop, levelup, LevelUp)
|
||||
|
||||
@@ -59,4 +59,5 @@ bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) {
|
||||
void desktop_scene_debug_on_exit(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
desktop_debug_reset_screen_idx(desktop->debug_view);
|
||||
desktop_start_new_idle_animation(desktop->animation);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
#include "../desktop_i.h"
|
||||
#include "../views/desktop_main.h"
|
||||
#include "applications.h"
|
||||
#include "assets_icons.h"
|
||||
#include "desktop/desktop.h"
|
||||
#include "desktop/helpers/desktop_animation.h"
|
||||
#include "dolphin/dolphin.h"
|
||||
#include "furi/pubsub.h"
|
||||
#include "furi/record.h"
|
||||
#include "storage/storage-glue.h"
|
||||
#include <loader/loader.h>
|
||||
#include <m-list.h>
|
||||
|
||||
#define LEVELUP_SCENE_PLAYING 0
|
||||
#define LEVELUP_SCENE_STOPPED 1
|
||||
|
||||
static void desktop_scene_levelup_callback(DesktopMainEvent event, void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, event);
|
||||
}
|
||||
|
||||
static void desktop_scene_levelup_animation_changed_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Desktop* desktop = context;
|
||||
view_dispatcher_send_custom_event(
|
||||
desktop->view_dispatcher, DesktopMainEventUpdateOneShotAnimation);
|
||||
}
|
||||
|
||||
void desktop_scene_levelup_on_enter(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
DesktopMainView* main_view = desktop->main_view;
|
||||
|
||||
desktop_main_set_callback(main_view, desktop_scene_levelup_callback, desktop);
|
||||
desktop_animation_set_animation_changed_callback(
|
||||
desktop->animation, desktop_scene_levelup_animation_changed_callback, desktop);
|
||||
|
||||
desktop_animation_start_oneshot_levelup(desktop->animation);
|
||||
const Icon* icon = desktop_animation_get_oneshot_frame(desktop->animation);
|
||||
desktop_main_switch_dolphin_icon(desktop->main_view, icon);
|
||||
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewMain);
|
||||
scene_manager_set_scene_state(
|
||||
desktop->scene_manager, DesktopSceneLevelUp, LEVELUP_SCENE_PLAYING);
|
||||
}
|
||||
|
||||
bool desktop_scene_levelup_on_event(void* context, SceneManagerEvent event) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
bool consumed = false;
|
||||
DesktopMainEvent main_event = event.event;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(main_event == DesktopMainEventUpdateOneShotAnimation) {
|
||||
const Icon* icon = desktop_animation_get_oneshot_frame(desktop->animation);
|
||||
if(icon) {
|
||||
desktop_main_switch_dolphin_icon(desktop->main_view, icon);
|
||||
} else {
|
||||
scene_manager_set_scene_state(
|
||||
desktop->scene_manager, DesktopSceneLevelUp, LEVELUP_SCENE_STOPPED);
|
||||
}
|
||||
consumed = true;
|
||||
} else {
|
||||
if(scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLevelUp) ==
|
||||
LEVELUP_SCENE_STOPPED) {
|
||||
scene_manager_previous_scene(desktop->scene_manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void desktop_scene_levelup_on_exit(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
|
||||
Dolphin* dolphin = furi_record_open("dolphin");
|
||||
dolphin_upgrade_level(dolphin);
|
||||
furi_record_close("dolphin");
|
||||
desktop_animation_set_animation_changed_callback(desktop->animation, NULL, NULL);
|
||||
desktop_start_new_idle_animation(desktop->animation);
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "../desktop_i.h"
|
||||
#include "../views/desktop_locked.h"
|
||||
#include "desktop/helpers/desktop_animation.h"
|
||||
#include "desktop/views/desktop_main.h"
|
||||
#include <furi-hal-lock.h>
|
||||
|
||||
void desktop_scene_locked_callback(DesktopLockedEvent event, void* context) {
|
||||
@@ -7,6 +9,12 @@ void desktop_scene_locked_callback(DesktopLockedEvent event, void* context) {
|
||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, event);
|
||||
}
|
||||
|
||||
static void desktop_scene_locked_animation_changed_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Desktop* desktop = context;
|
||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopMainEventUpdateAnimation);
|
||||
}
|
||||
|
||||
void desktop_scene_locked_on_enter(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
DesktopLockedView* locked_view = desktop->locked_view;
|
||||
@@ -14,7 +22,11 @@ void desktop_scene_locked_on_enter(void* context) {
|
||||
desktop_locked_set_callback(locked_view, desktop_scene_locked_callback, desktop);
|
||||
desktop_locked_reset_door_pos(locked_view);
|
||||
desktop_locked_update_hint_timeout(locked_view);
|
||||
desktop_locked_set_dolphin_animation(locked_view);
|
||||
|
||||
desktop_animation_set_animation_changed_callback(
|
||||
desktop->animation, desktop_scene_locked_animation_changed_callback, desktop);
|
||||
const Icon* icon = desktop_animation_get_animation(desktop->animation);
|
||||
desktop_locked_set_dolphin_animation(locked_view, icon);
|
||||
|
||||
uint32_t state = scene_manager_get_scene_state(desktop->scene_manager, DesktopViewLocked);
|
||||
|
||||
@@ -68,6 +80,12 @@ bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) {
|
||||
case DesktopLockedEventInputReset:
|
||||
desktop->pincode_buffer.length = 0;
|
||||
break;
|
||||
case DesktopMainEventUpdateAnimation: {
|
||||
const Icon* icon = desktop_animation_get_animation(desktop->animation);
|
||||
desktop_locked_set_dolphin_animation(desktop->locked_view, icon);
|
||||
consumed = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if(desktop_scene_locked_check_pin(desktop, event.event)) {
|
||||
scene_manager_set_scene_state(
|
||||
@@ -84,6 +102,7 @@ bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
void desktop_scene_locked_on_exit(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
desktop_animation_set_animation_changed_callback(desktop->animation, NULL, NULL);
|
||||
desktop_locked_reset_counter(desktop->locked_view);
|
||||
osTimerStop(desktop->locked_view->timer);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
#include "../desktop_i.h"
|
||||
#include "../views/desktop_main.h"
|
||||
#include "applications.h"
|
||||
#include "assets_icons.h"
|
||||
#include "dolphin/dolphin.h"
|
||||
#include "furi/pubsub.h"
|
||||
#include "furi/record.h"
|
||||
#include "storage/storage-glue.h"
|
||||
#include <loader/loader.h>
|
||||
#include <m-list.h>
|
||||
#define MAIN_VIEW_DEFAULT (0UL)
|
||||
|
||||
static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* flipper_app) {
|
||||
@@ -27,6 +33,12 @@ void desktop_scene_main_callback(DesktopMainEvent event, void* context) {
|
||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, event);
|
||||
}
|
||||
|
||||
static void desktop_scene_main_animation_changed_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Desktop* desktop = context;
|
||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopMainEventUpdateAnimation);
|
||||
}
|
||||
|
||||
void desktop_scene_main_on_enter(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
DesktopMainView* main_view = desktop->main_view;
|
||||
@@ -39,7 +51,11 @@ void desktop_scene_main_on_enter(void* context) {
|
||||
desktop_main_unlocked(desktop->main_view);
|
||||
}
|
||||
|
||||
desktop_main_switch_dolphin_animation(desktop->main_view);
|
||||
desktop_animation_activate(desktop->animation);
|
||||
desktop_animation_set_animation_changed_callback(
|
||||
desktop->animation, desktop_scene_main_animation_changed_callback, desktop);
|
||||
const Icon* icon = desktop_animation_get_animation(desktop->animation);
|
||||
desktop_main_switch_dolphin_animation(desktop->main_view, icon);
|
||||
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewMain);
|
||||
}
|
||||
|
||||
@@ -75,9 +91,30 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
case DesktopMainEventUpdateAnimation: {
|
||||
const Icon* icon = desktop_animation_get_animation(desktop->animation);
|
||||
desktop_main_switch_dolphin_animation(desktop->main_view, icon);
|
||||
consumed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case DesktopMainEventRightShort: {
|
||||
DesktopAnimationState state = desktop_animation_handle_right(desktop->animation);
|
||||
if(state == DesktopAnimationStateLevelUpIsPending) {
|
||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLevelUp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(event.event != DesktopMainEventUpdateAnimation) {
|
||||
desktop_animation_activate(desktop->animation);
|
||||
}
|
||||
} else if(event.type != SceneManagerEventTypeTick) {
|
||||
desktop_animation_activate(desktop->animation);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
@@ -85,6 +122,8 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
void desktop_scene_main_on_exit(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
|
||||
desktop_animation_set_animation_changed_callback(desktop->animation, NULL, NULL);
|
||||
scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneMain, MAIN_VIEW_DEFAULT);
|
||||
desktop_main_reset_hint(desktop->main_view);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ void desktop_debug_render(Canvas* canvas, void* model) {
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 2, 13, headers[m->screen]);
|
||||
canvas_draw_str(canvas, 2, 9, headers[m->screen]);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
if(m->screen != DesktopViewStatsMeta) {
|
||||
@@ -40,13 +40,13 @@ void desktop_debug_render(Canvas* canvas, void* model) {
|
||||
furi_hal_version_get_hw_body(),
|
||||
furi_hal_version_get_hw_connect(),
|
||||
my_name ? my_name : "Unknown");
|
||||
canvas_draw_str(canvas, 5, 23, buffer);
|
||||
canvas_draw_str(canvas, 5, 19, buffer);
|
||||
|
||||
ver = m->screen == DesktopViewStatsBoot ? furi_hal_version_get_bootloader_version() :
|
||||
furi_hal_version_get_firmware_version();
|
||||
|
||||
if(!ver) {
|
||||
canvas_draw_str(canvas, 5, 33, "No info");
|
||||
canvas_draw_str(canvas, 5, 29, "No info");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ void desktop_debug_render(Canvas* canvas, void* model) {
|
||||
"%s [%s]",
|
||||
version_get_version(ver),
|
||||
version_get_builddate(ver));
|
||||
canvas_draw_str(canvas, 5, 32, buffer);
|
||||
canvas_draw_str(canvas, 5, 28, buffer);
|
||||
|
||||
snprintf(
|
||||
buffer,
|
||||
@@ -64,27 +64,36 @@ void desktop_debug_render(Canvas* canvas, void* model) {
|
||||
"%s [%s]",
|
||||
version_get_githash(ver),
|
||||
version_get_gitbranchnum(ver));
|
||||
canvas_draw_str(canvas, 5, 43, buffer);
|
||||
canvas_draw_str(canvas, 5, 39, buffer);
|
||||
|
||||
snprintf(
|
||||
buffer, sizeof(buffer), "[%d] %s", version_get_target(ver), version_get_gitbranch(ver));
|
||||
canvas_draw_str(canvas, 5, 54, buffer);
|
||||
canvas_draw_str(canvas, 5, 50, buffer);
|
||||
|
||||
} else {
|
||||
char buffer[64];
|
||||
uint32_t current_lvl = dolphin_state_get_level(m->icounter);
|
||||
uint32_t remaining = dolphin_state_xp_to_levelup(m->icounter, current_lvl, true);
|
||||
Dolphin* dolphin = furi_record_open("dolphin");
|
||||
DolphinStats stats = dolphin_stats(dolphin);
|
||||
furi_record_close("dolphin");
|
||||
|
||||
uint32_t current_lvl = stats.level;
|
||||
uint32_t remaining = dolphin_state_xp_to_levelup(m->icounter);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
snprintf(buffer, 64, "Icounter: %ld Butthurt %ld", m->icounter, m->butthurt);
|
||||
canvas_draw_str(canvas, 5, 23, buffer);
|
||||
canvas_draw_str(canvas, 5, 19, buffer);
|
||||
|
||||
snprintf(buffer, 64, "Level: %ld To level up: %ld", current_lvl, remaining);
|
||||
canvas_draw_str(canvas, 5, 33, buffer);
|
||||
snprintf(
|
||||
buffer,
|
||||
64,
|
||||
"Level: %ld To level up: %ld",
|
||||
current_lvl,
|
||||
(remaining == (uint32_t)(-1) ? remaining : 0));
|
||||
canvas_draw_str(canvas, 5, 29, buffer);
|
||||
|
||||
snprintf(buffer, 64, "%s", asctime(localtime((const time_t*)&m->timestamp)));
|
||||
canvas_draw_str(canvas, 5, 43, buffer);
|
||||
canvas_draw_str(canvas, 0, 53, "[< >] icounter value [ok] save");
|
||||
canvas_draw_str(canvas, 5, 39, buffer);
|
||||
canvas_draw_str(canvas, 0, 49, "[< >] icounter value [ok] save");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,20 +26,20 @@ static void desktop_first_start_draw(Canvas* canvas, void* model) {
|
||||
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 - 48, &I_DolphinFirstStart0_70x53);
|
||||
elements_multiline_text_framed(canvas, 75, 20, "Hey m8,\npress > to\ncontinue");
|
||||
canvas_draw_icon(canvas, 0, height - 51, &I_DolphinFirstStart0_70x53);
|
||||
elements_multiline_text_framed(canvas, 75, 16, "Hey m8,\npress > to\ncontinue");
|
||||
} else if(m->page == 1) {
|
||||
canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart1_59x53);
|
||||
elements_multiline_text_framed(canvas, 64, 20, "First Of All,\n... >");
|
||||
canvas_draw_icon(canvas, 0, height - 51, &I_DolphinFirstStart1_59x53);
|
||||
elements_multiline_text_framed(canvas, 64, 16, "First Of All,\n... >");
|
||||
} else if(m->page == 2) {
|
||||
canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart2_59x51);
|
||||
elements_multiline_text_framed(canvas, 64, 20, "Thank you\nfor your\nsupport! >");
|
||||
canvas_draw_icon(canvas, 0, height - 51, &I_DolphinFirstStart2_59x51);
|
||||
elements_multiline_text_framed(canvas, 64, 16, "Thank you\nfor your\nsupport! >");
|
||||
} else if(m->page == 3) {
|
||||
canvas_draw_icon(canvas, width - 57, height - 48, &I_DolphinFirstStart3_57x48);
|
||||
elements_multiline_text_framed(canvas, 0, 20, "Kickstarter\ncampaign\nwas INSANE! >");
|
||||
canvas_draw_icon(canvas, width - 57, height - 45, &I_DolphinFirstStart3_57x48);
|
||||
elements_multiline_text_framed(canvas, 0, 16, "Kickstarter\ncampaign\nwas INSANE! >");
|
||||
} else if(m->page == 4) {
|
||||
canvas_draw_icon(canvas, width - 67, height - 50, &I_DolphinFirstStart4_67x53);
|
||||
elements_multiline_text_framed(canvas, 0, 17, "Now\nallow me\nto introduce\nmyself >");
|
||||
canvas_draw_icon(canvas, width - 67, height - 51, &I_DolphinFirstStart4_67x53);
|
||||
elements_multiline_text_framed(canvas, 0, 13, "Now\nallow me\nto introduce\nmyself >");
|
||||
} else if(m->page == 5) {
|
||||
char buf[64];
|
||||
snprintf(
|
||||
@@ -49,20 +49,20 @@ static void desktop_first_start_draw(Canvas* canvas, void* model) {
|
||||
"I am",
|
||||
my_name ? my_name : "Unknown",
|
||||
",\ncyberdolphin\nliving in your\npocket >");
|
||||
canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart5_54x49);
|
||||
elements_multiline_text_framed(canvas, 60, 17, buf);
|
||||
canvas_draw_icon(canvas, 0, height - 49, &I_DolphinFirstStart5_54x49);
|
||||
elements_multiline_text_framed(canvas, 60, 13, buf);
|
||||
} else if(m->page == 6) {
|
||||
canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart6_58x54);
|
||||
canvas_draw_icon(canvas, 0, height - 51, &I_DolphinFirstStart6_58x54);
|
||||
elements_multiline_text_framed(
|
||||
canvas, 63, 17, "I can grow\nsmart'n'cool\nif you use me\noften >");
|
||||
canvas, 63, 13, "I can grow\nsmart'n'cool\nif you use me\noften >");
|
||||
} else if(m->page == 7) {
|
||||
canvas_draw_icon(canvas, width - 61, height - 48, &I_DolphinFirstStart7_61x51);
|
||||
canvas_draw_icon(canvas, width - 61, height - 51, &I_DolphinFirstStart7_61x51);
|
||||
elements_multiline_text_framed(
|
||||
canvas, 0, 17, "As long as\nyou read, write\nand emulate >");
|
||||
canvas, 0, 13, "As long as\nyou read, write\nand emulate >");
|
||||
} else if(m->page == 8) {
|
||||
canvas_draw_icon(canvas, width - 56, height - 48, &I_DolphinFirstStart8_56x51);
|
||||
canvas_draw_icon(canvas, width - 56, height - 51, &I_DolphinFirstStart8_56x51);
|
||||
elements_multiline_text_framed(
|
||||
canvas, 0, 17, "You can check\nmy level and\nmood in the\nPassport menu");
|
||||
canvas, 0, 13, "You can check\nmy level and\nmood in the\nPassport menu");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,13 +17,13 @@ void locked_view_timer_callback(void* context) {
|
||||
locked_view->callback(DesktopLockedEventUpdate, locked_view->context);
|
||||
}
|
||||
|
||||
// temporary locked screen animation managment
|
||||
void desktop_locked_set_dolphin_animation(DesktopLockedView* locked_view) {
|
||||
void desktop_locked_set_dolphin_animation(DesktopLockedView* locked_view, const Icon* icon) {
|
||||
with_view_model(
|
||||
locked_view->view, (DesktopLockedViewModel * model) {
|
||||
if(model->animation) icon_animation_free(model->animation);
|
||||
model->animation = icon_animation_alloc(desktop_get_icon());
|
||||
model->animation = icon_animation_alloc(icon);
|
||||
view_tie_icon_animation(locked_view->view, model->animation);
|
||||
icon_animation_start(model->animation);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
@@ -100,7 +100,7 @@ void desktop_locked_render(Canvas* canvas, void* model) {
|
||||
}
|
||||
|
||||
if(m->animation && m->animation_seq_end) {
|
||||
canvas_draw_icon_animation(canvas, 0, -3, m->animation);
|
||||
canvas_draw_icon_animation(canvas, 0, 0, m->animation);
|
||||
}
|
||||
|
||||
if(now < m->hint_expire_at) {
|
||||
@@ -110,7 +110,7 @@ void desktop_locked_render(Canvas* canvas, void* model) {
|
||||
|
||||
} else if(!m->pin_lock) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_icon(canvas, 13, 5, &I_LockPopup_100x49);
|
||||
canvas_draw_icon(canvas, 13, 2, &I_LockPopup_100x49);
|
||||
elements_multiline_text(canvas, 65, 20, "To unlock\npress:");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ void desktop_locked_set_callback(
|
||||
DesktopLockedViewCallback callback,
|
||||
void* context);
|
||||
|
||||
void desktop_locked_set_dolphin_animation(DesktopLockedView* locked_view);
|
||||
void desktop_locked_set_dolphin_animation(DesktopLockedView* locked_view, const Icon* icon);
|
||||
void desktop_locked_update_hint_timeout(DesktopLockedView* locked_view);
|
||||
void desktop_locked_reset_counter(DesktopLockedView* locked_view);
|
||||
void desktop_locked_reset_door_pos(DesktopLockedView* locked_view);
|
||||
@@ -65,4 +65,4 @@ void desktop_locked_manage_redraw(DesktopLockedView* locked_view);
|
||||
View* desktop_locked_get_view(DesktopLockedView* locked_view);
|
||||
DesktopLockedView* desktop_locked_alloc();
|
||||
void desktop_locked_free(DesktopLockedView* locked_view);
|
||||
void desktop_locked_with_pin(DesktopLockedView* lock_menu, bool locked);
|
||||
void desktop_locked_with_pin(DesktopLockedView* lock_menu, bool locked);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include "gui/canvas.h"
|
||||
#include "input/input.h"
|
||||
#include <furi.h>
|
||||
#include "../desktop_i.h"
|
||||
#include "desktop_main.h"
|
||||
@@ -20,12 +22,24 @@ void desktop_main_reset_hint(DesktopMainView* main_view) {
|
||||
});
|
||||
}
|
||||
|
||||
void desktop_main_switch_dolphin_animation(DesktopMainView* main_view) {
|
||||
void desktop_main_switch_dolphin_animation(DesktopMainView* main_view, const Icon* icon) {
|
||||
with_view_model(
|
||||
main_view->view, (DesktopMainViewModel * model) {
|
||||
if(model->animation) icon_animation_free(model->animation);
|
||||
model->animation = icon_animation_alloc(desktop_get_icon());
|
||||
model->animation = icon_animation_alloc(icon);
|
||||
view_tie_icon_animation(main_view->view, model->animation);
|
||||
icon_animation_start(model->animation);
|
||||
model->icon = NULL;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void desktop_main_switch_dolphin_icon(DesktopMainView* main_view, const Icon* icon) {
|
||||
with_view_model(
|
||||
main_view->view, (DesktopMainViewModel * model) {
|
||||
if(model->animation) icon_animation_free(model->animation);
|
||||
model->animation = NULL;
|
||||
model->icon = icon;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
@@ -35,8 +49,10 @@ void desktop_main_render(Canvas* canvas, void* model) {
|
||||
DesktopMainViewModel* m = model;
|
||||
uint32_t now = osKernelGetTickCount();
|
||||
|
||||
if(m->animation) {
|
||||
canvas_draw_icon_animation(canvas, 0, -3, m->animation);
|
||||
if(m->icon) {
|
||||
canvas_draw_icon(canvas, 0, 0, m->icon);
|
||||
} else if(m->animation) {
|
||||
canvas_draw_icon_animation(canvas, 0, 0, m->animation);
|
||||
}
|
||||
|
||||
if(now < m->hint_expire_at) {
|
||||
@@ -66,6 +82,8 @@ bool desktop_main_input(InputEvent* event, void* context) {
|
||||
main_view->callback(DesktopMainEventOpenArchive, main_view->context);
|
||||
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
|
||||
main_view->callback(DesktopMainEventOpenFavorite, main_view->context);
|
||||
} else if(event->key == InputKeyRight && event->type == InputTypeShort) {
|
||||
main_view->callback(DesktopMainEventRightShort, main_view->context);
|
||||
}
|
||||
|
||||
desktop_main_reset_hint(main_view);
|
||||
|
||||
@@ -13,6 +13,9 @@ typedef enum {
|
||||
DesktopMainEventOpenMenu,
|
||||
DesktopMainEventOpenDebug,
|
||||
DesktopMainEventUnlocked,
|
||||
DesktopMainEventRightShort,
|
||||
DesktopMainEventUpdateAnimation,
|
||||
DesktopMainEventUpdateOneShotAnimation,
|
||||
} DesktopMainEvent;
|
||||
|
||||
typedef struct DesktopMainView DesktopMainView;
|
||||
@@ -27,6 +30,7 @@ struct DesktopMainView {
|
||||
|
||||
typedef struct {
|
||||
IconAnimation* animation;
|
||||
const Icon* icon;
|
||||
uint8_t scene_num;
|
||||
uint32_t hint_expire_at;
|
||||
} DesktopMainViewModel;
|
||||
@@ -39,6 +43,7 @@ void desktop_main_set_callback(
|
||||
View* desktop_main_get_view(DesktopMainView* main_view);
|
||||
DesktopMainView* desktop_main_alloc();
|
||||
void desktop_main_free(DesktopMainView* main_view);
|
||||
void desktop_main_switch_dolphin_animation(DesktopMainView* main_view);
|
||||
void desktop_main_switch_dolphin_animation(DesktopMainView* main_view, const Icon* icon);
|
||||
void desktop_main_unlocked(DesktopMainView* main_view);
|
||||
void desktop_main_reset_hint(DesktopMainView* main_view);
|
||||
void desktop_main_switch_dolphin_icon(DesktopMainView* main_view, const Icon* icon);
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
#include "dolphin/dolphin.h"
|
||||
#include "desktop/desktop.h"
|
||||
#include "dolphin/helpers/dolphin_state.h"
|
||||
#include "dolphin_i.h"
|
||||
#include "furi/pubsub.h"
|
||||
#include "sys/_stdint.h"
|
||||
#include <furi.h>
|
||||
#define DOLPHIN_TIMEGATE 86400 // one day
|
||||
#define DOLPHIN_LOCK_EVENT_FLAG (0x1)
|
||||
@@ -39,6 +44,7 @@ Dolphin* dolphin_alloc() {
|
||||
|
||||
dolphin->state = dolphin_state_alloc();
|
||||
dolphin->event_queue = osMessageQueueNew(8, sizeof(DolphinEvent), NULL);
|
||||
dolphin->pubsub = furi_pubsub_alloc();
|
||||
|
||||
return dolphin;
|
||||
}
|
||||
@@ -79,7 +85,7 @@ void dolphin_event_release(Dolphin* dolphin, DolphinEvent* event) {
|
||||
|
||||
static void dolphin_check_butthurt(DolphinState* state) {
|
||||
furi_assert(state);
|
||||
float diff_time = difftime(dolphin_state_get_timestamp(state), dolphin_state_timestamp());
|
||||
float diff_time = difftime(state->data.timestamp, dolphin_state_timestamp());
|
||||
|
||||
if((fabs(diff_time)) > DOLPHIN_TIMEGATE) {
|
||||
FURI_LOG_I("DolphinState", "Increasing butthurt");
|
||||
@@ -87,6 +93,10 @@ static void dolphin_check_butthurt(DolphinState* state) {
|
||||
}
|
||||
}
|
||||
|
||||
FuriPubSub* dolphin_get_pubsub(Dolphin* dolphin) {
|
||||
return dolphin->pubsub;
|
||||
}
|
||||
|
||||
int32_t dolphin_srv(void* p) {
|
||||
Dolphin* dolphin = dolphin_alloc();
|
||||
furi_record_create("dolphin", dolphin);
|
||||
@@ -97,11 +107,17 @@ int32_t dolphin_srv(void* p) {
|
||||
while(1) {
|
||||
if(osMessageQueueGet(dolphin->event_queue, &event, NULL, 60000) == osOK) {
|
||||
if(event.type == DolphinEventTypeDeed) {
|
||||
dolphin_state_on_deed(dolphin->state, event.deed);
|
||||
if(dolphin_state_on_deed(dolphin->state, event.deed)) {
|
||||
DolphinPubsubEvent event = DolphinPubsubEventUpdate;
|
||||
furi_pubsub_publish(dolphin->pubsub, &event);
|
||||
}
|
||||
} else if(event.type == DolphinEventTypeStats) {
|
||||
event.stats->icounter = dolphin_state_get_icounter(dolphin->state);
|
||||
event.stats->butthurt = dolphin_state_get_butthurt(dolphin->state);
|
||||
event.stats->timestamp = dolphin_state_get_timestamp(dolphin->state);
|
||||
event.stats->icounter = dolphin->state->data.icounter;
|
||||
event.stats->butthurt = dolphin->state->data.butthurt;
|
||||
event.stats->timestamp = dolphin->state->data.timestamp;
|
||||
event.stats->level = dolphin_get_level(dolphin->state->data.icounter);
|
||||
event.stats->level_up_is_pending =
|
||||
!dolphin_state_xp_to_levelup(dolphin->state->data.icounter);
|
||||
} else if(event.type == DolphinEventTypeFlush) {
|
||||
dolphin_state_save(dolphin->state);
|
||||
}
|
||||
@@ -116,3 +132,8 @@ int32_t dolphin_srv(void* p) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dolphin_upgrade_level(Dolphin* dolphin) {
|
||||
dolphin_state_increase_level(dolphin->state);
|
||||
dolphin_flush(dolphin);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "furi/pubsub.h"
|
||||
#include "helpers/dolphin_deed.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct Dolphin Dolphin;
|
||||
|
||||
@@ -8,8 +10,14 @@ typedef struct {
|
||||
uint32_t icounter;
|
||||
uint32_t butthurt;
|
||||
uint64_t timestamp;
|
||||
uint8_t level;
|
||||
bool level_up_is_pending;
|
||||
} DolphinStats;
|
||||
|
||||
typedef enum {
|
||||
DolphinPubsubEventUpdate,
|
||||
} DolphinPubsubEvent;
|
||||
|
||||
/** Deed complete notification. Call it on deed completion.
|
||||
* See dolphin_deed.h for available deeds. In futures it will become part of assets.
|
||||
* Thread safe, async
|
||||
@@ -24,4 +32,8 @@ DolphinStats dolphin_stats(Dolphin* dolphin);
|
||||
/** Flush dolphin queue and save state
|
||||
* Thread safe, blocking
|
||||
*/
|
||||
void dolphin_flush(Dolphin* dolphin);
|
||||
void dolphin_flush(Dolphin* dolphin);
|
||||
|
||||
void dolphin_upgrade_level(Dolphin* dolphin);
|
||||
|
||||
FuriPubSub* dolphin_get_pubsub(Dolphin* dolphin);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "furi/pubsub.h"
|
||||
#include <furi.h>
|
||||
#include <furi-hal.h>
|
||||
|
||||
@@ -26,6 +27,7 @@ struct Dolphin {
|
||||
DolphinState* state;
|
||||
// Queue
|
||||
osMessageQueueId_t event_queue;
|
||||
FuriPubSub* pubsub;
|
||||
};
|
||||
|
||||
Dolphin* dolphin_alloc();
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
#include "dolphin_deed.h"
|
||||
#include <furi.h>
|
||||
|
||||
static const DolphinDeedWeight dolphin_deed_weights[DolphinDeedMax] = {
|
||||
{1, 2, 60},
|
||||
{1, 2, 60},
|
||||
{1, 2, 60},
|
||||
{-1, 2, 60},
|
||||
{1, -1, 60},
|
||||
{1, -1, 60},
|
||||
{1, -1, 60},
|
||||
{-1, 1, 60},
|
||||
};
|
||||
|
||||
const DolphinDeedWeight* dolphin_deed_weight(DolphinDeed deed) {
|
||||
furi_assert(deed < DolphinDeedMax);
|
||||
return &dolphin_deed_weights[deed];
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "dolphin_state.h"
|
||||
#include <stdint.h>
|
||||
#include <storage/storage.h>
|
||||
#include <furi.h>
|
||||
#include <math.h>
|
||||
@@ -9,23 +10,8 @@
|
||||
#define DOLPHIN_STATE_HEADER_MAGIC 0xD0
|
||||
#define DOLPHIN_STATE_HEADER_VERSION 0x01
|
||||
#define DOLPHIN_LVL_THRESHOLD 20.0f
|
||||
|
||||
typedef struct {
|
||||
uint32_t limit_ibutton;
|
||||
uint32_t limit_nfc;
|
||||
uint32_t limit_ir;
|
||||
uint32_t limit_rfid;
|
||||
|
||||
uint32_t flags;
|
||||
uint32_t icounter;
|
||||
uint32_t butthurt;
|
||||
uint64_t timestamp;
|
||||
} DolphinStoreData;
|
||||
|
||||
struct DolphinState {
|
||||
DolphinStoreData data;
|
||||
bool dirty;
|
||||
};
|
||||
#define LEVEL2_THRESHOLD 20
|
||||
#define LEVEL3_THRESHOLD 100
|
||||
|
||||
DolphinState* dolphin_state_alloc() {
|
||||
return furi_alloc(sizeof(DolphinState));
|
||||
@@ -93,18 +79,62 @@ uint64_t dolphin_state_timestamp() {
|
||||
return mktime(¤t);
|
||||
}
|
||||
|
||||
void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed) {
|
||||
bool dolphin_state_is_levelup(uint32_t icounter) {
|
||||
return (icounter == LEVEL2_THRESHOLD) || (icounter == LEVEL3_THRESHOLD);
|
||||
}
|
||||
|
||||
uint8_t dolphin_get_level(uint32_t icounter) {
|
||||
if(icounter <= LEVEL2_THRESHOLD) {
|
||||
return 1;
|
||||
} else if(icounter <= LEVEL3_THRESHOLD) {
|
||||
return 2;
|
||||
} else {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t dolphin_state_xp_to_levelup(uint32_t icounter) {
|
||||
uint32_t threshold = 0;
|
||||
if(icounter <= LEVEL2_THRESHOLD) {
|
||||
threshold = LEVEL2_THRESHOLD;
|
||||
} else if(icounter <= LEVEL3_THRESHOLD) {
|
||||
threshold = LEVEL3_THRESHOLD;
|
||||
} else {
|
||||
threshold = (uint32_t)-1;
|
||||
}
|
||||
return threshold - icounter;
|
||||
}
|
||||
|
||||
bool dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed) {
|
||||
const DolphinDeedWeight* deed_weight = dolphin_deed_weight(deed);
|
||||
int32_t icounter = dolphin_state->data.icounter + deed_weight->icounter;
|
||||
int32_t butthurt = dolphin_state->data.butthurt;
|
||||
bool level_up = false;
|
||||
bool mood_changed = false;
|
||||
|
||||
if(icounter >= 0) {
|
||||
dolphin_state->data.icounter = icounter;
|
||||
dolphin_state->data.butthurt = MAX(butthurt - deed_weight->icounter, 0);
|
||||
dolphin_state->data.timestamp = dolphin_state_timestamp();
|
||||
if(icounter <= 0) {
|
||||
icounter = 0;
|
||||
if(dolphin_state->data.icounter == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t xp_to_levelup = dolphin_state_xp_to_levelup(dolphin_state->data.icounter);
|
||||
if(xp_to_levelup) {
|
||||
level_up = true;
|
||||
dolphin_state->data.icounter += MIN(xp_to_levelup, deed_weight->icounter);
|
||||
}
|
||||
|
||||
uint32_t new_butthurt =
|
||||
CLAMP(((int32_t)dolphin_state->data.butthurt) + deed_weight->butthurt, 14, 0);
|
||||
|
||||
if(!!dolphin_state->data.butthurt != !!new_butthurt) {
|
||||
mood_changed = true;
|
||||
}
|
||||
dolphin_state->data.butthurt = new_butthurt;
|
||||
dolphin_state->data.timestamp = dolphin_state_timestamp();
|
||||
dolphin_state->dirty = true;
|
||||
|
||||
return level_up || mood_changed;
|
||||
}
|
||||
|
||||
void dolphin_state_butthurted(DolphinState* dolphin_state) {
|
||||
@@ -113,22 +143,7 @@ void dolphin_state_butthurted(DolphinState* dolphin_state) {
|
||||
dolphin_state->dirty = true;
|
||||
}
|
||||
|
||||
uint32_t dolphin_state_get_icounter(DolphinState* dolphin_state) {
|
||||
return dolphin_state->data.icounter;
|
||||
}
|
||||
|
||||
uint32_t dolphin_state_get_butthurt(DolphinState* dolphin_state) {
|
||||
return dolphin_state->data.butthurt;
|
||||
}
|
||||
|
||||
uint64_t dolphin_state_get_timestamp(DolphinState* dolphin_state) {
|
||||
return dolphin_state->data.timestamp;
|
||||
}
|
||||
|
||||
uint32_t dolphin_state_get_level(uint32_t icounter) {
|
||||
return 0.5f + sqrtf(1.0f + 8.0f * ((float)icounter / DOLPHIN_LVL_THRESHOLD)) / 2.0f;
|
||||
}
|
||||
|
||||
uint32_t dolphin_state_xp_to_levelup(uint32_t icounter, uint32_t level, bool remaining) {
|
||||
return (DOLPHIN_LVL_THRESHOLD * level * (level + 1) / 2) - (remaining ? icounter : 0);
|
||||
void dolphin_state_increase_level(DolphinState* dolphin_state) {
|
||||
++dolphin_state->data.icounter;
|
||||
dolphin_state->dirty = true;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,22 @@
|
||||
#include <time.h>
|
||||
|
||||
typedef struct DolphinState DolphinState;
|
||||
typedef struct {
|
||||
uint32_t limit_ibutton;
|
||||
uint32_t limit_nfc;
|
||||
uint32_t limit_ir;
|
||||
uint32_t limit_rfid;
|
||||
|
||||
uint32_t flags;
|
||||
uint32_t icounter;
|
||||
uint32_t butthurt;
|
||||
uint64_t timestamp;
|
||||
} DolphinStoreData;
|
||||
|
||||
struct DolphinState {
|
||||
DolphinStoreData data;
|
||||
bool dirty;
|
||||
};
|
||||
|
||||
DolphinState* dolphin_state_alloc();
|
||||
|
||||
@@ -20,16 +36,14 @@ void dolphin_state_clear(DolphinState* dolphin_state);
|
||||
|
||||
uint64_t dolphin_state_timestamp();
|
||||
|
||||
void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed);
|
||||
bool dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed);
|
||||
|
||||
void dolphin_state_butthurted(DolphinState* dolphin_state);
|
||||
|
||||
uint32_t dolphin_state_get_icounter(DolphinState* dolphin_state);
|
||||
uint32_t dolphin_state_xp_to_levelup(uint32_t icounter);
|
||||
|
||||
uint32_t dolphin_state_get_butthurt(DolphinState* dolphin_state);
|
||||
bool dolphin_state_is_levelup(uint32_t icounter);
|
||||
|
||||
uint64_t dolphin_state_get_timestamp(DolphinState* dolphin_state);
|
||||
void dolphin_state_increase_level(DolphinState* dolphin_state);
|
||||
|
||||
uint32_t dolphin_state_get_level(uint32_t icounter);
|
||||
|
||||
uint32_t dolphin_state_xp_to_levelup(uint32_t icounter, uint32_t level, bool remaining);
|
||||
uint8_t dolphin_get_level(uint32_t icounter);
|
||||
|
||||
+10
-10
@@ -134,10 +134,10 @@ void gui_redraw_status_bar(Gui* gui) {
|
||||
}
|
||||
}
|
||||
|
||||
bool gui_redraw_normal(Gui* gui) {
|
||||
bool gui_redraw_window(Gui* gui) {
|
||||
canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal);
|
||||
canvas_frame_set(gui->canvas, GUI_MAIN_X, GUI_MAIN_Y, GUI_MAIN_WIDTH, GUI_MAIN_HEIGHT);
|
||||
ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerMain]);
|
||||
canvas_frame_set(gui->canvas, GUI_WINDOW_X, GUI_WINDOW_Y, GUI_WINDOW_WIDTH, GUI_WINDOW_HEIGHT);
|
||||
ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerWindow]);
|
||||
if(view_port) {
|
||||
view_port_draw(view_port, gui->canvas);
|
||||
return true;
|
||||
@@ -145,10 +145,10 @@ bool gui_redraw_normal(Gui* gui) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool gui_redraw_none(Gui* gui) {
|
||||
bool gui_redraw_desktop(Gui* gui) {
|
||||
canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal);
|
||||
canvas_frame_set(gui->canvas, GUI_MAIN_X, GUI_MAIN_Y, GUI_MAIN_WIDTH, GUI_MAIN_HEIGHT);
|
||||
ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerNone]);
|
||||
canvas_frame_set(gui->canvas, GUI_WINDOW_X, GUI_WINDOW_Y, GUI_WINDOW_WIDTH, GUI_WINDOW_HEIGHT);
|
||||
ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]);
|
||||
if(view_port) {
|
||||
view_port_draw(view_port, gui->canvas);
|
||||
return true;
|
||||
@@ -164,8 +164,8 @@ void gui_redraw(Gui* gui) {
|
||||
canvas_reset(gui->canvas);
|
||||
|
||||
if(!gui_redraw_fs(gui)) {
|
||||
if(!gui_redraw_normal(gui)) {
|
||||
gui_redraw_none(gui);
|
||||
if(!gui_redraw_window(gui)) {
|
||||
gui_redraw_desktop(gui);
|
||||
}
|
||||
gui_redraw_status_bar(gui);
|
||||
}
|
||||
@@ -203,8 +203,8 @@ void gui_input(Gui* gui, InputEvent* input_event) {
|
||||
gui_lock(gui);
|
||||
|
||||
ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]);
|
||||
if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerMain]);
|
||||
if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerNone]);
|
||||
if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerWindow]);
|
||||
if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]);
|
||||
|
||||
if(!(gui->ongoing_input & ~key_bit) && input_event->type == InputTypePress) {
|
||||
gui->ongoing_input_view_port = view_port;
|
||||
|
||||
@@ -14,12 +14,14 @@ extern "C" {
|
||||
|
||||
/** Gui layers */
|
||||
typedef enum {
|
||||
GuiLayerNone, /**< Special layer for internal use only */
|
||||
GuiLayerDesktop, /**< Desktop layer for internal use. Like fullscreen but with status bar */
|
||||
|
||||
GuiLayerWindow, /**< Window layer, status bar is shown */
|
||||
|
||||
GuiLayerStatusBarLeft, /**< Status bar left-side layer, auto-layout */
|
||||
GuiLayerStatusBarRight, /**< Status bar right-side layer, auto-layout */
|
||||
GuiLayerMain, /**< Main layer, status bar is shown */
|
||||
GuiLayerFullscreen, /**< Fullscreen layer */
|
||||
|
||||
GuiLayerFullscreen, /**< Fullscreen layer, no status bar */
|
||||
|
||||
GuiLayerMAX /**< Don't use or move, special value */
|
||||
} GuiLayer;
|
||||
|
||||
@@ -25,10 +25,10 @@
|
||||
#define GUI_STATUS_BAR_WIDTH GUI_DISPLAY_WIDTH
|
||||
#define GUI_STATUS_BAR_HEIGHT 13
|
||||
|
||||
#define GUI_MAIN_X 0
|
||||
#define GUI_MAIN_Y 9
|
||||
#define GUI_MAIN_WIDTH GUI_DISPLAY_WIDTH
|
||||
#define GUI_MAIN_HEIGHT (GUI_DISPLAY_HEIGHT - GUI_MAIN_Y)
|
||||
#define GUI_WINDOW_X 0
|
||||
#define GUI_WINDOW_Y GUI_STATUS_BAR_HEIGHT
|
||||
#define GUI_WINDOW_WIDTH GUI_DISPLAY_WIDTH
|
||||
#define GUI_WINDOW_HEIGHT (GUI_DISPLAY_HEIGHT - GUI_WINDOW_Y)
|
||||
|
||||
#define GUI_THREAD_FLAG_DRAW (1 << 0)
|
||||
#define GUI_THREAD_FLAG_INPUT (1 << 1)
|
||||
|
||||
@@ -62,6 +62,7 @@ void icon_animation_start(IconAnimation* instance) {
|
||||
furi_assert(instance);
|
||||
if(!instance->animating) {
|
||||
instance->animating = true;
|
||||
furi_assert(instance->icon->frame_rate);
|
||||
furi_check(
|
||||
xTimerChangePeriod(
|
||||
instance->timer, (osKernelGetTickFreq() / instance->icon->frame_rate), 0) ==
|
||||
|
||||
@@ -199,12 +199,12 @@ void view_dispatcher_attach_to_gui(
|
||||
furi_assert(view_dispatcher->gui == NULL);
|
||||
furi_assert(gui);
|
||||
|
||||
if(type == ViewDispatcherTypeNone) {
|
||||
gui_add_view_port(gui, view_dispatcher->view_port, GuiLayerNone);
|
||||
if(type == ViewDispatcherTypeDesktop) {
|
||||
gui_add_view_port(gui, view_dispatcher->view_port, GuiLayerDesktop);
|
||||
} else if(type == ViewDispatcherTypeWindow) {
|
||||
gui_add_view_port(gui, view_dispatcher->view_port, GuiLayerWindow);
|
||||
} else if(type == ViewDispatcherTypeFullscreen) {
|
||||
gui_add_view_port(gui, view_dispatcher->view_port, GuiLayerFullscreen);
|
||||
} else if(type == ViewDispatcherTypeWindow) {
|
||||
gui_add_view_port(gui, view_dispatcher->view_port, GuiLayerMain);
|
||||
} else {
|
||||
furi_check(NULL);
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@ extern "C" {
|
||||
|
||||
/** ViewDispatcher view_port placement */
|
||||
typedef enum {
|
||||
ViewDispatcherTypeNone, /**< Special layer for internal use only */
|
||||
ViewDispatcherTypeWindow, /**< Main view_port layer, status bar is shown */
|
||||
ViewDispatcherTypeFullscreen /**< Fullscreen view_port layer */
|
||||
ViewDispatcherTypeDesktop, /**< Desktop layer: fullscreen with status bar on top of it. For internal usage. */
|
||||
ViewDispatcherTypeWindow, /**< Window layer: with status bar */
|
||||
ViewDispatcherTypeFullscreen /**< Fullscreen layer: without status bar */
|
||||
} ViewDispatcherType;
|
||||
|
||||
typedef struct ViewDispatcher ViewDispatcher;
|
||||
|
||||
Executable → Regular
+5
@@ -7,6 +7,11 @@
|
||||
#include <gui/view.h>
|
||||
|
||||
#define POWER_OFF_TIMEOUT 90
|
||||
#define POWER_BATTERY_WELL_LEVEL 99
|
||||
|
||||
bool power_is_battery_well(PowerInfo* info) {
|
||||
return info->health > POWER_BATTERY_WELL_LEVEL;
|
||||
}
|
||||
|
||||
void power_draw_battery_callback(Canvas* canvas, void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <furi/pubsub.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct Power Power;
|
||||
|
||||
@@ -63,3 +64,5 @@ void power_get_info(Power* power, PowerInfo* info);
|
||||
* @param power - Power instance
|
||||
*/
|
||||
FuriPubSub* power_get_pubsub(Power* power);
|
||||
|
||||
bool power_is_battery_well(PowerInfo* info);
|
||||
|
||||
@@ -391,6 +391,10 @@ void storage_file_free(File* file) {
|
||||
free(file);
|
||||
}
|
||||
|
||||
FuriPubSub* storage_get_pubsub(Storage* storage) {
|
||||
return storage->pubsub;
|
||||
}
|
||||
|
||||
bool storage_simply_remove_recursive(Storage* storage, const char* path) {
|
||||
furi_assert(storage);
|
||||
furi_assert(path);
|
||||
|
||||
@@ -19,9 +19,11 @@ typedef struct {
|
||||
struct Storage {
|
||||
osMessageQueueId_t message_queue;
|
||||
StorageData storage[STORAGE_COUNT];
|
||||
StorageStatus prev_ext_storage_status;
|
||||
StorageSDGui sd_gui;
|
||||
FuriPubSub* pubsub;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "storage-i.h"
|
||||
#include "storage-message.h"
|
||||
#include "storage-processing.h"
|
||||
#include "storage/storage-glue.h"
|
||||
#include "storages/storage-int.h"
|
||||
#include "storages/storage-ext.h"
|
||||
|
||||
@@ -31,6 +32,7 @@ static void storage_app_sd_icon_draw_callback(Canvas* canvas, void* context) {
|
||||
Storage* storage_app_alloc() {
|
||||
Storage* app = malloc(sizeof(Storage));
|
||||
app->message_queue = osMessageQueueNew(8, sizeof(StorageMessage), NULL);
|
||||
app->pubsub = furi_pubsub_alloc();
|
||||
|
||||
for(uint8_t i = 0; i < STORAGE_COUNT; i++) {
|
||||
storage_data_init(&app->storage[i]);
|
||||
@@ -61,6 +63,11 @@ void storage_tick(Storage* app) {
|
||||
}
|
||||
}
|
||||
|
||||
if(app->storage[ST_EXT].status != app->prev_ext_storage_status) {
|
||||
app->prev_ext_storage_status = app->storage[ST_EXT].status;
|
||||
furi_pubsub_publish(app->pubsub, &app->storage[ST_EXT].status);
|
||||
}
|
||||
|
||||
// storage not enabled but was enabled (sd card unmount)
|
||||
if(app->storage[ST_EXT].status == StorageStatusNotReady && app->sd_gui.enabled == true) {
|
||||
app->sd_gui.enabled = false;
|
||||
@@ -93,4 +100,4 @@ int32_t storage_srv(void* p) {
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ File* storage_file_alloc(Storage* storage);
|
||||
*/
|
||||
void storage_file_free(File* file);
|
||||
|
||||
FuriPubSub* storage_get_pubsub(Storage* storage);
|
||||
|
||||
/******************* File Functions *******************/
|
||||
|
||||
/** Opens an existing file or create a new one.
|
||||
|
||||
@@ -16,7 +16,7 @@ void subghz_scene_need_saving_on_enter(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
|
||||
widget_add_string_multiline_element(
|
||||
subghz->widget, 64, 13, AlignCenter, AlignCenter, FontPrimary, "Exit to Gub-Ghz menu?");
|
||||
subghz->widget, 64, 13, AlignCenter, AlignCenter, FontPrimary, "Exit to Sub-Ghz menu?");
|
||||
widget_add_string_multiline_element(
|
||||
subghz->widget,
|
||||
64,
|
||||
|
||||
Reference in New Issue
Block a user