[FL-1824] Dolphin refactoring (#701)

* refactoring p1
* refactoring p2
* cleanups
* locked screen refresh rate fix
* better locked view logic
* seperate dolphin service and desktop app
* Desktop: Favorite app acess (Left key), Settings app
* Desktop settings version, submenu header
* remove unused icon anomation + naming fix

Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
its your bedtime 2021-09-28 12:40:39 +03:00 committed by GitHub
parent a8981d317a
commit 1c4e6ec74d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 1974 additions and 728 deletions

View File

@ -14,6 +14,7 @@ extern int32_t notification_srv(void* p);
extern int32_t power_observer_srv(void* p);
extern int32_t power_srv(void* p);
extern int32_t storage_srv(void* p);
extern int32_t desktop_srv(void* p);
// Apps
extern int32_t accessor_app(void* p);
@ -52,6 +53,7 @@ extern void power_cli_init();
extern int32_t notification_settings_app(void* p);
extern int32_t storage_settings_app(void* p);
extern int32_t bt_settings_app(void* p);
extern int32_t desktop_settings_app(void* p);
extern int32_t about_settings_app(void* p);
extern int32_t power_settings_app(void* p);
@ -73,6 +75,10 @@ const FlipperApplication FLIPPER_SERVICES[] = {
{.app = dolphin_srv, .name = "Dolphin", .stack_size = 1024, .icon = NULL},
#endif
#ifdef SRV_DESKTOP
{.app = desktop_srv, .name = "Desktop", .stack_size = 1024, .icon = NULL},
#endif
#ifdef SRV_GUI
{.app = gui_srv, .name = "Gui", .stack_size = 8192, .icon = NULL},
#endif
@ -282,6 +288,10 @@ const FlipperApplication FLIPPER_SETTINGS_APPS[] = {
{.app = power_settings_app, .name = "Power", .stack_size = 1024, .icon = NULL},
#endif
#ifdef APP_DESKTOP
{.app = desktop_settings_app, .name = "Desktop", .stack_size = 1024, .icon = NULL},
#endif
#ifdef APP_ABOUT
{.app = about_settings_app, .name = "About", .stack_size = 1024, .icon = NULL},
#endif

View File

@ -25,6 +25,7 @@ SRV_POWER_OBSERVER = 1
SRV_STORAGE = 1
# Apps
SRV_DESKTOP = 1
APP_ARCHIVE = 1
APP_GPIO_TEST = 1
APP_IBUTTON = 1
@ -95,6 +96,12 @@ ifeq ($(APP_UNIT_TESTS), 1)
CFLAGS += -DAPP_UNIT_TESTS
endif
SRV_DESKTOP ?= 0
ifeq ($(SRV_DESKTOP), 1)
CFLAGS += -DSRV_DESKTOP
SRV_DESKTOP = 1
endif
APP_ARCHIVE ?= 0
ifeq ($(APP_NFC), 1)
CFLAGS += -DAPP_ARCHIVE

View File

@ -0,0 +1,128 @@
#include "desktop_i.h"
#include "applications/dolphin/dolphin.h"
static void desktop_lock_icon_callback(Canvas* canvas, void* context) {
furi_assert(canvas);
canvas_draw_icon(canvas, 0, 0, &I_Lock_8x8);
}
bool desktop_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
Desktop* desktop = (Desktop*)context;
return scene_manager_handle_custom_event(desktop->scene_manager, event);
}
bool desktop_back_event_callback(void* context) {
furi_assert(context);
Desktop* desktop = (Desktop*)context;
return scene_manager_handle_back_event(desktop->scene_manager);
}
Desktop* desktop_alloc() {
Desktop* desktop = furi_alloc(sizeof(Desktop));
desktop->menu_vm = furi_record_open("menu");
desktop->gui = furi_record_open("gui");
desktop->scene_thread = furi_thread_alloc();
desktop->view_dispatcher = view_dispatcher_alloc();
desktop->scene_manager = scene_manager_alloc(&desktop_scene_handlers, desktop);
view_dispatcher_enable_queue(desktop->view_dispatcher);
view_dispatcher_attach_to_gui(
desktop->view_dispatcher, desktop->gui, ViewDispatcherTypeWindow);
view_dispatcher_set_event_callback_context(desktop->view_dispatcher, desktop);
view_dispatcher_set_custom_event_callback(
desktop->view_dispatcher, desktop_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
desktop->view_dispatcher, desktop_back_event_callback);
desktop->main_view = desktop_main_alloc();
desktop->lock_menu = desktop_lock_menu_alloc();
desktop->locked_view = desktop_locked_alloc();
desktop->debug_view = desktop_debug_alloc();
desktop->first_start_view = desktop_first_start_alloc();
desktop->hw_mismatch_view = desktop_hw_mismatch_alloc();
view_dispatcher_add_view(
desktop->view_dispatcher, DesktopViewMain, desktop_main_get_view(desktop->main_view));
view_dispatcher_add_view(
desktop->view_dispatcher,
DesktopViewLockMenu,
desktop_lock_menu_get_view(desktop->lock_menu));
view_dispatcher_add_view(
desktop->view_dispatcher, DesktopViewDebug, desktop_debug_get_view(desktop->debug_view));
view_dispatcher_add_view(
desktop->view_dispatcher,
DesktopViewLocked,
desktop_locked_get_view(desktop->locked_view));
view_dispatcher_add_view(
desktop->view_dispatcher,
DesktopViewFirstStart,
desktop_first_start_get_view(desktop->first_start_view));
view_dispatcher_add_view(
desktop->view_dispatcher,
DesktopViewHwMismatch,
desktop_hw_mismatch_get_view(desktop->hw_mismatch_view));
// Lock icon
desktop->lock_viewport = view_port_alloc();
view_port_set_width(desktop->lock_viewport, icon_get_width(&I_Lock_8x8));
view_port_draw_callback_set(desktop->lock_viewport, desktop_lock_icon_callback, desktop);
view_port_enabled_set(desktop->lock_viewport, false);
gui_add_view_port(desktop->gui, desktop->lock_viewport, GuiLayerStatusBarLeft);
return desktop;
}
void desktop_free(Desktop* desktop) {
furi_assert(desktop);
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewMain);
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewLockMenu);
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewLocked);
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewDebug);
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewFirstStart);
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewHwMismatch);
view_dispatcher_free(desktop->view_dispatcher);
scene_manager_free(desktop->scene_manager);
desktop_main_free(desktop->main_view);
desktop_lock_menu_free(desktop->lock_menu);
desktop_locked_free(desktop->locked_view);
desktop_debug_free(desktop->debug_view);
desktop_first_start_free(desktop->first_start_view);
desktop_hw_mismatch_free(desktop->hw_mismatch_view);
furi_record_close("gui");
desktop->gui = NULL;
furi_thread_free(desktop->scene_thread);
furi_record_close("menu");
desktop->menu_vm = NULL;
free(desktop);
}
int32_t desktop_srv(void* p) {
Desktop* desktop = desktop_alloc();
Dolphin* dolphin = furi_record_open("dolphin");
if(dolphin_load(dolphin)) {
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
} else {
scene_manager_next_scene(desktop->scene_manager, DesktopSceneFirstStart);
}
furi_record_close("dolphin");
if(!furi_hal_version_do_i_belong_here()) {
scene_manager_next_scene(desktop->scene_manager, DesktopSceneHwMismatch);
}
view_dispatcher_run(desktop->view_dispatcher);
desktop_free(desktop);
return 0;
}

View File

@ -0,0 +1,3 @@
#pragma once
typedef struct Desktop Desktop;

View File

@ -0,0 +1,59 @@
#pragma once
#include <furi.h>
#include <furi-hal.h>
#include <menu/menu.h>
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <assets_icons.h>
#include "desktop.h"
#include "views/desktop_main.h"
#include "views/desktop_first_start.h"
#include "views/desktop_hw_mismatch.h"
#include "views/desktop_lock_menu.h"
#include "views/desktop_locked.h"
#include "views/desktop_debug.h"
#include "scenes/desktop_scene.h"
#include "desktop/desktop_settings/desktop_settings.h"
#define HINT_TIMEOUT_L 2
#define HINT_TIMEOUT_H 11
typedef enum {
DesktopViewMain,
DesktopViewLockMenu,
DesktopViewLocked,
DesktopViewDebug,
DesktopViewFirstStart,
DesktopViewHwMismatch,
DesktopViewTotal,
} DesktopViewEnum;
struct Desktop {
// Menu
ValueMutex* menu_vm;
// Scene
FuriThread* scene_thread;
// GUI
Gui* gui;
ViewDispatcher* view_dispatcher;
SceneManager* scene_manager;
DesktopFirstStartView* first_start_view;
DesktopHwMismatchView* hw_mismatch_view;
DesktopMainView* main_view;
DesktopLockMenuView* lock_menu;
DesktopLockedView* locked_view;
DesktopDebugView* debug_view;
DesktopSettings settings;
ViewPort* lock_viewport;
};
Desktop* desktop_alloc();
void desktop_free(Desktop* desktop);

View File

@ -0,0 +1,49 @@
#include <furi.h>
#include <file-worker.h>
#include "desktop_settings.h"
#define DESKTOP_SETTINGS_TAG "Desktop settings"
#define DESKTOP_SETTINGS_PATH "/int/desktop.settings"
bool desktop_settings_load(DesktopSettings* desktop_settings) {
furi_assert(desktop_settings);
bool file_loaded = false;
DesktopSettings settings = {};
FURI_LOG_I(DESKTOP_SETTINGS_TAG, "Loading settings from \"%s\"", DESKTOP_SETTINGS_PATH);
FileWorker* file_worker = file_worker_alloc(true);
if(file_worker_open(file_worker, DESKTOP_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
if(file_worker_read(file_worker, &settings, sizeof(settings))) {
file_loaded = true;
}
}
file_worker_free(file_worker);
if(file_loaded) {
if(settings.version != DESKTOP_SETTINGS_VER) {
FURI_LOG_E(DESKTOP_SETTINGS_TAG, "Settings version mismatch");
} else {
osKernelLock();
*desktop_settings = settings;
osKernelUnlock();
}
} else {
FURI_LOG_E(DESKTOP_SETTINGS_TAG, "Settings load failed");
}
return file_loaded;
}
bool desktop_settings_save(DesktopSettings* desktop_settings) {
furi_assert(desktop_settings);
bool result = false;
FileWorker* file_worker = file_worker_alloc(true);
if(file_worker_open(file_worker, DESKTOP_SETTINGS_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) {
if(file_worker_write(file_worker, desktop_settings, sizeof(DesktopSettings))) {
FURI_LOG_I(DESKTOP_SETTINGS_TAG, "Settings saved to \"%s\"", DESKTOP_SETTINGS_PATH);
result = true;
}
}
file_worker_free(file_worker);
return result;
}

View File

@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#define DESKTOP_SETTINGS_VER (0)
typedef struct {
uint8_t version;
uint16_t favorite;
} DesktopSettings;
bool desktop_settings_load(DesktopSettings* desktop_settings);
bool desktop_settings_save(DesktopSettings* desktop_settings);

View File

@ -0,0 +1,65 @@
#include "desktop_settings_app.h"
static bool desktop_settings_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
DesktopSettingsApp* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool desktop_settings_back_event_callback(void* context) {
furi_assert(context);
DesktopSettingsApp* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
DesktopSettingsApp* desktop_settings_app_alloc() {
DesktopSettingsApp* app = furi_alloc(sizeof(DesktopSettingsApp));
app->settings.version = DESKTOP_SETTINGS_VER;
desktop_settings_load(&app->settings);
app->gui = furi_record_open("gui");
app->view_dispatcher = view_dispatcher_alloc();
app->scene_manager = scene_manager_alloc(&desktop_settings_scene_handlers, app);
view_dispatcher_enable_queue(app->view_dispatcher);
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
view_dispatcher_set_custom_event_callback(
app->view_dispatcher, desktop_settings_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, desktop_settings_back_event_callback);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
app->submenu = submenu_alloc();
view_dispatcher_add_view(
app->view_dispatcher, DesktopSettingsAppViewMain, submenu_get_view(app->submenu));
view_dispatcher_add_view(
app->view_dispatcher, DesktopSettingsAppViewFavorite, submenu_get_view(app->submenu));
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneStart);
return app;
}
void desktop_settings_app_free(DesktopSettingsApp* app) {
furi_assert(app);
// Variable item list
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewMain);
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewFavorite);
submenu_free(app->submenu);
// View dispatcher
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);
// Records
furi_record_close("gui");
free(app);
}
extern int32_t desktop_settings_app(void* p) {
DesktopSettingsApp* app = desktop_settings_app_alloc();
view_dispatcher_run(app->view_dispatcher);
desktop_settings_save(&app->settings);
desktop_settings_app_free(app);
return 0;
}

View File

@ -0,0 +1,25 @@
#pragma once
#include <furi.h>
#include <gui/gui.h>
#include <gui/view.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/submenu.h>
#include "desktop_settings.h"
#include "scenes/desktop_settings_scene.h"
typedef enum {
DesktopSettingsAppViewMain,
DesktopSettingsAppViewFavorite,
} DesktopSettingsAppView;
typedef struct {
DesktopSettings settings;
Gui* gui;
SceneManager* scene_manager;
ViewDispatcher* view_dispatcher;
Submenu* submenu;
} DesktopSettingsApp;

View File

@ -0,0 +1,30 @@
#include "desktop_settings_scene.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const desktop_settings_on_enter_handlers[])(void*) = {
#include "desktop_settings_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_event handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
bool (*const desktop_settings_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "desktop_settings_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_exit handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
void (*const desktop_settings_on_exit_handlers[])(void* context) = {
#include "desktop_settings_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers desktop_settings_scene_handlers = {
.on_enter_handlers = desktop_settings_on_enter_handlers,
.on_event_handlers = desktop_settings_on_event_handlers,
.on_exit_handlers = desktop_settings_on_exit_handlers,
.scene_num = DesktopSettingsAppSceneNum,
};

View File

@ -0,0 +1,29 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) DesktopSettingsAppScene##id,
typedef enum {
#include "desktop_settings_scene_config.h"
DesktopSettingsAppSceneNum,
} DesktopSettingsAppScene;
#undef ADD_SCENE
extern const SceneManagerHandlers desktop_settings_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "desktop_settings_scene_config.h"
#undef ADD_SCENE
// Generate scene on_event handlers declaration
#define ADD_SCENE(prefix, name, id) \
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
#include "desktop_settings_scene_config.h"
#undef ADD_SCENE
// Generate scene on_exit handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
#include "desktop_settings_scene_config.h"
#undef ADD_SCENE

View File

@ -0,0 +1,2 @@
ADD_SCENE(desktop_settings, start, Start)
ADD_SCENE(desktop_settings, favorite, Favorite)

View File

@ -0,0 +1,47 @@
#include "../desktop_settings_app.h"
#include "applications.h"
static void desktop_settings_scene_favorite_submenu_callback(void* context, uint32_t index) {
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void desktop_settings_scene_favorite_on_enter(void* context) {
DesktopSettingsApp* app = context;
Submenu* submenu = app->submenu;
submenu_clean(submenu);
for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
submenu_add_item(
submenu,
FLIPPER_APPS[i].name,
i,
desktop_settings_scene_favorite_submenu_callback,
app);
}
submenu_set_header(app->submenu, "Quick access app:");
submenu_set_selected_item(app->submenu, app->settings.favorite);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewFavorite);
}
bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent event) {
DesktopSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
default:
app->settings.favorite = event.event;
scene_manager_previous_scene(app->scene_manager);
consumed = true;
break;
}
}
return consumed;
}
void desktop_settings_scene_favorite_on_exit(void* context) {
DesktopSettingsApp* app = context;
submenu_clean(app->submenu);
}

View File

@ -0,0 +1,53 @@
#include "../desktop_settings_app.h"
#include "applications.h"
enum DesktopSettingsStartSubmenuIndex {
DesktopSettingsStartSubmenuIndexFavorite,
DesktopSettingsStartSubmenuIndexPinSetup,
};
static void desktop_settings_scene_start_submenu_callback(void* context, uint32_t index) {
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void desktop_settings_scene_start_on_enter(void* context) {
DesktopSettingsApp* app = context;
Submenu* submenu = app->submenu;
submenu_add_item(
submenu,
"Favorite App",
DesktopSettingsStartSubmenuIndexFavorite,
desktop_settings_scene_start_submenu_callback,
app);
submenu_add_item(
submenu,
"PIN Setup",
DesktopSettingsStartSubmenuIndexPinSetup,
desktop_settings_scene_start_submenu_callback,
app);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMain);
}
bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent event) {
DesktopSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DesktopSettingsStartSubmenuIndexFavorite:
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppViewFavorite);
consumed = true;
break;
}
}
return consumed;
}
void desktop_settings_scene_start_on_exit(void* context) {
DesktopSettingsApp* app = context;
submenu_clean(app->submenu);
}

View File

@ -0,0 +1,30 @@
#include "desktop_scene.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const desktop_on_enter_handlers[])(void*) = {
#include "desktop_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_event handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
bool (*const desktop_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "desktop_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_exit handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
void (*const desktop_on_exit_handlers[])(void* context) = {
#include "desktop_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers desktop_scene_handlers = {
.on_enter_handlers = desktop_on_enter_handlers,
.on_event_handlers = desktop_on_event_handlers,
.on_exit_handlers = desktop_on_exit_handlers,
.scene_num = DesktopSceneNum,
};

View File

@ -0,0 +1,29 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) DesktopScene##id,
typedef enum {
#include "desktop_scene_config.h"
DesktopSceneNum,
} DesktopScene;
#undef ADD_SCENE
extern const SceneManagerHandlers desktop_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "desktop_scene_config.h"
#undef ADD_SCENE
// Generate scene on_event handlers declaration
#define ADD_SCENE(prefix, name, id) \
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
#include "desktop_scene_config.h"
#undef ADD_SCENE
// Generate scene on_exit handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
#include "desktop_scene_config.h"
#undef ADD_SCENE

View File

@ -0,0 +1,6 @@
ADD_SCENE(desktop, main, Main)
ADD_SCENE(desktop, lock_menu, LockMenu)
ADD_SCENE(desktop, locked, Locked)
ADD_SCENE(desktop, debug, Debug)
ADD_SCENE(desktop, first_start, FirstStart)
ADD_SCENE(desktop, hw_mismatch, HwMismatch)

View File

@ -0,0 +1,62 @@
#include "../desktop_i.h"
#include "../views/desktop_debug.h"
#include "applications/dolphin/dolphin.h"
#include "applications/dolphin/helpers/dolphin_deed.h"
void desktop_scene_debug_callback(DesktopDebugEvent event, void* context) {
Desktop* desktop = (Desktop*)context;
view_dispatcher_send_custom_event(desktop->view_dispatcher, event);
}
const void desktop_scene_debug_on_enter(void* context) {
Desktop* desktop = (Desktop*)context;
desktop_debug_get_dolphin_data(desktop->debug_view);
desktop_debug_set_callback(desktop->debug_view, desktop_scene_debug_callback, desktop);
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewDebug);
}
const bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) {
Desktop* desktop = (Desktop*)context;
Dolphin* dolphin = furi_record_open("dolphin");
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DesktopDebugEventExit:
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
dolphin_save(dolphin);
consumed = true;
break;
case DesktopDebugEventDeed:
dolphin_deed(dolphin, DolphinDeedIButtonEmulate);
desktop_debug_get_dolphin_data(desktop->debug_view);
consumed = true;
break;
case DesktopDebugEventWrongDeed:
dolphin_deed(dolphin, DolphinDeedWrong);
desktop_debug_get_dolphin_data(desktop->debug_view);
consumed = true;
break;
case DesktopDebugEventSaveState:
dolphin_save(dolphin);
consumed = true;
break;
default:
break;
}
}
furi_record_close("dolphin");
return consumed;
}
const void desktop_scene_debug_on_exit(void* context) {
Desktop* desktop = (Desktop*)context;
desktop_debug_reset_screen_idx(desktop->debug_view);
}

View File

@ -0,0 +1,43 @@
#include "../desktop_i.h"
#include "../views/desktop_first_start.h"
#include "applications/dolphin/dolphin.h"
void desktop_scene_first_start_callback(DesktopFirstStartEvent event, void* context) {
Desktop* desktop = (Desktop*)context;
view_dispatcher_send_custom_event(desktop->view_dispatcher, event);
}
const 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, DesktopViewFirstStart);
}
const bool desktop_scene_first_start_on_event(void* context, SceneManagerEvent event) {
Desktop* desktop = (Desktop*)context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DesktopFirstStartCompleted:
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
consumed = true;
break;
default:
break;
}
}
return consumed;
}
const void desktop_scene_first_start_on_exit(void* context) {
// Desktop* desktop = (Desktop*)context;
Dolphin* dolphin = furi_record_open("dolphin");
dolphin_save(dolphin);
furi_record_close("dolphin");
}

View File

@ -0,0 +1,37 @@
#include "../desktop_i.h"
#include "../views/desktop_hw_mismatch.h"
void desktop_scene_hw_mismatch_callback(DesktopHwMismatchEvent event, void* context) {
Desktop* desktop = (Desktop*)context;
view_dispatcher_send_custom_event(desktop->view_dispatcher, event);
}
const void desktop_scene_hw_mismatch_on_enter(void* context) {
Desktop* desktop = (Desktop*)context;
desktop_hw_mismatch_set_callback(
desktop->hw_mismatch_view, desktop_scene_hw_mismatch_callback, desktop);
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewHwMismatch);
}
const bool desktop_scene_hw_mismatch_on_event(void* context, SceneManagerEvent event) {
Desktop* desktop = (Desktop*)context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DesktopHwMismatchEventExit:
scene_manager_previous_scene(desktop->scene_manager);
consumed = true;
break;
default:
break;
}
}
return consumed;
}
const void desktop_scene_hw_mismatch_on_exit(void* context) {
// Desktop* desktop = (Desktop*)context;
}

View File

@ -0,0 +1,42 @@
#include "../desktop_i.h"
#include "../views/desktop_lock_menu.h"
void desktop_scene_lock_menu_callback(DesktopLockMenuEvent event, void* context) {
Desktop* desktop = (Desktop*)context;
view_dispatcher_send_custom_event(desktop->view_dispatcher, event);
}
const void desktop_scene_lock_menu_on_enter(void* context) {
Desktop* desktop = (Desktop*)context;
desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop);
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLockMenu);
}
const bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) {
Desktop* desktop = (Desktop*)context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DesktopLockMenuEventLock:
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked);
consumed = true;
break;
case DesktopLockMenuEventExit:
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
consumed = true;
break;
default:
break;
}
}
return consumed;
}
const void desktop_scene_lock_menu_on_exit(void* context) {
Desktop* desktop = (Desktop*)context;
desktop_lock_menu_reset_idx(desktop->lock_menu);
}

View File

@ -0,0 +1,51 @@
#include "../desktop_i.h"
#include "../views/desktop_locked.h"
void desktop_scene_locked_callback(DesktopLockedEvent event, void* context) {
Desktop* desktop = (Desktop*)context;
view_dispatcher_send_custom_event(desktop->view_dispatcher, event);
}
const void desktop_scene_locked_on_enter(void* context) {
Desktop* desktop = (Desktop*)context;
DesktopLockedView* locked_view = desktop->locked_view;
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);
view_port_enabled_set(desktop->lock_viewport, true);
osTimerStart(locked_view->timer, 63);
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLocked);
}
const bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) {
Desktop* desktop = (Desktop*)context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DesktopLockedEventUnlock:
scene_manager_set_scene_state(
desktop->scene_manager, DesktopSceneMain, DesktopMainEventUnlocked);
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
consumed = true;
break;
case DesktopLockedEventUpdate:
desktop_locked_manage_redraw(desktop->locked_view);
consumed = true;
default:
break;
}
}
return consumed;
}
const void desktop_scene_locked_on_exit(void* context) {
Desktop* desktop = (Desktop*)context;
DesktopLockedView* locked_view = desktop->locked_view;
desktop_locked_reset_counter(desktop->locked_view);
osTimerStop(locked_view->timer);
}

View File

@ -0,0 +1,88 @@
#include "../desktop_i.h"
#include "../views/desktop_main.h"
#include "applications.h"
#define MAIN_VIEW_DEFAULT (0UL)
static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* flipper_app) {
furi_assert(desktop);
furi_assert(flipper_app);
furi_assert(flipper_app->app);
furi_assert(flipper_app->name);
if(furi_thread_get_state(desktop->scene_thread) != FuriThreadStateStopped) {
FURI_LOG_E("Desktop", "Thread is already running");
return;
}
furi_thread_set_name(desktop->scene_thread, flipper_app->name);
furi_thread_set_stack_size(desktop->scene_thread, flipper_app->stack_size);
furi_thread_set_callback(desktop->scene_thread, flipper_app->app);
furi_thread_start(desktop->scene_thread);
}
void desktop_scene_main_callback(DesktopMainEvent event, void* context) {
Desktop* desktop = (Desktop*)context;
view_dispatcher_send_custom_event(desktop->view_dispatcher, event);
}
const void desktop_scene_main_on_enter(void* context) {
Desktop* desktop = (Desktop*)context;
DesktopMainView* main_view = desktop->main_view;
desktop_main_set_callback(main_view, desktop_scene_main_callback, desktop);
view_port_enabled_set(desktop->lock_viewport, false);
if(scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneMain) ==
DesktopMainEventUnlocked) {
desktop_main_unlocked(desktop->main_view);
}
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewMain);
}
const bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
Desktop* desktop = (Desktop*)context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DesktopMainEventOpenMenu:
with_value_mutex(
desktop->menu_vm, (Menu * menu) { menu_ok(menu); });
consumed = true;
break;
case DesktopMainEventOpenLockMenu:
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLockMenu);
consumed = true;
break;
case DesktopMainEventOpenDebug:
scene_manager_next_scene(desktop->scene_manager, DesktopViewDebug);
consumed = true;
break;
case DesktopMainEventOpenArchive:
desktop_switch_to_app(desktop, &FLIPPER_ARCHIVE);
consumed = true;
break;
case DesktopMainEventOpenFavorite:
desktop_settings_load(&desktop->settings);
desktop_switch_to_app(desktop, &FLIPPER_APPS[desktop->settings.favorite]);
consumed = true;
break;
default:
break;
}
}
return consumed;
}
const void desktop_scene_main_on_exit(void* context) {
Desktop* desktop = (Desktop*)context;
scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneMain, MAIN_VIEW_DEFAULT);
desktop_main_reset_hint(desktop->main_view);
}

View File

@ -0,0 +1,166 @@
#include <furi.h>
#include "../desktop_i.h"
#include "desktop_debug.h"
#include "applications/dolphin/helpers/dolphin_state.h"
#include "applications/dolphin/dolphin.h"
void desktop_debug_set_callback(
DesktopDebugView* debug_view,
DesktopDebugViewCallback callback,
void* context) {
furi_assert(debug_view);
furi_assert(callback);
debug_view->callback = callback;
debug_view->context = context;
}
void desktop_debug_render(Canvas* canvas, void* model) {
canvas_clear(canvas);
DesktopDebugViewModel* m = model;
const Version* ver;
char buffer[64];
static const char* headers[] = {"FW Version info:", "Boot Version info:", "Desktop info:"};
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2, 13, headers[m->screen]);
canvas_set_font(canvas, FontSecondary);
if(m->screen != DesktopViewStatsMeta) {
// Hardware version
const char* my_name = furi_hal_version_get_name_ptr();
snprintf(
buffer,
sizeof(buffer),
"HW: %d.F%dB%dC%d %s",
furi_hal_version_get_hw_version(),
furi_hal_version_get_hw_target(),
furi_hal_version_get_hw_body(),
furi_hal_version_get_hw_connect(),
my_name ? my_name : "Unknown");
canvas_draw_str(canvas, 5, 23, buffer);
ver = m->screen == DesktopViewStatsBoot ? furi_hal_version_get_boot_version() :
furi_hal_version_get_firmware_version();
if(!ver) {
canvas_draw_str(canvas, 5, 33, "No info");
return;
}
snprintf(
buffer,
sizeof(buffer),
"%s [%s]",
version_get_version(ver),
version_get_builddate(ver));
canvas_draw_str(canvas, 5, 33, buffer);
snprintf(
buffer,
sizeof(buffer),
"%s [%s]",
version_get_githash(ver),
version_get_gitbranchnum(ver));
canvas_draw_str(canvas, 5, 43, buffer);
snprintf(
buffer, sizeof(buffer), "[%s] %s", version_get_target(ver), version_get_gitbranch(ver));
canvas_draw_str(canvas, 5, 53, buffer);
} else {
char buffer[64];
canvas_set_font(canvas, FontSecondary);
snprintf(buffer, 64, "Icounter: %ld", m->icounter);
canvas_draw_str(canvas, 5, 30, buffer);
snprintf(buffer, 64, "Butthurt: %ld", m->butthurt);
canvas_draw_str(canvas, 5, 40, buffer);
canvas_draw_str(canvas, 0, 53, "[< >] icounter value [ok] save");
}
}
View* desktop_debug_get_view(DesktopDebugView* debug_view) {
furi_assert(debug_view);
return debug_view->view;
}
bool desktop_debug_input(InputEvent* event, void* context) {
furi_assert(event);
furi_assert(context);
DesktopDebugView* debug_view = context;
if(event->type != InputTypeShort) return false;
DesktopViewStatsScreens current = 0;
with_view_model(
debug_view->view, (DesktopDebugViewModel * model) {
if(event->key == InputKeyDown) {
model->screen = (model->screen + 1) % DesktopViewStatsTotalCount;
} else if(event->key == InputKeyUp) {
model->screen = ((model->screen - 1) + DesktopViewStatsTotalCount) %
DesktopViewStatsTotalCount;
}
current = model->screen;
return true;
});
if(current == DesktopViewStatsMeta) {
if(event->key == InputKeyLeft) {
debug_view->callback(DesktopDebugEventWrongDeed, debug_view->context);
} else if(event->key == InputKeyRight) {
debug_view->callback(DesktopDebugEventDeed, debug_view->context);
} else if(event->key == InputKeyOk) {
debug_view->callback(DesktopDebugEventSaveState, debug_view->context);
} else {
return false;
}
}
if(event->key == InputKeyBack) {
debug_view->callback(DesktopDebugEventExit, debug_view->context);
}
return true;
}
DesktopDebugView* desktop_debug_alloc() {
DesktopDebugView* debug_view = furi_alloc(sizeof(DesktopDebugView));
debug_view->view = view_alloc();
view_allocate_model(debug_view->view, ViewModelTypeLocking, sizeof(DesktopDebugViewModel));
view_set_context(debug_view->view, debug_view);
view_set_draw_callback(debug_view->view, (ViewDrawCallback)desktop_debug_render);
view_set_input_callback(debug_view->view, desktop_debug_input);
return debug_view;
}
void desktop_debug_free(DesktopDebugView* debug_view) {
furi_assert(debug_view);
view_free(debug_view->view);
free(debug_view);
}
void desktop_debug_get_dolphin_data(DesktopDebugView* debug_view) {
Dolphin* dolphin = furi_record_open("dolphin");
DolphinDeedWeight stats = dolphin_stats(dolphin);
with_view_model(
debug_view->view, (DesktopDebugViewModel * model) {
model->icounter = stats.icounter;
model->butthurt = stats.butthurt;
return true;
});
furi_record_close("dolphin");
}
void desktop_debug_reset_screen_idx(DesktopDebugView* debug_view) {
with_view_model(
debug_view->view, (DesktopDebugViewModel * model) {
model->screen = 0;
return true;
});
}

View File

@ -0,0 +1,52 @@
#pragma once
#include <gui/gui_i.h>
#include <gui/view.h>
#include <gui/canvas.h>
#include <gui/elements.h>
#include <furi.h>
#include <storage/storage.h>
typedef enum {
DesktopDebugEventDeed,
DesktopDebugEventWrongDeed,
DesktopDebugEventSaveState,
DesktopDebugEventExit,
} DesktopDebugEvent;
typedef struct DesktopDebugView DesktopDebugView;
typedef void (*DesktopDebugViewCallback)(DesktopDebugEvent event, void* context);
// Debug info
typedef enum {
DesktopViewStatsFw,
DesktopViewStatsBoot,
DesktopViewStatsMeta,
DesktopViewStatsTotalCount,
} DesktopViewStatsScreens;
struct DesktopDebugView {
View* view;
DesktopDebugViewCallback callback;
void* context;
};
typedef struct {
uint32_t icounter;
uint32_t butthurt;
DesktopViewStatsScreens screen;
} DesktopDebugViewModel;
void desktop_debug_set_callback(
DesktopDebugView* debug_view,
DesktopDebugViewCallback callback,
void* context);
View* desktop_debug_get_view(DesktopDebugView* debug_view);
DesktopDebugView* desktop_debug_alloc();
void desktop_debug_free(DesktopDebugView* debug_view);
void desktop_debug_get_dolphin_data(DesktopDebugView* debug_view);
void desktop_debug_reset_screen_idx(DesktopDebugView* debug_view);

View File

@ -0,0 +1,107 @@
#include <furi.h>
#include "../desktop_i.h"
#include "desktop_first_start.h"
void desktop_first_start_set_callback(
DesktopFirstStartView* first_start_view,
DesktopFirstStartViewCallback callback,
void* context) {
furi_assert(first_start_view);
furi_assert(callback);
first_start_view->callback = callback;
first_start_view->context = context;
}
void desktop_first_start_render(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 - 48, &I_DolphinFirstStart0_70x53);
elements_multiline_text_framed(canvas, 75, 20, "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... >");
} 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! >");
} 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! >");
} 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 >");
} else if(m->page == 5) {
char buf[64];
snprintf(
buf,
64,
"%s %s%s",
"I am",
my_name ? my_name : "Unknown",
",\ncyberdesktop\nliving in your\npocket >");
canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart5_54x49);
elements_multiline_text_framed(canvas, 60, 17, buf);
} else if(m->page == 6) {
canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart6_58x54);
elements_multiline_text_framed(
canvas, 63, 17, "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);
elements_multiline_text_framed(
canvas, 0, 17, "As long as\nyou read, write\nand emulate >");
} else if(m->page == 8) {
canvas_draw_icon(canvas, width - 56, height - 48, &I_DolphinFirstStart8_56x51);
elements_multiline_text_framed(
canvas, 0, 17, "You can check\nmy level and\nmood in the\nPassport menu");
}
}
View* desktop_first_start_get_view(DesktopFirstStartView* first_start_view) {
furi_assert(first_start_view);
return first_start_view->view;
}
bool desktop_first_start_input(InputEvent* event, void* context) {
furi_assert(event);
DesktopFirstStartView* first_start_view = context;
if(event->type == InputTypeShort) {
DesktopFirstStartViewModel* model = view_get_model(first_start_view->view);
if(event->key == InputKeyLeft) {
if(model->page > 0) model->page--;
} else if(event->key == InputKeyRight) {
uint32_t page = ++model->page;
if(page > 8) {
first_start_view->callback(DesktopFirstStartCompleted, first_start_view->context);
}
}
view_commit_model(first_start_view->view, true);
}
return true;
}
DesktopFirstStartView* desktop_first_start_alloc() {
DesktopFirstStartView* first_start_view = furi_alloc(sizeof(DesktopFirstStartView));
first_start_view->view = view_alloc();
view_allocate_model(
first_start_view->view, ViewModelTypeLocking, sizeof(DesktopFirstStartViewModel));
view_set_context(first_start_view->view, first_start_view);
view_set_draw_callback(first_start_view->view, (ViewDrawCallback)desktop_first_start_render);
view_set_input_callback(first_start_view->view, desktop_first_start_input);
return first_start_view;
}
void desktop_first_start_free(DesktopFirstStartView* first_start_view) {
furi_assert(first_start_view);
view_free(first_start_view->view);
free(first_start_view);
}

View File

@ -0,0 +1,35 @@
#pragma once
#include <gui/gui_i.h>
#include <gui/view.h>
#include <gui/canvas.h>
#include <gui/elements.h>
#include <furi.h>
typedef enum {
DesktopFirstStartCompleted,
} DesktopFirstStartEvent;
typedef struct DesktopFirstStartView DesktopFirstStartView;
typedef void (*DesktopFirstStartViewCallback)(DesktopFirstStartEvent event, void* context);
struct DesktopFirstStartView {
View* view;
DesktopFirstStartViewCallback callback;
void* context;
};
typedef struct {
uint8_t page;
} DesktopFirstStartViewModel;
void desktop_first_start_set_callback(
DesktopFirstStartView* main_view,
DesktopFirstStartViewCallback callback,
void* context);
View* desktop_first_start_get_view(DesktopFirstStartView* main_view);
DesktopFirstStartView* desktop_first_start_alloc();
void desktop_first_start_free(DesktopFirstStartView* main_view);

View File

@ -0,0 +1,66 @@
#include <furi.h>
#include "../desktop_i.h"
#include <furi-hal.h>
#include <furi-hal-version.h>
#include "desktop_hw_mismatch.h"
void desktop_hw_mismatch_set_callback(
DesktopHwMismatchView* main_view,
DesktopHwMismatchViewCallback callback,
void* context) {
furi_assert(main_view);
furi_assert(callback);
main_view->callback = callback;
main_view->context = context;
}
void desktop_hw_mismatch_render(Canvas* canvas, void* model) {
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2, 15, "!!!! HW Mismatch !!!!");
char buffer[64];
canvas_set_font(canvas, FontSecondary);
snprintf(buffer, 64, "HW target: F%d", furi_hal_version_get_hw_target());
canvas_draw_str(canvas, 5, 27, buffer);
canvas_draw_str(canvas, 5, 38, "FW target: " TARGET);
}
View* desktop_hw_mismatch_get_view(DesktopHwMismatchView* hw_mismatch_view) {
furi_assert(hw_mismatch_view);
return hw_mismatch_view->view;
}
bool desktop_hw_mismatch_input(InputEvent* event, void* context) {
furi_assert(event);
furi_assert(context);
DesktopHwMismatchView* hw_mismatch_view = context;
if(event->type == InputTypeShort) {
hw_mismatch_view->callback(DesktopHwMismatchEventExit, hw_mismatch_view->context);
}
return true;
}
DesktopHwMismatchView* desktop_hw_mismatch_alloc() {
DesktopHwMismatchView* hw_mismatch_view = furi_alloc(sizeof(DesktopHwMismatchView));
hw_mismatch_view->view = view_alloc();
view_allocate_model(
hw_mismatch_view->view, ViewModelTypeLocking, sizeof(DesktopHwMismatchViewModel));
view_set_context(hw_mismatch_view->view, hw_mismatch_view);
view_set_draw_callback(hw_mismatch_view->view, (ViewDrawCallback)desktop_hw_mismatch_render);
view_set_input_callback(hw_mismatch_view->view, desktop_hw_mismatch_input);
return hw_mismatch_view;
}
void desktop_hw_mismatch_free(DesktopHwMismatchView* hw_mismatch_view) {
furi_assert(hw_mismatch_view);
view_free(hw_mismatch_view->view);
free(hw_mismatch_view);
}

View File

@ -0,0 +1,38 @@
#pragma once
#include <gui/gui_i.h>
#include <gui/view.h>
#include <gui/canvas.h>
#include <gui/elements.h>
#include <furi.h>
typedef enum {
DesktopHwMismatchEventExit,
} DesktopHwMismatchEvent;
typedef struct DesktopHwMismatchView DesktopHwMismatchView;
typedef void (*DesktopHwMismatchViewCallback)(DesktopHwMismatchEvent event, void* context);
struct DesktopHwMismatchView {
View* view;
DesktopHwMismatchViewCallback callback;
void* context;
};
typedef struct {
IconAnimation* animation;
uint8_t scene_num;
uint8_t hint_timeout;
bool locked;
} DesktopHwMismatchViewModel;
void desktop_hw_mismatch_set_callback(
DesktopHwMismatchView* hw_mismatch_view,
DesktopHwMismatchViewCallback callback,
void* context);
View* desktop_hw_mismatch_get_view(DesktopHwMismatchView* hw_mismatch_view);
DesktopHwMismatchView* desktop_hw_mismatch_alloc();
void desktop_hw_mismatch_free(DesktopHwMismatchView* hw_mismatch_view);

View File

@ -0,0 +1,111 @@
#include <furi.h>
#include "../desktop_i.h"
#include "desktop_lock_menu.h"
void desktop_lock_menu_set_callback(
DesktopLockMenuView* lock_menu,
DesktopLockMenuViewCallback callback,
void* context) {
furi_assert(lock_menu);
furi_assert(callback);
lock_menu->callback = callback;
lock_menu->context = context;
}
void desktop_lock_menu_reset_idx(DesktopLockMenuView* lock_menu) {
with_view_model(
lock_menu->view, (DesktopLockMenuViewModel * model) {
model->idx = 0;
return true;
});
}
static void lock_menu_callback(void* context, uint8_t index) {
furi_assert(context);
DesktopLockMenuView* lock_menu = context;
switch(index) {
case 0: // lock
lock_menu->callback(DesktopLockMenuEventLock, lock_menu->context);
default: // wip message
with_view_model(
lock_menu->view, (DesktopLockMenuViewModel * model) {
model->hint_timeout = HINT_TIMEOUT_L;
return true;
});
break;
}
}
void desktop_lock_menu_render(Canvas* canvas, void* model) {
const char* Lockmenu_Items[3] = {"Lock", "Set PIN", "DUMB mode"};
DesktopLockMenuViewModel* m = model;
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
canvas_draw_icon(canvas, -57, 0, &I_DoorLeft_70x55);
canvas_draw_icon(canvas, 115, 0, &I_DoorRight_70x55);
canvas_set_font(canvas, FontSecondary);
for(uint8_t i = 0; i < 3; ++i) {
canvas_draw_str_aligned(
canvas,
64,
13 + (i * 17),
AlignCenter,
AlignCenter,
(m->hint_timeout && m->idx == i && m->idx) ? "Not implemented" : Lockmenu_Items[i]);
if(m->idx == i) elements_frame(canvas, 15, 5 + (i * 17), 98, 15);
}
}
View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu) {
furi_assert(lock_menu);
return lock_menu->view;
}
bool desktop_lock_menu_input(InputEvent* event, void* context) {
furi_assert(event);
furi_assert(context);
DesktopLockMenuView* lock_menu = context;
uint8_t idx;
if(event->type != InputTypeShort) return false;
with_view_model(
lock_menu->view, (DesktopLockMenuViewModel * model) {
model->hint_timeout = 0; // clear hint timeout
if(event->key == InputKeyUp) {
model->idx = CLAMP(model->idx - 1, 2, 0);
} else if(event->key == InputKeyDown) {
model->idx = CLAMP(model->idx + 1, 2, 0);
}
idx = model->idx;
return true;
});
if(event->key == InputKeyBack) {
lock_menu->callback(DesktopLockMenuEventExit, lock_menu->context);
} else if(event->key == InputKeyOk) {
lock_menu_callback(lock_menu, idx);
}
return true;
}
DesktopLockMenuView* desktop_lock_menu_alloc() {
DesktopLockMenuView* lock_menu = furi_alloc(sizeof(DesktopLockMenuView));
lock_menu->view = view_alloc();
view_allocate_model(lock_menu->view, ViewModelTypeLocking, sizeof(DesktopLockMenuViewModel));
view_set_context(lock_menu->view, lock_menu);
view_set_draw_callback(lock_menu->view, (ViewDrawCallback)desktop_lock_menu_render);
view_set_input_callback(lock_menu->view, desktop_lock_menu_input);
return lock_menu;
}
void desktop_lock_menu_free(DesktopLockMenuView* lock_menu_view) {
furi_assert(lock_menu_view);
view_free(lock_menu_view->view);
free(lock_menu_view);
}

View File

@ -0,0 +1,39 @@
#pragma once
#include <gui/gui_i.h>
#include <gui/view.h>
#include <gui/canvas.h>
#include <gui/elements.h>
#include <furi.h>
typedef enum {
DesktopLockMenuEventLock,
DesktopLockMenuEventUnlock,
DesktopLockMenuEventExit,
} DesktopLockMenuEvent;
typedef struct DesktopLockMenuView DesktopLockMenuView;
typedef void (*DesktopLockMenuViewCallback)(DesktopLockMenuEvent event, void* context);
struct DesktopLockMenuView {
View* view;
DesktopLockMenuViewCallback callback;
void* context;
};
typedef struct {
uint8_t idx;
uint8_t hint_timeout;
bool locked;
} DesktopLockMenuViewModel;
void desktop_lock_menu_set_callback(
DesktopLockMenuView* lock_menu,
DesktopLockMenuViewCallback callback,
void* context);
View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu);
void desktop_lock_menu_reset_idx(DesktopLockMenuView* lock_menu);
DesktopLockMenuView* desktop_lock_menu_alloc();
void desktop_lock_menu_free(DesktopLockMenuView* lock_menu);

View File

@ -0,0 +1,171 @@
#include <furi.h>
#include "../desktop_i.h"
#include "desktop_locked.h"
static const Icon* idle_scenes[] = {&A_Wink_128x64, &A_WatchingTV_128x64};
void desktop_locked_set_callback(
DesktopLockedView* locked_view,
DesktopLockedViewCallback callback,
void* context) {
furi_assert(locked_view);
furi_assert(callback);
locked_view->callback = callback;
locked_view->context = context;
}
void locked_view_timer_callback(void* context) {
DesktopLockedView* locked_view = context;
locked_view->callback(DesktopLockedEventUpdate, locked_view->context);
}
// temporary locked screen animation managment
static void
desktop_scene_handler_set_scene(DesktopLockedView* locked_view, const Icon* icon_data) {
with_view_model(
locked_view->view, (DesktopLockedViewModel * model) {
if(model->animation) icon_animation_free(model->animation);
model->animation = icon_animation_alloc(icon_data);
icon_animation_start(model->animation);
return true;
});
}
void desktop_locked_update_hint_timeout(DesktopLockedView* locked_view) {
with_view_model(
locked_view->view, (DesktopLockedViewModel * model) {
model->hint_timeout = HINT_TIMEOUT_H;
return true;
});
}
void desktop_locked_reset_door_pos(DesktopLockedView* locked_view) {
with_view_model(
locked_view->view, (DesktopLockedViewModel * model) {
model->animation_seq_end = false;
model->door_left_x = -57;
model->door_right_x = 115;
return true;
});
}
void desktop_locked_manage_redraw(DesktopLockedView* locked_view) {
bool animation_seq_end;
with_view_model(
locked_view->view, (DesktopLockedViewModel * model) {
model->animation_seq_end = !model->door_left_x;
animation_seq_end = model->animation_seq_end;
if(!model->animation_seq_end) {
model->door_left_x = CLAMP(model->door_left_x + 5, 0, -57);
model->door_right_x = CLAMP(model->door_right_x - 5, 115, 60);
}
return true;
});
if(animation_seq_end) {
osTimerStop(locked_view->timer);
}
}
void desktop_locked_reset_counter(DesktopLockedView* locked_view) {
locked_view->lock_count = 0;
locked_view->lock_lastpress = 0;
with_view_model(
locked_view->view, (DesktopLockedViewModel * model) {
model->hint_timeout = 0;
return true;
});
}
void desktop_locked_render(Canvas* canvas, void* model) {
DesktopLockedViewModel* m = model;
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
if(!m->animation_seq_end) {
canvas_draw_icon(canvas, m->door_left_x, 0, &I_DoorLeft_70x55);
canvas_draw_icon(canvas, m->door_right_x, 0, &I_DoorRight_70x55);
}
if(m->animation && m->animation_seq_end) {
canvas_draw_icon_animation(canvas, 0, -3, m->animation);
}
if(m->hint_timeout) {
m->hint_timeout--;
if(!m->animation_seq_end) {
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_framed(canvas, 42, 30, "Locked");
} else {
canvas_set_font(canvas, FontSecondary);
canvas_draw_icon(canvas, 13, 5, &I_LockPopup_100x49);
elements_multiline_text(canvas, 65, 20, "To unlock\npress:");
}
}
}
View* desktop_locked_get_view(DesktopLockedView* locked_view) {
furi_assert(locked_view);
return locked_view->view;
}
bool desktop_locked_input(InputEvent* event, void* context) {
furi_assert(event);
furi_assert(context);
DesktopLockedView* locked_view = context;
if(event->type == InputTypeShort) {
with_view_model(
locked_view->view, (DesktopLockedViewModel * model) {
model->hint_timeout = HINT_TIMEOUT_L;
return true;
});
if(event->key == InputKeyBack) {
uint32_t press_time = HAL_GetTick();
// check if pressed sequentially
if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT) {
locked_view->lock_lastpress = press_time;
locked_view->lock_count = 0;
} else if(press_time - locked_view->lock_lastpress < UNLOCK_RST_TIMEOUT) {
locked_view->lock_lastpress = press_time;
locked_view->lock_count++;
}
if(locked_view->lock_count == UNLOCK_CNT) {
locked_view->lock_count = 0;
locked_view->callback(DesktopLockedEventUnlock, locked_view->context);
}
}
}
// All events consumed
return true;
}
DesktopLockedView* desktop_locked_alloc() {
DesktopLockedView* locked_view = furi_alloc(sizeof(DesktopLockedView));
locked_view->view = view_alloc();
locked_view->timer =
osTimerNew(locked_view_timer_callback, osTimerPeriodic, locked_view, NULL);
view_allocate_model(locked_view->view, ViewModelTypeLocking, sizeof(DesktopLockedViewModel));
view_set_context(locked_view->view, locked_view);
view_set_draw_callback(locked_view->view, (ViewDrawCallback)desktop_locked_render);
view_set_input_callback(locked_view->view, desktop_locked_input);
desktop_scene_handler_set_scene(locked_view, idle_scenes[random() % COUNT_OF(idle_scenes)]);
return locked_view;
}
void desktop_locked_free(DesktopLockedView* locked_view) {
furi_assert(locked_view);
osTimerDelete(locked_view->timer);
view_free(locked_view->view);
free(locked_view);
}

View File

@ -0,0 +1,55 @@
#pragma once
#include <gui/gui_i.h>
#include <gui/view.h>
#include <gui/canvas.h>
#include <gui/elements.h>
#include <furi.h>
#define UNLOCK_RST_TIMEOUT 200
#define UNLOCK_CNT 2 // 3 actually
typedef enum {
DesktopLockedEventUnlock,
DesktopLockedEventUpdate,
} DesktopLockedEvent;
typedef struct DesktopLockedView DesktopLockedView;
typedef void (*DesktopLockedViewCallback)(DesktopLockedEvent event, void* context);
struct DesktopLockedView {
View* view;
DesktopLockedViewCallback callback;
void* context;
osTimerId_t timer;
uint8_t lock_count;
uint32_t lock_lastpress;
};
typedef struct {
IconAnimation* animation;
uint8_t scene_num;
int8_t door_left_x;
int8_t door_right_x;
uint8_t hint_timeout;
bool animation_seq_end;
} DesktopLockedViewModel;
void desktop_locked_set_callback(
DesktopLockedView* locked_view,
DesktopLockedViewCallback callback,
void* context);
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);
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_main_unlocked(DesktopMainView* main_view);
void desktop_main_reset_hint(DesktopMainView* main_view);

View File

@ -0,0 +1,116 @@
#include <furi.h>
#include "../desktop_i.h"
#include "desktop_main.h"
static const Icon* idle_scenes[] = {&A_Wink_128x64, &A_WatchingTV_128x64};
void desktop_main_set_callback(
DesktopMainView* main_view,
DesktopMainViewCallback callback,
void* context) {
furi_assert(main_view);
furi_assert(callback);
main_view->callback = callback;
main_view->context = context;
}
void desktop_main_reset_hint(DesktopMainView* main_view) {
with_view_model(
main_view->view, (DesktopMainViewModel * model) {
model->hint_timeout = 0;
return true;
});
}
// temporary main screen animation managment
void desktop_scene_handler_set_scene(DesktopMainView* main_view, const Icon* icon_data) {
with_view_model(
main_view->view, (DesktopMainViewModel * model) {
if(model->animation) icon_animation_free(model->animation);
model->animation = icon_animation_alloc(icon_data);
icon_animation_start(model->animation);
return true;
});
}
void desktop_scene_handler_switch_scene(DesktopMainView* main_view) {
with_view_model(
main_view->view, (DesktopMainViewModel * model) {
if(icon_animation_is_last_frame(model->animation)) {
if(model->animation) icon_animation_free(model->animation);
model->animation = icon_animation_alloc(idle_scenes[model->scene_num]);
icon_animation_start(model->animation);
model->scene_num = random() % COUNT_OF(idle_scenes);
}
return true;
});
}
void desktop_main_render(Canvas* canvas, void* model) {
canvas_clear(canvas);
DesktopMainViewModel* m = model;
if(m->animation) {
canvas_draw_icon_animation(canvas, 0, -3, m->animation);
}
if(m->unlocked && m->hint_timeout) {
m->hint_timeout = CLAMP(m->hint_timeout - 1, 2, 0);
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_framed(canvas, 42, 30, "Unlocked");
}
}
View* desktop_main_get_view(DesktopMainView* main_view) {
furi_assert(main_view);
return main_view->view;
}
bool desktop_main_input(InputEvent* event, void* context) {
furi_assert(event);
furi_assert(context);
DesktopMainView* main_view = context;
if(event->key == InputKeyOk && event->type == InputTypeShort) {
main_view->callback(DesktopMainEventOpenMenu, main_view->context);
} else if(event->key == InputKeyDown && event->type == InputTypeLong) {
main_view->callback(DesktopMainEventOpenDebug, main_view->context);
} else if(event->key == InputKeyUp && event->type == InputTypeShort) {
main_view->callback(DesktopMainEventOpenLockMenu, main_view->context);
} else if(event->key == InputKeyDown && event->type == InputTypeShort) {
main_view->callback(DesktopMainEventOpenArchive, main_view->context);
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
main_view->callback(DesktopMainEventOpenFavorite, main_view->context);
}
desktop_main_reset_hint(main_view);
return true;
}
DesktopMainView* desktop_main_alloc() {
DesktopMainView* main_view = furi_alloc(sizeof(DesktopMainView));
main_view->view = view_alloc();
view_allocate_model(main_view->view, ViewModelTypeLocking, sizeof(DesktopMainViewModel));
view_set_context(main_view->view, main_view);
view_set_draw_callback(main_view->view, (ViewDrawCallback)desktop_main_render);
view_set_input_callback(main_view->view, desktop_main_input);
desktop_scene_handler_set_scene(main_view, idle_scenes[random() % COUNT_OF(idle_scenes)]);
return main_view;
}
void desktop_main_free(DesktopMainView* main_view) {
furi_assert(main_view);
view_free(main_view->view);
free(main_view);
}
void desktop_main_unlocked(DesktopMainView* main_view) {
with_view_model(
main_view->view, (DesktopMainViewModel * model) {
model->unlocked = true;
model->hint_timeout = 2;
return true;
});
}

View File

@ -0,0 +1,43 @@
#pragma once
#include <gui/gui_i.h>
#include <gui/view.h>
#include <gui/canvas.h>
#include <gui/elements.h>
#include <furi.h>
typedef enum {
DesktopMainEventOpenMenu,
DesktopMainEventOpenLockMenu,
DesktopMainEventOpenDebug,
DesktopMainEventUnlocked,
DesktopMainEventOpenArchive,
DesktopMainEventOpenFavorite,
} DesktopMainEvent;
typedef struct DesktopMainView DesktopMainView;
typedef void (*DesktopMainViewCallback)(DesktopMainEvent event, void* context);
struct DesktopMainView {
View* view;
DesktopMainViewCallback callback;
void* context;
};
typedef struct {
IconAnimation* animation;
uint8_t scene_num;
uint8_t hint_timeout;
bool unlocked;
} DesktopMainViewModel;
void desktop_main_set_callback(
DesktopMainView* main_view,
DesktopMainViewCallback callback,
void* context);
View* desktop_main_get_view(DesktopMainView* main_view);
DesktopMainView* desktop_main_alloc();
void desktop_main_free(DesktopMainView* main_view);

View File

@ -1,389 +1,9 @@
#include "dolphin_i.h"
#include <stdlib.h>
#include "applications.h"
#include <furi.h>
const Icon* idle_scenes[] = {&A_Wink_128x64, &A_WatchingTV_128x64};
static void dolphin_switch_to_app(Dolphin* dolphin, const FlipperApplication* flipper_app) {
bool dolphin_load(Dolphin* dolphin) {
furi_assert(dolphin);
furi_assert(flipper_app);
furi_assert(flipper_app->app);
furi_assert(flipper_app->name);
if(furi_thread_get_state(dolphin->scene_thread) != FuriThreadStateStopped) {
FURI_LOG_E("Dolphin", "Thread is already running");
return;
}
furi_thread_set_name(dolphin->scene_thread, flipper_app->name);
furi_thread_set_stack_size(dolphin->scene_thread, flipper_app->stack_size);
furi_thread_set_callback(dolphin->scene_thread, flipper_app->app);
furi_thread_start(dolphin->scene_thread);
}
// temporary main screen animation managment
void dolphin_scene_handler_set_scene(Dolphin* dolphin, const Icon* icon_data) {
with_view_model(
dolphin->idle_view_main, (DolphinViewMainModel * model) {
if(model->animation) icon_animation_free(model->animation);
model->animation = icon_animation_alloc(icon_data);
icon_animation_start(model->animation);
return true;
});
}
void dolphin_scene_handler_switch_scene(Dolphin* dolphin) {
with_view_model(
dolphin->idle_view_main, (DolphinViewMainModel * model) {
if(icon_animation_is_last_frame(model->animation)) {
if(model->animation) icon_animation_free(model->animation);
model->animation = icon_animation_alloc(idle_scenes[model->scene_num]);
icon_animation_start(model->animation);
model->scene_num = random() % COUNT_OF(idle_scenes);
}
return true;
});
}
bool dolphin_view_first_start_input(InputEvent* event, void* context) {
furi_assert(event);
furi_assert(context);
Dolphin* dolphin = context;
if(event->type == InputTypeShort) {
DolphinViewFirstStartModel* model = view_get_model(dolphin->idle_view_first_start);
if(event->key == InputKeyLeft) {
if(model->page > 0) model->page--;
} else if(event->key == InputKeyRight) {
uint32_t page = ++model->page;
if(page > 8) {
dolphin_save(dolphin);
view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain);
}
}
view_commit_model(dolphin->idle_view_first_start, true);
}
// All evennts cosumed
return true;
}
void dolphin_lock_handler(InputEvent* event, Dolphin* dolphin) {
furi_assert(event);
furi_assert(dolphin);
with_view_model(
dolphin->idle_view_main, (DolphinViewMainModel * model) {
model->hint_timeout = HINT_TIMEOUT_L;
return true;
});
if(event->key == InputKeyBack && event->type == InputTypeShort) {
uint32_t press_time = HAL_GetTick();
// check if pressed sequentially
if(press_time - dolphin->lock_lastpress > UNLOCK_RST_TIMEOUT) {
dolphin->lock_lastpress = press_time;
dolphin->lock_count = 0;
} else if(press_time - dolphin->lock_lastpress < UNLOCK_RST_TIMEOUT) {
dolphin->lock_lastpress = press_time;
dolphin->lock_count++;
}
if(dolphin->lock_count == 2) {
dolphin->locked = false;
dolphin->lock_count = 0;
with_view_model(
dolphin->view_lockmenu, (DolphinViewLockMenuModel * model) {
model->locked = false;
model->door_left_x = -57; // move doors to default pos
model->door_right_x = 115;
return true;
});
with_view_model(
dolphin->idle_view_main, (DolphinViewMainModel * model) {
model->hint_timeout = HINT_TIMEOUT_L; // "unlocked" hint timeout
model->locked = false;
return true;
});
view_port_enabled_set(dolphin->lock_viewport, false);
}
}
}
bool dolphin_view_idle_main_input(InputEvent* event, void* context) {
furi_assert(event);
furi_assert(context);
Dolphin* dolphin = context;
// unlocked
if(!dolphin->locked) {
if(event->key == InputKeyOk && event->type == InputTypeShort) {
with_value_mutex(
dolphin->menu_vm, (Menu * menu) { menu_ok(menu); });
} else if(event->key == InputKeyUp && event->type == InputTypeShort) {
osTimerStart(dolphin->timeout_timer, 64);
view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewLockMenu);
} else if(event->key == InputKeyDown && event->type == InputTypeShort) {
dolphin_switch_to_app(dolphin, &FLIPPER_ARCHIVE);
} else if(event->key == InputKeyDown && event->type == InputTypeLong) {
view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewStats);
} else if(event->key == InputKeyBack && event->type == InputTypeShort) {
view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain);
}
with_view_model(
dolphin->idle_view_main, (DolphinViewMainModel * model) {
model->hint_timeout = 0; // clear hint timeout
return true;
});
} else {
// locked
dolphin_lock_handler(event, dolphin);
dolphin_scene_handler_switch_scene(dolphin);
}
// All events consumed
return true;
}
void lock_menu_refresh_handler(void* p) {
osMessageQueueId_t event_queue = p;
DolphinEvent event;
event.type = DolphinEventTypeTick;
// Some tick events may lost and we don't care.
osMessageQueuePut(event_queue, &event, 0, 0);
}
static void lock_menu_callback(void* context, uint8_t index) {
furi_assert(context);
Dolphin* dolphin = context;
switch(index) {
// lock
case 0:
dolphin->locked = true;
with_view_model(
dolphin->view_lockmenu, (DolphinViewLockMenuModel * model) {
model->locked = true;
model->exit_timeout = HINT_TIMEOUT_H;
return true;
});
with_view_model(
dolphin->idle_view_main, (DolphinViewMainModel * model) {
model->locked = true;
return true;
});
break;
default:
// wip message
with_view_model(
dolphin->view_lockmenu, (DolphinViewLockMenuModel * model) {
model->hint_timeout = HINT_TIMEOUT_H;
return true;
});
break;
}
}
static void lock_icon_callback(Canvas* canvas, void* context) {
furi_assert(context);
Dolphin* dolphin = context;
canvas_draw_icon_animation(canvas, 0, 0, dolphin->lock_icon);
}
bool dolphin_view_lockmenu_input(InputEvent* event, void* context) {
furi_assert(event);
furi_assert(context);
Dolphin* dolphin = context;
if(event->type != InputTypeShort) return false;
DolphinViewLockMenuModel* model = view_get_model(dolphin->view_lockmenu);
model->hint_timeout = 0; // clear hint timeout
if(event->key == InputKeyUp) {
model->idx = CLAMP(model->idx - 1, 2, 0);
} else if(event->key == InputKeyDown) {
model->idx = CLAMP(model->idx + 1, 2, 0);
} else if(event->key == InputKeyOk) {
lock_menu_callback(context, model->idx);
} else if(event->key == InputKeyBack) {
model->idx = 0;
view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain);
if(random() % 100 > 50)
dolphin_scene_handler_set_scene(
dolphin, idle_scenes[random() % COUNT_OF(idle_scenes)]);
}
view_commit_model(dolphin->view_lockmenu, true);
return true;
}
bool dolphin_view_idle_down_input(InputEvent* event, void* context) {
furi_assert(event);
furi_assert(context);
Dolphin* dolphin = context;
DolphinViewStatsScreens current;
if(event->type != InputTypeShort) return false;
DolphinViewStatsModel* model = view_get_model(dolphin->idle_view_dolphin_stats);
current = model->screen;
if(event->key == InputKeyDown) {
model->screen = (model->screen + 1) % DolphinViewStatsTotalCount;
} else if(event->key == InputKeyUp) {
model->screen =
((model->screen - 1) + DolphinViewStatsTotalCount) % DolphinViewStatsTotalCount;
}
view_commit_model(dolphin->idle_view_dolphin_stats, true);
if(current == DolphinViewStatsMeta) {
if(event->key == InputKeyLeft) {
dolphin_deed(dolphin, DolphinDeedWrong);
} else if(event->key == InputKeyRight) {
dolphin_deed(dolphin, DolphinDeedIButtonRead);
} else if(event->key == InputKeyOk) {
dolphin_save(dolphin);
} else {
return false;
}
}
if(event->key == InputKeyBack) {
view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain);
}
return true;
}
Dolphin* dolphin_alloc() {
Dolphin* dolphin = furi_alloc(sizeof(Dolphin));
// Message queue
dolphin->event_queue = osMessageQueueNew(8, sizeof(DolphinEvent), NULL);
furi_check(dolphin->event_queue);
// State
dolphin->state = dolphin_state_alloc();
// Menu
dolphin->menu_vm = furi_record_open("menu");
// Scene thread
dolphin->scene_thread = furi_thread_alloc();
// GUI
dolphin->gui = furi_record_open("gui");
// Dispatcher
dolphin->idle_view_dispatcher = view_dispatcher_alloc();
// First start View
dolphin->idle_view_first_start = view_alloc();
view_allocate_model(
dolphin->idle_view_first_start, ViewModelTypeLockFree, sizeof(DolphinViewFirstStartModel));
view_set_context(dolphin->idle_view_first_start, dolphin);
view_set_draw_callback(dolphin->idle_view_first_start, dolphin_view_first_start_draw);
view_set_input_callback(dolphin->idle_view_first_start, dolphin_view_first_start_input);
view_dispatcher_add_view(
dolphin->idle_view_dispatcher, DolphinViewFirstStart, dolphin->idle_view_first_start);
// Main Idle View
dolphin->idle_view_main = view_alloc();
view_set_context(dolphin->idle_view_main, dolphin);
view_allocate_model(
dolphin->idle_view_main, ViewModelTypeLockFree, sizeof(DolphinViewMainModel));
view_set_draw_callback(dolphin->idle_view_main, dolphin_view_idle_main_draw);
view_set_input_callback(dolphin->idle_view_main, dolphin_view_idle_main_input);
view_dispatcher_add_view(
dolphin->idle_view_dispatcher, DolphinViewIdleMain, dolphin->idle_view_main);
// Lock Menu View
dolphin->view_lockmenu = view_alloc();
view_set_context(dolphin->view_lockmenu, dolphin);
view_allocate_model(
dolphin->view_lockmenu, ViewModelTypeLockFree, sizeof(DolphinViewLockMenuModel));
view_set_draw_callback(dolphin->view_lockmenu, dolphin_view_lockmenu_draw);
view_set_input_callback(dolphin->view_lockmenu, dolphin_view_lockmenu_input);
view_set_previous_callback(dolphin->view_lockmenu, dolphin_view_idle_back);
view_dispatcher_add_view(
dolphin->idle_view_dispatcher, DolphinViewLockMenu, dolphin->view_lockmenu);
// default doors xpos
with_view_model(
dolphin->view_lockmenu, (DolphinViewLockMenuModel * model) {
model->door_left_x = -57; // defaults
model->door_right_x = 115; // defaults
return true;
});
dolphin->timeout_timer =
osTimerNew(lock_menu_refresh_handler, osTimerPeriodic, dolphin->event_queue, NULL);
// Stats Idle View
dolphin->idle_view_dolphin_stats = view_alloc();
view_set_context(dolphin->idle_view_dolphin_stats, dolphin);
view_allocate_model(
dolphin->idle_view_dolphin_stats, ViewModelTypeLockFree, sizeof(DolphinViewStatsModel));
view_set_draw_callback(dolphin->idle_view_dolphin_stats, dolphin_view_idle_down_draw);
view_set_input_callback(dolphin->idle_view_dolphin_stats, dolphin_view_idle_down_input);
view_set_previous_callback(dolphin->idle_view_dolphin_stats, dolphin_view_idle_back);
view_dispatcher_add_view(
dolphin->idle_view_dispatcher, DolphinViewStats, dolphin->idle_view_dolphin_stats);
// HW Mismatch
dolphin->view_hw_mismatch = view_alloc();
view_set_draw_callback(dolphin->view_hw_mismatch, dolphin_view_hw_mismatch_draw);
view_set_previous_callback(dolphin->view_hw_mismatch, dolphin_view_idle_back);
view_dispatcher_add_view(
dolphin->idle_view_dispatcher, DolphinViewHwMismatch, dolphin->view_hw_mismatch);
// Lock icon
dolphin->lock_icon = icon_animation_alloc(&I_Lock_8x8);
dolphin->lock_viewport = view_port_alloc();
view_port_set_width(dolphin->lock_viewport, icon_animation_get_width(dolphin->lock_icon));
view_port_draw_callback_set(dolphin->lock_viewport, lock_icon_callback, dolphin);
view_port_enabled_set(dolphin->lock_viewport, false);
// Main screen animation
dolphin_scene_handler_set_scene(dolphin, idle_scenes[random() % COUNT_OF(idle_scenes)]);
view_dispatcher_attach_to_gui(
dolphin->idle_view_dispatcher, dolphin->gui, ViewDispatcherTypeWindow);
gui_add_view_port(dolphin->gui, dolphin->lock_viewport, GuiLayerStatusBarLeft);
return dolphin;
}
void dolphin_free(Dolphin* dolphin) {
furi_assert(dolphin);
gui_remove_view_port(dolphin->gui, dolphin->lock_viewport);
view_port_free(dolphin->lock_viewport);
icon_animation_free(dolphin->lock_icon);
osTimerDelete(dolphin->timeout_timer);
view_dispatcher_free(dolphin->idle_view_dispatcher);
furi_record_close("gui");
dolphin->gui = NULL;
furi_thread_free(dolphin->scene_thread);
furi_record_close("menu");
dolphin->menu_vm = NULL;
dolphin_state_free(dolphin->state);
osMessageQueueDelete(dolphin->event_queue);
free(dolphin);
return dolphin_state_load(dolphin->state);
}
void dolphin_save(Dolphin* dolphin) {
@ -401,56 +21,53 @@ void dolphin_deed(Dolphin* dolphin, DolphinDeed deed) {
furi_check(osMessageQueuePut(dolphin->event_queue, &event, 0, osWaitForever) == osOK);
}
int32_t dolphin_srv() {
DolphinDeedWeight dolphin_stats(Dolphin* dolphin) {
DolphinDeedWeight stats;
stats.butthurt = dolphin_state_get_butthurt(dolphin->state);
stats.icounter = dolphin_state_get_icounter(dolphin->state);
return stats;
}
Dolphin* dolphin_alloc() {
Dolphin* dolphin = furi_alloc(sizeof(Dolphin));
dolphin->state = dolphin_state_alloc();
dolphin->event_queue = osMessageQueueNew(8, sizeof(DolphinEvent), NULL);
return dolphin;
}
void dolphin_free(Dolphin* dolphin) {
furi_assert(dolphin);
dolphin_state_free(dolphin->state);
osMessageQueueDelete(dolphin->event_queue);
free(dolphin);
}
int32_t dolphin_srv(void* p) {
Dolphin* dolphin = dolphin_alloc();
if(dolphin_state_load(dolphin->state)) {
view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain);
} else {
view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewFirstStart);
}
with_view_model(
dolphin->idle_view_dolphin_stats, (DolphinViewStatsModel * model) {
model->icounter = dolphin_state_get_icounter(dolphin->state);
model->butthurt = dolphin_state_get_butthurt(dolphin->state);
return true;
});
furi_record_create("dolphin", dolphin);
if(!furi_hal_version_do_i_belong_here()) {
view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewHwMismatch);
}
DolphinEvent event;
while(1) {
furi_check(osMessageQueueGet(dolphin->event_queue, &event, NULL, osWaitForever) == osOK);
DolphinViewLockMenuModel* lock_model = view_get_model(dolphin->view_lockmenu);
if(lock_model->locked && lock_model->exit_timeout == 0 &&
osTimerIsRunning(dolphin->timeout_timer)) {
osTimerStop(dolphin->timeout_timer);
osDelay(1); // smol enterprise delay
view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain);
}
if(event.type == DolphinEventTypeTick) {
view_commit_model(dolphin->view_lockmenu, true);
} else if(event.type == DolphinEventTypeDeed) {
switch(event.type) {
case DolphinEventTypeDeed:
dolphin_state_on_deed(dolphin->state, event.deed);
with_view_model(
dolphin->idle_view_dolphin_stats, (DolphinViewStatsModel * model) {
model->icounter = dolphin_state_get_icounter(dolphin->state);
model->butthurt = dolphin_state_get_butthurt(dolphin->state);
return true;
});
} else if(event.type == DolphinEventTypeSave) {
break;
case DolphinEventTypeSave:
dolphin_state_save(dolphin->state);
break;
default:
break;
}
}
dolphin_free(dolphin);
return 0;
}

View File

@ -1,11 +1,30 @@
#pragma once
#include "dolphin_deed.h"
#include "helpers/dolphin_deed.h"
typedef struct Dolphin Dolphin;
/* Load Dolphin state
* Thread safe
*/
bool dolphin_load(Dolphin* dolphin);
/* 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
*/
void dolphin_deed(Dolphin* dolphin, DolphinDeed deed);
/* Save Dolphin state (write to permanent memory)
* Thread safe
*/
void dolphin_save(Dolphin* dolphin);
/* Retrieve dolphin's icounter and butthurt values
* Thread safe
*/
DolphinDeedWeight dolphin_stats(Dolphin* dolphin);

View File

@ -1,22 +1,10 @@
#pragma once
#include "dolphin.h"
#include "dolphin_state.h"
#include "dolphin_views.h"
#include <furi.h>
#include <furi-hal.h>
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/canvas.h>
#include <menu/menu.h>
#include <assets_icons.h>
#include <stdint.h>
#define UNLOCK_RST_TIMEOUT 500 // keypress counter reset timeout (ms)
#define HINT_TIMEOUT_L 3 // low refresh rate timeout (app ticks)
#define HINT_TIMEOUT_H 40 // high refresh rate timeout (app ticks)
#include "dolphin.h"
#include "helpers/dolphin_state.h"
typedef enum {
DolphinEventTypeDeed,
@ -32,36 +20,12 @@ typedef struct {
} DolphinEvent;
struct Dolphin {
// Internal message queue
osMessageQueueId_t event_queue;
// State
DolphinState* state;
// Menu
ValueMutex* menu_vm;
// Scene
FuriThread* scene_thread;
// GUI
Gui* gui;
ViewDispatcher* idle_view_dispatcher;
View* idle_view_first_start;
View* idle_view_main;
View* idle_view_dolphin_stats;
View* view_hw_mismatch;
View* view_lockmenu;
ViewPort* lock_viewport;
IconAnimation* lock_icon;
bool locked;
uint8_t lock_count;
uint32_t lock_lastpress;
osTimerId_t timeout_timer;
// Queue
osMessageQueueId_t event_queue;
};
Dolphin* dolphin_alloc();
void dolphin_free(Dolphin* dolphin);
/* Save Dolphin state (write to permanent memory)
* Thread safe
*/
void dolphin_save(Dolphin* dolphin);

View File

@ -1,197 +0,0 @@
#include "dolphin_views.h"
#include <gui/view.h>
#include <gui/gui.h>
#include <gui/elements.h>
#include <furi-hal.h>
#include <furi-hal-version.h>
static char* Lockmenu_Items[3] = {"Lock", "Set PIN", "DUMB mode"};
void dolphin_view_first_start_draw(Canvas* canvas, void* model) {
DolphinViewFirstStartModel* 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 - 48, &I_DolphinFirstStart0_70x53);
elements_multiline_text_framed(canvas, 75, 20, "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... >");
} 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! >");
} 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! >");
} 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 >");
} 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 - 48, &I_DolphinFirstStart5_54x49);
elements_multiline_text_framed(canvas, 60, 17, buf);
} else if(m->page == 6) {
canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart6_58x54);
elements_multiline_text_framed(
canvas, 63, 17, "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);
elements_multiline_text_framed(
canvas, 0, 17, "As long as\nyou read, write\nand emulate >");
} else if(m->page == 8) {
canvas_draw_icon(canvas, width - 56, height - 48, &I_DolphinFirstStart8_56x51);
elements_multiline_text_framed(
canvas, 0, 17, "You can check\nmy level and\nmood in the\nPassport menu");
}
}
void dolphin_view_idle_main_draw(Canvas* canvas, void* model) {
canvas_clear(canvas);
DolphinViewMainModel* m = model;
if(m->animation) {
canvas_draw_icon_animation(canvas, 0, -3, m->animation);
}
if(m->hint_timeout) {
m->hint_timeout--;
if(m->locked) {
canvas_draw_icon(canvas, 13, 5, &I_LockPopup_100x49);
elements_multiline_text(canvas, 65, 20, "To unlock\npress:");
} else {
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_framed(canvas, 42, 30, "Unlocked");
}
}
}
void dolphin_view_lockmenu_draw(Canvas* canvas, void* model) {
DolphinViewLockMenuModel* m = model;
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
canvas_draw_icon(canvas, m->door_left_x, 0, &I_DoorLeft_70x55);
canvas_draw_icon(canvas, m->door_right_x, 0, &I_DoorRight_70x55);
canvas_set_font(canvas, FontSecondary);
if(m->locked) {
m->exit_timeout--;
m->door_left_x = CLAMP(m->door_left_x + 5, 0, -57);
m->door_right_x = CLAMP(m->door_right_x - 5, 115, 60);
if(m->door_left_x > -10) {
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_framed(canvas, 42, 30, "Locked");
}
} else {
if(m->door_left_x == -57) {
for(uint8_t i = 0; i < 3; ++i) {
canvas_draw_str_aligned(
canvas,
64,
13 + (i * 17),
AlignCenter,
AlignCenter,
(m->hint_timeout && m->idx == i) ? "Not implemented" : Lockmenu_Items[i]);
if(m->idx == i) elements_frame(canvas, 15, 5 + (i * 17), 98, 15);
}
}
if(m->hint_timeout) {
m->hint_timeout--;
}
}
}
void dolphin_view_idle_down_draw(Canvas* canvas, void* model) {
DolphinViewStatsModel* m = model;
const Version* ver;
char buffer[64];
static const char* headers[] = {"FW Version info:", "Boot Version info:", "Dolphin info:"};
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2, 13, headers[m->screen]);
canvas_set_font(canvas, FontSecondary);
if(m->screen != DolphinViewStatsMeta) {
// Hardware version
const char* my_name = furi_hal_version_get_name_ptr();
snprintf(
buffer,
sizeof(buffer),
"HW: %d.F%dB%dC%d %s",
furi_hal_version_get_hw_version(),
furi_hal_version_get_hw_target(),
furi_hal_version_get_hw_body(),
furi_hal_version_get_hw_connect(),
my_name ? my_name : "Unknown");
canvas_draw_str(canvas, 5, 23, buffer);
ver = m->screen == DolphinViewStatsBoot ? furi_hal_version_get_boot_version() :
furi_hal_version_get_firmware_version();
if(!ver) {
canvas_draw_str(canvas, 5, 33, "No info");
return;
}
snprintf(
buffer,
sizeof(buffer),
"%s [%s]",
version_get_version(ver),
version_get_builddate(ver));
canvas_draw_str(canvas, 5, 33, buffer);
snprintf(
buffer,
sizeof(buffer),
"%s [%s]",
version_get_githash(ver),
version_get_gitbranchnum(ver));
canvas_draw_str(canvas, 5, 43, buffer);
snprintf(
buffer, sizeof(buffer), "[%s] %s", version_get_target(ver), version_get_gitbranch(ver));
canvas_draw_str(canvas, 5, 53, buffer);
} else {
char buffer[64];
canvas_set_font(canvas, FontSecondary);
snprintf(buffer, 64, "Icounter: %ld", m->icounter);
canvas_draw_str(canvas, 5, 30, buffer);
snprintf(buffer, 64, "Butthurt: %ld", m->butthurt);
canvas_draw_str(canvas, 5, 40, buffer);
canvas_draw_str(canvas, 0, 53, "[< >] icounter value [ok] save");
}
}
void dolphin_view_hw_mismatch_draw(Canvas* canvas, void* model) {
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2, 15, "!!!! HW Mismatch !!!!");
char buffer[64];
canvas_set_font(canvas, FontSecondary);
snprintf(buffer, 64, "HW target: F%d", furi_hal_version_get_hw_target());
canvas_draw_str(canvas, 5, 27, buffer);
canvas_draw_str(canvas, 5, 38, "FW target: " TARGET);
}
uint32_t dolphin_view_idle_back(void* context) {
return DolphinViewIdleMain;
}

View File

@ -1,67 +0,0 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <gui/canvas.h>
#include <input/input.h>
#include <furi.h>
// Idle screen
typedef enum {
DolphinViewIdleMain,
DolphinViewFirstStart,
DolphinViewStats,
DolphinViewHwMismatch,
DolphinViewLockMenu,
} DolphinViewIdle;
// Debug info
typedef enum {
DolphinViewStatsFw,
DolphinViewStatsBoot,
DolphinViewStatsMeta,
DolphinViewStatsTotalCount,
} DolphinViewStatsScreens;
typedef struct {
uint32_t page;
} DolphinViewFirstStartModel;
void dolphin_view_first_start_draw(Canvas* canvas, void* model);
bool dolphin_view_first_start_input(InputEvent* event, void* context);
typedef struct {
uint32_t icounter;
uint32_t butthurt;
DolphinViewStatsScreens screen;
} DolphinViewStatsModel;
typedef struct {
uint8_t idx;
int8_t door_left_x;
int8_t door_right_x;
uint8_t exit_timeout;
uint8_t hint_timeout;
bool locked;
} DolphinViewLockMenuModel;
typedef struct {
IconAnimation* animation;
uint8_t scene_num;
uint8_t hint_timeout;
bool locked;
} DolphinViewMainModel;
void dolphin_view_idle_main_draw(Canvas* canvas, void* model);
bool dolphin_view_idle_main_input(InputEvent* event, void* context);
void dolphin_view_idle_up_draw(Canvas* canvas, void* model);
void dolphin_view_lockmenu_draw(Canvas* canvas, void* model);
void dolphin_view_idle_down_draw(Canvas* canvas, void* model);
void dolphin_view_hw_mismatch_draw(Canvas* canvas, void* model);
uint32_t dolphin_view_idle_back(void* context);

View File

@ -16,6 +16,7 @@ typedef enum {
typedef struct {
int32_t icounter; // how many icounter get by Deed
int32_t butthurt; // how many icounter get by Deed
uint32_t limit_value; // how many deeds in limit interval
uint32_t limit_interval; // interval, in minutes
} DolphinDeedWeight;