Merge branch 'release-candidate' into release
This commit is contained in:
commit
20deb6458a
225
applications/about/about.c
Normal file
225
applications/about/about.c
Normal file
@ -0,0 +1,225 @@
|
||||
#include <furi.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/modules/empty_screen.h>
|
||||
#include <m-string.h>
|
||||
#include <furi-hal-version.h>
|
||||
|
||||
typedef DialogMessageButton (*AboutDialogScreen)(DialogsApp* dialogs, DialogMessage* message);
|
||||
|
||||
static DialogMessageButton product_screen(DialogsApp* dialogs, DialogMessage* message) {
|
||||
DialogMessageButton result;
|
||||
|
||||
const char* screen_header = "Product: Flipper Zero\n"
|
||||
"Model: FZ.1\n";
|
||||
const char* screen_text = "FCC ID: 2A2V6-FZ\n"
|
||||
"IC ID: 27624-FZ";
|
||||
|
||||
dialog_message_set_header(message, screen_header, 0, 0, AlignLeft, AlignTop);
|
||||
dialog_message_set_text(message, screen_text, 0, 26, AlignLeft, AlignTop);
|
||||
result = dialog_message_show(dialogs, message);
|
||||
dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop);
|
||||
dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static DialogMessageButton address_screen(DialogsApp* dialogs, DialogMessage* message) {
|
||||
DialogMessageButton result;
|
||||
|
||||
const char* screen_text = "Flipper Devices Inc\n"
|
||||
"Suite B #551, 2803\n"
|
||||
"Philadelphia Pike, Claymont\n"
|
||||
"DE, USA 19703\n";
|
||||
|
||||
dialog_message_set_text(message, screen_text, 0, 0, AlignLeft, AlignTop);
|
||||
result = dialog_message_show(dialogs, message);
|
||||
dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static DialogMessageButton compliance_screen(DialogsApp* dialogs, DialogMessage* message) {
|
||||
DialogMessageButton result;
|
||||
|
||||
const char* screen_text = "For all compliance\n"
|
||||
"certificates please visit\n"
|
||||
"www.flipp.dev/compliance";
|
||||
|
||||
dialog_message_set_text(message, screen_text, 0, 0, AlignLeft, AlignTop);
|
||||
result = dialog_message_show(dialogs, message);
|
||||
dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static DialogMessageButton icon1_screen(DialogsApp* dialogs, DialogMessage* message) {
|
||||
DialogMessageButton result;
|
||||
|
||||
dialog_message_set_icon(message, &I_Certification1_103x23, 12, 12);
|
||||
result = dialog_message_show(dialogs, message);
|
||||
dialog_message_set_icon(message, NULL, 0, 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static DialogMessageButton icon2_screen(DialogsApp* dialogs, DialogMessage* message) {
|
||||
DialogMessageButton result;
|
||||
|
||||
dialog_message_set_icon(message, &I_Certification2_119x30, 4, 9);
|
||||
result = dialog_message_show(dialogs, message);
|
||||
dialog_message_set_icon(message, NULL, 0, 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static DialogMessageButton hw_version_screen(DialogsApp* dialogs, DialogMessage* message) {
|
||||
DialogMessageButton result;
|
||||
string_t buffer;
|
||||
string_init(buffer);
|
||||
const char* my_name = furi_hal_version_get_name_ptr();
|
||||
|
||||
string_cat_printf(
|
||||
buffer,
|
||||
"%d.F%dB%dC%d %s\n",
|
||||
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");
|
||||
|
||||
dialog_message_set_header(message, "HW Version info:", 0, 0, AlignLeft, AlignTop);
|
||||
dialog_message_set_text(message, string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop);
|
||||
result = dialog_message_show(dialogs, message);
|
||||
dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop);
|
||||
dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop);
|
||||
string_clear(buffer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage* message) {
|
||||
DialogMessageButton result;
|
||||
string_t buffer;
|
||||
string_init(buffer);
|
||||
const Version* ver = furi_hal_version_get_firmware_version();
|
||||
|
||||
if(!ver) {
|
||||
string_cat_printf(buffer, "No info\n");
|
||||
} else {
|
||||
string_cat_printf(
|
||||
buffer,
|
||||
"%s [%s]\n%s [%s]\n[%s] %s",
|
||||
version_get_version(ver),
|
||||
version_get_builddate(ver),
|
||||
version_get_githash(ver),
|
||||
version_get_gitbranchnum(ver),
|
||||
version_get_target(ver),
|
||||
version_get_gitbranch(ver));
|
||||
}
|
||||
|
||||
dialog_message_set_header(message, "FW Version info:", 0, 0, AlignLeft, AlignTop);
|
||||
dialog_message_set_text(message, string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop);
|
||||
result = dialog_message_show(dialogs, message);
|
||||
dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop);
|
||||
dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop);
|
||||
string_clear(buffer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static DialogMessageButton boot_version_screen(DialogsApp* dialogs, DialogMessage* message) {
|
||||
DialogMessageButton result;
|
||||
string_t buffer;
|
||||
string_init(buffer);
|
||||
const Version* ver = furi_hal_version_get_boot_version();
|
||||
|
||||
if(!ver) {
|
||||
string_cat_printf(buffer, "No info\n");
|
||||
} else {
|
||||
string_cat_printf(
|
||||
buffer,
|
||||
"%s [%s]\n%s [%s]\n[%s] %s",
|
||||
version_get_version(ver),
|
||||
version_get_builddate(ver),
|
||||
version_get_githash(ver),
|
||||
version_get_gitbranchnum(ver),
|
||||
version_get_target(ver),
|
||||
version_get_gitbranch(ver));
|
||||
}
|
||||
|
||||
dialog_message_set_header(message, "Boot Version info:", 0, 0, AlignLeft, AlignTop);
|
||||
dialog_message_set_text(message, string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop);
|
||||
result = dialog_message_show(dialogs, message);
|
||||
dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop);
|
||||
dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop);
|
||||
string_clear(buffer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const AboutDialogScreen about_screens[] = {
|
||||
product_screen,
|
||||
compliance_screen,
|
||||
address_screen,
|
||||
icon1_screen,
|
||||
icon2_screen,
|
||||
hw_version_screen,
|
||||
fw_version_screen,
|
||||
boot_version_screen};
|
||||
|
||||
const size_t about_screens_count = sizeof(about_screens) / sizeof(AboutDialogScreen);
|
||||
|
||||
int32_t about_settings_app(void* p) {
|
||||
DialogsApp* dialogs = furi_record_open("dialogs");
|
||||
DialogMessage* message = dialog_message_alloc();
|
||||
|
||||
Gui* gui = furi_record_open("gui");
|
||||
ViewDispatcher* view_dispatcher = view_dispatcher_alloc();
|
||||
EmptyScreen* empty_screen = empty_screen_alloc();
|
||||
const uint32_t empty_screen_index = 0;
|
||||
|
||||
size_t screen_index = 0;
|
||||
DialogMessageButton screen_result;
|
||||
|
||||
// draw empty screen to prevent menu flickering
|
||||
view_dispatcher_add_view(
|
||||
view_dispatcher, empty_screen_index, empty_screen_get_view(empty_screen));
|
||||
view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen);
|
||||
view_dispatcher_switch_to_view(view_dispatcher, empty_screen_index);
|
||||
|
||||
while(1) {
|
||||
if(screen_index >= about_screens_count - 1) {
|
||||
dialog_message_set_buttons(message, "Back", NULL, NULL);
|
||||
} else {
|
||||
dialog_message_set_buttons(message, "Back", NULL, "Next");
|
||||
}
|
||||
|
||||
screen_result = about_screens[screen_index](dialogs, message);
|
||||
|
||||
if(screen_result == DialogMessageButtonLeft) {
|
||||
if(screen_index <= 0) {
|
||||
break;
|
||||
} else {
|
||||
screen_index--;
|
||||
}
|
||||
} else if(screen_result == DialogMessageButtonRight) {
|
||||
if(screen_index < about_screens_count) {
|
||||
screen_index++;
|
||||
}
|
||||
} else if(screen_result == DialogMessageButtonBack) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dialog_message_free(message);
|
||||
furi_record_close("dialogs");
|
||||
|
||||
view_dispatcher_remove_view(view_dispatcher, empty_screen_index);
|
||||
view_dispatcher_free(view_dispatcher);
|
||||
empty_screen_free(empty_screen);
|
||||
furi_record_close("gui");
|
||||
|
||||
return 0;
|
||||
}
|
||||
96
applications/applications.c
Normal file → Executable file
96
applications/applications.c
Normal file → Executable file
@ -9,11 +9,11 @@ extern int32_t dolphin_srv(void* p);
|
||||
extern int32_t gui_srv(void* p);
|
||||
extern int32_t input_srv(void* p);
|
||||
extern int32_t loader_srv(void* p);
|
||||
extern int32_t menu_srv(void* p);
|
||||
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);
|
||||
@ -46,11 +46,15 @@ extern void lfrfid_cli_init();
|
||||
extern void nfc_cli_init();
|
||||
extern void storage_cli_init();
|
||||
extern void subghz_cli_init();
|
||||
extern void power_cli_init();
|
||||
|
||||
// Settings
|
||||
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);
|
||||
|
||||
const FlipperApplication FLIPPER_SERVICES[] = {
|
||||
/* Services */
|
||||
@ -70,6 +74,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
|
||||
@ -78,8 +86,7 @@ const FlipperApplication FLIPPER_SERVICES[] = {
|
||||
{.app = input_srv, .name = "Input", .stack_size = 1024, .icon = NULL},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_MENU
|
||||
{.app = menu_srv, .name = "Menu", .stack_size = 1024, .icon = NULL},
|
||||
#ifdef SRV_LOADER
|
||||
{.app = loader_srv, .name = "Loader", .stack_size = 1024, .icon = NULL},
|
||||
#endif
|
||||
|
||||
@ -98,43 +105,6 @@ const FlipperApplication FLIPPER_SERVICES[] = {
|
||||
#ifdef SRV_STORAGE
|
||||
{.app = storage_srv, .name = "Storage", .stack_size = 4096, .icon = NULL},
|
||||
#endif
|
||||
|
||||
/* Fake services (autorun) */
|
||||
#ifdef SRV_BLINK
|
||||
{.app = blink_test_app, .name = "Blink", .stack_size = 1024, .icon = &A_Plugins_14},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_LF_RFID
|
||||
{.app = lfrfid_app, .name = "125 kHz RFID", .stack_size = 2048, .icon = &A_Plugins_14},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_IRDA
|
||||
{.app = irda_app, .name = "Infrared", .stack_size = 1024 * 3, .icon = &A_Plugins_14},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_MUSIC_PLAYER
|
||||
{.app = music_player_app, .name = "Music Player", .stack_size = 1024, .icon = &A_Plugins_14},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_IBUTTON
|
||||
{.app = ibutton_app, .name = "iButton", .stack_size = 2048, .icon = &A_Plugins_14},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_GPIO_TEST
|
||||
{.app = gpio_test_app, .name = "GPIO Test", .stack_size = 1024, .icon = &A_Plugins_14},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_KEYPAD_TEST
|
||||
{.app = keypad_test_app, .name = "Keypad Test", .stack_size = 1024, .icon = &A_Plugins_14},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_ACCESSOR
|
||||
{.app = accessor_app, .name = "Accessor", .stack_size = 4096, .icon = &A_Plugins_14},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_STORAGE_TEST
|
||||
{.app = storage_test_app, .name = "Storage Test", .stack_size = 1024, .icon = &A_Plugins_14},
|
||||
#endif
|
||||
};
|
||||
|
||||
const size_t FLIPPER_SERVICES_COUNT = sizeof(FLIPPER_SERVICES) / sizeof(FlipperApplication);
|
||||
@ -142,18 +112,14 @@ const size_t FLIPPER_SERVICES_COUNT = sizeof(FLIPPER_SERVICES) / sizeof(FlipperA
|
||||
// Main menu APP
|
||||
const FlipperApplication FLIPPER_APPS[] = {
|
||||
|
||||
#ifdef APP_IBUTTON
|
||||
{.app = ibutton_app, .name = "iButton", .stack_size = 2048, .icon = &A_iButton_14},
|
||||
#ifdef APP_SUBGHZ
|
||||
{.app = subghz_app, .name = "Sub-GHz", .stack_size = 2048, .icon = &A_Sub1ghz_14},
|
||||
#endif
|
||||
|
||||
#ifdef APP_NFC
|
||||
{.app = nfc_app, .name = "NFC", .stack_size = 4096, .icon = &A_NFC_14},
|
||||
#endif
|
||||
|
||||
#ifdef APP_SUBGHZ
|
||||
{.app = subghz_app, .name = "Sub-GHz", .stack_size = 2048, .icon = &A_Sub1ghz_14},
|
||||
#endif
|
||||
|
||||
#ifdef APP_LF_RFID
|
||||
{.app = lfrfid_app, .name = "125 kHz RFID", .stack_size = 2048, .icon = &A_125khz_14},
|
||||
#endif
|
||||
@ -162,6 +128,10 @@ const FlipperApplication FLIPPER_APPS[] = {
|
||||
{.app = irda_app, .name = "Infrared", .stack_size = 1024 * 3, .icon = &A_Infrared_14},
|
||||
#endif
|
||||
|
||||
#ifdef APP_IBUTTON
|
||||
{.app = ibutton_app, .name = "iButton", .stack_size = 2048, .icon = &A_iButton_14},
|
||||
#endif
|
||||
|
||||
#ifdef APP_GPIO_TEST
|
||||
{.app = gpio_test_app, .name = "GPIO", .stack_size = 1024, .icon = &A_GPIO_14},
|
||||
#endif
|
||||
@ -175,22 +145,35 @@ const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[] = {
|
||||
#ifdef SRV_CLI
|
||||
crypto_cli_init,
|
||||
#endif
|
||||
|
||||
#ifdef APP_IRDA
|
||||
irda_cli_init,
|
||||
#endif
|
||||
|
||||
#ifdef APP_NFC
|
||||
nfc_cli_init,
|
||||
#endif
|
||||
|
||||
#ifdef APP_SUBGHZ
|
||||
subghz_cli_init,
|
||||
#endif
|
||||
|
||||
#ifdef APP_LF_RFID
|
||||
lfrfid_cli_init,
|
||||
#endif
|
||||
|
||||
#ifdef APP_IBUTTON
|
||||
ibutton_cli_init,
|
||||
#endif
|
||||
|
||||
#ifdef SRV_BT
|
||||
bt_cli_init,
|
||||
#endif
|
||||
|
||||
#ifdef SRV_POWER
|
||||
power_cli_init,
|
||||
#endif
|
||||
|
||||
#ifdef SRV_STORAGE
|
||||
storage_cli_init,
|
||||
#endif
|
||||
@ -257,16 +240,31 @@ const FlipperApplication FLIPPER_ARCHIVE =
|
||||
|
||||
// Settings menu
|
||||
const FlipperApplication FLIPPER_SETTINGS_APPS[] = {
|
||||
#ifdef SRV_BT
|
||||
{.app = bt_settings_app, .name = "Bluetooth", .stack_size = 1024, .icon = NULL},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_NOTIFICATION
|
||||
{.app = notification_settings_app, .name = "Notification", .stack_size = 1024, .icon = NULL},
|
||||
{.app = notification_settings_app,
|
||||
.name = "LCD and notifications",
|
||||
.stack_size = 1024,
|
||||
.icon = NULL},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_STORAGE
|
||||
{.app = storage_settings_app, .name = "Storage", .stack_size = 2048, .icon = NULL},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_BT
|
||||
{.app = bt_settings_app, .name = "Bluetooth", .stack_size = 1024, .icon = NULL},
|
||||
#ifdef SRV_POWER
|
||||
{.app = power_settings_app, .name = "Power", .stack_size = 1024, .icon = NULL},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_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
|
||||
};
|
||||
|
||||
|
||||
@ -1,264 +1,249 @@
|
||||
APP_DIR = $(PROJECT_ROOT)/applications
|
||||
LIB_DIR = $(PROJECT_ROOT)/lib
|
||||
LIB_DIR = $(PROJECT_ROOT)/lib
|
||||
|
||||
CFLAGS += -I$(APP_DIR)
|
||||
C_SOURCES += $(shell find $(APP_DIR) -name *.c)
|
||||
CPP_SOURCES += $(shell find $(APP_DIR) -name *.cpp)
|
||||
C_SOURCES += $(shell find $(APP_DIR) -name *.c)
|
||||
CPP_SOURCES += $(shell find $(APP_DIR) -name *.cpp)
|
||||
|
||||
|
||||
# Use SRV_* for autostart app
|
||||
# Use APP_* for add app to build
|
||||
|
||||
APP_RELEASE ?= 1
|
||||
ifeq ($(APP_RELEASE), 1)
|
||||
# Services
|
||||
SRV_BT = 1
|
||||
SRV_CLI = 1
|
||||
SRV_DIALOGS = 1
|
||||
SRV_DOLPHIN = 1
|
||||
SRV_GUI = 1
|
||||
SRV_INPUT = 1
|
||||
SRV_MENU = 1
|
||||
SRV_BT = 1
|
||||
SRV_CLI = 1
|
||||
SRV_DIALOGS = 1
|
||||
SRV_DOLPHIN = 1
|
||||
SRV_GUI = 1
|
||||
SRV_INPUT = 1
|
||||
SRV_LOADER = 1
|
||||
SRV_NOTIFICATION = 1
|
||||
SRV_POWER = 1
|
||||
SRV_POWER = 1
|
||||
SRV_POWER_OBSERVER = 1
|
||||
SRV_STORAGE = 1
|
||||
SRV_STORAGE = 1
|
||||
|
||||
# Apps
|
||||
APP_ARCHIVE = 1
|
||||
SRV_DESKTOP = 1
|
||||
APP_ARCHIVE = 1
|
||||
APP_GPIO_TEST = 1
|
||||
APP_IBUTTON = 1
|
||||
APP_IRDA = 1
|
||||
APP_LF_RFID = 1
|
||||
APP_NFC = 1
|
||||
APP_SUBGHZ = 1
|
||||
APP_IBUTTON = 1
|
||||
APP_IRDA = 1
|
||||
APP_LF_RFID = 1
|
||||
APP_NFC = 1
|
||||
APP_SUBGHZ = 1
|
||||
APP_ABOUT = 1
|
||||
|
||||
# Plugins
|
||||
APP_MUSIC_PLAYER = 1
|
||||
|
||||
# Debug
|
||||
APP_ACCESSOR = 1
|
||||
APP_BLINK = 1
|
||||
APP_BLINK = 1
|
||||
APP_IRDA_MONITOR = 1
|
||||
APP_KEYPAD_TEST = 1
|
||||
APP_SD_TEST = 1
|
||||
APP_SD_TEST = 1
|
||||
APP_UNIT_TESTS = 0
|
||||
APP_VIBRO_DEMO = 1
|
||||
endif
|
||||
|
||||
|
||||
SRV_BT ?= 0
|
||||
ifeq ($(SRV_BT), 1)
|
||||
SRV_CLI = 1
|
||||
CFLAGS += -DSRV_BT
|
||||
endif
|
||||
# Applications
|
||||
# that will be shown in menu
|
||||
# Prefix with APP_*
|
||||
|
||||
SRV_DOLPHIN ?= 0
|
||||
ifeq ($(SRV_DOLPHIN), 1)
|
||||
SRV_MENU = 1
|
||||
CFLAGS += -DSRV_DOLPHIN
|
||||
endif
|
||||
|
||||
SRV_POWER ?= 0
|
||||
ifeq ($(SRV_POWER), 1)
|
||||
SRV_GUI = 1
|
||||
SRV_CLI = 1
|
||||
CFLAGS += -DSRV_POWER
|
||||
endif
|
||||
|
||||
SRV_POWER_OBSERVER ?= 0
|
||||
ifeq ($(SRV_POWER_OBSERVER), 1)
|
||||
SRV_POWER = 1
|
||||
CFLAGS += -DSRV_POWER_OBSERVER
|
||||
endif
|
||||
|
||||
SRV_MENU ?= 0
|
||||
ifeq ($(SRV_MENU), 1)
|
||||
CFLAGS += -DSRV_MENU
|
||||
APP_MENU = 1
|
||||
endif
|
||||
APP_MENU ?= 0
|
||||
ifeq ($(APP_MENU), 1)
|
||||
SRV_INPUT = 1
|
||||
SRV_GUI = 1
|
||||
CFLAGS += -DAPP_MENU
|
||||
endif
|
||||
|
||||
APP_IRDA_MONITOR ?= 0
|
||||
ifeq ($(APP_IRDA_MONITOR), 1)
|
||||
CFLAGS += -DAPP_IRDA_MONITOR
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
|
||||
APP_UNIT_TESTS ?= 0
|
||||
ifeq ($(APP_UNIT_TESTS), 1)
|
||||
CFLAGS += -DAPP_UNIT_TESTS
|
||||
endif
|
||||
|
||||
|
||||
APP_ARCHIVE ?= 0
|
||||
ifeq ($(APP_NFC), 1)
|
||||
ifeq ($(APP_ARCHIVE), 1)
|
||||
CFLAGS += -DAPP_ARCHIVE
|
||||
APP_ARCHIVE = 1
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
SRV_BLINK ?= 0
|
||||
ifeq ($(SRV_BLINK), 1)
|
||||
CFLAGS += -DSRV_BLINK
|
||||
APP_BLINK = 1
|
||||
endif
|
||||
|
||||
APP_BLINK ?= 0
|
||||
ifeq ($(APP_BLINK), 1)
|
||||
CFLAGS += -DAPP_BLINK
|
||||
SRV_INPUT = 1
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
SRV_UART_WRITE ?= 0
|
||||
ifeq ($(SRV_UART_WRITE), 1)
|
||||
CFLAGS += -DSRV_UART_WRITE
|
||||
APP_UART_WRITE = 1
|
||||
endif
|
||||
APP_UART_WRITE ?= 0
|
||||
ifeq ($(APP_UART_WRITE), 1)
|
||||
CFLAGS += -DAPP_UART_WRITE
|
||||
endif
|
||||
|
||||
SRV_IPC ?= 0
|
||||
ifeq ($(SRV_IPC), 1)
|
||||
CFLAGS += -DSRV_IPC
|
||||
APP_IPC = 1
|
||||
endif
|
||||
APP_IPC ?= 0
|
||||
ifeq ($(APP_IPC), 1)
|
||||
CFLAGS += -DAPP_IPC
|
||||
endif
|
||||
|
||||
APP_SUBGHZ ?= 0
|
||||
ifeq ($(APP_SUBGHZ), 1)
|
||||
CFLAGS += -DAPP_SUBGHZ
|
||||
SRV_INPUT = 1
|
||||
SRV_GUI = 1
|
||||
SRV_CLI = 1
|
||||
SRV_GUI = 1
|
||||
SRV_CLI = 1
|
||||
endif
|
||||
|
||||
SRV_LF_RFID ?= 0
|
||||
ifeq ($(SRV_LF_RFID), 1)
|
||||
CFLAGS += -DSRV_LF_RFID
|
||||
APP_LF_RFID = 1
|
||||
|
||||
APP_ABOUT ?= 0
|
||||
ifeq ($(APP_ABOUT), 1)
|
||||
CFLAGS += -DAPP_ABOUT
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
|
||||
APP_LF_RFID ?= 0
|
||||
ifeq ($(APP_LF_RFID), 1)
|
||||
CFLAGS += -DAPP_LF_RFID
|
||||
SRV_INPUT = 1
|
||||
SRV_GUI = 1
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
|
||||
APP_NFC ?= 0
|
||||
ifeq ($(APP_NFC), 1)
|
||||
CFLAGS += -DAPP_NFC
|
||||
SRV_MENU = 1
|
||||
SRV_INPUT = 1
|
||||
SRV_GUI = 1
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
SRV_IRDA ?= 0
|
||||
ifeq ($(SRV_IRDA), 1)
|
||||
CFLAGS += -DSRV_IRDA
|
||||
APP_IRDA = 1
|
||||
endif
|
||||
|
||||
APP_IRDA ?= 0
|
||||
ifeq ($(APP_IRDA), 1)
|
||||
CFLAGS += -DAPP_IRDA
|
||||
SRV_INPUT = 1
|
||||
SRV_GUI = 1
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
|
||||
APP_VIBRO_DEMO ?= 0
|
||||
ifeq ($(APP_VIBRO_DEMO), 1)
|
||||
CFLAGS += -DAPP_VIBRO_DEMO
|
||||
SRV_INPUT = 1
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
SRV_KEYPAD_TEST ?= 0
|
||||
ifeq ($(SRV_KEYPAD_TEST), 1)
|
||||
CFLAGS += -DSRV_KEYPAD_TEST
|
||||
APP_KEYPAD_TEST = 1
|
||||
endif
|
||||
|
||||
APP_KEYPAD_TEST ?= 0
|
||||
ifeq ($(APP_KEYPAD_TEST), 1)
|
||||
CFLAGS += -DAPP_KEYPAD_TEST
|
||||
APP_KEYPAD_TEST = 1
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
SRV_ACCESSOR ?= 0
|
||||
ifeq ($(SRV_ACCESSOR), 1)
|
||||
CFLAGS += -DSRV_ACCESSOR
|
||||
APP_ACCESSOR = 1
|
||||
endif
|
||||
|
||||
APP_ACCESSOR ?= 0
|
||||
ifeq ($(APP_ACCESSOR), 1)
|
||||
CFLAGS += -DAPP_ACCESSOR
|
||||
APP_ACCESSOR = 1
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
SRV_GPIO_TEST ?= 0
|
||||
ifeq ($(SRV_GPIO_TEST), 1)
|
||||
CFLAGS += -DSRV_GPIO_TEST
|
||||
APP_GPIO_TEST = 1
|
||||
endif
|
||||
|
||||
APP_GPIO_TEST ?= 0
|
||||
ifeq ($(APP_GPIO_TEST), 1)
|
||||
CFLAGS += -DAPP_GPIO_TEST
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
SRV_MUSIC_PLAYER ?= 0
|
||||
ifeq ($(SRV_MUSIC_PLAYER), 1)
|
||||
CFLAGS += -DSRV_MUSIC_PLAYER
|
||||
APP_MUSIC_PLAYER = 1
|
||||
endif
|
||||
|
||||
APP_MUSIC_PLAYER ?= 0
|
||||
ifeq ($(APP_MUSIC_PLAYER), 1)
|
||||
CFLAGS += -DAPP_MUSIC_PLAYER
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
SRV_IBUTTON ?= 0
|
||||
ifeq ($(SRV_IBUTTON), 1)
|
||||
CFLAGS += -DSRV_IBUTTON
|
||||
APP_IBUTTON = 1
|
||||
endif
|
||||
|
||||
APP_IBUTTON ?= 0
|
||||
ifeq ($(APP_IBUTTON), 1)
|
||||
CFLAGS += -DAPP_IBUTTON
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
|
||||
# Services
|
||||
# that will start with OS
|
||||
# Prefix with SRV_*
|
||||
|
||||
|
||||
SRV_BT ?= 0
|
||||
ifeq ($(SRV_BT), 1)
|
||||
CFLAGS += -DSRV_BT
|
||||
SRV_CLI = 1
|
||||
endif
|
||||
|
||||
|
||||
SRV_DESKTOP ?= 0
|
||||
ifeq ($(SRV_DESKTOP), 1)
|
||||
CFLAGS += -DSRV_DESKTOP
|
||||
SRV_DOLPHIN = 1
|
||||
SRV_STORAGE = 1
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
|
||||
SRV_DOLPHIN ?= 0
|
||||
ifeq ($(SRV_DOLPHIN), 1)
|
||||
CFLAGS += -DSRV_DOLPHIN
|
||||
endif
|
||||
|
||||
|
||||
SRV_POWER_OBSERVER ?= 0
|
||||
ifeq ($(SRV_POWER_OBSERVER), 1)
|
||||
CFLAGS += -DSRV_POWER_OBSERVER
|
||||
SRV_POWER = 1
|
||||
endif
|
||||
|
||||
|
||||
SRV_POWER ?= 0
|
||||
ifeq ($(SRV_POWER), 1)
|
||||
CFLAGS += -DSRV_POWER
|
||||
SRV_GUI = 1
|
||||
SRV_CLI = 1
|
||||
endif
|
||||
|
||||
|
||||
SRV_LOADER ?= 0
|
||||
ifeq ($(SRV_LOADER), 1)
|
||||
CFLAGS += -DSRV_LOADER
|
||||
SRV_GUI = 1
|
||||
# Loader autostart hook
|
||||
LOADER_AUTOSTART ?= ""
|
||||
ifneq ($(strip $(LOADER_AUTOSTART)), "")
|
||||
CFLAGS += -DLOADER_AUTOSTART="\"$(LOADER_AUTOSTART)\""
|
||||
endif
|
||||
# Loader autostart hook END
|
||||
endif
|
||||
|
||||
|
||||
SRV_DIALOGS ?= 0
|
||||
ifeq ($(SRV_DIALOGS), 1)
|
||||
CFLAGS += -DSRV_DIALOGS
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
#
|
||||
# Essential services
|
||||
#
|
||||
|
||||
SRV_GUI ?= 0
|
||||
ifeq ($(SRV_GUI), 1)
|
||||
CFLAGS += -DSRV_GUI
|
||||
SRV_INPUT = 1
|
||||
endif
|
||||
|
||||
|
||||
SRV_INPUT ?= 0
|
||||
ifeq ($(SRV_INPUT), 1)
|
||||
CFLAGS += -DSRV_INPUT
|
||||
endif
|
||||
|
||||
|
||||
SRV_CLI ?= 0
|
||||
ifeq ($(SRV_CLI), 1)
|
||||
SRV_GUI = 1
|
||||
CFLAGS += -DSRV_CLI
|
||||
endif
|
||||
|
||||
|
||||
SRV_NOTIFICATION ?= 0
|
||||
ifeq ($(SRV_NOTIFICATION), 1)
|
||||
CFLAGS += -DSRV_NOTIFICATION
|
||||
endif
|
||||
|
||||
|
||||
SRV_STORAGE ?= 0
|
||||
ifeq ($(SRV_STORAGE), 1)
|
||||
CFLAGS += -DSRV_STORAGE
|
||||
endif
|
||||
|
||||
SRV_DIALOGS ?= 0
|
||||
ifeq ($(SRV_DIALOGS), 1)
|
||||
CFLAGS += -DSRV_DIALOGS
|
||||
endif
|
||||
|
||||
@ -31,10 +31,10 @@ ArchiveApp* archive_alloc() {
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
archive->view_dispatcher, archive_back_event_callback);
|
||||
|
||||
archive->main_view = main_view_alloc();
|
||||
archive->browser = browser_alloc();
|
||||
|
||||
view_dispatcher_add_view(
|
||||
archive->view_dispatcher, ArchiveViewBrowser, archive_main_get_view(archive->main_view));
|
||||
archive->view_dispatcher, ArchiveViewBrowser, archive_browser_get_view(archive->browser));
|
||||
|
||||
view_dispatcher_add_view(
|
||||
archive->view_dispatcher, ArchiveViewTextInput, text_input_get_view(archive->text_input));
|
||||
@ -49,7 +49,7 @@ void archive_free(ArchiveApp* archive) {
|
||||
view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewTextInput);
|
||||
view_dispatcher_free(archive->view_dispatcher);
|
||||
scene_manager_free(archive->scene_manager);
|
||||
main_view_free(archive->main_view);
|
||||
browser_free(archive->browser);
|
||||
|
||||
text_input_free(archive->text_input);
|
||||
|
||||
|
||||
@ -9,72 +9,20 @@
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <loader/loader.h>
|
||||
|
||||
#include <m-string.h>
|
||||
#include <m-array.h>
|
||||
#include <storage/storage.h>
|
||||
#include "applications.h"
|
||||
#include "file-worker.h"
|
||||
|
||||
#include "views/archive_main_view.h"
|
||||
#include "views/archive_browser_view.h"
|
||||
#include "scenes/archive_scene.h"
|
||||
|
||||
#define MAX_FILE_SIZE 128
|
||||
|
||||
typedef enum {
|
||||
ArchiveViewBrowser,
|
||||
ArchiveViewTextInput,
|
||||
ArchiveViewTotal,
|
||||
} ArchiveViewEnum;
|
||||
|
||||
static const char* tab_default_paths[] = {
|
||||
[ArchiveTabFavorites] = "/any/favorites",
|
||||
[ArchiveTabIButton] = "/any/ibutton",
|
||||
[ArchiveTabNFC] = "/any/nfc",
|
||||
[ArchiveTabSubGhz] = "/any/subghz/saved",
|
||||
[ArchiveTabLFRFID] = "/any/lfrfid",
|
||||
[ArchiveTabIrda] = "/any/irda",
|
||||
[ArchiveTabBrowser] = "/any",
|
||||
};
|
||||
|
||||
static inline const char* get_default_path(ArchiveFileTypeEnum type) {
|
||||
switch(type) {
|
||||
case ArchiveFileTypeIButton:
|
||||
return tab_default_paths[ArchiveTabIButton];
|
||||
case ArchiveFileTypeNFC:
|
||||
return tab_default_paths[ArchiveTabNFC];
|
||||
case ArchiveFileTypeSubGhz:
|
||||
return tab_default_paths[ArchiveTabSubGhz];
|
||||
case ArchiveFileTypeLFRFID:
|
||||
return tab_default_paths[ArchiveTabLFRFID];
|
||||
case ArchiveFileTypeIrda:
|
||||
return tab_default_paths[ArchiveTabIrda];
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char* get_favorites_path() {
|
||||
return tab_default_paths[ArchiveTabFavorites];
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
EventTypeTick,
|
||||
EventTypeKey,
|
||||
EventTypeExit,
|
||||
} EventType;
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
InputEvent input;
|
||||
} value;
|
||||
EventType type;
|
||||
} AppEvent;
|
||||
|
||||
struct ArchiveApp {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
ArchiveMainView* main_view;
|
||||
ArchiveBrowserView* browser;
|
||||
TextInput* text_input;
|
||||
char text_store[MAX_NAME_LEN];
|
||||
};
|
||||
|
||||
260
applications/archive/helpers/archive_browser.c
Normal file
260
applications/archive/helpers/archive_browser.c
Normal file
@ -0,0 +1,260 @@
|
||||
#include "archive_browser.h"
|
||||
#include "math.h"
|
||||
|
||||
void archive_update_offset(ArchiveBrowserView* browser) {
|
||||
furi_assert(browser);
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
size_t array_size = files_array_size(model->files);
|
||||
uint16_t bounds = array_size > 3 ? 2 : array_size;
|
||||
|
||||
if(array_size > 3 && model->idx >= array_size - 1) {
|
||||
model->list_offset = model->idx - 3;
|
||||
} else if(model->last_offset && model->last_offset != model->list_offset) {
|
||||
model->list_offset = model->last_offset;
|
||||
model->last_offset = !model->last_offset;
|
||||
} else if(model->list_offset < model->idx - bounds) {
|
||||
model->list_offset = CLAMP(model->idx - 2, array_size - bounds, 0);
|
||||
} else if(model->list_offset > model->idx - bounds) {
|
||||
model->list_offset = CLAMP(model->idx - 1, array_size - bounds, 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void archive_update_focus(ArchiveBrowserView* browser, const char* target) {
|
||||
furi_assert(browser);
|
||||
furi_assert(target);
|
||||
|
||||
archive_get_filenames(browser, string_get_cstr(browser->path));
|
||||
|
||||
if(!archive_file_array_size(browser) && !archive_get_depth(browser)) {
|
||||
archive_switch_tab(browser, DEFAULT_TAB_DIR);
|
||||
} else {
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
uint16_t idx = 0;
|
||||
while(idx < files_array_size(model->files)) {
|
||||
ArchiveFile_t* current = files_array_get(model->files, idx);
|
||||
if(!string_search(current->name, target)) {
|
||||
model->idx = idx;
|
||||
break;
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
archive_update_offset(browser);
|
||||
}
|
||||
}
|
||||
|
||||
size_t archive_file_array_size(ArchiveBrowserView* browser) {
|
||||
uint16_t size = 0;
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
size = files_array_size(model->files);
|
||||
return false;
|
||||
});
|
||||
return size;
|
||||
}
|
||||
|
||||
void archive_file_array_rm_selected(ArchiveBrowserView* browser) {
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
files_array_remove_v(model->files, model->idx, model->idx + 1);
|
||||
model->idx = CLAMP(model->idx, files_array_size(model->files) - 1, 0);
|
||||
return false;
|
||||
});
|
||||
|
||||
if(!archive_file_array_size(browser) && !archive_get_depth(browser)) {
|
||||
archive_switch_tab(browser, DEFAULT_TAB_DIR);
|
||||
}
|
||||
|
||||
archive_update_offset(browser);
|
||||
}
|
||||
|
||||
void archive_file_array_rm_all(ArchiveBrowserView* browser) {
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
files_array_clean(model->files);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
ArchiveFile_t* archive_get_current_file(ArchiveBrowserView* browser) {
|
||||
ArchiveFile_t* selected;
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
selected = files_array_size(model->files) ? files_array_get(model->files, model->idx) :
|
||||
NULL;
|
||||
return false;
|
||||
});
|
||||
return selected;
|
||||
}
|
||||
|
||||
ArchiveTabEnum archive_get_tab(ArchiveBrowserView* browser) {
|
||||
ArchiveTabEnum tab_id;
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
tab_id = model->tab_idx;
|
||||
return false;
|
||||
});
|
||||
return tab_id;
|
||||
}
|
||||
|
||||
uint8_t archive_get_depth(ArchiveBrowserView* browser) {
|
||||
uint8_t depth;
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
depth = model->depth;
|
||||
return false;
|
||||
});
|
||||
|
||||
return depth;
|
||||
}
|
||||
|
||||
const char* archive_get_path(ArchiveBrowserView* browser) {
|
||||
return string_get_cstr(browser->path);
|
||||
}
|
||||
|
||||
const char* archive_get_name(ArchiveBrowserView* browser) {
|
||||
ArchiveFile_t* selected = archive_get_current_file(browser);
|
||||
return string_get_cstr(selected->name);
|
||||
}
|
||||
|
||||
void archive_set_tab(ArchiveBrowserView* browser, ArchiveTabEnum tab) {
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
model->tab_idx = tab;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
void archive_set_last_tab(ArchiveBrowserView* browser, ArchiveTabEnum tab) {
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
model->last_tab = model->tab_idx;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void archive_add_item(ArchiveBrowserView* browser, FileInfo* file_info, const char* name) {
|
||||
furi_assert(browser);
|
||||
furi_assert(file_info);
|
||||
furi_assert(name);
|
||||
|
||||
ArchiveFile_t item;
|
||||
|
||||
if(filter_by_extension(file_info, get_tab_ext(archive_get_tab(browser)), name)) {
|
||||
ArchiveFile_t_init(&item);
|
||||
string_init_set_str(item.name, name);
|
||||
set_file_type(&item, file_info);
|
||||
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
files_array_push_back(model->files, item);
|
||||
return false;
|
||||
});
|
||||
|
||||
ArchiveFile_t_clear(&item);
|
||||
}
|
||||
}
|
||||
|
||||
void archive_show_file_menu(ArchiveBrowserView* browser, bool show) {
|
||||
furi_assert(browser);
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
model->menu = show;
|
||||
model->menu_idx = 0;
|
||||
|
||||
if(show) {
|
||||
ArchiveFile_t* selected = files_array_get(model->files, model->idx);
|
||||
selected->fav = archive_is_favorite(
|
||||
"%s/%s", string_get_cstr(browser->path), string_get_cstr(selected->name));
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void archive_switch_dir(ArchiveBrowserView* browser, const char* path) {
|
||||
furi_assert(browser);
|
||||
furi_assert(path);
|
||||
|
||||
string_set(browser->path, path);
|
||||
archive_get_filenames(browser, string_get_cstr(browser->path));
|
||||
archive_update_offset(browser);
|
||||
}
|
||||
|
||||
void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) {
|
||||
furi_assert(browser);
|
||||
ArchiveTabEnum tab = archive_get_tab(browser);
|
||||
|
||||
if(key == InputKeyLeft) {
|
||||
tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal;
|
||||
} else if(key == InputKeyRight) {
|
||||
tab = (tab + 1) % ArchiveTabTotal;
|
||||
}
|
||||
|
||||
archive_set_tab(browser, tab);
|
||||
|
||||
if((tab != ArchiveTabFavorites &&
|
||||
!archive_dir_empty(browser, archive_get_default_path(tab))) ||
|
||||
(tab == ArchiveTabFavorites && !archive_favorites_count(browser))) {
|
||||
if(tab != ArchiveTabBrowser) {
|
||||
archive_switch_tab(browser, key);
|
||||
}
|
||||
} else {
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
if(model->last_tab != model->tab_idx) {
|
||||
model->idx = 0;
|
||||
model->depth = 0;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
archive_switch_dir(browser, archive_get_default_path(tab));
|
||||
}
|
||||
archive_set_last_tab(browser, tab);
|
||||
}
|
||||
|
||||
void archive_enter_dir(ArchiveBrowserView* browser, string_t name) {
|
||||
furi_assert(browser);
|
||||
furi_assert(name);
|
||||
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
model->last_idx = model->idx;
|
||||
model->last_offset = model->list_offset;
|
||||
model->idx = 0;
|
||||
model->depth = CLAMP(model->depth + 1, MAX_DEPTH, 0);
|
||||
return false;
|
||||
});
|
||||
|
||||
string_cat(browser->path, "/");
|
||||
string_cat(browser->path, name);
|
||||
|
||||
archive_switch_dir(browser, string_get_cstr(browser->path));
|
||||
}
|
||||
|
||||
void archive_leave_dir(ArchiveBrowserView* browser) {
|
||||
furi_assert(browser);
|
||||
|
||||
const char* path = archive_get_path(browser);
|
||||
char* last_char_ptr = strrchr(path, '/');
|
||||
|
||||
if(last_char_ptr) {
|
||||
size_t pos = last_char_ptr - path;
|
||||
string_left(browser->path, pos);
|
||||
}
|
||||
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
model->depth = CLAMP(model->depth - 1, MAX_DEPTH, 0);
|
||||
model->idx = model->last_idx;
|
||||
return false;
|
||||
});
|
||||
|
||||
archive_switch_dir(browser, path);
|
||||
}
|
||||
68
applications/archive/helpers/archive_browser.h
Normal file
68
applications/archive/helpers/archive_browser.h
Normal file
@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include "../archive_i.h"
|
||||
|
||||
#define DEFAULT_TAB_DIR InputKeyRight //default tab swith direction
|
||||
|
||||
static const char* tab_default_paths[] = {
|
||||
[ArchiveTabFavorites] = "/any/favorites",
|
||||
[ArchiveTabIButton] = "/any/ibutton",
|
||||
[ArchiveTabNFC] = "/any/nfc",
|
||||
[ArchiveTabSubGhz] = "/any/subghz/saved",
|
||||
[ArchiveTabLFRFID] = "/any/lfrfid",
|
||||
[ArchiveTabIrda] = "/any/irda",
|
||||
[ArchiveTabBrowser] = "/any",
|
||||
};
|
||||
|
||||
static const char* known_ext[] = {
|
||||
[ArchiveFileTypeIButton] = ".ibtn",
|
||||
[ArchiveFileTypeNFC] = ".nfc",
|
||||
[ArchiveFileTypeSubGhz] = ".sub",
|
||||
[ArchiveFileTypeLFRFID] = ".rfid",
|
||||
[ArchiveFileTypeIrda] = ".ir",
|
||||
};
|
||||
|
||||
static inline const char* get_tab_ext(ArchiveTabEnum tab) {
|
||||
switch(tab) {
|
||||
case ArchiveTabIButton:
|
||||
return known_ext[ArchiveFileTypeIButton];
|
||||
case ArchiveTabNFC:
|
||||
return known_ext[ArchiveFileTypeNFC];
|
||||
case ArchiveTabSubGhz:
|
||||
return known_ext[ArchiveFileTypeSubGhz];
|
||||
case ArchiveTabLFRFID:
|
||||
return known_ext[ArchiveFileTypeLFRFID];
|
||||
case ArchiveTabIrda:
|
||||
return known_ext[ArchiveFileTypeIrda];
|
||||
default:
|
||||
return "*";
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char* archive_get_default_path(ArchiveTabEnum tab) {
|
||||
return tab_default_paths[tab];
|
||||
}
|
||||
|
||||
inline bool is_known_app(ArchiveFileTypeEnum type) {
|
||||
return (type != ArchiveFileTypeFolder && type != ArchiveFileTypeUnknown);
|
||||
}
|
||||
|
||||
void archive_update_offset(ArchiveBrowserView* browser);
|
||||
void archive_update_focus(ArchiveBrowserView* browser, const char* target);
|
||||
|
||||
size_t archive_file_array_size(ArchiveBrowserView* browser);
|
||||
void archive_file_array_rm_selected(ArchiveBrowserView* browser);
|
||||
void archive_file_array_rm_all(ArchiveBrowserView* browser);
|
||||
|
||||
ArchiveFile_t* archive_get_current_file(ArchiveBrowserView* browser);
|
||||
ArchiveTabEnum archive_get_tab(ArchiveBrowserView* browser);
|
||||
uint8_t archive_get_depth(ArchiveBrowserView* browser);
|
||||
const char* archive_get_path(ArchiveBrowserView* browser);
|
||||
const char* archive_get_name(ArchiveBrowserView* browser);
|
||||
|
||||
void archive_add_item(ArchiveBrowserView* browser, FileInfo* file_info, const char* name);
|
||||
void archive_show_file_menu(ArchiveBrowserView* browser, bool show);
|
||||
|
||||
void archive_switch_tab(ArchiveBrowserView* browser, InputKey key);
|
||||
void archive_enter_dir(ArchiveBrowserView* browser, string_t name);
|
||||
void archive_leave_dir(ArchiveBrowserView* browser);
|
||||
@ -1,18 +1,47 @@
|
||||
|
||||
#include "archive_favorites.h"
|
||||
#include "archive_files.h"
|
||||
#include "../views/archive_main_view.h"
|
||||
#include "archive_browser.h"
|
||||
|
||||
uint16_t archive_favorites_count(void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
FileWorker* file_worker = file_worker_alloc(true);
|
||||
|
||||
string_t buffer;
|
||||
string_init(buffer);
|
||||
|
||||
bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
|
||||
uint16_t lines = 0;
|
||||
|
||||
if(result) {
|
||||
while(1) {
|
||||
if(!file_worker_read_until(file_worker, buffer, '\n')) {
|
||||
break;
|
||||
}
|
||||
if(!string_size(buffer)) {
|
||||
break;
|
||||
}
|
||||
++lines;
|
||||
}
|
||||
}
|
||||
|
||||
string_clear(buffer);
|
||||
file_worker_close(file_worker);
|
||||
file_worker_free(file_worker);
|
||||
return lines;
|
||||
}
|
||||
|
||||
bool archive_favorites_read(void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
ArchiveMainView* archive_view = context;
|
||||
ArchiveBrowserView* archive_view = context;
|
||||
FileWorker* file_worker = file_worker_alloc(true);
|
||||
|
||||
string_t buffer;
|
||||
FileInfo file_info;
|
||||
string_init(buffer);
|
||||
|
||||
bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_ALWAYS);
|
||||
bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
|
||||
|
||||
if(result) {
|
||||
while(1) {
|
||||
@ -23,7 +52,7 @@ bool archive_favorites_read(void* context) {
|
||||
break;
|
||||
}
|
||||
|
||||
archive_view_add_item(archive_view, &file_info, string_get_cstr(buffer));
|
||||
archive_add_item(archive_view, &file_info, string_get_cstr(buffer));
|
||||
string_clean(buffer);
|
||||
}
|
||||
}
|
||||
@ -33,18 +62,19 @@ bool archive_favorites_read(void* context) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool archive_favorites_delete(const char* file_path, const char* name) {
|
||||
furi_assert(file_path);
|
||||
furi_assert(name);
|
||||
bool archive_favorites_delete(const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
uint8_t len = vsnprintf(NULL, 0, format, args);
|
||||
char filename[len + 1];
|
||||
vsnprintf(filename, len + 1, format, args);
|
||||
va_end(args);
|
||||
|
||||
FileWorker* file_worker = file_worker_alloc(true);
|
||||
|
||||
string_t path;
|
||||
string_t buffer;
|
||||
string_init(buffer);
|
||||
|
||||
string_init_printf(path, "%s/%s", file_path, name);
|
||||
|
||||
bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
|
||||
if(result) {
|
||||
while(1) {
|
||||
@ -55,17 +85,13 @@ bool archive_favorites_delete(const char* file_path, const char* name) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(string_search(buffer, path)) {
|
||||
string_t temp;
|
||||
string_init_printf(temp, "%s\r\n", string_get_cstr(buffer));
|
||||
archive_file_append(ARCHIVE_FAV_TEMP_PATH, temp);
|
||||
string_clear(temp);
|
||||
if(string_search_str(buffer, filename)) {
|
||||
archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\r\n", string_get_cstr(buffer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string_clear(buffer);
|
||||
string_clear(path);
|
||||
|
||||
file_worker_close(file_worker);
|
||||
file_worker_remove(file_worker, ARCHIVE_FAV_PATH);
|
||||
@ -76,19 +102,20 @@ bool archive_favorites_delete(const char* file_path, const char* name) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool archive_is_favorite(const char* file_path, const char* name) {
|
||||
furi_assert(file_path);
|
||||
furi_assert(name);
|
||||
bool archive_is_favorite(const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
uint8_t len = vsnprintf(NULL, 0, format, args);
|
||||
char filename[len + 1];
|
||||
vsnprintf(filename, len + 1, format, args);
|
||||
va_end(args);
|
||||
|
||||
FileWorker* file_worker = file_worker_alloc(true);
|
||||
|
||||
string_t path;
|
||||
string_t buffer;
|
||||
string_init(buffer);
|
||||
bool found = false;
|
||||
|
||||
string_init_printf(path, "%s/%s", file_path, name);
|
||||
bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_ALWAYS);
|
||||
bool found = false;
|
||||
bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
|
||||
|
||||
if(result) {
|
||||
while(1) {
|
||||
@ -98,7 +125,7 @@ bool archive_is_favorite(const char* file_path, const char* name) {
|
||||
if(!string_size(buffer)) {
|
||||
break;
|
||||
}
|
||||
if(!string_search(buffer, path)) {
|
||||
if(!string_search_str(buffer, filename)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@ -106,7 +133,6 @@ bool archive_is_favorite(const char* file_path, const char* name) {
|
||||
}
|
||||
|
||||
string_clear(buffer);
|
||||
string_clear(path);
|
||||
file_worker_close(file_worker);
|
||||
file_worker_free(file_worker);
|
||||
|
||||
@ -122,10 +148,8 @@ bool archive_favorites_rename(const char* file_path, const char* src, const char
|
||||
|
||||
string_t path;
|
||||
string_t buffer;
|
||||
string_t temp;
|
||||
|
||||
string_init(buffer);
|
||||
string_init(temp);
|
||||
string_init(path);
|
||||
|
||||
string_printf(path, "%s/%s", file_path, src);
|
||||
@ -139,14 +163,14 @@ bool archive_favorites_rename(const char* file_path, const char* src, const char
|
||||
if(!string_size(buffer)) {
|
||||
break;
|
||||
}
|
||||
string_printf(
|
||||
temp, "%s\r\n", string_search(buffer, path) ? string_get_cstr(buffer) : dst);
|
||||
archive_file_append(ARCHIVE_FAV_TEMP_PATH, temp);
|
||||
string_clean(temp);
|
||||
|
||||
archive_file_append(
|
||||
ARCHIVE_FAV_TEMP_PATH,
|
||||
"%s\r\n",
|
||||
string_search(buffer, path) ? string_get_cstr(buffer) : dst);
|
||||
}
|
||||
}
|
||||
|
||||
string_clear(temp);
|
||||
string_clear(buffer);
|
||||
string_clear(path);
|
||||
|
||||
@ -159,13 +183,8 @@ bool archive_favorites_rename(const char* file_path, const char* src, const char
|
||||
return result;
|
||||
}
|
||||
|
||||
void archive_add_to_favorites(const char* file_path, const char* name) {
|
||||
void archive_add_to_favorites(const char* file_path) {
|
||||
furi_assert(file_path);
|
||||
furi_assert(name);
|
||||
|
||||
string_t buffer_src;
|
||||
|
||||
string_init_printf(buffer_src, "%s/%s\r\n", file_path, name);
|
||||
archive_file_append(ARCHIVE_FAV_PATH, buffer_src);
|
||||
string_clear(buffer_src);
|
||||
archive_file_append(ARCHIVE_FAV_PATH, "%s\r\n", file_path);
|
||||
}
|
||||
|
||||
@ -4,8 +4,9 @@
|
||||
#define ARCHIVE_FAV_PATH "/any/favorites.txt"
|
||||
#define ARCHIVE_FAV_TEMP_PATH "/any/favorites.tmp"
|
||||
|
||||
uint16_t archive_favorites_count(void* context);
|
||||
bool archive_favorites_read(void* context);
|
||||
bool archive_favorites_delete(const char* file_path, const char* name);
|
||||
bool archive_is_favorite(const char* file_path, const char* name);
|
||||
bool archive_favorites_delete(const char* format, ...);
|
||||
bool archive_is_favorite(const char* format, ...);
|
||||
bool archive_favorites_rename(const char* file_path, const char* src, const char* dst);
|
||||
void archive_add_to_favorites(const char* file_path, const char* name);
|
||||
void archive_add_to_favorites(const char* file_path);
|
||||
@ -1,6 +1,5 @@
|
||||
#include "archive_files.h"
|
||||
#include "archive_favorites.h"
|
||||
#include "../archive_i.h"
|
||||
#include "archive_browser.h"
|
||||
|
||||
bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name) {
|
||||
furi_assert(file_info);
|
||||
@ -20,14 +19,12 @@ bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* n
|
||||
return result;
|
||||
}
|
||||
|
||||
void archive_trim_file_ext(char* name) {
|
||||
size_t str_len = strlen(name);
|
||||
char* end = name + str_len;
|
||||
while(end > name && *end != '.' && *end != '\\' && *end != '/') {
|
||||
--end;
|
||||
}
|
||||
if((end > name && *end == '.') && (*(end - 1) != '\\' && *(end - 1) != '/')) {
|
||||
*end = '\0';
|
||||
void archive_trim_file_path(char* name, bool ext) {
|
||||
char* slash = strrchr(name, '/') + 1;
|
||||
if(strlen(slash)) strlcpy(name, slash, strlen(slash) + 1);
|
||||
if(ext) {
|
||||
char* dot = strrchr(name, '.');
|
||||
if(strlen(dot)) *dot = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,24 +46,24 @@ void set_file_type(ArchiveFile_t* file, FileInfo* file_info) {
|
||||
}
|
||||
}
|
||||
|
||||
bool archive_get_filenames(void* context, uint8_t tab_id, const char* path) {
|
||||
bool archive_get_filenames(void* context, const char* path) {
|
||||
furi_assert(context);
|
||||
|
||||
ArchiveMainView* main_view = context;
|
||||
archive_file_array_clean(main_view);
|
||||
bool res;
|
||||
ArchiveBrowserView* browser = context;
|
||||
archive_file_array_rm_all(browser);
|
||||
|
||||
if(tab_id != ArchiveTabFavorites) {
|
||||
archive_read_dir(main_view, path);
|
||||
if(archive_get_tab(browser) != ArchiveTabFavorites) {
|
||||
res = archive_read_dir(browser, path);
|
||||
} else {
|
||||
archive_favorites_read(main_view);
|
||||
res = archive_favorites_read(browser);
|
||||
}
|
||||
return true;
|
||||
return res;
|
||||
}
|
||||
|
||||
bool archive_read_dir(void* context, const char* path) {
|
||||
bool archive_dir_empty(void* context, const char* path) { // can be simpler?
|
||||
furi_assert(context);
|
||||
|
||||
ArchiveMainView* main_view = context;
|
||||
FileInfo file_info;
|
||||
Storage* fs_api = furi_record_open("storage");
|
||||
File* directory = storage_file_alloc(fs_api);
|
||||
@ -78,17 +75,52 @@ bool archive_read_dir(void* context, const char* path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool files_found = false;
|
||||
while(1) {
|
||||
if(!storage_dir_read(directory, &file_info, name, MAX_NAME_LEN)) {
|
||||
break;
|
||||
}
|
||||
if(files_found) {
|
||||
break;
|
||||
} else if(storage_file_get_error(directory) == FSE_OK) {
|
||||
files_found = name[0];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
storage_dir_close(directory);
|
||||
storage_file_free(directory);
|
||||
|
||||
uint16_t files_cnt = archive_file_array_size(main_view);
|
||||
furi_record_close("storage");
|
||||
|
||||
return files_found;
|
||||
}
|
||||
|
||||
bool archive_read_dir(void* context, const char* path) {
|
||||
furi_assert(context);
|
||||
|
||||
ArchiveBrowserView* browser = context;
|
||||
FileInfo file_info;
|
||||
Storage* fs_api = furi_record_open("storage");
|
||||
File* directory = storage_file_alloc(fs_api);
|
||||
char name[MAX_NAME_LEN];
|
||||
size_t files_cnt = 0;
|
||||
|
||||
if(!storage_dir_open(directory, path)) {
|
||||
storage_dir_close(directory);
|
||||
storage_file_free(directory);
|
||||
return false;
|
||||
}
|
||||
|
||||
while(1) {
|
||||
if(!storage_dir_read(directory, &file_info, name, MAX_NAME_LEN)) {
|
||||
break;
|
||||
}
|
||||
if(files_cnt > MAX_FILES) {
|
||||
break;
|
||||
} else if(storage_file_get_error(directory) == FSE_OK) {
|
||||
archive_view_add_item(main_view, &file_info, name);
|
||||
archive_add_item(browser, &file_info, name);
|
||||
++files_cnt;
|
||||
} else {
|
||||
storage_dir_close(directory);
|
||||
storage_file_free(directory);
|
||||
@ -103,9 +135,15 @@ bool archive_read_dir(void* context, const char* path) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void archive_file_append(const char* path, string_t string) {
|
||||
void archive_file_append(const char* path, const char* format, ...) {
|
||||
furi_assert(path);
|
||||
furi_assert(string);
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
uint8_t len = vsnprintf(NULL, 0, format, args);
|
||||
char cstr_buff[len + 1];
|
||||
vsnprintf(cstr_buff, len + 1, format, args);
|
||||
va_end(args);
|
||||
|
||||
FileWorker* file_worker = file_worker_alloc(false);
|
||||
|
||||
@ -113,7 +151,7 @@ void archive_file_append(const char* path, string_t string) {
|
||||
FURI_LOG_E("Archive", "Append open error");
|
||||
}
|
||||
|
||||
if(!file_worker_write(file_worker, string_get_cstr(string), string_size(string))) {
|
||||
if(!file_worker_write(file_worker, cstr_buff, strlen(cstr_buff))) {
|
||||
FURI_LOG_E("Archive", "Append write error");
|
||||
}
|
||||
|
||||
@ -125,19 +163,22 @@ void archive_delete_file(void* context, string_t path, string_t name) {
|
||||
furi_assert(context);
|
||||
furi_assert(path);
|
||||
furi_assert(name);
|
||||
ArchiveMainView* main_view = context;
|
||||
FileWorker* file_worker = file_worker_alloc(false);
|
||||
ArchiveBrowserView* browser = context;
|
||||
FileWorker* file_worker = file_worker_alloc(true);
|
||||
|
||||
string_t full_path;
|
||||
string_init(full_path);
|
||||
string_printf(full_path, "%s/%s", string_get_cstr(path), string_get_cstr(name));
|
||||
file_worker_remove(file_worker, string_get_cstr(full_path));
|
||||
file_worker_free(file_worker);
|
||||
string_clear(full_path);
|
||||
string_init_printf(full_path, "%s/%s", string_get_cstr(path), string_get_cstr(name));
|
||||
|
||||
if(archive_is_favorite(string_get_cstr(path), string_get_cstr(name))) {
|
||||
archive_favorites_delete(string_get_cstr(path), string_get_cstr(name));
|
||||
bool res = file_worker_remove(file_worker, string_get_cstr(full_path));
|
||||
file_worker_free(file_worker);
|
||||
|
||||
if(archive_is_favorite(string_get_cstr(full_path))) {
|
||||
archive_favorites_delete(string_get_cstr(full_path));
|
||||
}
|
||||
|
||||
archive_file_array_remove_selected(main_view);
|
||||
if(res) {
|
||||
archive_file_array_rm_selected(browser);
|
||||
}
|
||||
|
||||
string_clear(full_path);
|
||||
}
|
||||
|
||||
@ -49,8 +49,9 @@ ARRAY_DEF(
|
||||
|
||||
bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name);
|
||||
void set_file_type(ArchiveFile_t* file, FileInfo* file_info);
|
||||
void archive_trim_file_ext(char* name);
|
||||
bool archive_get_filenames(void* context, uint8_t tab_id, const char* path);
|
||||
void archive_trim_file_path(char* name, bool ext);
|
||||
bool archive_get_filenames(void* context, const char* path);
|
||||
bool archive_dir_empty(void* context, const char* path);
|
||||
bool archive_read_dir(void* context, const char* path);
|
||||
void archive_file_append(const char* path, string_t string);
|
||||
void archive_file_append(const char* path, const char* format, ...);
|
||||
void archive_delete_file(void* context, string_t path, string_t name);
|
||||
@ -1,32 +1,114 @@
|
||||
#include "../archive_i.h"
|
||||
#include "../views/archive_main_view.h"
|
||||
#include "../helpers/archive_files.h"
|
||||
#include "../helpers/archive_favorites.h"
|
||||
#include "../helpers/archive_browser.h"
|
||||
#include "../views/archive_browser_view.h"
|
||||
|
||||
static const char* flipper_app_name[] = {
|
||||
[ArchiveFileTypeIButton] = "iButton",
|
||||
[ArchiveFileTypeNFC] = "NFC",
|
||||
[ArchiveFileTypeSubGhz] = "Sub-GHz",
|
||||
[ArchiveFileTypeLFRFID] = "125 kHz RFID",
|
||||
[ArchiveFileTypeIrda] = "Infrared",
|
||||
};
|
||||
|
||||
static void archive_run_in_app(
|
||||
ArchiveBrowserView* browser,
|
||||
ArchiveFile_t* selected,
|
||||
bool full_path_provided) {
|
||||
Loader* loader = furi_record_open("loader");
|
||||
|
||||
string_t full_path;
|
||||
if(!full_path_provided) {
|
||||
string_init_printf(
|
||||
full_path, "%s/%s", string_get_cstr(browser->path), string_get_cstr(selected->name));
|
||||
} else {
|
||||
string_init_set(full_path, selected->name);
|
||||
}
|
||||
loader_start(loader, flipper_app_name[selected->type], string_get_cstr(full_path));
|
||||
|
||||
string_clear(full_path);
|
||||
furi_record_close("loader");
|
||||
}
|
||||
|
||||
void archive_scene_browser_callback(ArchiveBrowserEvent event, void* context) {
|
||||
ArchiveApp* archive = (ArchiveApp*)context;
|
||||
view_dispatcher_send_custom_event(archive->view_dispatcher, event);
|
||||
}
|
||||
|
||||
const void archive_scene_browser_on_enter(void* context) {
|
||||
void archive_scene_browser_on_enter(void* context) {
|
||||
ArchiveApp* archive = (ArchiveApp*)context;
|
||||
ArchiveMainView* main_view = archive->main_view;
|
||||
ArchiveBrowserView* browser = archive->browser;
|
||||
|
||||
archive_browser_set_callback(main_view, archive_scene_browser_callback, archive);
|
||||
archive_browser_update(main_view);
|
||||
archive_browser_set_callback(browser, archive_scene_browser_callback, archive);
|
||||
archive_update_focus(browser, archive->text_store);
|
||||
view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewBrowser);
|
||||
}
|
||||
|
||||
const bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
|
||||
bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
|
||||
ArchiveApp* archive = (ArchiveApp*)context;
|
||||
bool consumed;
|
||||
ArchiveBrowserView* browser = archive->browser;
|
||||
ArchiveFile_t* selected = archive_get_current_file(browser);
|
||||
|
||||
const char* path = archive_get_path(browser);
|
||||
const char* name = archive_get_name(browser);
|
||||
bool known_app = is_known_app(selected->type);
|
||||
bool favorites = archive_get_tab(browser) == ArchiveTabFavorites;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
switch(event.event) {
|
||||
case ArchiveBrowserEventRename:
|
||||
scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneRename);
|
||||
case ArchiveBrowserEventFileMenuOpen:
|
||||
archive_show_file_menu(browser, true);
|
||||
consumed = true;
|
||||
break;
|
||||
case ArchiveBrowserEventFileMenuClose:
|
||||
archive_show_file_menu(browser, false);
|
||||
consumed = true;
|
||||
break;
|
||||
case ArchiveBrowserEventFileMenuRun:
|
||||
if(known_app) {
|
||||
archive_run_in_app(browser, selected, favorites);
|
||||
}
|
||||
consumed = true;
|
||||
break;
|
||||
case ArchiveBrowserEventFileMenuPin:
|
||||
if(favorites) {
|
||||
archive_favorites_delete(name);
|
||||
archive_file_array_rm_selected(browser);
|
||||
} else if(known_app) {
|
||||
if(archive_is_favorite("%s/%s", path, name)) {
|
||||
archive_favorites_delete("%s/%s", path, name);
|
||||
} else {
|
||||
archive_file_append(ARCHIVE_FAV_PATH, "%s/%s\r\n", path, name);
|
||||
}
|
||||
}
|
||||
archive_show_file_menu(browser, false);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
case ArchiveBrowserEventFileMenuRename:
|
||||
if(known_app && !favorites) {
|
||||
scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneRename);
|
||||
}
|
||||
consumed = true;
|
||||
break;
|
||||
case ArchiveBrowserEventFileMenuDelete:
|
||||
archive_delete_file(browser, browser->path, selected->name);
|
||||
archive_show_file_menu(browser, false);
|
||||
consumed = true;
|
||||
break;
|
||||
case ArchiveBrowserEventEnterDir:
|
||||
archive_enter_dir(browser, selected->name);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
case ArchiveBrowserEventExit:
|
||||
view_dispatcher_stop(archive->view_dispatcher);
|
||||
if(archive_get_depth(browser)) {
|
||||
archive_leave_dir(browser);
|
||||
} else {
|
||||
view_dispatcher_stop(archive->view_dispatcher);
|
||||
}
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
@ -37,6 +119,6 @@ const bool archive_scene_browser_on_event(void* context, SceneManagerEvent event
|
||||
return consumed;
|
||||
}
|
||||
|
||||
const void archive_scene_browser_on_exit(void* context) {
|
||||
void archive_scene_browser_on_exit(void* context) {
|
||||
// ArchiveApp* archive = (ArchiveApp*)context;
|
||||
}
|
||||
|
||||
@ -1,22 +1,24 @@
|
||||
#include "../archive_i.h"
|
||||
#include "../helpers/archive_favorites.h"
|
||||
#include "../helpers/archive_files.h"
|
||||
#include "../helpers/archive_browser.h"
|
||||
|
||||
#define SCENE_RENAME_CUSTOM_EVENT (0UL)
|
||||
#define MAX_TEXT_INPUT_LEN 22
|
||||
|
||||
void archive_scene_rename_text_input_callback(void* context) {
|
||||
ArchiveApp* archive = (ArchiveApp*)context;
|
||||
view_dispatcher_send_custom_event(archive->view_dispatcher, SCENE_RENAME_CUSTOM_EVENT);
|
||||
}
|
||||
|
||||
const void archive_scene_rename_on_enter(void* context) {
|
||||
void archive_scene_rename_on_enter(void* context) {
|
||||
ArchiveApp* archive = (ArchiveApp*)context;
|
||||
|
||||
TextInput* text_input = archive->text_input;
|
||||
ArchiveFile_t* current = archive_get_current_file(archive->main_view);
|
||||
ArchiveFile_t* current = archive_get_current_file(archive->browser);
|
||||
strlcpy(archive->text_store, string_get_cstr(current->name), MAX_NAME_LEN);
|
||||
|
||||
archive_trim_file_ext(archive->text_store);
|
||||
archive_trim_file_path(archive->text_store, true);
|
||||
|
||||
text_input_set_header_text(text_input, "Rename:");
|
||||
|
||||
@ -25,13 +27,13 @@ const void archive_scene_rename_on_enter(void* context) {
|
||||
archive_scene_rename_text_input_callback,
|
||||
archive,
|
||||
archive->text_store,
|
||||
MAX_NAME_LEN,
|
||||
MAX_TEXT_INPUT_LEN,
|
||||
false);
|
||||
|
||||
view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput);
|
||||
}
|
||||
|
||||
const bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) {
|
||||
bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) {
|
||||
ArchiveApp* archive = (ArchiveApp*)context;
|
||||
bool consumed = false;
|
||||
|
||||
@ -42,16 +44,14 @@ const bool archive_scene_rename_on_event(void* context, SceneManagerEvent event)
|
||||
string_t buffer_src;
|
||||
string_t buffer_dst;
|
||||
|
||||
const char* path = archive_get_path(archive->main_view);
|
||||
const char* name = archive_get_name(archive->main_view);
|
||||
const char* path = archive_get_path(archive->browser);
|
||||
const char* name = archive_get_name(archive->browser);
|
||||
|
||||
string_init_printf(buffer_src, "%s/%s", path, name);
|
||||
string_init_printf(buffer_dst, "%s/%s", path, archive->text_store);
|
||||
|
||||
archive_set_name(archive->main_view, archive->text_store);
|
||||
|
||||
// append extension
|
||||
ArchiveFile_t* file = archive_get_current_file(archive->main_view);
|
||||
ArchiveFile_t* file = archive_get_current_file(archive->browser);
|
||||
|
||||
string_cat(buffer_dst, known_ext[file->type]);
|
||||
storage_common_rename(
|
||||
@ -72,9 +72,8 @@ const bool archive_scene_rename_on_event(void* context, SceneManagerEvent event)
|
||||
return consumed;
|
||||
}
|
||||
|
||||
const void archive_scene_rename_on_exit(void* context) {
|
||||
void archive_scene_rename_on_exit(void* context) {
|
||||
ArchiveApp* archive = (ArchiveApp*)context;
|
||||
// Clear view
|
||||
text_input_set_header_text(archive->text_input, NULL);
|
||||
text_input_set_result_callback(archive->text_input, NULL, NULL, NULL, 0, false);
|
||||
text_input_clean(archive->text_input);
|
||||
}
|
||||
|
||||
294
applications/archive/views/archive_browser_view.c
Normal file
294
applications/archive/views/archive_browser_view.c
Normal file
@ -0,0 +1,294 @@
|
||||
#include <furi.h>
|
||||
#include "../archive_i.h"
|
||||
#include "archive_browser_view.h"
|
||||
#include "../helpers/archive_browser.h"
|
||||
|
||||
static const char* ArchiveTabNames[] = {
|
||||
[ArchiveTabFavorites] = "Favorites",
|
||||
[ArchiveTabIButton] = "iButton",
|
||||
[ArchiveTabNFC] = "NFC",
|
||||
[ArchiveTabSubGhz] = "Sub-GHz",
|
||||
[ArchiveTabLFRFID] = "RFID LF",
|
||||
[ArchiveTabIrda] = "Infrared",
|
||||
[ArchiveTabBrowser] = "Browser"};
|
||||
|
||||
static const Icon* ArchiveItemIcons[] = {
|
||||
[ArchiveFileTypeIButton] = &I_ibutt_10px,
|
||||
[ArchiveFileTypeNFC] = &I_Nfc_10px,
|
||||
[ArchiveFileTypeSubGhz] = &I_sub1_10px,
|
||||
[ArchiveFileTypeLFRFID] = &I_125_10px,
|
||||
[ArchiveFileTypeIrda] = &I_ir_10px,
|
||||
[ArchiveFileTypeFolder] = &I_dir_10px,
|
||||
[ArchiveFileTypeUnknown] = &I_unknown_10px,
|
||||
};
|
||||
|
||||
void archive_browser_set_callback(
|
||||
ArchiveBrowserView* browser,
|
||||
ArchiveBrowserViewCallback callback,
|
||||
void* context) {
|
||||
furi_assert(browser);
|
||||
furi_assert(callback);
|
||||
browser->callback = callback;
|
||||
browser->context = context;
|
||||
}
|
||||
|
||||
static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_box(canvas, 71, 17, 57, 46);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
elements_slightly_rounded_frame(canvas, 70, 16, 58, 48);
|
||||
|
||||
string_t menu[MENU_ITEMS];
|
||||
|
||||
string_init_set_str(menu[0], "Run in app");
|
||||
string_init_set_str(menu[1], "Pin");
|
||||
string_init_set_str(menu[2], "Rename");
|
||||
string_init_set_str(menu[3], "Delete");
|
||||
|
||||
ArchiveFile_t* selected = files_array_get(model->files, model->idx);
|
||||
|
||||
if(!is_known_app(selected->type)) {
|
||||
string_set_str(menu[0], "---");
|
||||
string_set_str(menu[1], "---");
|
||||
string_set_str(menu[2], "---");
|
||||
} else if(selected->fav) {
|
||||
string_set_str(menu[1], "Unpin");
|
||||
} else if(model->tab_idx == ArchiveTabFavorites) {
|
||||
string_set_str(menu[1], "Unpin");
|
||||
string_set_str(menu[2], "---");
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < MENU_ITEMS; i++) {
|
||||
canvas_draw_str(canvas, 82, 27 + i * 11, string_get_cstr(menu[i]));
|
||||
string_clear(menu[i]);
|
||||
}
|
||||
|
||||
canvas_draw_icon(canvas, 74, 20 + model->menu_idx * 11, &I_ButtonRight_4x7);
|
||||
}
|
||||
|
||||
static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_box(canvas, 0, 15 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT);
|
||||
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_dot(canvas, 0, 15 + idx * FRAME_HEIGHT);
|
||||
canvas_draw_dot(canvas, 1, 15 + idx * FRAME_HEIGHT);
|
||||
canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 1);
|
||||
|
||||
canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 11);
|
||||
canvas_draw_dot(canvas, scrollbar ? 121 : 126, 15 + idx * FRAME_HEIGHT);
|
||||
canvas_draw_dot(canvas, scrollbar ? 121 : 126, (15 + idx * FRAME_HEIGHT) + 11);
|
||||
}
|
||||
|
||||
static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) {
|
||||
furi_assert(model);
|
||||
|
||||
size_t array_size = files_array_size(model->files);
|
||||
bool scrollbar = array_size > 4;
|
||||
|
||||
for(size_t i = 0; i < MIN(array_size, MENU_ITEMS); ++i) {
|
||||
string_t str_buff;
|
||||
char cstr_buff[MAX_NAME_LEN];
|
||||
|
||||
size_t idx = CLAMP(i + model->list_offset, array_size, 0);
|
||||
ArchiveFile_t* file = files_array_get(model->files, CLAMP(idx, array_size - 1, 0));
|
||||
|
||||
strlcpy(cstr_buff, string_get_cstr(file->name), string_size(file->name) + 1);
|
||||
archive_trim_file_path(cstr_buff, is_known_app(file->type));
|
||||
string_init_set_str(str_buff, cstr_buff);
|
||||
elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX);
|
||||
|
||||
if(model->idx == idx) {
|
||||
archive_draw_frame(canvas, i, scrollbar);
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
|
||||
canvas_draw_icon(canvas, 2, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file->type]);
|
||||
canvas_draw_str(canvas, 15, 24 + i * FRAME_HEIGHT, string_get_cstr(str_buff));
|
||||
string_clear(str_buff);
|
||||
}
|
||||
|
||||
if(scrollbar) {
|
||||
elements_scrollbar_pos(canvas, 126, 15, 49, model->idx, array_size);
|
||||
}
|
||||
|
||||
if(model->menu) {
|
||||
render_item_menu(canvas, model);
|
||||
}
|
||||
}
|
||||
|
||||
static void archive_render_status_bar(Canvas* canvas, ArchiveBrowserViewModel* model) {
|
||||
furi_assert(model);
|
||||
|
||||
const char* tab_name = ArchiveTabNames[model->tab_idx];
|
||||
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Background_128x11);
|
||||
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_box(canvas, 0, 0, 50, 13);
|
||||
canvas_draw_box(canvas, 107, 0, 20, 13);
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_frame(canvas, 1, 0, 50, 12);
|
||||
canvas_draw_line(canvas, 0, 1, 0, 11);
|
||||
canvas_draw_line(canvas, 1, 12, 49, 12);
|
||||
canvas_draw_str_aligned(canvas, 26, 9, AlignCenter, AlignBottom, tab_name);
|
||||
|
||||
canvas_draw_frame(canvas, 108, 0, 20, 12);
|
||||
canvas_draw_line(canvas, 107, 1, 107, 11);
|
||||
canvas_draw_line(canvas, 108, 12, 126, 12);
|
||||
|
||||
canvas_draw_icon(canvas, 112, 2, &I_ButtonLeft_4x7);
|
||||
canvas_draw_icon(canvas, 120, 2, &I_ButtonRight_4x7);
|
||||
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_dot(canvas, 50, 0);
|
||||
canvas_draw_dot(canvas, 127, 0);
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
|
||||
void archive_view_render(Canvas* canvas, void* model) {
|
||||
ArchiveBrowserViewModel* m = model;
|
||||
|
||||
archive_render_status_bar(canvas, model);
|
||||
|
||||
if(files_array_size(m->files)) {
|
||||
draw_list(canvas, m);
|
||||
} else {
|
||||
canvas_draw_str_aligned(
|
||||
canvas, GUI_DISPLAY_WIDTH / 2, 40, AlignCenter, AlignCenter, "Empty");
|
||||
}
|
||||
}
|
||||
|
||||
View* archive_browser_get_view(ArchiveBrowserView* browser) {
|
||||
furi_assert(browser);
|
||||
return browser->view;
|
||||
}
|
||||
|
||||
bool archive_view_input(InputEvent* event, void* context) {
|
||||
furi_assert(event);
|
||||
furi_assert(context);
|
||||
|
||||
ArchiveBrowserView* browser = context;
|
||||
|
||||
bool in_menu;
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
in_menu = model->menu;
|
||||
return false;
|
||||
});
|
||||
|
||||
if(in_menu) {
|
||||
if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyUp || event->key == InputKeyDown) {
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
if(event->key == InputKeyUp) {
|
||||
model->menu_idx = ((model->menu_idx - 1) + MENU_ITEMS) % MENU_ITEMS;
|
||||
} else if(event->key == InputKeyDown) {
|
||||
model->menu_idx = (model->menu_idx + 1) % MENU_ITEMS;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if(event->key == InputKeyOk) {
|
||||
uint8_t idx;
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
idx = model->menu_idx;
|
||||
return false;
|
||||
});
|
||||
browser->callback(file_menu_actions[idx], browser->context);
|
||||
} else if(event->key == InputKeyBack) {
|
||||
browser->callback(ArchiveBrowserEventFileMenuClose, browser->context);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyLeft || event->key == InputKeyRight) {
|
||||
archive_switch_tab(browser, event->key);
|
||||
} else if(event->key == InputKeyBack) {
|
||||
browser->callback(ArchiveBrowserEventExit, browser->context);
|
||||
}
|
||||
}
|
||||
if(event->key == InputKeyUp || event->key == InputKeyDown) {
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
uint16_t num_elements = (uint16_t)files_array_size(model->files);
|
||||
if((event->type == InputTypeShort || event->type == InputTypeRepeat)) {
|
||||
if(event->key == InputKeyUp) {
|
||||
model->idx = ((model->idx - 1) + num_elements) % num_elements;
|
||||
} else if(event->key == InputKeyDown) {
|
||||
model->idx = (model->idx + 1) % num_elements;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
archive_update_offset(browser);
|
||||
}
|
||||
|
||||
if(event->key == InputKeyOk) {
|
||||
ArchiveFile_t* selected = archive_get_current_file(browser);
|
||||
|
||||
if(selected) {
|
||||
bool favorites = archive_get_tab(browser) == ArchiveTabFavorites;
|
||||
bool folder = selected->type == ArchiveFileTypeFolder;
|
||||
|
||||
if(event->type == InputTypeShort) {
|
||||
if(favorites) {
|
||||
browser->callback(ArchiveBrowserEventFileMenuRun, browser->context);
|
||||
} else if(folder) {
|
||||
browser->callback(ArchiveBrowserEventEnterDir, browser->context);
|
||||
} else {
|
||||
browser->callback(ArchiveBrowserEventFileMenuOpen, browser->context);
|
||||
}
|
||||
} else if(event->type == InputTypeLong) {
|
||||
if(folder || favorites) {
|
||||
browser->callback(ArchiveBrowserEventFileMenuOpen, browser->context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ArchiveBrowserView* browser_alloc() {
|
||||
ArchiveBrowserView* browser = furi_alloc(sizeof(ArchiveBrowserView));
|
||||
browser->view = view_alloc();
|
||||
view_allocate_model(browser->view, ViewModelTypeLocking, sizeof(ArchiveBrowserViewModel));
|
||||
view_set_context(browser->view, browser);
|
||||
view_set_draw_callback(browser->view, (ViewDrawCallback)archive_view_render);
|
||||
view_set_input_callback(browser->view, archive_view_input);
|
||||
|
||||
string_init(browser->path);
|
||||
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
files_array_init(model->files);
|
||||
return true;
|
||||
});
|
||||
|
||||
return browser;
|
||||
}
|
||||
|
||||
void browser_free(ArchiveBrowserView* browser) {
|
||||
furi_assert(browser);
|
||||
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
files_array_clear(model->files);
|
||||
return false;
|
||||
});
|
||||
|
||||
string_clear(browser->path);
|
||||
|
||||
view_free(browser->view);
|
||||
free(browser);
|
||||
}
|
||||
89
applications/archive/views/archive_browser_view.h
Normal file
89
applications/archive/views/archive_browser_view.h
Normal file
@ -0,0 +1,89 @@
|
||||
#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>
|
||||
#include "../helpers/archive_files.h"
|
||||
#include "../helpers/archive_favorites.h"
|
||||
|
||||
#define MAX_LEN_PX 110
|
||||
#define MAX_NAME_LEN 255
|
||||
#define FRAME_HEIGHT 12
|
||||
#define MENU_ITEMS 4
|
||||
#define MAX_DEPTH 32
|
||||
|
||||
typedef enum {
|
||||
ArchiveTabFavorites,
|
||||
ArchiveTabLFRFID,
|
||||
ArchiveTabSubGhz,
|
||||
ArchiveTabNFC,
|
||||
ArchiveTabIButton,
|
||||
ArchiveTabIrda,
|
||||
ArchiveTabBrowser,
|
||||
ArchiveTabTotal,
|
||||
} ArchiveTabEnum;
|
||||
|
||||
typedef enum {
|
||||
ArchiveBrowserEventFileMenuOpen,
|
||||
ArchiveBrowserEventFileMenuClose,
|
||||
ArchiveBrowserEventFileMenuRun,
|
||||
ArchiveBrowserEventFileMenuPin,
|
||||
ArchiveBrowserEventFileMenuRename,
|
||||
ArchiveBrowserEventFileMenuDelete,
|
||||
ArchiveBrowserEventEnterDir,
|
||||
ArchiveBrowserEventExit,
|
||||
} ArchiveBrowserEvent;
|
||||
|
||||
static const uint8_t file_menu_actions[MENU_ITEMS] = {
|
||||
[0] = ArchiveBrowserEventFileMenuRun,
|
||||
[1] = ArchiveBrowserEventFileMenuPin,
|
||||
[2] = ArchiveBrowserEventFileMenuRename,
|
||||
[3] = ArchiveBrowserEventFileMenuDelete,
|
||||
};
|
||||
|
||||
typedef struct ArchiveBrowserView ArchiveBrowserView;
|
||||
|
||||
typedef void (*ArchiveBrowserViewCallback)(ArchiveBrowserEvent event, void* context);
|
||||
|
||||
typedef enum {
|
||||
BrowserActionBrowse,
|
||||
BrowserActionItemMenu,
|
||||
BrowserActionTotal,
|
||||
} BrowserActionEnum;
|
||||
|
||||
struct ArchiveBrowserView {
|
||||
View* view;
|
||||
ArchiveBrowserViewCallback callback;
|
||||
void* context;
|
||||
|
||||
string_t path;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
ArchiveTabEnum tab_idx;
|
||||
ArchiveTabEnum last_tab;
|
||||
files_array_t files;
|
||||
|
||||
uint8_t menu_idx;
|
||||
bool menu;
|
||||
|
||||
uint16_t idx;
|
||||
uint16_t last_idx;
|
||||
uint16_t list_offset;
|
||||
uint16_t last_offset;
|
||||
uint8_t depth;
|
||||
|
||||
} ArchiveBrowserViewModel;
|
||||
|
||||
void archive_browser_set_callback(
|
||||
ArchiveBrowserView* browser,
|
||||
ArchiveBrowserViewCallback callback,
|
||||
void* context);
|
||||
|
||||
View* archive_browser_get_view(ArchiveBrowserView* browser);
|
||||
|
||||
ArchiveBrowserView* browser_alloc();
|
||||
void browser_free(ArchiveBrowserView* browser);
|
||||
@ -1,641 +0,0 @@
|
||||
#include <furi.h>
|
||||
#include "../archive_i.h"
|
||||
#include "archive_main_view.h"
|
||||
|
||||
static const char* flipper_app_name[] = {
|
||||
[ArchiveFileTypeIButton] = "iButton",
|
||||
[ArchiveFileTypeNFC] = "NFC",
|
||||
[ArchiveFileTypeSubGhz] = "Sub-GHz",
|
||||
[ArchiveFileTypeLFRFID] = "125 kHz RFID",
|
||||
[ArchiveFileTypeIrda] = "Infrared",
|
||||
};
|
||||
|
||||
static const char* ArchiveTabNames[] = {
|
||||
[ArchiveTabFavorites] = "Favorites",
|
||||
[ArchiveTabIButton] = "iButton",
|
||||
[ArchiveTabNFC] = "NFC",
|
||||
[ArchiveTabSubGhz] = "Sub-GHz",
|
||||
[ArchiveTabLFRFID] = "RFID LF",
|
||||
[ArchiveTabIrda] = "Infrared",
|
||||
[ArchiveTabBrowser] = "Browser"};
|
||||
|
||||
static const Icon* ArchiveItemIcons[] = {
|
||||
[ArchiveFileTypeIButton] = &I_ibutt_10px,
|
||||
[ArchiveFileTypeNFC] = &I_Nfc_10px,
|
||||
[ArchiveFileTypeSubGhz] = &I_sub1_10px,
|
||||
[ArchiveFileTypeLFRFID] = &I_125_10px,
|
||||
[ArchiveFileTypeIrda] = &I_ir_10px,
|
||||
[ArchiveFileTypeFolder] = &I_dir_10px,
|
||||
[ArchiveFileTypeUnknown] = &I_unknown_10px,
|
||||
};
|
||||
|
||||
void archive_browser_set_callback(
|
||||
ArchiveMainView* main_view,
|
||||
ArchiveMainViewCallback callback,
|
||||
void* context) {
|
||||
furi_assert(main_view);
|
||||
furi_assert(callback);
|
||||
main_view->callback = callback;
|
||||
main_view->context = context;
|
||||
}
|
||||
|
||||
void update_offset(ArchiveMainView* main_view) {
|
||||
furi_assert(main_view);
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
size_t array_size = files_array_size(model->files);
|
||||
uint16_t bounds = array_size > 3 ? 2 : array_size;
|
||||
|
||||
if(array_size > 3 && model->idx >= array_size - 1) {
|
||||
model->list_offset = model->idx - 3;
|
||||
} else if(model->list_offset < model->idx - bounds) {
|
||||
model->list_offset = CLAMP(model->idx - 2, array_size - bounds, 0);
|
||||
} else if(model->list_offset > model->idx - bounds) {
|
||||
model->list_offset = CLAMP(model->idx - 1, array_size - bounds, 0);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
size_t archive_file_array_size(ArchiveMainView* main_view) {
|
||||
uint16_t size = 0;
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
size = files_array_size(model->files);
|
||||
return true;
|
||||
});
|
||||
return size;
|
||||
}
|
||||
|
||||
void archive_file_array_remove_selected(ArchiveMainView* main_view) {
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
files_array_remove_v(model->files, model->idx, model->idx + 1);
|
||||
model->idx = CLAMP(model->idx, files_array_size(model->files) - 1, 0);
|
||||
return true;
|
||||
});
|
||||
|
||||
update_offset(main_view);
|
||||
}
|
||||
|
||||
void archive_file_array_clean(ArchiveMainView* main_view) {
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
files_array_clean(model->files);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
ArchiveFile_t* archive_get_current_file(ArchiveMainView* main_view) {
|
||||
ArchiveFile_t* selected;
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
selected = files_array_size(model->files) > 0 ?
|
||||
files_array_get(model->files, model->idx) :
|
||||
NULL;
|
||||
return true;
|
||||
});
|
||||
return selected;
|
||||
}
|
||||
|
||||
ArchiveTabEnum archive_get_tab(ArchiveMainView* main_view) {
|
||||
ArchiveTabEnum tab_id;
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
tab_id = model->tab_idx;
|
||||
return true;
|
||||
});
|
||||
return tab_id;
|
||||
}
|
||||
|
||||
void archive_set_tab(ArchiveMainView* main_view, ArchiveTabEnum tab) {
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
model->tab_idx = tab;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
uint8_t archive_get_depth(ArchiveMainView* main_view) {
|
||||
uint8_t depth;
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
depth = model->depth;
|
||||
return true;
|
||||
});
|
||||
|
||||
return depth;
|
||||
}
|
||||
|
||||
const char* archive_get_path(ArchiveMainView* main_view) {
|
||||
return string_get_cstr(main_view->path);
|
||||
}
|
||||
const char* archive_get_name(ArchiveMainView* main_view) {
|
||||
ArchiveFile_t* selected = archive_get_current_file(main_view);
|
||||
return string_get_cstr(selected->name);
|
||||
}
|
||||
|
||||
void archive_set_name(ArchiveMainView* main_view, const char* name) {
|
||||
furi_assert(main_view);
|
||||
furi_assert(name);
|
||||
|
||||
string_set(main_view->name, name);
|
||||
}
|
||||
|
||||
void archive_browser_update(ArchiveMainView* main_view) {
|
||||
furi_assert(main_view);
|
||||
|
||||
archive_get_filenames(main_view, archive_get_tab(main_view), string_get_cstr(main_view->path));
|
||||
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
uint16_t idx = 0;
|
||||
while(idx < files_array_size(model->files)) {
|
||||
ArchiveFile_t* current = files_array_get(model->files, idx);
|
||||
if(!string_search(current->name, string_get_cstr(main_view->name))) {
|
||||
model->idx = idx;
|
||||
break;
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
update_offset(main_view);
|
||||
}
|
||||
|
||||
void archive_view_add_item(ArchiveMainView* main_view, FileInfo* file_info, const char* name) {
|
||||
furi_assert(main_view);
|
||||
furi_assert(file_info);
|
||||
furi_assert(name);
|
||||
|
||||
ArchiveFile_t item;
|
||||
|
||||
if(filter_by_extension(file_info, get_tab_ext(archive_get_tab(main_view)), name)) {
|
||||
ArchiveFile_t_init(&item);
|
||||
string_init_set_str(item.name, name);
|
||||
set_file_type(&item, file_info);
|
||||
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
files_array_push_back(model->files, item);
|
||||
return true;
|
||||
});
|
||||
|
||||
ArchiveFile_t_clear(&item);
|
||||
}
|
||||
}
|
||||
|
||||
static void render_item_menu(Canvas* canvas, ArchiveMainViewModel* model) {
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_box(canvas, 71, 17, 57, 46);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
elements_slightly_rounded_frame(canvas, 70, 16, 58, 48);
|
||||
|
||||
string_t menu[MENU_ITEMS];
|
||||
|
||||
string_init_set_str(menu[0], "Run in app");
|
||||
string_init_set_str(menu[1], "Pin");
|
||||
string_init_set_str(menu[2], "Rename");
|
||||
string_init_set_str(menu[3], "Delete");
|
||||
|
||||
ArchiveFile_t* selected = files_array_get(model->files, model->idx);
|
||||
|
||||
if(!is_known_app(selected->type)) {
|
||||
string_set_str(menu[0], "---");
|
||||
string_set_str(menu[1], "---");
|
||||
string_set_str(menu[2], "---");
|
||||
} else if(selected->fav) {
|
||||
string_set_str(menu[1], "Unpin");
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < MENU_ITEMS; i++) {
|
||||
canvas_draw_str(canvas, 82, 27 + i * 11, string_get_cstr(menu[i]));
|
||||
string_clear(menu[i]);
|
||||
}
|
||||
|
||||
canvas_draw_icon(canvas, 74, 20 + model->menu_idx * 11, &I_ButtonRight_4x7);
|
||||
}
|
||||
|
||||
static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_box(canvas, 0, 15 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT);
|
||||
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_dot(canvas, 0, 15 + idx * FRAME_HEIGHT);
|
||||
canvas_draw_dot(canvas, 1, 15 + idx * FRAME_HEIGHT);
|
||||
canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 1);
|
||||
|
||||
canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 11);
|
||||
canvas_draw_dot(canvas, scrollbar ? 121 : 126, 15 + idx * FRAME_HEIGHT);
|
||||
canvas_draw_dot(canvas, scrollbar ? 121 : 126, (15 + idx * FRAME_HEIGHT) + 11);
|
||||
}
|
||||
|
||||
static void draw_list(Canvas* canvas, ArchiveMainViewModel* model) {
|
||||
furi_assert(model);
|
||||
|
||||
size_t array_size = files_array_size(model->files);
|
||||
bool scrollbar = array_size > 4;
|
||||
|
||||
for(size_t i = 0; i < MIN(array_size, MENU_ITEMS); ++i) {
|
||||
string_t str_buff;
|
||||
char cstr_buff[MAX_NAME_LEN];
|
||||
|
||||
size_t idx = CLAMP(i + model->list_offset, array_size, 0);
|
||||
ArchiveFile_t* file = files_array_get(model->files, CLAMP(idx, array_size - 1, 0));
|
||||
|
||||
string_init_set(str_buff, file->name);
|
||||
string_right(str_buff, string_search_rchar(str_buff, '/') + 1);
|
||||
strlcpy(cstr_buff, string_get_cstr(str_buff), string_size(str_buff) + 1);
|
||||
|
||||
if(is_known_app(file->type)) archive_trim_file_ext(cstr_buff);
|
||||
|
||||
string_clean(str_buff);
|
||||
string_set_str(str_buff, cstr_buff);
|
||||
|
||||
elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX);
|
||||
|
||||
if(model->idx == idx) {
|
||||
archive_draw_frame(canvas, i, scrollbar);
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
|
||||
canvas_draw_icon(canvas, 2, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file->type]);
|
||||
canvas_draw_str(canvas, 15, 24 + i * FRAME_HEIGHT, string_get_cstr(str_buff));
|
||||
string_clear(str_buff);
|
||||
}
|
||||
|
||||
if(scrollbar) {
|
||||
elements_scrollbar_pos(canvas, 126, 15, 49, model->idx, array_size);
|
||||
}
|
||||
|
||||
if(model->action == BrowserActionItemMenu) {
|
||||
render_item_menu(canvas, model);
|
||||
}
|
||||
}
|
||||
|
||||
static void archive_render_status_bar(Canvas* canvas, ArchiveMainViewModel* model) {
|
||||
furi_assert(model);
|
||||
|
||||
const char* tab_name = ArchiveTabNames[model->tab_idx];
|
||||
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Background_128x11);
|
||||
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_box(canvas, 0, 0, 50, 13);
|
||||
canvas_draw_box(canvas, 107, 0, 20, 13);
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_frame(canvas, 1, 0, 50, 12);
|
||||
canvas_draw_line(canvas, 0, 1, 0, 11);
|
||||
canvas_draw_line(canvas, 1, 12, 49, 12);
|
||||
canvas_draw_str_aligned(canvas, 26, 9, AlignCenter, AlignBottom, tab_name);
|
||||
|
||||
canvas_draw_frame(canvas, 108, 0, 20, 12);
|
||||
canvas_draw_line(canvas, 107, 1, 107, 11);
|
||||
canvas_draw_line(canvas, 108, 12, 126, 12);
|
||||
|
||||
if(model->tab_idx > 0) {
|
||||
canvas_draw_icon(canvas, 112, 2, &I_ButtonLeft_4x7);
|
||||
}
|
||||
if(model->tab_idx < SIZEOF_ARRAY(ArchiveTabNames) - 1) {
|
||||
canvas_draw_icon(canvas, 120, 2, &I_ButtonRight_4x7);
|
||||
}
|
||||
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_dot(canvas, 50, 0);
|
||||
canvas_draw_dot(canvas, 127, 0);
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
|
||||
void archive_view_render(Canvas* canvas, void* model) {
|
||||
ArchiveMainViewModel* m = model;
|
||||
|
||||
archive_render_status_bar(canvas, model);
|
||||
|
||||
if(files_array_size(m->files) > 0) {
|
||||
draw_list(canvas, m);
|
||||
} else {
|
||||
canvas_draw_str_aligned(
|
||||
canvas, GUI_DISPLAY_WIDTH / 2, 40, AlignCenter, AlignCenter, "Empty");
|
||||
}
|
||||
}
|
||||
|
||||
View* archive_main_get_view(ArchiveMainView* main_view) {
|
||||
furi_assert(main_view);
|
||||
return main_view->view;
|
||||
}
|
||||
|
||||
static void archive_show_file_menu(ArchiveMainView* main_view) {
|
||||
furi_assert(main_view);
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
ArchiveFile_t* selected;
|
||||
selected = files_array_get(model->files, model->idx);
|
||||
model->action = BrowserActionItemMenu;
|
||||
model->menu_idx = 0;
|
||||
selected->fav = is_known_app(selected->type) ? archive_is_favorite(
|
||||
string_get_cstr(main_view->path),
|
||||
string_get_cstr(selected->name)) :
|
||||
false;
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void archive_close_file_menu(ArchiveMainView* main_view) {
|
||||
furi_assert(main_view);
|
||||
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
model->action = BrowserActionBrowse;
|
||||
model->menu_idx = 0;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void archive_run_in_app(
|
||||
ArchiveMainView* main_view,
|
||||
ArchiveFile_t* selected,
|
||||
bool full_path_provided) {
|
||||
Loader* loader = furi_record_open("loader");
|
||||
|
||||
string_t full_path;
|
||||
|
||||
if(!full_path_provided) {
|
||||
string_init_printf(
|
||||
full_path, "%s/%s", string_get_cstr(main_view->path), string_get_cstr(selected->name));
|
||||
} else {
|
||||
string_init_set(full_path, selected->name);
|
||||
}
|
||||
loader_start(loader, flipper_app_name[selected->type], string_get_cstr(full_path));
|
||||
|
||||
string_clear(full_path);
|
||||
furi_record_close("loader");
|
||||
}
|
||||
|
||||
static void archive_file_menu_callback(ArchiveMainView* main_view) {
|
||||
furi_assert(main_view);
|
||||
|
||||
ArchiveFile_t* selected = archive_get_current_file(main_view);
|
||||
const char* path = archive_get_path(main_view);
|
||||
const char* name = archive_get_name(main_view);
|
||||
|
||||
uint8_t idx;
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
idx = model->menu_idx;
|
||||
return true;
|
||||
});
|
||||
|
||||
switch(idx) {
|
||||
case 0:
|
||||
if(is_known_app(selected->type)) {
|
||||
archive_run_in_app(main_view, selected, false);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if(is_known_app(selected->type)) {
|
||||
if(!archive_is_favorite(path, name)) {
|
||||
archive_set_name(main_view, string_get_cstr(selected->name));
|
||||
archive_add_to_favorites(path, name);
|
||||
} else {
|
||||
// delete from favorites
|
||||
archive_favorites_delete(path, name);
|
||||
}
|
||||
archive_close_file_menu(main_view);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
// open rename view
|
||||
if(is_known_app(selected->type)) {
|
||||
main_view->callback(ArchiveBrowserEventRename, main_view->context);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
// confirmation?
|
||||
archive_delete_file(main_view, main_view->path, selected->name);
|
||||
archive_close_file_menu(main_view);
|
||||
break;
|
||||
|
||||
default:
|
||||
archive_close_file_menu(main_view);
|
||||
break;
|
||||
}
|
||||
selected = NULL;
|
||||
}
|
||||
|
||||
static void archive_switch_dir(ArchiveMainView* main_view, const char* path) {
|
||||
furi_assert(main_view);
|
||||
furi_assert(path);
|
||||
|
||||
string_set(main_view->path, path);
|
||||
archive_get_filenames(main_view, archive_get_tab(main_view), string_get_cstr(main_view->path));
|
||||
update_offset(main_view);
|
||||
}
|
||||
|
||||
void archive_switch_tab(ArchiveMainView* main_view) {
|
||||
furi_assert(main_view);
|
||||
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
model->idx = 0;
|
||||
model->depth = 0;
|
||||
return true;
|
||||
});
|
||||
|
||||
archive_switch_dir(main_view, tab_default_paths[archive_get_tab(main_view)]);
|
||||
}
|
||||
|
||||
static void archive_enter_dir(ArchiveMainView* main_view, string_t name) {
|
||||
furi_assert(main_view);
|
||||
furi_assert(name);
|
||||
|
||||
// update last index
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
model->last_idx[model->depth] =
|
||||
CLAMP(model->idx, files_array_size(model->files) - 1, 0);
|
||||
model->idx = 0;
|
||||
model->depth = CLAMP(model->depth + 1, MAX_DEPTH, 0);
|
||||
return true;
|
||||
});
|
||||
|
||||
string_cat(main_view->path, "/");
|
||||
string_cat(main_view->path, main_view->name);
|
||||
|
||||
archive_switch_dir(main_view, string_get_cstr(main_view->path));
|
||||
}
|
||||
|
||||
static void archive_leave_dir(ArchiveMainView* main_view) {
|
||||
furi_assert(main_view);
|
||||
|
||||
char* last_char_ptr = strrchr(string_get_cstr(main_view->path), '/');
|
||||
|
||||
if(last_char_ptr) {
|
||||
size_t pos = last_char_ptr - string_get_cstr(main_view->path);
|
||||
string_left(main_view->path, pos);
|
||||
}
|
||||
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
model->depth = CLAMP(model->depth - 1, MAX_DEPTH, 0);
|
||||
model->idx = model->last_idx[model->depth];
|
||||
return true;
|
||||
});
|
||||
|
||||
archive_switch_dir(main_view, string_get_cstr(main_view->path));
|
||||
}
|
||||
|
||||
bool archive_view_input(InputEvent* event, void* context) {
|
||||
furi_assert(event);
|
||||
furi_assert(context);
|
||||
|
||||
ArchiveMainView* main_view = context;
|
||||
|
||||
BrowserActionEnum action;
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
action = model->action;
|
||||
return true;
|
||||
});
|
||||
|
||||
switch(action) {
|
||||
case BrowserActionItemMenu:
|
||||
|
||||
if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyUp || event->key == InputKeyDown) {
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
if(event->key == InputKeyUp) {
|
||||
model->menu_idx = ((model->menu_idx - 1) + MENU_ITEMS) % MENU_ITEMS;
|
||||
} else if(event->key == InputKeyDown) {
|
||||
model->menu_idx = (model->menu_idx + 1) % MENU_ITEMS;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if(event->key == InputKeyOk) {
|
||||
archive_file_menu_callback(main_view);
|
||||
} else if(event->key == InputKeyBack) {
|
||||
archive_close_file_menu(main_view);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case BrowserActionBrowse:
|
||||
|
||||
if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyLeft) {
|
||||
ArchiveTabEnum tab = archive_get_tab(main_view);
|
||||
if(tab) {
|
||||
archive_set_tab(main_view, CLAMP(tab - 1, ArchiveTabTotal, 0));
|
||||
archive_switch_tab(main_view);
|
||||
return true;
|
||||
}
|
||||
} else if(event->key == InputKeyRight) {
|
||||
ArchiveTabEnum tab = archive_get_tab(main_view);
|
||||
|
||||
if(tab < ArchiveTabTotal - 1) {
|
||||
archive_set_tab(main_view, CLAMP(tab + 1, ArchiveTabTotal - 1, 0));
|
||||
archive_switch_tab(main_view);
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if(event->key == InputKeyBack) {
|
||||
if(!archive_get_depth(main_view)) {
|
||||
main_view->callback(ArchiveBrowserEventExit, main_view->context);
|
||||
} else {
|
||||
archive_leave_dir(main_view);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(event->key == InputKeyUp || event->key == InputKeyDown) {
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
uint16_t num_elements = (uint16_t)files_array_size(model->files);
|
||||
if((event->type == InputTypeShort || event->type == InputTypeRepeat)) {
|
||||
if(event->key == InputKeyUp) {
|
||||
model->idx = ((model->idx - 1) + num_elements) % num_elements;
|
||||
} else if(event->key == InputKeyDown) {
|
||||
model->idx = (model->idx + 1) % num_elements;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
update_offset(main_view);
|
||||
}
|
||||
|
||||
if(event->key == InputKeyOk) {
|
||||
ArchiveFile_t* selected = archive_get_current_file(main_view);
|
||||
|
||||
if(selected) {
|
||||
archive_set_name(main_view, string_get_cstr(selected->name));
|
||||
if(selected->type == ArchiveFileTypeFolder) {
|
||||
if(event->type == InputTypeShort) {
|
||||
archive_enter_dir(main_view, main_view->name);
|
||||
} else if(event->type == InputTypeLong) {
|
||||
archive_show_file_menu(main_view);
|
||||
}
|
||||
} else {
|
||||
if(event->type == InputTypeShort) {
|
||||
if(archive_get_tab(main_view) == ArchiveTabFavorites) {
|
||||
if(is_known_app(selected->type)) {
|
||||
archive_run_in_app(main_view, selected, true);
|
||||
}
|
||||
} else {
|
||||
archive_show_file_menu(main_view);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ArchiveMainView* main_view_alloc() {
|
||||
ArchiveMainView* main_view = furi_alloc(sizeof(ArchiveMainView));
|
||||
main_view->view = view_alloc();
|
||||
view_allocate_model(main_view->view, ViewModelTypeLocking, sizeof(ArchiveMainViewModel));
|
||||
view_set_context(main_view->view, main_view);
|
||||
view_set_draw_callback(main_view->view, (ViewDrawCallback)archive_view_render);
|
||||
view_set_input_callback(main_view->view, archive_view_input);
|
||||
|
||||
string_init(main_view->name);
|
||||
string_init(main_view->path);
|
||||
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
files_array_init(model->files);
|
||||
return true;
|
||||
});
|
||||
|
||||
return main_view;
|
||||
}
|
||||
|
||||
void main_view_free(ArchiveMainView* main_view) {
|
||||
furi_assert(main_view);
|
||||
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
files_array_clear(model->files);
|
||||
return false;
|
||||
});
|
||||
|
||||
string_clear(main_view->name);
|
||||
string_clear(main_view->path);
|
||||
|
||||
view_free(main_view->view);
|
||||
free(main_view);
|
||||
}
|
||||
@ -1,117 +0,0 @@
|
||||
#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>
|
||||
#include "../helpers/archive_files.h"
|
||||
#include "../helpers/archive_favorites.h"
|
||||
|
||||
#define MAX_LEN_PX 110
|
||||
#define MAX_NAME_LEN 255
|
||||
#define FRAME_HEIGHT 12
|
||||
#define MENU_ITEMS 4
|
||||
#define MAX_DEPTH 32
|
||||
|
||||
typedef enum {
|
||||
ArchiveTabFavorites,
|
||||
ArchiveTabLFRFID,
|
||||
ArchiveTabSubGhz,
|
||||
ArchiveTabNFC,
|
||||
ArchiveTabIButton,
|
||||
ArchiveTabIrda,
|
||||
ArchiveTabBrowser,
|
||||
ArchiveTabTotal,
|
||||
} ArchiveTabEnum;
|
||||
|
||||
static const char* known_ext[] = {
|
||||
[ArchiveFileTypeIButton] = ".ibtn",
|
||||
[ArchiveFileTypeNFC] = ".nfc",
|
||||
[ArchiveFileTypeSubGhz] = ".sub",
|
||||
[ArchiveFileTypeLFRFID] = ".rfid",
|
||||
[ArchiveFileTypeIrda] = ".ir",
|
||||
};
|
||||
|
||||
static inline const char* get_tab_ext(ArchiveTabEnum tab) {
|
||||
switch(tab) {
|
||||
case ArchiveTabIButton:
|
||||
return known_ext[ArchiveFileTypeIButton];
|
||||
case ArchiveTabNFC:
|
||||
return known_ext[ArchiveFileTypeNFC];
|
||||
case ArchiveTabSubGhz:
|
||||
return known_ext[ArchiveFileTypeSubGhz];
|
||||
case ArchiveTabLFRFID:
|
||||
return known_ext[ArchiveFileTypeLFRFID];
|
||||
case ArchiveTabIrda:
|
||||
return known_ext[ArchiveFileTypeIrda];
|
||||
default:
|
||||
return "*";
|
||||
}
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
ArchiveBrowserEventRename,
|
||||
ArchiveBrowserEventExit,
|
||||
ArchiveBrowserEventLeaveDir,
|
||||
} ArchiveBrowserEvent;
|
||||
|
||||
typedef struct ArchiveMainView ArchiveMainView;
|
||||
|
||||
typedef void (*ArchiveMainViewCallback)(ArchiveBrowserEvent event, void* context);
|
||||
|
||||
typedef enum {
|
||||
BrowserActionBrowse,
|
||||
BrowserActionItemMenu,
|
||||
BrowserActionTotal,
|
||||
} BrowserActionEnum;
|
||||
|
||||
struct ArchiveMainView {
|
||||
View* view;
|
||||
ArchiveMainViewCallback callback;
|
||||
void* context;
|
||||
|
||||
string_t name;
|
||||
string_t path;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
ArchiveTabEnum tab_idx;
|
||||
BrowserActionEnum action;
|
||||
files_array_t files;
|
||||
|
||||
uint8_t depth;
|
||||
uint8_t menu_idx;
|
||||
|
||||
uint16_t idx;
|
||||
uint16_t last_idx[MAX_DEPTH];
|
||||
uint16_t list_offset;
|
||||
|
||||
} ArchiveMainViewModel;
|
||||
|
||||
void archive_browser_set_callback(
|
||||
ArchiveMainView* main_view,
|
||||
ArchiveMainViewCallback callback,
|
||||
void* context);
|
||||
|
||||
View* archive_main_get_view(ArchiveMainView* main_view);
|
||||
|
||||
ArchiveMainView* main_view_alloc();
|
||||
void main_view_free(ArchiveMainView* main_view);
|
||||
|
||||
void archive_file_array_remove_selected(ArchiveMainView* main_view);
|
||||
void archive_file_array_clean(ArchiveMainView* main_view);
|
||||
|
||||
void archive_view_add_item(ArchiveMainView* main_view, FileInfo* file_info, const char* name);
|
||||
void archive_browser_update(ArchiveMainView* main_view);
|
||||
|
||||
size_t archive_file_array_size(ArchiveMainView* main_view);
|
||||
ArchiveFile_t* archive_get_current_file(ArchiveMainView* main_view);
|
||||
const char* archive_get_path(ArchiveMainView* main_view);
|
||||
const char* archive_get_name(ArchiveMainView* main_view);
|
||||
void archive_set_name(ArchiveMainView* main_view, const char* name);
|
||||
|
||||
static inline bool is_known_app(ArchiveFileTypeEnum type) {
|
||||
return (type != ArchiveFileTypeFolder && type != ArchiveFileTypeUnknown);
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
#include "bt_cli.h"
|
||||
#include <furi.h>
|
||||
#include <furi-hal.h>
|
||||
#include "bt_settings.h"
|
||||
|
||||
void bt_cli_init() {
|
||||
Cli* cli = furi_record_open("cli");
|
||||
@ -18,13 +19,16 @@ void bt_cli_command_info(Cli* cli, string_t args, void* context) {
|
||||
string_t buffer;
|
||||
string_init(buffer);
|
||||
furi_hal_bt_dump_state(buffer);
|
||||
printf(string_get_cstr(buffer));
|
||||
printf("%s", string_get_cstr(buffer));
|
||||
string_clear(buffer);
|
||||
}
|
||||
|
||||
void bt_cli_command_carrier_tx(Cli* cli, string_t args, void* context) {
|
||||
uint16_t channel;
|
||||
uint16_t power;
|
||||
BtSettings bt_settings;
|
||||
bt_settings_load(&bt_settings);
|
||||
|
||||
int ret = sscanf(string_get_cstr(args), "%hu %hu", &channel, &power);
|
||||
if(ret != 2) {
|
||||
printf("sscanf returned %d, channel: %hu, power: %hu\r\n", ret, channel, power);
|
||||
@ -39,6 +43,8 @@ void bt_cli_command_carrier_tx(Cli* cli, string_t args, void* context) {
|
||||
printf("Power must be in 0...6 dB range, not %hu\r\n", power);
|
||||
return;
|
||||
}
|
||||
|
||||
furi_hal_bt_stop_advertising();
|
||||
printf("Transmitting carrier at %hu channel at %hu dB power\r\n", channel, power);
|
||||
printf("Press CTRL+C to stop\r\n");
|
||||
furi_hal_bt_start_tone_tx(channel, 0x19 + power);
|
||||
@ -47,10 +53,15 @@ void bt_cli_command_carrier_tx(Cli* cli, string_t args, void* context) {
|
||||
osDelay(250);
|
||||
}
|
||||
furi_hal_bt_stop_tone_tx();
|
||||
if(bt_settings.enabled) {
|
||||
furi_hal_bt_start_advertising();
|
||||
}
|
||||
}
|
||||
|
||||
void bt_cli_command_carrier_rx(Cli* cli, string_t args, void* context) {
|
||||
uint16_t channel;
|
||||
BtSettings bt_settings;
|
||||
bt_settings_load(&bt_settings);
|
||||
int ret = sscanf(string_get_cstr(args), "%hu", &channel);
|
||||
if(ret != 1) {
|
||||
printf("sscanf returned %d, channel: %hu\r\n", ret, channel);
|
||||
@ -61,6 +72,8 @@ void bt_cli_command_carrier_rx(Cli* cli, string_t args, void* context) {
|
||||
printf("Channel number must be in 0...39 range, not %hu\r\n", channel);
|
||||
return;
|
||||
}
|
||||
|
||||
furi_hal_bt_stop_advertising();
|
||||
printf("Receiving carrier at %hu channel\r\n", channel);
|
||||
printf("Press CTRL+C to stop\r\n");
|
||||
|
||||
@ -73,12 +86,17 @@ void bt_cli_command_carrier_rx(Cli* cli, string_t args, void* context) {
|
||||
}
|
||||
|
||||
furi_hal_bt_stop_packet_test();
|
||||
if(bt_settings.enabled) {
|
||||
furi_hal_bt_start_advertising();
|
||||
}
|
||||
}
|
||||
|
||||
void bt_cli_command_packet_tx(Cli* cli, string_t args, void* context) {
|
||||
uint16_t channel;
|
||||
uint16_t pattern;
|
||||
uint16_t datarate;
|
||||
BtSettings bt_settings;
|
||||
bt_settings_load(&bt_settings);
|
||||
int ret = sscanf(string_get_cstr(args), "%hu %hu %hu", &channel, &pattern, &datarate);
|
||||
if(ret != 3) {
|
||||
printf("sscanf returned %d, channel: %hu %hu %hu\r\n", ret, channel, pattern, datarate);
|
||||
@ -105,6 +123,7 @@ void bt_cli_command_packet_tx(Cli* cli, string_t args, void* context) {
|
||||
return;
|
||||
}
|
||||
|
||||
furi_hal_bt_stop_advertising();
|
||||
printf(
|
||||
"Transmitting %hu pattern packet at %hu channel at %hu M datarate\r\n",
|
||||
pattern,
|
||||
@ -118,11 +137,16 @@ void bt_cli_command_packet_tx(Cli* cli, string_t args, void* context) {
|
||||
}
|
||||
furi_hal_bt_stop_packet_test();
|
||||
printf("Transmitted %lu packets", furi_hal_bt_get_transmitted_packets());
|
||||
if(bt_settings.enabled) {
|
||||
furi_hal_bt_start_advertising();
|
||||
}
|
||||
}
|
||||
|
||||
void bt_cli_command_packet_rx(Cli* cli, string_t args, void* context) {
|
||||
uint16_t channel;
|
||||
uint16_t datarate;
|
||||
BtSettings bt_settings;
|
||||
bt_settings_load(&bt_settings);
|
||||
int ret = sscanf(string_get_cstr(args), "%hu %hu", &channel, &datarate);
|
||||
if(ret != 2) {
|
||||
printf("sscanf returned %d, channel: %hu datarate: %hu\r\n", ret, channel, datarate);
|
||||
@ -137,6 +161,8 @@ void bt_cli_command_packet_rx(Cli* cli, string_t args, void* context) {
|
||||
printf("Datarate must be in 1 or 2 Mb, not %hu\r\n", datarate);
|
||||
return;
|
||||
}
|
||||
|
||||
furi_hal_bt_stop_advertising();
|
||||
printf("Receiving packets at %hu channel at %hu M datarate\r\n", channel, datarate);
|
||||
printf("Press CTRL+C to stop\r\n");
|
||||
furi_hal_bt_start_packet_rx(channel, datarate);
|
||||
@ -150,4 +176,7 @@ void bt_cli_command_packet_rx(Cli* cli, string_t args, void* context) {
|
||||
}
|
||||
uint16_t packets_received = furi_hal_bt_stop_packet_test();
|
||||
printf("Received %hu packets", packets_received);
|
||||
if(bt_settings.enabled) {
|
||||
furi_hal_bt_start_advertising();
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,6 +26,17 @@ static void bt_pin_code_show_event_handler(Bt* bt, uint32_t pin) {
|
||||
string_clear(pin_str);
|
||||
}
|
||||
|
||||
static void bt_battery_level_changed_callback(const void* _event, void* context) {
|
||||
furi_assert(_event);
|
||||
furi_assert(context);
|
||||
|
||||
Bt* bt = context;
|
||||
const PowerEvent* event = _event;
|
||||
if(event->type == PowerEventTypeBatteryLevelChanged) {
|
||||
bt_update_battery_level(bt, event->data.battery_level);
|
||||
}
|
||||
}
|
||||
|
||||
Bt* bt_alloc() {
|
||||
Bt* bt = furi_alloc(sizeof(Bt));
|
||||
// Load settings
|
||||
@ -45,6 +56,11 @@ Bt* bt_alloc() {
|
||||
bt->dialogs = furi_record_open("dialogs");
|
||||
bt->dialog_message = dialog_message_alloc();
|
||||
|
||||
// Power
|
||||
bt->power = furi_record_open("power");
|
||||
PubSub* power_pubsub = power_get_pubsub(bt->power);
|
||||
subscribe_pubsub(power_pubsub, bt_battery_level_changed_callback, bt);
|
||||
|
||||
return bt;
|
||||
}
|
||||
|
||||
|
||||
@ -9,7 +9,8 @@
|
||||
#include <gui/view_port.h>
|
||||
#include <gui/view.h>
|
||||
|
||||
#include <applications/dialogs/dialogs.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <power/power_service/power.h>
|
||||
|
||||
#include "../bt_settings.h"
|
||||
|
||||
@ -36,4 +37,5 @@ struct Bt {
|
||||
ViewPort* statusbar_view_port;
|
||||
DialogsApp* dialogs;
|
||||
DialogMessage* dialog_message;
|
||||
Power* power;
|
||||
};
|
||||
|
||||
@ -276,7 +276,7 @@ static void cli_handle_escape(Cli* cli, char c) {
|
||||
string_set(cli->line, cli->last_line);
|
||||
cli->cursor_position = string_size(cli->line);
|
||||
// Show new line to user
|
||||
printf(string_get_cstr(cli->line));
|
||||
printf("%s", string_get_cstr(cli->line));
|
||||
}
|
||||
} else if(c == 'B') {
|
||||
} else if(c == 'C') {
|
||||
|
||||
@ -138,7 +138,7 @@ void cli_command_help(Cli* cli, string_t args, void* context) {
|
||||
}
|
||||
// Right Column
|
||||
if(!CliCommandTree_end_p(it_right)) {
|
||||
printf(string_get_cstr(*CliCommandTree_ref(it_right)->key_ptr));
|
||||
printf("%s", string_get_cstr(*CliCommandTree_ref(it_right)->key_ptr));
|
||||
CliCommandTree_next(it_right);
|
||||
}
|
||||
};
|
||||
@ -146,7 +146,7 @@ void cli_command_help(Cli* cli, string_t args, void* context) {
|
||||
if(string_size(args) > 0) {
|
||||
cli_nl();
|
||||
printf("Also I have no clue what '");
|
||||
printf(string_get_cstr(args));
|
||||
printf("%s", string_get_cstr(args));
|
||||
printf("' is.");
|
||||
}
|
||||
}
|
||||
|
||||
131
applications/desktop/desktop.c
Normal file
131
applications/desktop/desktop.c
Normal file
@ -0,0 +1,131 @@
|
||||
#include "desktop_i.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->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");
|
||||
|
||||
free(desktop);
|
||||
}
|
||||
|
||||
static bool desktop_is_first_start() {
|
||||
Storage* storage = furi_record_open("storage");
|
||||
bool exists = storage_common_stat(storage, "/int/first_start", NULL) == FSE_OK;
|
||||
furi_record_close("storage");
|
||||
|
||||
return exists;
|
||||
}
|
||||
|
||||
int32_t desktop_srv(void* p) {
|
||||
Desktop* desktop = desktop_alloc();
|
||||
|
||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
|
||||
|
||||
if(desktop_is_first_start()) {
|
||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneFirstStart);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
3
applications/desktop/desktop.h
Normal file
3
applications/desktop/desktop.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct Desktop Desktop;
|
||||
59
applications/desktop/desktop_i.h
Normal file
59
applications/desktop/desktop_i.h
Normal file
@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include "desktop.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi-hal.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <assets_icons.h>
|
||||
#include <storage/storage.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 {
|
||||
// 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);
|
||||
49
applications/desktop/desktop_settings/desktop_settings.c
Normal file
49
applications/desktop/desktop_settings/desktop_settings.c
Normal 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;
|
||||
}
|
||||
15
applications/desktop/desktop_settings/desktop_settings.h
Normal file
15
applications/desktop/desktop_settings/desktop_settings.h
Normal 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);
|
||||
65
applications/desktop/desktop_settings/desktop_settings_app.c
Normal file
65
applications/desktop/desktop_settings/desktop_settings_app.c
Normal 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;
|
||||
}
|
||||
25
applications/desktop/desktop_settings/desktop_settings_app.h
Normal file
25
applications/desktop/desktop_settings/desktop_settings_app.h
Normal 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;
|
||||
@ -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,
|
||||
};
|
||||
@ -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
|
||||
@ -0,0 +1,2 @@
|
||||
ADD_SCENE(desktop_settings, start, Start)
|
||||
ADD_SCENE(desktop_settings, favorite, Favorite)
|
||||
@ -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);
|
||||
}
|
||||
53
applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c
Executable file
53
applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c
Executable 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);
|
||||
}
|
||||
30
applications/desktop/scenes/desktop_scene.c
Normal file
30
applications/desktop/scenes/desktop_scene.c
Normal 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,
|
||||
};
|
||||
29
applications/desktop/scenes/desktop_scene.h
Normal file
29
applications/desktop/scenes/desktop_scene.h
Normal 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
|
||||
6
applications/desktop/scenes/desktop_scene_config.h
Normal file
6
applications/desktop/scenes/desktop_scene_config.h
Normal 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)
|
||||
62
applications/desktop/scenes/desktop_scene_debug.c
Normal file
62
applications/desktop/scenes/desktop_scene_debug.c
Normal file
@ -0,0 +1,62 @@
|
||||
#include "../desktop_i.h"
|
||||
#include "../views/desktop_debug.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
#include <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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void desktop_scene_debug_on_exit(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
desktop_debug_reset_screen_idx(desktop->debug_view);
|
||||
}
|
||||
42
applications/desktop/scenes/desktop_scene_first_start.c
Normal file
42
applications/desktop/scenes/desktop_scene_first_start.c
Normal file
@ -0,0 +1,42 @@
|
||||
#include "../desktop_i.h"
|
||||
#include "../views/desktop_first_start.h"
|
||||
|
||||
void desktop_scene_first_start_callback(DesktopFirstStartEvent event, void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, event);
|
||||
}
|
||||
|
||||
void desktop_scene_first_start_on_enter(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
DesktopFirstStartView* first_start_view = desktop->first_start_view;
|
||||
|
||||
desktop_first_start_set_callback(
|
||||
first_start_view, desktop_scene_first_start_callback, desktop);
|
||||
|
||||
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewFirstStart);
|
||||
}
|
||||
|
||||
bool desktop_scene_first_start_on_event(void* context, SceneManagerEvent event) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
bool consumed = false;
|
||||
Storage* storage = NULL;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
switch(event.event) {
|
||||
case DesktopFirstStartCompleted:
|
||||
storage = furi_record_open("storage");
|
||||
storage_common_remove(storage, "/int/first_start");
|
||||
furi_record_close("storage");
|
||||
scene_manager_previous_scene(desktop->scene_manager);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void desktop_scene_first_start_on_exit(void* context) {
|
||||
}
|
||||
37
applications/desktop/scenes/desktop_scene_hw_mismatch.c
Normal file
37
applications/desktop/scenes/desktop_scene_hw_mismatch.c
Normal 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void desktop_scene_hw_mismatch_on_exit(void* context) {
|
||||
// Desktop* desktop = (Desktop*)context;
|
||||
}
|
||||
42
applications/desktop/scenes/desktop_scene_lock_menu.c
Normal file
42
applications/desktop/scenes/desktop_scene_lock_menu.c
Normal 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void desktop_scene_lock_menu_on_exit(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
desktop_lock_menu_reset_idx(desktop->lock_menu);
|
||||
}
|
||||
51
applications/desktop/scenes/desktop_scene_locked.c
Normal file
51
applications/desktop/scenes/desktop_scene_locked.c
Normal 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
88
applications/desktop/scenes/desktop_scene_main.c
Normal file
88
applications/desktop/scenes/desktop_scene_main.c
Normal file
@ -0,0 +1,88 @@
|
||||
#include "../desktop_i.h"
|
||||
#include "../views/desktop_main.h"
|
||||
#include "applications.h"
|
||||
#include <loader/loader.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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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:
|
||||
loader_show_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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
166
applications/desktop/views/desktop_debug.c
Normal file
166
applications/desktop/views/desktop_debug.c
Normal 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;
|
||||
});
|
||||
}
|
||||
52
applications/desktop/views/desktop_debug.h
Normal file
52
applications/desktop/views/desktop_debug.h
Normal 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);
|
||||
107
applications/desktop/views/desktop_first_start.c
Normal file
107
applications/desktop/views/desktop_first_start.c
Normal 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);
|
||||
}
|
||||
35
applications/desktop/views/desktop_first_start.h
Normal file
35
applications/desktop/views/desktop_first_start.h
Normal 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);
|
||||
66
applications/desktop/views/desktop_hw_mismatch.c
Normal file
66
applications/desktop/views/desktop_hw_mismatch.c
Normal 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);
|
||||
}
|
||||
38
applications/desktop/views/desktop_hw_mismatch.h
Normal file
38
applications/desktop/views/desktop_hw_mismatch.h
Normal 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);
|
||||
111
applications/desktop/views/desktop_lock_menu.c
Normal file
111
applications/desktop/views/desktop_lock_menu.c
Normal 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);
|
||||
}
|
||||
39
applications/desktop/views/desktop_lock_menu.h
Normal file
39
applications/desktop/views/desktop_lock_menu.h
Normal 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);
|
||||
171
applications/desktop/views/desktop_locked.c
Normal file
171
applications/desktop/views/desktop_locked.c
Normal 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);
|
||||
}
|
||||
55
applications/desktop/views/desktop_locked.h
Normal file
55
applications/desktop/views/desktop_locked.h
Normal 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);
|
||||
116
applications/desktop/views/desktop_main.c
Normal file
116
applications/desktop/views/desktop_main.c
Normal 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;
|
||||
});
|
||||
}
|
||||
43
applications/desktop/views/desktop_main.h
Normal file
43
applications/desktop/views/desktop_main.h
Normal 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);
|
||||
@ -1,8 +1,18 @@
|
||||
#pragma once
|
||||
#define API_LOCK_INIT_LOCKED() osSemaphoreNew(1, 0, NULL);
|
||||
|
||||
typedef osEventFlagsId_t FuriApiLock;
|
||||
|
||||
#define API_LOCK_EVENT (1U << 0)
|
||||
|
||||
#define API_LOCK_INIT_LOCKED() osEventFlagsNew(NULL);
|
||||
|
||||
#define API_LOCK_WAIT_UNTIL_UNLOCK(_lock) \
|
||||
osEventFlagsWait(_lock, API_LOCK_EVENT, osFlagsWaitAny, osWaitForever);
|
||||
|
||||
#define API_LOCK_FREE(_lock) osEventFlagsDelete(_lock);
|
||||
|
||||
#define API_LOCK_UNLOCK(_lock) osEventFlagsSet(_lock, API_LOCK_EVENT);
|
||||
|
||||
#define API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(_lock) \
|
||||
osSemaphoreAcquire(_lock, osWaitForever); \
|
||||
osSemaphoreDelete(_lock);
|
||||
|
||||
#define API_LOCK_UNLOCK(_lock) osSemaphoreRelease(_lock);
|
||||
API_LOCK_WAIT_UNTIL_UNLOCK(_lock); \
|
||||
API_LOCK_FREE(_lock);
|
||||
|
||||
@ -10,8 +10,8 @@ bool dialog_file_select_show(
|
||||
char* result,
|
||||
uint8_t result_size,
|
||||
const char* preselected_filename) {
|
||||
osSemaphoreId_t semaphore = API_LOCK_INIT_LOCKED();
|
||||
furi_check(semaphore != NULL);
|
||||
FuriApiLock lock = API_LOCK_INIT_LOCKED();
|
||||
furi_check(lock != NULL);
|
||||
|
||||
DialogsAppData data = {
|
||||
.file_select = {
|
||||
@ -24,14 +24,14 @@ bool dialog_file_select_show(
|
||||
|
||||
DialogsAppReturn return_data;
|
||||
DialogsAppMessage message = {
|
||||
.semaphore = semaphore,
|
||||
.lock = lock,
|
||||
.command = DialogsAppCommandFileOpen,
|
||||
.data = &data,
|
||||
.return_data = &return_data,
|
||||
};
|
||||
|
||||
furi_check(osMessageQueuePut(context->message_queue, &message, 0, osWaitForever) == osOK);
|
||||
API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(semaphore);
|
||||
API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(lock);
|
||||
|
||||
return return_data.bool_value;
|
||||
}
|
||||
@ -39,8 +39,8 @@ bool dialog_file_select_show(
|
||||
/****************** Message ******************/
|
||||
|
||||
DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage* dialog_message) {
|
||||
osSemaphoreId_t semaphore = API_LOCK_INIT_LOCKED();
|
||||
furi_check(semaphore != NULL);
|
||||
FuriApiLock lock = API_LOCK_INIT_LOCKED();
|
||||
furi_check(lock != NULL);
|
||||
|
||||
DialogsAppData data = {
|
||||
.dialog = {
|
||||
@ -49,14 +49,14 @@ DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage
|
||||
|
||||
DialogsAppReturn return_data;
|
||||
DialogsAppMessage message = {
|
||||
.semaphore = semaphore,
|
||||
.lock = lock,
|
||||
.command = DialogsAppCommandDialog,
|
||||
.data = &data,
|
||||
.return_data = &return_data,
|
||||
};
|
||||
|
||||
furi_check(osMessageQueuePut(context->message_queue, &message, 0, osWaitForever) == osOK);
|
||||
API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(semaphore);
|
||||
API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(lock);
|
||||
|
||||
return return_data.dialog_value;
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include <furi.h>
|
||||
#include "dialogs-i.h"
|
||||
#include "dialogs-api-lock.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -34,7 +35,7 @@ typedef enum {
|
||||
} DialogsAppCommand;
|
||||
|
||||
typedef struct {
|
||||
osSemaphoreId_t semaphore;
|
||||
FuriApiLock lock;
|
||||
DialogsAppCommand command;
|
||||
DialogsAppData* data;
|
||||
DialogsAppReturn* return_data;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
#include <gui/modules/file_select.h>
|
||||
|
||||
typedef struct {
|
||||
osSemaphoreId_t semaphore;
|
||||
FuriApiLock lock;
|
||||
bool result;
|
||||
} DialogsAppFileSelectContext;
|
||||
|
||||
@ -11,14 +11,14 @@ static void dialogs_app_file_select_back_callback(void* context) {
|
||||
furi_assert(context);
|
||||
DialogsAppFileSelectContext* file_select_context = context;
|
||||
file_select_context->result = false;
|
||||
API_LOCK_UNLOCK(file_select_context->semaphore);
|
||||
API_LOCK_UNLOCK(file_select_context->lock);
|
||||
}
|
||||
|
||||
static void dialogs_app_file_select_callback(bool result, void* context) {
|
||||
furi_assert(context);
|
||||
DialogsAppFileSelectContext* file_select_context = context;
|
||||
file_select_context->result = result;
|
||||
API_LOCK_UNLOCK(file_select_context->semaphore);
|
||||
API_LOCK_UNLOCK(file_select_context->lock);
|
||||
}
|
||||
|
||||
bool dialogs_app_process_module_file_select(const DialogsAppMessageDataFileSelect* data) {
|
||||
@ -27,7 +27,7 @@ bool dialogs_app_process_module_file_select(const DialogsAppMessageDataFileSelec
|
||||
|
||||
DialogsAppFileSelectContext* file_select_context =
|
||||
furi_alloc(sizeof(DialogsAppFileSelectContext));
|
||||
file_select_context->semaphore = API_LOCK_INIT_LOCKED();
|
||||
file_select_context->lock = API_LOCK_INIT_LOCKED();
|
||||
|
||||
ViewHolder* view_holder = view_holder_alloc();
|
||||
view_holder_attach_to_gui(view_holder, gui);
|
||||
@ -45,14 +45,15 @@ bool dialogs_app_process_module_file_select(const DialogsAppMessageDataFileSelec
|
||||
|
||||
view_holder_set_view(view_holder, file_select_get_view(file_select));
|
||||
view_holder_start(view_holder);
|
||||
API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(file_select_context->semaphore);
|
||||
API_LOCK_WAIT_UNTIL_UNLOCK(file_select_context->lock);
|
||||
|
||||
ret = file_select_context->result;
|
||||
|
||||
free(file_select_context);
|
||||
view_holder_stop(view_holder);
|
||||
view_holder_free(view_holder);
|
||||
file_select_free(file_select);
|
||||
API_LOCK_FREE(file_select_context->lock);
|
||||
free(file_select_context);
|
||||
furi_record_close("gui");
|
||||
|
||||
return ret;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
|
||||
typedef struct {
|
||||
osSemaphoreId_t semaphore;
|
||||
FuriApiLock lock;
|
||||
DialogMessageButton result;
|
||||
} DialogsAppMessageContext;
|
||||
|
||||
@ -30,7 +30,7 @@ static void dialogs_app_message_back_callback(void* context) {
|
||||
furi_assert(context);
|
||||
DialogsAppMessageContext* message_context = context;
|
||||
message_context->result = DialogMessageButtonBack;
|
||||
API_LOCK_UNLOCK(message_context->semaphore);
|
||||
API_LOCK_UNLOCK(message_context->lock);
|
||||
}
|
||||
|
||||
static void dialogs_app_message_callback(DialogExResult result, void* context) {
|
||||
@ -47,7 +47,7 @@ static void dialogs_app_message_callback(DialogExResult result, void* context) {
|
||||
message_context->result = DialogMessageButtonCenter;
|
||||
break;
|
||||
}
|
||||
API_LOCK_UNLOCK(message_context->semaphore);
|
||||
API_LOCK_UNLOCK(message_context->lock);
|
||||
}
|
||||
|
||||
DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDataDialog* data) {
|
||||
@ -55,7 +55,7 @@ DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDa
|
||||
Gui* gui = furi_record_open("gui");
|
||||
const DialogMessage* message = data->message;
|
||||
DialogsAppMessageContext* message_context = furi_alloc(sizeof(DialogsAppMessageContext));
|
||||
message_context->semaphore = API_LOCK_INIT_LOCKED();
|
||||
message_context->lock = API_LOCK_INIT_LOCKED();
|
||||
|
||||
ViewHolder* view_holder = view_holder_alloc();
|
||||
view_holder_attach_to_gui(view_holder, gui);
|
||||
@ -85,14 +85,15 @@ DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDa
|
||||
|
||||
view_holder_set_view(view_holder, dialog_ex_get_view(dialog_ex));
|
||||
view_holder_start(view_holder);
|
||||
API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(message_context->semaphore);
|
||||
API_LOCK_WAIT_UNTIL_UNLOCK(message_context->lock);
|
||||
|
||||
ret = message_context->result;
|
||||
|
||||
free(message_context);
|
||||
view_holder_stop(view_holder);
|
||||
view_holder_free(view_holder);
|
||||
dialog_ex_free(dialog_ex);
|
||||
API_LOCK_FREE(message_context->lock);
|
||||
free(message_context);
|
||||
furi_record_close("gui");
|
||||
|
||||
return ret;
|
||||
|
||||
@ -21,7 +21,7 @@ static void dialogs_app_process_message(DialogsApp* app, DialogsAppMessage* mess
|
||||
dialogs_app_process_module_message(&message->data->dialog);
|
||||
break;
|
||||
}
|
||||
API_LOCK_UNLOCK(message->semaphore);
|
||||
API_LOCK_UNLOCK(message->lock);
|
||||
}
|
||||
|
||||
int32_t dialogs_srv(void* p) {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
@ -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;
|
||||
@ -18,6 +18,7 @@ static const GpioItem GPIO_PINS[] = {
|
||||
{"1.7: PC3", &gpio_ext_pc3},
|
||||
{"2.7: PC1", &gpio_ext_pc1},
|
||||
{"2.8: PC0", &gpio_ext_pc0},
|
||||
{"*.*: ALL", NULL},
|
||||
};
|
||||
|
||||
static const size_t GPIO_PINS_COUNT = sizeof(GPIO_PINS) / sizeof(GPIO_PINS[0]);
|
||||
@ -49,8 +50,20 @@ static void gpio_test_input_callback(InputEvent* input_event, void* ctx) {
|
||||
|
||||
static void gpio_test_configure_pins(GpioMode mode) {
|
||||
for(size_t i = 0; i < GPIO_PINS_COUNT; i++) {
|
||||
if(!GPIO_PINS[i].pin) continue;
|
||||
hal_gpio_write(GPIO_PINS[i].pin, false);
|
||||
hal_gpio_init(GPIO_PINS[i].pin, mode, GpioPullNo, GpioSpeedLow);
|
||||
hal_gpio_init(GPIO_PINS[i].pin, mode, GpioPullNo, GpioSpeedVeryHigh);
|
||||
}
|
||||
}
|
||||
|
||||
static void gpio_test_set_pin(uint8_t index, bool level) {
|
||||
if(GPIO_PINS[index].pin) {
|
||||
hal_gpio_write(GPIO_PINS[index].pin, level);
|
||||
} else {
|
||||
for(size_t i = 0; i < GPIO_PINS_COUNT; i++) {
|
||||
if(!GPIO_PINS[i].pin) continue;
|
||||
hal_gpio_write(GPIO_PINS[i].pin, level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,10 +130,10 @@ int32_t gpio_test_app(void* p) {
|
||||
} else {
|
||||
if(event.key == InputKeyOk) {
|
||||
if(event.type == InputTypePress) {
|
||||
hal_gpio_write(GPIO_PINS[gpio_test->gpio_index].pin, true);
|
||||
gpio_test_set_pin(gpio_test->gpio_index, true);
|
||||
notification_message(gpio_test->notification, &sequence_set_green_255);
|
||||
} else if(event.type == InputTypeRelease) {
|
||||
hal_gpio_write(GPIO_PINS[gpio_test->gpio_index].pin, false);
|
||||
gpio_test_set_pin(gpio_test->gpio_index, false);
|
||||
notification_message(gpio_test->notification, &sequence_reset_green);
|
||||
}
|
||||
}
|
||||
|
||||
@ -327,6 +327,17 @@ void elements_slightly_rounded_box(
|
||||
canvas_draw_rbox(canvas, x, y, width, height, 1);
|
||||
}
|
||||
|
||||
void elements_bubble(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height) {
|
||||
furi_assert(canvas);
|
||||
canvas_draw_rframe(canvas, x + 4, y, width, height, 3);
|
||||
uint8_t y_corner = y + height * 2 / 3;
|
||||
canvas_draw_line(canvas, x, y_corner, x + 4, y_corner - 4);
|
||||
canvas_draw_line(canvas, x, y_corner, x + 4, y_corner + 4);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_line(canvas, x + 4, y_corner - 3, x + 4, y_corner + 3);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
|
||||
void elements_string_fit_width(Canvas* canvas, string_t string, uint8_t width) {
|
||||
furi_assert(canvas);
|
||||
furi_assert(string);
|
||||
|
||||
@ -8,8 +8,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Draw progress bar.
|
||||
/** Draw progress bar.
|
||||
* @param x - progress bar position on X axis
|
||||
* @param y - progress bar position on Y axis
|
||||
* @param width - progress bar width
|
||||
@ -24,8 +23,7 @@ void elements_progress_bar(
|
||||
uint8_t progress,
|
||||
uint8_t total);
|
||||
|
||||
/*
|
||||
* Draw scrollbar on canvas at specific position.
|
||||
/** Draw scrollbar on canvas at specific position.
|
||||
* @param x - scrollbar position on X axis
|
||||
* @param y - scrollbar position on Y axis
|
||||
* @param height - scrollbar height
|
||||
@ -40,41 +38,35 @@ void elements_scrollbar_pos(
|
||||
uint16_t pos,
|
||||
uint16_t total);
|
||||
|
||||
/*
|
||||
* Draw scrollbar on canvas.
|
||||
/** Draw scrollbar on canvas.
|
||||
* width 3px, height equal to canvas height
|
||||
* @param pos - current element of total elements
|
||||
* @param total - total elements
|
||||
*/
|
||||
void elements_scrollbar(Canvas* canvas, uint16_t pos, uint16_t total);
|
||||
|
||||
/*
|
||||
* Draw rounded frame
|
||||
/** Draw rounded frame
|
||||
* @param x, y - top left corner coordinates
|
||||
* @param width, height - frame width and height
|
||||
*/
|
||||
void elements_frame(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height);
|
||||
|
||||
/*
|
||||
* Draw button in left corner
|
||||
/** Draw button in left corner
|
||||
* @param str - button text
|
||||
*/
|
||||
void elements_button_left(Canvas* canvas, const char* str);
|
||||
|
||||
/*
|
||||
* Draw button in right corner
|
||||
/** Draw button in right corner
|
||||
* @param str - button text
|
||||
*/
|
||||
void elements_button_right(Canvas* canvas, const char* str);
|
||||
|
||||
/*
|
||||
* Draw button in center
|
||||
/** Draw button in center
|
||||
* @param str - button text
|
||||
*/
|
||||
void elements_button_center(Canvas* canvas, const char* str);
|
||||
|
||||
/*
|
||||
* Draw aligned multiline text
|
||||
/** Draw aligned multiline text
|
||||
* @param x, y - coordinates based on align param
|
||||
* @param horizontal, vertical - aligment of multiline text
|
||||
* @param text - string (possible multiline)
|
||||
@ -87,22 +79,19 @@ void elements_multiline_text_aligned(
|
||||
Align vertical,
|
||||
const char* text);
|
||||
|
||||
/*
|
||||
* Draw multiline text
|
||||
/** Draw multiline text
|
||||
* @param x, y - top left corner coordinates
|
||||
* @param text - string (possible multiline)
|
||||
*/
|
||||
void elements_multiline_text(Canvas* canvas, uint8_t x, uint8_t y, const char* text);
|
||||
|
||||
/*
|
||||
* Draw framed multiline text
|
||||
/** Draw framed multiline text
|
||||
* @param x, y - top left corner coordinates
|
||||
* @param text - string (possible multiline)
|
||||
*/
|
||||
void elements_multiline_text_framed(Canvas* canvas, uint8_t x, uint8_t y, const char* text);
|
||||
|
||||
/*
|
||||
* Draw slightly rounded frame
|
||||
/** Draw slightly rounded frame
|
||||
* @param x, y - top left corner coordinates
|
||||
* @param width, height - size of frame
|
||||
*/
|
||||
@ -113,8 +102,7 @@ void elements_slightly_rounded_frame(
|
||||
uint8_t width,
|
||||
uint8_t height);
|
||||
|
||||
/*
|
||||
* Draw slightly rounded box
|
||||
/** Draw slightly rounded box
|
||||
* @param x, y - top left corner coordinates
|
||||
* @param width, height - size of box
|
||||
*/
|
||||
@ -125,8 +113,15 @@ void elements_slightly_rounded_box(
|
||||
uint8_t width,
|
||||
uint8_t height);
|
||||
|
||||
/*
|
||||
* Trim string buffer to fit width in pixels
|
||||
/** Draw bubble frame for text
|
||||
* @param x - left x coordinates
|
||||
* @param y - top y coordinate
|
||||
* @param width - bubble width
|
||||
* @param height - bubble height
|
||||
*/
|
||||
void elements_bubble(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height);
|
||||
|
||||
/** Trim string buffer to fit width in pixels
|
||||
* @param string - string to trim
|
||||
* @param width - max width
|
||||
*/
|
||||
|
||||
34
applications/gui/modules/empty_screen.c
Normal file
34
applications/gui/modules/empty_screen.c
Normal file
@ -0,0 +1,34 @@
|
||||
#include "empty_screen.h"
|
||||
#include <furi.h>
|
||||
|
||||
struct EmptyScreen {
|
||||
View* view;
|
||||
};
|
||||
|
||||
static void empty_screen_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
canvas_clear(canvas);
|
||||
}
|
||||
|
||||
static bool empty_screen_view_input_callback(InputEvent* event, void* context) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EmptyScreen* empty_screen_alloc() {
|
||||
EmptyScreen* empty_screen = furi_alloc(sizeof(EmptyScreen));
|
||||
empty_screen->view = view_alloc();
|
||||
view_set_context(empty_screen->view, empty_screen);
|
||||
view_set_draw_callback(empty_screen->view, empty_screen_view_draw_callback);
|
||||
view_set_input_callback(empty_screen->view, empty_screen_view_input_callback);
|
||||
return empty_screen;
|
||||
}
|
||||
|
||||
void empty_screen_free(EmptyScreen* empty_screen) {
|
||||
furi_assert(empty_screen);
|
||||
view_free(empty_screen->view);
|
||||
free(empty_screen);
|
||||
}
|
||||
|
||||
View* empty_screen_get_view(EmptyScreen* empty_screen) {
|
||||
furi_assert(empty_screen);
|
||||
return empty_screen->view;
|
||||
}
|
||||
29
applications/gui/modules/empty_screen.h
Normal file
29
applications/gui/modules/empty_screen.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include <gui/view.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Empty screen anonymous structure */
|
||||
typedef struct EmptyScreen EmptyScreen;
|
||||
|
||||
/* Allocate and initialize empty screen
|
||||
* This empty screen used to ask simple questions like Yes/
|
||||
*/
|
||||
EmptyScreen* empty_screen_alloc();
|
||||
|
||||
/* Deinitialize and free empty screen
|
||||
* @param empty_screen - Empty screen instance
|
||||
*/
|
||||
void empty_screen_free(EmptyScreen* empty_screen);
|
||||
|
||||
/* Get empty screen view
|
||||
* @param empty_screen - Empty screen instance
|
||||
* @return View instance that can be used for embedding
|
||||
*/
|
||||
View* empty_screen_get_view(EmptyScreen* empty_screen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,7 +1,6 @@
|
||||
#include "file_select.h"
|
||||
#include <gui/elements.h>
|
||||
#include <m-string.h>
|
||||
#include <sys/param.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
#define FILENAME_COUNT 4
|
||||
|
||||
211
applications/gui/modules/menu.c
Executable file
211
applications/gui/modules/menu.c
Executable file
@ -0,0 +1,211 @@
|
||||
#include "menu.h"
|
||||
|
||||
#include <m-array.h>
|
||||
#include <gui/elements.h>
|
||||
#include <furi.h>
|
||||
|
||||
struct Menu {
|
||||
View* view;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const char* label;
|
||||
IconAnimation* icon;
|
||||
uint32_t index;
|
||||
MenuItemCallback callback;
|
||||
void* callback_context;
|
||||
} MenuItem;
|
||||
|
||||
ARRAY_DEF(MenuItemArray, MenuItem, M_POD_OPLIST);
|
||||
|
||||
typedef struct {
|
||||
MenuItemArray_t items;
|
||||
uint8_t position;
|
||||
} MenuModel;
|
||||
|
||||
static void menu_process_up(Menu* menu);
|
||||
static void menu_process_down(Menu* menu);
|
||||
static void menu_process_ok(Menu* menu);
|
||||
|
||||
static void menu_draw_callback(Canvas* canvas, void* _model) {
|
||||
MenuModel* model = _model;
|
||||
|
||||
canvas_clear(canvas);
|
||||
|
||||
uint8_t position = model->position;
|
||||
size_t items_count = MenuItemArray_size(model->items);
|
||||
if(items_count) {
|
||||
MenuItem* item;
|
||||
size_t shift_position;
|
||||
// First line
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
shift_position = (0 + position + items_count - 1) % items_count;
|
||||
item = MenuItemArray_get(model->items, shift_position);
|
||||
if(item->icon) {
|
||||
canvas_draw_icon_animation(canvas, 4, 3, item->icon);
|
||||
icon_animation_stop(item->icon);
|
||||
}
|
||||
canvas_draw_str(canvas, 22, 14, item->label);
|
||||
// Second line main
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
shift_position = (1 + position + items_count - 1) % items_count;
|
||||
item = MenuItemArray_get(model->items, shift_position);
|
||||
if(item->icon) {
|
||||
canvas_draw_icon_animation(canvas, 4, 25, item->icon);
|
||||
icon_animation_start(item->icon);
|
||||
}
|
||||
canvas_draw_str(canvas, 22, 36, item->label);
|
||||
// Third line
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
shift_position = (2 + position + items_count - 1) % items_count;
|
||||
item = MenuItemArray_get(model->items, shift_position);
|
||||
if(item->icon) {
|
||||
canvas_draw_icon_animation(canvas, 4, 47, item->icon);
|
||||
icon_animation_stop(item->icon);
|
||||
}
|
||||
canvas_draw_str(canvas, 22, 58, item->label);
|
||||
// Frame and scrollbar
|
||||
elements_frame(canvas, 0, 21, 128 - 5, 21);
|
||||
elements_scrollbar(canvas, position, items_count);
|
||||
} else {
|
||||
canvas_draw_str(canvas, 2, 32, "Empty");
|
||||
elements_scrollbar(canvas, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static bool menu_input_callback(InputEvent* event, void* context) {
|
||||
Menu* menu = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyUp) {
|
||||
consumed = true;
|
||||
menu_process_up(menu);
|
||||
} else if(event->key == InputKeyDown) {
|
||||
consumed = true;
|
||||
menu_process_down(menu);
|
||||
} else if(event->key == InputKeyOk) {
|
||||
consumed = true;
|
||||
menu_process_ok(menu);
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
Menu* menu_alloc() {
|
||||
Menu* menu = furi_alloc(sizeof(Menu));
|
||||
menu->view = view_alloc(menu->view);
|
||||
view_set_context(menu->view, menu);
|
||||
view_allocate_model(menu->view, ViewModelTypeLocking, sizeof(MenuModel));
|
||||
view_set_draw_callback(menu->view, menu_draw_callback);
|
||||
view_set_input_callback(menu->view, menu_input_callback);
|
||||
|
||||
with_view_model(
|
||||
menu->view, (MenuModel * model) {
|
||||
MenuItemArray_init(model->items);
|
||||
model->position = 0;
|
||||
return true;
|
||||
});
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
void menu_free(Menu* menu) {
|
||||
furi_assert(menu);
|
||||
with_view_model(
|
||||
menu->view, (MenuModel * model) {
|
||||
MenuItemArray_clear(model->items);
|
||||
return true;
|
||||
});
|
||||
view_free(menu->view);
|
||||
free(menu);
|
||||
}
|
||||
|
||||
View* menu_get_view(Menu* menu) {
|
||||
furi_assert(menu);
|
||||
return (menu->view);
|
||||
}
|
||||
|
||||
void menu_add_item(
|
||||
Menu* menu,
|
||||
const char* label,
|
||||
IconAnimation* icon,
|
||||
uint32_t index,
|
||||
MenuItemCallback callback,
|
||||
void* context) {
|
||||
furi_assert(menu);
|
||||
furi_assert(label);
|
||||
|
||||
MenuItem* item = NULL;
|
||||
with_view_model(
|
||||
menu->view, (MenuModel * model) {
|
||||
item = MenuItemArray_push_new(model->items);
|
||||
item->label = label;
|
||||
item->icon = icon;
|
||||
item->index = index;
|
||||
item->callback = callback;
|
||||
item->callback_context = context;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void menu_clean(Menu* menu) {
|
||||
furi_assert(menu);
|
||||
with_view_model(
|
||||
menu->view, (MenuModel * model) {
|
||||
MenuItemArray_clean(model->items);
|
||||
model->position = 0;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void menu_set_selected_item(Menu* menu, uint32_t index) {
|
||||
with_view_model(
|
||||
menu->view, (MenuModel * model) {
|
||||
if(index >= MenuItemArray_size(model->items)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
model->position = index;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void menu_process_up(Menu* menu) {
|
||||
with_view_model(
|
||||
menu->view, (MenuModel * model) {
|
||||
if(model->position > 0) {
|
||||
model->position--;
|
||||
} else {
|
||||
model->position = MenuItemArray_size(model->items) - 1;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void menu_process_down(Menu* menu) {
|
||||
with_view_model(
|
||||
menu->view, (MenuModel * model) {
|
||||
if(model->position < MenuItemArray_size(model->items) - 1) {
|
||||
model->position++;
|
||||
} else {
|
||||
model->position = 0;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void menu_process_ok(Menu* menu) {
|
||||
MenuItem* item = NULL;
|
||||
with_view_model(
|
||||
menu->view, (MenuModel * model) {
|
||||
if(model->position < MenuItemArray_size(model->items)) {
|
||||
item = MenuItemArray_get(model->items, model->position);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if(item && item->callback) {
|
||||
item->callback(item->callback_context, item->index);
|
||||
}
|
||||
}
|
||||
58
applications/gui/modules/menu.h
Executable file
58
applications/gui/modules/menu.h
Executable file
@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
#include <gui/view.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Menu anonymous structure */
|
||||
typedef struct Menu Menu;
|
||||
typedef void (*MenuItemCallback)(void* context, uint32_t index);
|
||||
|
||||
/** Menu allocation and initialization
|
||||
* @return Menu instance
|
||||
*/
|
||||
Menu* menu_alloc();
|
||||
|
||||
/** Free menu
|
||||
* @param menu - Menu instance
|
||||
*/
|
||||
void menu_free(Menu* menu);
|
||||
|
||||
/** Get Menu view
|
||||
* @param menu - Menu instance
|
||||
* @return View instance
|
||||
*/
|
||||
View* menu_get_view(Menu* menu);
|
||||
|
||||
/** Add item to menu
|
||||
* @param menu - Menu instance
|
||||
* @param label - menu item string label
|
||||
* @param icon - IconAnimation instance
|
||||
* @param index - menu item index
|
||||
* @param callback - MenuItemCallback instance
|
||||
* @param context - pointer to context
|
||||
*/
|
||||
void menu_add_item(
|
||||
Menu* menu,
|
||||
const char* label,
|
||||
IconAnimation* icon,
|
||||
uint32_t index,
|
||||
MenuItemCallback callback,
|
||||
void* context);
|
||||
|
||||
/** Clean menu
|
||||
* Note: this function does not free menu instance
|
||||
* @param menu - Menu instance
|
||||
*/
|
||||
void menu_clean(Menu* menu);
|
||||
|
||||
/** Set current menu item
|
||||
* @param submenu
|
||||
* @param index
|
||||
*/
|
||||
void menu_set_selected_item(Menu* menu, uint32_t index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,23 +1,22 @@
|
||||
#include "submenu.h"
|
||||
#include "gui/canvas.h"
|
||||
|
||||
#include <m-array.h>
|
||||
#include <furi.h>
|
||||
#include <gui/elements.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct SubmenuItem {
|
||||
const char* label;
|
||||
uint32_t index;
|
||||
SubmenuItemCallback callback;
|
||||
void* callback_context;
|
||||
};
|
||||
|
||||
ARRAY_DEF(SubmenuItemArray, SubmenuItem, M_POD_OPLIST);
|
||||
#include <furi.h>
|
||||
|
||||
struct Submenu {
|
||||
View* view;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const char* label;
|
||||
uint32_t index;
|
||||
SubmenuItemCallback callback;
|
||||
void* callback_context;
|
||||
} SubmenuItem;
|
||||
|
||||
ARRAY_DEF(SubmenuItemArray, SubmenuItem, M_POD_OPLIST);
|
||||
|
||||
typedef struct {
|
||||
SubmenuItemArray_t items;
|
||||
const char* header;
|
||||
@ -149,7 +148,7 @@ View* submenu_get_view(Submenu* submenu) {
|
||||
return submenu->view;
|
||||
}
|
||||
|
||||
SubmenuItem* submenu_add_item(
|
||||
void submenu_add_item(
|
||||
Submenu* submenu,
|
||||
const char* label,
|
||||
uint32_t index,
|
||||
@ -168,8 +167,6 @@ SubmenuItem* submenu_add_item(
|
||||
item->callback_context = callback_context;
|
||||
return true;
|
||||
});
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void submenu_clean(Submenu* submenu) {
|
||||
|
||||
@ -7,7 +7,6 @@ extern "C" {
|
||||
|
||||
/* Submenu anonymous structure */
|
||||
typedef struct Submenu Submenu;
|
||||
typedef struct SubmenuItem SubmenuItem;
|
||||
typedef void (*SubmenuItemCallback)(void* context, uint32_t index);
|
||||
|
||||
/**
|
||||
@ -36,9 +35,8 @@ View* submenu_get_view(Submenu* submenu);
|
||||
* @param index - menu item index, used for callback, may be the same with other items
|
||||
* @param callback - menu item callback
|
||||
* @param callback_context - menu item callback context
|
||||
* @return SubmenuItem instance that can be used to modify or delete that item
|
||||
*/
|
||||
SubmenuItem* submenu_add_item(
|
||||
void submenu_add_item(
|
||||
Submenu* submenu,
|
||||
const char* label,
|
||||
uint32_t index,
|
||||
|
||||
@ -369,15 +369,7 @@ TextInput* text_input_alloc() {
|
||||
view_set_draw_callback(text_input->view, text_input_view_draw_callback);
|
||||
view_set_input_callback(text_input->view, text_input_view_input_callback);
|
||||
|
||||
with_view_model(
|
||||
text_input->view, (TextInputModel * model) {
|
||||
model->text_buffer_size = 0;
|
||||
model->header = "";
|
||||
model->selected_row = 0;
|
||||
model->selected_column = 0;
|
||||
model->clear_default_text = false;
|
||||
return true;
|
||||
});
|
||||
text_input_clean(text_input);
|
||||
|
||||
return text_input;
|
||||
}
|
||||
@ -388,6 +380,23 @@ void text_input_free(TextInput* text_input) {
|
||||
free(text_input);
|
||||
}
|
||||
|
||||
void text_input_clean(TextInput* text_input) {
|
||||
furi_assert(text_input);
|
||||
with_view_model(
|
||||
text_input->view, (TextInputModel * model) {
|
||||
model->text_buffer_size = 0;
|
||||
model->header = "";
|
||||
model->selected_row = 0;
|
||||
model->selected_column = 0;
|
||||
model->clear_default_text = false;
|
||||
model->text_buffer = NULL;
|
||||
model->text_buffer_size = 0;
|
||||
model->callback = NULL;
|
||||
model->callback_context = NULL;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
View* text_input_get_view(TextInput* text_input) {
|
||||
furi_assert(text_input);
|
||||
return text_input->view;
|
||||
@ -407,6 +416,11 @@ void text_input_set_result_callback(
|
||||
model->text_buffer = text_buffer;
|
||||
model->text_buffer_size = text_buffer_size;
|
||||
model->clear_default_text = clear_default_text;
|
||||
if(text_buffer && text_buffer[0] != '\0') {
|
||||
// Set focus on Save
|
||||
model->selected_row = 2;
|
||||
model->selected_column = 8;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@ -9,32 +9,31 @@ extern "C" {
|
||||
typedef struct TextInput TextInput;
|
||||
typedef void (*TextInputCallback)(void* context);
|
||||
|
||||
/**
|
||||
* @brief Allocate and initialize text input
|
||||
* This text input is used to enter string
|
||||
*
|
||||
/** Allocate and initialize text input
|
||||
* This text input is used to enter string
|
||||
* @return TextInput instance
|
||||
*/
|
||||
TextInput* text_input_alloc();
|
||||
|
||||
/**
|
||||
* @brief Deinitialize and free text input
|
||||
*
|
||||
* @param text_input - Text input instance
|
||||
/** Deinitialize and free text input
|
||||
* @param text_input - TextInput instance
|
||||
*/
|
||||
void text_input_free(TextInput* text_input);
|
||||
|
||||
/**
|
||||
* @brief Get text input view
|
||||
*
|
||||
/** Clean text input view
|
||||
* Note: this function does not free memory
|
||||
* @param text_input - Text input instance
|
||||
*/
|
||||
void text_input_clean(TextInput* text_input);
|
||||
|
||||
/** Get text input view
|
||||
* @param text_input - TextInput instance
|
||||
* @return View instance that can be used for embedding
|
||||
*/
|
||||
View* text_input_get_view(TextInput* text_input);
|
||||
|
||||
/**
|
||||
* @brief Set text input result callback
|
||||
*
|
||||
* @param text_input - Text input instance
|
||||
/** Set text input result callback
|
||||
* @param text_input - TextInput instance
|
||||
* @param callback - callback fn
|
||||
* @param callback_context - callback context
|
||||
* @param text_buffer - pointer to YOUR text buffer, that we going to modify
|
||||
@ -49,10 +48,8 @@ void text_input_set_result_callback(
|
||||
size_t text_buffer_size,
|
||||
bool clear_default_text);
|
||||
|
||||
/**
|
||||
* @brief Set text input header text
|
||||
*
|
||||
* @param text input - Text input instance
|
||||
/** Set text input header text
|
||||
* @param text_input - TextInput instance
|
||||
* @param text - text to be shown
|
||||
*/
|
||||
void text_input_set_header_text(TextInput* text_input, const char* text);
|
||||
|
||||
@ -144,7 +144,7 @@ void KeyEmulator::start_metakom_emulate(iButtonKey* key) {
|
||||
uint8_t* key_data = key->get_data();
|
||||
|
||||
// start pulse
|
||||
pulse_data[0] = metakom_period_full * 4;
|
||||
pulse_data[0] = metakom_period_full;
|
||||
|
||||
// start triplet
|
||||
set_pulse_data_metakom(pd_index, metakom_period_zero);
|
||||
|
||||
@ -116,15 +116,11 @@ bool KeyReader::verify_key(iButtonKeyType key_type, const uint8_t* const data, u
|
||||
|
||||
void KeyReader::start_comaparator(void) {
|
||||
// pulldown lf-rfid pins to prevent interference
|
||||
// TODO open record
|
||||
GpioPin rfid_pull_pin = {.port = RFID_PULL_GPIO_Port, .pin = RFID_PULL_Pin};
|
||||
hal_gpio_init(&rfid_pull_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow);
|
||||
hal_gpio_write(&rfid_pull_pin, false);
|
||||
hal_gpio_init(&gpio_rfid_pull, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow);
|
||||
hal_gpio_write(&gpio_rfid_pull, false);
|
||||
|
||||
// TODO open record
|
||||
GpioPin rfid_out_pin = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin};
|
||||
hal_gpio_init(&rfid_out_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow);
|
||||
hal_gpio_write(&rfid_out_pin, false);
|
||||
hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow);
|
||||
hal_gpio_write(&gpio_rfid_carrier_out, false);
|
||||
|
||||
comparator_callback_pointer =
|
||||
cbc::obtain_connector(this, &KeyReader::comparator_trigger_callback);
|
||||
@ -149,7 +145,7 @@ void KeyReader::comparator_trigger_callback(void* hcomp, void* comp_ctx) {
|
||||
_this->metakom_decoder.process_front(
|
||||
hal_gpio_get_rfid_in_level(), current_dwt_value - last_dwt_value);
|
||||
|
||||
last_dwt_value = DWT->CYCCNT;
|
||||
last_dwt_value = current_dwt_value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -54,7 +54,6 @@ void PulseSequencer::init_timer(uint32_t period) {
|
||||
Error_Handler();
|
||||
}
|
||||
|
||||
HAL_NVIC_SetPriority(TIM1_UP_TIM16_IRQn, 5, 0);
|
||||
HAL_NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn);
|
||||
|
||||
hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow);
|
||||
|
||||
@ -248,6 +248,8 @@ void onewire_cli_search(Cli* cli) {
|
||||
printf("Search started\r\n");
|
||||
|
||||
onewire.start();
|
||||
furi_hal_power_enable_otg();
|
||||
|
||||
while(!done) {
|
||||
if(onewire.search(address, true) != 1) {
|
||||
printf("Search finished\r\n");
|
||||
@ -263,6 +265,8 @@ void onewire_cli_search(Cli* cli) {
|
||||
}
|
||||
delay(100);
|
||||
}
|
||||
|
||||
furi_hal_power_disable_otg();
|
||||
onewire.stop();
|
||||
}
|
||||
|
||||
|
||||
@ -48,8 +48,7 @@ bool iButtonSceneSaveName::on_event(iButtonApp* app, iButtonEvent* event) {
|
||||
|
||||
void iButtonSceneSaveName::on_exit(iButtonApp* app) {
|
||||
TextInput* text_input = app->get_view_manager()->get_text_input();
|
||||
text_input_set_header_text(text_input, "");
|
||||
text_input_set_result_callback(text_input, NULL, NULL, NULL, 0, false);
|
||||
text_input_clean(text_input);
|
||||
}
|
||||
|
||||
void iButtonSceneSaveName::text_input_callback(void* context) {
|
||||
|
||||
280
applications/loader/loader.c
Normal file → Executable file
280
applications/loader/loader.c
Normal file → Executable file
@ -1,8 +1,11 @@
|
||||
#include "loader_i.h"
|
||||
|
||||
#define LOADER_THREAD_FLAG_SHOW_MENU (1 << 0)
|
||||
#define LOADER_THREAD_FLAG_ALL (LOADER_THREAD_FLAG_SHOW_MENU)
|
||||
|
||||
static Loader* loader_instance = NULL;
|
||||
|
||||
static void loader_menu_callback(void* _ctx) {
|
||||
static void loader_menu_callback(void* _ctx, uint32_t index) {
|
||||
const FlipperApplication* flipper_app = _ctx;
|
||||
|
||||
furi_assert(flipper_app->app);
|
||||
@ -27,6 +30,11 @@ static void loader_menu_callback(void* _ctx) {
|
||||
furi_thread_start(loader_instance->thread);
|
||||
}
|
||||
|
||||
static void loader_submenu_callback(void* context, uint32_t index) {
|
||||
uint32_t view_id = (uint32_t)context;
|
||||
view_dispatcher_switch_to_view(loader_instance->view_dispatcher, view_id);
|
||||
}
|
||||
|
||||
static void loader_cli_callback(Cli* cli, string_t args, void* _ctx) {
|
||||
furi_assert(_ctx);
|
||||
const FlipperApplication* flipper_app = (FlipperApplication*)_ctx;
|
||||
@ -60,6 +68,15 @@ bool loader_start(Loader* instance, const char* name, const char* args) {
|
||||
}
|
||||
}
|
||||
|
||||
if(!flipper_app) {
|
||||
for(size_t i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) {
|
||||
if(strcmp(FLIPPER_DEBUG_APPS[i].name, name) == 0) {
|
||||
flipper_app = &FLIPPER_DEBUG_APPS[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!flipper_app) {
|
||||
FURI_LOG_E(LOADER_LOG_TAG, "Can't find application with name %s", name);
|
||||
return false;
|
||||
@ -73,9 +90,11 @@ bool loader_start(Loader* instance, const char* name, const char* args) {
|
||||
}
|
||||
|
||||
instance->current_app = flipper_app;
|
||||
void* thread_args = NULL;
|
||||
if(args) {
|
||||
string_set_str(instance->args, args);
|
||||
string_strim(instance->args);
|
||||
thread_args = (void*)string_get_cstr(instance->args);
|
||||
FURI_LOG_I(LOADER_LOG_TAG, "Start %s app with args: %s", name, args);
|
||||
} else {
|
||||
string_clean(instance->args);
|
||||
@ -84,7 +103,7 @@ bool loader_start(Loader* instance, const char* name, const char* args) {
|
||||
|
||||
furi_thread_set_name(instance->thread, flipper_app->name);
|
||||
furi_thread_set_stack_size(instance->thread, flipper_app->stack_size);
|
||||
furi_thread_set_context(instance->thread, (void*)string_get_cstr(instance->args));
|
||||
furi_thread_set_context(instance->thread, thread_args);
|
||||
furi_thread_set_callback(instance->thread, flipper_app->app);
|
||||
|
||||
return furi_thread_start(instance->thread);
|
||||
@ -138,6 +157,14 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t loader_hide_menu(void* context) {
|
||||
return VIEW_NONE;
|
||||
}
|
||||
|
||||
static uint32_t loader_back_to_primary_menu(void* context) {
|
||||
return LoaderMenuViewPrimary;
|
||||
}
|
||||
|
||||
static Loader* loader_alloc() {
|
||||
Loader* instance = furi_alloc(sizeof(Loader));
|
||||
|
||||
@ -150,10 +177,45 @@ static Loader* loader_alloc() {
|
||||
|
||||
instance->mutex = osMutexNew(NULL);
|
||||
|
||||
instance->menu_vm = furi_record_open("menu");
|
||||
|
||||
instance->cli = furi_record_open("cli");
|
||||
|
||||
instance->loader_thread = osThreadGetId();
|
||||
|
||||
// Gui
|
||||
instance->gui = furi_record_open("gui");
|
||||
instance->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_attach_to_gui(
|
||||
instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen);
|
||||
// Primary menu
|
||||
instance->primary_menu = menu_alloc();
|
||||
view_set_previous_callback(menu_get_view(instance->primary_menu), loader_hide_menu);
|
||||
view_dispatcher_add_view(
|
||||
instance->view_dispatcher, LoaderMenuViewPrimary, menu_get_view(instance->primary_menu));
|
||||
// Plugins menu
|
||||
instance->plugins_menu = submenu_alloc();
|
||||
view_set_previous_callback(
|
||||
submenu_get_view(instance->plugins_menu), loader_back_to_primary_menu);
|
||||
view_dispatcher_add_view(
|
||||
instance->view_dispatcher,
|
||||
LoaderMenuViewPlugins,
|
||||
submenu_get_view(instance->plugins_menu));
|
||||
// Debug menu
|
||||
instance->debug_menu = submenu_alloc();
|
||||
view_set_previous_callback(
|
||||
submenu_get_view(instance->debug_menu), loader_back_to_primary_menu);
|
||||
view_dispatcher_add_view(
|
||||
instance->view_dispatcher, LoaderMenuViewDebug, submenu_get_view(instance->debug_menu));
|
||||
// Settings menu
|
||||
instance->settings_menu = submenu_alloc();
|
||||
view_set_previous_callback(
|
||||
submenu_get_view(instance->settings_menu), loader_back_to_primary_menu);
|
||||
view_dispatcher_add_view(
|
||||
instance->view_dispatcher,
|
||||
LoaderMenuViewSettings,
|
||||
submenu_get_view(instance->settings_menu));
|
||||
|
||||
view_dispatcher_enable_queue(instance->view_dispatcher);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@ -162,133 +224,111 @@ static void loader_free(Loader* instance) {
|
||||
|
||||
furi_record_close("cli");
|
||||
|
||||
furi_record_close("menu");
|
||||
|
||||
osMutexDelete(instance->mutex);
|
||||
|
||||
string_clear(instance->args);
|
||||
|
||||
furi_thread_free(instance->thread);
|
||||
|
||||
menu_free(loader_instance->primary_menu);
|
||||
view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewPrimary);
|
||||
submenu_free(loader_instance->plugins_menu);
|
||||
view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewPlugins);
|
||||
submenu_free(loader_instance->debug_menu);
|
||||
view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewDebug);
|
||||
submenu_free(loader_instance->settings_menu);
|
||||
view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewSettings);
|
||||
view_dispatcher_free(loader_instance->view_dispatcher);
|
||||
|
||||
furi_record_close("gui");
|
||||
|
||||
free(instance);
|
||||
instance = NULL;
|
||||
}
|
||||
|
||||
static void loader_add_cli_command(FlipperApplication* app) {
|
||||
string_t cli_name;
|
||||
string_init_printf(cli_name, "app_%s", app->name);
|
||||
cli_add_command(
|
||||
loader_instance->cli,
|
||||
string_get_cstr(cli_name),
|
||||
CliCommandFlagDefault,
|
||||
loader_cli_callback,
|
||||
app);
|
||||
string_clear(cli_name);
|
||||
}
|
||||
|
||||
static void loader_build_menu() {
|
||||
FURI_LOG_I(LOADER_LOG_TAG, "Building main menu");
|
||||
with_value_mutex(
|
||||
loader_instance->menu_vm, (Menu * menu) {
|
||||
for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
|
||||
// Add menu item
|
||||
menu_item_add(
|
||||
menu,
|
||||
menu_item_alloc_function(
|
||||
FLIPPER_APPS[i].name,
|
||||
FLIPPER_APPS[i].icon ? icon_animation_alloc(FLIPPER_APPS[i].icon) : NULL,
|
||||
loader_menu_callback,
|
||||
(void*)&FLIPPER_APPS[i]));
|
||||
|
||||
// Add cli command
|
||||
string_t cli_name;
|
||||
string_init_set_str(cli_name, "app_");
|
||||
string_cat_str(cli_name, FLIPPER_APPS[i].name);
|
||||
cli_add_command(
|
||||
loader_instance->cli,
|
||||
string_get_cstr(cli_name),
|
||||
CliCommandFlagDefault,
|
||||
loader_cli_callback,
|
||||
(void*)&FLIPPER_APPS[i]);
|
||||
string_clear(cli_name);
|
||||
}
|
||||
});
|
||||
size_t i;
|
||||
for(i = 0; i < FLIPPER_APPS_COUNT; i++) {
|
||||
loader_add_cli_command((FlipperApplication*)&FLIPPER_APPS[i]);
|
||||
menu_add_item(
|
||||
loader_instance->primary_menu,
|
||||
FLIPPER_APPS[i].name,
|
||||
FLIPPER_APPS[i].icon ? icon_animation_alloc(FLIPPER_APPS[i].icon) : NULL,
|
||||
i,
|
||||
loader_menu_callback,
|
||||
(void*)&FLIPPER_APPS[i]);
|
||||
}
|
||||
menu_add_item(
|
||||
loader_instance->primary_menu,
|
||||
"Plugins",
|
||||
icon_animation_alloc(&A_Plugins_14),
|
||||
i++,
|
||||
loader_submenu_callback,
|
||||
(void*)LoaderMenuViewPlugins);
|
||||
menu_add_item(
|
||||
loader_instance->primary_menu,
|
||||
"Debug tools",
|
||||
icon_animation_alloc(&A_Debug_14),
|
||||
i++,
|
||||
loader_submenu_callback,
|
||||
(void*)LoaderMenuViewDebug);
|
||||
menu_add_item(
|
||||
loader_instance->primary_menu,
|
||||
"Settings",
|
||||
icon_animation_alloc(&A_Settings_14),
|
||||
i++,
|
||||
loader_submenu_callback,
|
||||
(void*)LoaderMenuViewSettings);
|
||||
|
||||
FURI_LOG_I(LOADER_LOG_TAG, "Building plugins menu");
|
||||
with_value_mutex(
|
||||
loader_instance->menu_vm, (Menu * menu) {
|
||||
MenuItem* menu_plugins =
|
||||
menu_item_alloc_menu("Plugins", icon_animation_alloc(&A_Plugins_14));
|
||||
|
||||
for(size_t i = 0; i < FLIPPER_PLUGINS_COUNT; i++) {
|
||||
// Add menu item
|
||||
menu_item_subitem_add(
|
||||
menu_plugins,
|
||||
menu_item_alloc_function(
|
||||
FLIPPER_PLUGINS[i].name,
|
||||
FLIPPER_PLUGINS[i].icon ? icon_animation_alloc(FLIPPER_PLUGINS[i].icon) :
|
||||
NULL,
|
||||
loader_menu_callback,
|
||||
(void*)&FLIPPER_PLUGINS[i]));
|
||||
|
||||
// Add cli command
|
||||
string_t cli_name;
|
||||
string_init_set_str(cli_name, "app_");
|
||||
string_cat_str(cli_name, FLIPPER_PLUGINS[i].name);
|
||||
cli_add_command(
|
||||
loader_instance->cli,
|
||||
string_get_cstr(cli_name),
|
||||
CliCommandFlagDefault,
|
||||
loader_cli_callback,
|
||||
(void*)&FLIPPER_PLUGINS[i]);
|
||||
string_clear(cli_name);
|
||||
}
|
||||
|
||||
menu_item_add(menu, menu_plugins);
|
||||
});
|
||||
for(i = 0; i < FLIPPER_PLUGINS_COUNT; i++) {
|
||||
loader_add_cli_command((FlipperApplication*)&FLIPPER_PLUGINS[i]);
|
||||
submenu_add_item(
|
||||
loader_instance->plugins_menu,
|
||||
FLIPPER_PLUGINS[i].name,
|
||||
i,
|
||||
loader_menu_callback,
|
||||
(void*)&FLIPPER_PLUGINS[i]);
|
||||
}
|
||||
|
||||
FURI_LOG_I(LOADER_LOG_TAG, "Building debug menu");
|
||||
with_value_mutex(
|
||||
loader_instance->menu_vm, (Menu * menu) {
|
||||
MenuItem* menu_debug =
|
||||
menu_item_alloc_menu("Debug tools", icon_animation_alloc(&A_Debug_14));
|
||||
|
||||
for(size_t i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) {
|
||||
// Add menu item
|
||||
menu_item_subitem_add(
|
||||
menu_debug,
|
||||
menu_item_alloc_function(
|
||||
FLIPPER_DEBUG_APPS[i].name,
|
||||
FLIPPER_DEBUG_APPS[i].icon ?
|
||||
icon_animation_alloc(FLIPPER_DEBUG_APPS[i].icon) :
|
||||
NULL,
|
||||
loader_menu_callback,
|
||||
(void*)&FLIPPER_DEBUG_APPS[i]));
|
||||
|
||||
// Add cli command
|
||||
string_t cli_name;
|
||||
string_init_set_str(cli_name, "app_");
|
||||
string_cat_str(cli_name, FLIPPER_DEBUG_APPS[i].name);
|
||||
cli_add_command(
|
||||
loader_instance->cli,
|
||||
string_get_cstr(cli_name),
|
||||
CliCommandFlagDefault,
|
||||
loader_cli_callback,
|
||||
(void*)&FLIPPER_DEBUG_APPS[i]);
|
||||
string_clear(cli_name);
|
||||
}
|
||||
|
||||
menu_item_add(menu, menu_debug);
|
||||
});
|
||||
for(i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) {
|
||||
loader_add_cli_command((FlipperApplication*)&FLIPPER_DEBUG_APPS[i]);
|
||||
submenu_add_item(
|
||||
loader_instance->debug_menu,
|
||||
FLIPPER_DEBUG_APPS[i].name,
|
||||
i,
|
||||
loader_menu_callback,
|
||||
(void*)&FLIPPER_DEBUG_APPS[i]);
|
||||
}
|
||||
|
||||
FURI_LOG_I(LOADER_LOG_TAG, "Building settings menu");
|
||||
with_value_mutex(
|
||||
loader_instance->menu_vm, (Menu * menu) {
|
||||
MenuItem* menu_debug =
|
||||
menu_item_alloc_menu("Settings", icon_animation_alloc(&A_Settings_14));
|
||||
for(i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) {
|
||||
submenu_add_item(
|
||||
loader_instance->settings_menu,
|
||||
FLIPPER_SETTINGS_APPS[i].name,
|
||||
i,
|
||||
loader_menu_callback,
|
||||
(void*)&FLIPPER_SETTINGS_APPS[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) {
|
||||
// Add menu item
|
||||
menu_item_subitem_add(
|
||||
menu_debug,
|
||||
menu_item_alloc_function(
|
||||
FLIPPER_SETTINGS_APPS[i].name,
|
||||
FLIPPER_SETTINGS_APPS[i].icon ?
|
||||
icon_animation_alloc(FLIPPER_SETTINGS_APPS[i].icon) :
|
||||
NULL,
|
||||
loader_menu_callback,
|
||||
(void*)&FLIPPER_SETTINGS_APPS[i]));
|
||||
}
|
||||
|
||||
menu_item_add(menu, menu_debug);
|
||||
});
|
||||
void loader_show_menu() {
|
||||
furi_assert(loader_instance);
|
||||
osThreadFlagsSet(loader_instance->loader_thread, LOADER_THREAD_FLAG_SHOW_MENU);
|
||||
}
|
||||
|
||||
int32_t loader_srv(void* p) {
|
||||
@ -300,15 +340,25 @@ int32_t loader_srv(void* p) {
|
||||
|
||||
// Call on start hooks
|
||||
for(size_t i = 0; i < FLIPPER_ON_SYSTEM_START_COUNT; i++) {
|
||||
(*FLIPPER_ON_SYSTEM_START[i])();
|
||||
FLIPPER_ON_SYSTEM_START[i]();
|
||||
}
|
||||
|
||||
FURI_LOG_I(LOADER_LOG_TAG, "Started");
|
||||
|
||||
furi_record_create("loader", loader_instance);
|
||||
|
||||
#ifdef LOADER_AUTOSTART
|
||||
loader_start(loader_instance, LOADER_AUTOSTART, NULL);
|
||||
#endif
|
||||
|
||||
while(1) {
|
||||
osThreadSuspend(osThreadGetId());
|
||||
uint32_t flags = osThreadFlagsWait(LOADER_THREAD_FLAG_ALL, osFlagsWaitAny, osWaitForever);
|
||||
if(flags & LOADER_THREAD_FLAG_SHOW_MENU) {
|
||||
menu_set_selected_item(loader_instance->primary_menu, 0);
|
||||
view_dispatcher_switch_to_view(
|
||||
loader_instance->view_dispatcher, LoaderMenuViewPrimary);
|
||||
view_dispatcher_run(loader_instance->view_dispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
loader_free(loader_instance);
|
||||
|
||||
@ -18,3 +18,6 @@ bool loader_lock(Loader* instance);
|
||||
|
||||
/** Unlock application start */
|
||||
void loader_unlock(Loader* instance);
|
||||
|
||||
/** Show primary loader */
|
||||
void loader_show_menu();
|
||||
|
||||
@ -3,20 +3,39 @@
|
||||
#include <furi.h>
|
||||
#include <furi-hal.h>
|
||||
#include <cli/cli.h>
|
||||
#include <menu/menu.h>
|
||||
#include <menu/menu_item.h>
|
||||
|
||||
#include <gui/view_dispatcher.h>
|
||||
|
||||
#include <gui/modules/menu.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
|
||||
#include <applications.h>
|
||||
#include <assets_icons.h>
|
||||
|
||||
#define LOADER_LOG_TAG "loader"
|
||||
|
||||
struct Loader {
|
||||
osThreadId_t loader_thread;
|
||||
FuriThread* thread;
|
||||
const FlipperApplication* current_app;
|
||||
string_t args;
|
||||
Cli* cli;
|
||||
ValueMutex* menu_vm;
|
||||
Gui* gui;
|
||||
|
||||
ViewDispatcher* view_dispatcher;
|
||||
Menu* primary_menu;
|
||||
Submenu* plugins_menu;
|
||||
Submenu* debug_menu;
|
||||
Submenu* settings_menu;
|
||||
|
||||
size_t free_heap_size;
|
||||
osMutexId_t mutex;
|
||||
volatile uint8_t lock_semaphore;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
LoaderMenuViewPrimary,
|
||||
LoaderMenuViewPlugins,
|
||||
LoaderMenuViewDebug,
|
||||
LoaderMenuViewSettings,
|
||||
} LoaderMenuView;
|
||||
|
||||
@ -1,337 +0,0 @@
|
||||
#include "menu.h"
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/elements.h>
|
||||
|
||||
#include "menu_event.h"
|
||||
#include "menu_item.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
struct Menu {
|
||||
MenuEvent* event;
|
||||
|
||||
// GUI
|
||||
Gui* gui;
|
||||
ViewPort* view_port;
|
||||
IconAnimation* icon;
|
||||
|
||||
// State
|
||||
MenuItem* root;
|
||||
MenuItem* settings;
|
||||
MenuItem* current;
|
||||
};
|
||||
|
||||
void menu_view_port_callback(Canvas* canvas, void* context);
|
||||
|
||||
ValueMutex* menu_init() {
|
||||
Menu* menu = furi_alloc(sizeof(Menu));
|
||||
|
||||
// Event dispatcher
|
||||
menu->event = menu_event_alloc();
|
||||
|
||||
ValueMutex* menu_mutex = furi_alloc(sizeof(ValueMutex));
|
||||
if(menu_mutex == NULL || !init_mutex(menu_mutex, menu, sizeof(Menu))) {
|
||||
printf("[menu_task] cannot create menu mutex\r\n");
|
||||
furi_crash(NULL);
|
||||
}
|
||||
|
||||
// OpenGui record
|
||||
menu->gui = furi_record_open("gui");
|
||||
|
||||
// Allocate and configure view_port
|
||||
menu->view_port = view_port_alloc();
|
||||
|
||||
// Open GUI and register fullscreen view_port
|
||||
gui_add_view_port(menu->gui, menu->view_port, GuiLayerFullscreen);
|
||||
|
||||
view_port_enabled_set(menu->view_port, false);
|
||||
view_port_draw_callback_set(menu->view_port, menu_view_port_callback, menu_mutex);
|
||||
view_port_input_callback_set(menu->view_port, menu_event_input_callback, menu->event);
|
||||
|
||||
return menu_mutex;
|
||||
}
|
||||
|
||||
void menu_build_main(Menu* menu) {
|
||||
furi_assert(menu);
|
||||
// Root point
|
||||
menu->root = menu_item_alloc_menu(NULL, NULL);
|
||||
}
|
||||
|
||||
void menu_item_add(Menu* menu, MenuItem* item) {
|
||||
menu_item_subitem_add(menu->root, item);
|
||||
}
|
||||
|
||||
void menu_settings_item_add(Menu* menu, MenuItem* item) {
|
||||
menu_item_subitem_add(menu->settings, item);
|
||||
}
|
||||
|
||||
void menu_draw_primary(Menu* menu, Canvas* canvas) {
|
||||
size_t position = menu_item_get_position(menu->current);
|
||||
MenuItemArray_t* items = menu_item_get_subitems(menu->current);
|
||||
size_t items_count = MenuItemArray_size(*items);
|
||||
if(items_count) {
|
||||
MenuItem* item;
|
||||
size_t shift_position;
|
||||
// First line
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
shift_position = (0 + position + items_count - 1) % (MenuItemArray_size(*items));
|
||||
item = *MenuItemArray_get(*items, shift_position);
|
||||
canvas_draw_icon_animation(canvas, 4, 3, menu_item_get_icon(item));
|
||||
canvas_draw_str(canvas, 22, 14, menu_item_get_label(item));
|
||||
// Second line main
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
shift_position = (1 + position + items_count - 1) % (MenuItemArray_size(*items));
|
||||
item = *MenuItemArray_get(*items, shift_position);
|
||||
canvas_draw_icon_animation(canvas, 4, 25, menu_item_get_icon(item));
|
||||
canvas_draw_str(canvas, 22, 36, menu_item_get_label(item));
|
||||
// Third line
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
shift_position = (2 + position + items_count - 1) % (MenuItemArray_size(*items));
|
||||
item = *MenuItemArray_get(*items, shift_position);
|
||||
canvas_draw_icon_animation(canvas, 4, 47, menu_item_get_icon(item));
|
||||
canvas_draw_str(canvas, 22, 58, menu_item_get_label(item));
|
||||
// Frame and scrollbar
|
||||
// elements_frame(canvas, 0, 0, 128 - 5, 21);
|
||||
elements_frame(canvas, 0, 21, 128 - 5, 21);
|
||||
// elements_frame(canvas, 0, 42, 128 - 5, 21);
|
||||
elements_scrollbar(canvas, position, items_count);
|
||||
} else {
|
||||
canvas_draw_str(canvas, 2, 32, "Empty");
|
||||
elements_scrollbar(canvas, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_draw_secondary(Menu* menu, Canvas* canvas) {
|
||||
size_t position = 0;
|
||||
size_t selected_position = menu_item_get_position(menu->current);
|
||||
size_t window_position = menu_item_get_window_position(menu->current);
|
||||
MenuItemArray_t* items = menu_item_get_subitems(menu->current);
|
||||
const uint8_t items_on_screen = 4;
|
||||
const uint8_t item_height = 16;
|
||||
const uint8_t item_width = 123;
|
||||
size_t items_count = MenuItemArray_size(*items);
|
||||
MenuItemArray_it_t it;
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
for(MenuItemArray_it(it, *items); !MenuItemArray_end_p(it); MenuItemArray_next(it)) {
|
||||
size_t item_position = position - window_position;
|
||||
|
||||
if(item_position < items_on_screen) {
|
||||
if(position == selected_position) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
elements_slightly_rounded_box(
|
||||
canvas, 0, (item_position * item_height) + 1, item_width, item_height - 2);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
canvas_draw_str(
|
||||
canvas,
|
||||
6,
|
||||
(item_position * item_height) + item_height - 4,
|
||||
menu_item_get_label(*MenuItemArray_ref(it)));
|
||||
}
|
||||
|
||||
position++;
|
||||
}
|
||||
|
||||
elements_scrollbar(canvas, selected_position, items_count);
|
||||
}
|
||||
|
||||
void menu_view_port_callback(Canvas* canvas, void* context) {
|
||||
furi_assert(canvas);
|
||||
furi_assert(context);
|
||||
|
||||
Menu* menu = acquire_mutex((ValueMutex*)context, 100); // wait 10 ms to get mutex
|
||||
if(menu == NULL) return; // redraw fail
|
||||
|
||||
furi_assert(menu->current);
|
||||
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// if top level
|
||||
if(menu_item_get_parent(menu->current) == NULL) {
|
||||
menu_draw_primary(menu, canvas);
|
||||
} else {
|
||||
menu_draw_secondary(menu, canvas);
|
||||
}
|
||||
|
||||
release_mutex((ValueMutex*)context, menu);
|
||||
}
|
||||
|
||||
void menu_set_icon(Menu* menu, IconAnimation* icon) {
|
||||
furi_assert(menu);
|
||||
|
||||
if(menu->icon) {
|
||||
icon_animation_stop(menu->icon);
|
||||
}
|
||||
|
||||
menu->icon = icon;
|
||||
|
||||
if(menu->icon) {
|
||||
icon_animation_start(menu->icon);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_update(Menu* menu) {
|
||||
furi_assert(menu);
|
||||
|
||||
if(menu->current) {
|
||||
size_t position = menu_item_get_position(menu->current);
|
||||
MenuItemArray_t* items = menu_item_get_subitems(menu->current);
|
||||
size_t items_count = MenuItemArray_size(*items);
|
||||
if(items_count) {
|
||||
MenuItem* item = *MenuItemArray_get(*items, position);
|
||||
menu_set_icon(menu, menu_item_get_icon(item));
|
||||
}
|
||||
}
|
||||
|
||||
menu_event_activity_notify(menu->event);
|
||||
view_port_update(menu->view_port);
|
||||
}
|
||||
|
||||
void menu_up(Menu* menu) {
|
||||
furi_assert(menu);
|
||||
size_t position = menu_item_get_position(menu->current);
|
||||
size_t window_position = menu_item_get_window_position(menu->current);
|
||||
MenuItemArray_t* items = menu_item_get_subitems(menu->current);
|
||||
|
||||
const uint8_t items_on_screen = 4;
|
||||
|
||||
if(position > 0) {
|
||||
position--;
|
||||
if(((position - window_position) < 1) && window_position > 0) {
|
||||
window_position--;
|
||||
}
|
||||
} else {
|
||||
position = MenuItemArray_size(*items) - 1;
|
||||
if(position > (items_on_screen - 1)) {
|
||||
window_position = position - (items_on_screen - 1);
|
||||
}
|
||||
}
|
||||
|
||||
menu_item_set_position(menu->current, position);
|
||||
menu_item_set_window_position(menu->current, window_position);
|
||||
menu_update(menu);
|
||||
}
|
||||
|
||||
void menu_down(Menu* menu) {
|
||||
furi_assert(menu);
|
||||
size_t position = menu_item_get_position(menu->current);
|
||||
size_t window_position = menu_item_get_window_position(menu->current);
|
||||
MenuItemArray_t* items = menu_item_get_subitems(menu->current);
|
||||
|
||||
const uint8_t items_on_screen = 4;
|
||||
if(position < (MenuItemArray_size(*items) - 1)) {
|
||||
position++;
|
||||
if((position - window_position) > (items_on_screen - 2) &&
|
||||
window_position < (MenuItemArray_size(*items) - items_on_screen)) {
|
||||
window_position++;
|
||||
}
|
||||
} else {
|
||||
position = 0;
|
||||
window_position = 0;
|
||||
}
|
||||
|
||||
menu_item_set_position(menu->current, position);
|
||||
menu_item_set_window_position(menu->current, window_position);
|
||||
menu_update(menu);
|
||||
}
|
||||
|
||||
void menu_ok(Menu* menu) {
|
||||
furi_assert(menu);
|
||||
|
||||
if(!menu->current) {
|
||||
view_port_enabled_set(menu->view_port, true);
|
||||
menu->current = menu->root;
|
||||
menu_item_set_position(menu->current, 0);
|
||||
menu_item_set_window_position(menu->current, 0);
|
||||
menu_update(menu);
|
||||
return;
|
||||
}
|
||||
|
||||
MenuItemArray_t* items = menu_item_get_subitems(menu->current);
|
||||
if(!items || MenuItemArray_size(*items) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t position = menu_item_get_position(menu->current);
|
||||
MenuItem* item = *MenuItemArray_get(*items, position);
|
||||
MenuItemType type = menu_item_get_type(item);
|
||||
|
||||
if(type == MenuItemTypeMenu) {
|
||||
menu->current = item;
|
||||
menu_item_set_position(menu->current, 0);
|
||||
menu_item_set_window_position(menu->current, 0);
|
||||
menu_update(menu);
|
||||
} else if(type == MenuItemTypeFunction) {
|
||||
menu_item_function_call(item);
|
||||
gui_send_view_port_back(menu->gui, menu->view_port);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_back(Menu* menu) {
|
||||
furi_assert(menu);
|
||||
MenuItem* parent = menu_item_get_parent(menu->current);
|
||||
if(parent) {
|
||||
menu->current = parent;
|
||||
menu_update(menu);
|
||||
} else {
|
||||
menu_exit(menu);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_exit(Menu* menu) {
|
||||
furi_assert(menu);
|
||||
view_port_enabled_set(menu->view_port, false);
|
||||
menu->current = NULL;
|
||||
menu_update(menu);
|
||||
}
|
||||
|
||||
int32_t menu_srv(void* p) {
|
||||
ValueMutex* menu_mutex = menu_init();
|
||||
|
||||
MenuEvent* menu_event = NULL;
|
||||
{
|
||||
Menu* menu = acquire_mutex_block(menu_mutex);
|
||||
furi_check(menu);
|
||||
|
||||
menu_build_main(menu);
|
||||
|
||||
// immutable thread-safe object
|
||||
menu_event = menu->event;
|
||||
|
||||
release_mutex(menu_mutex, menu);
|
||||
}
|
||||
|
||||
furi_record_create("menu", menu_mutex);
|
||||
|
||||
while(1) {
|
||||
MenuMessage m = menu_event_next(menu_event);
|
||||
|
||||
Menu* menu = acquire_mutex_block(menu_mutex);
|
||||
|
||||
if(!menu->current && m.type != MenuMessageTypeOk) {
|
||||
} else if(m.type == MenuMessageTypeUp) {
|
||||
menu_up(menu);
|
||||
} else if(m.type == MenuMessageTypeDown) {
|
||||
menu_down(menu);
|
||||
} else if(m.type == MenuMessageTypeOk) {
|
||||
menu_ok(menu);
|
||||
} else if(m.type == MenuMessageTypeBack) {
|
||||
menu_back(menu);
|
||||
} else if(m.type == MenuMessageTypeIdle) {
|
||||
menu_exit(menu);
|
||||
} else {
|
||||
// TODO: fail somehow?
|
||||
}
|
||||
|
||||
release_mutex(menu_mutex, menu);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "menu/menu_item.h"
|
||||
|
||||
typedef struct Menu Menu;
|
||||
typedef struct MenuItem MenuItem;
|
||||
|
||||
// Add menu item to root menu
|
||||
void menu_item_add(Menu* menu, MenuItem* item);
|
||||
|
||||
// Add menu item to settings menu
|
||||
void menu_settings_item_add(Menu* menu, MenuItem* item);
|
||||
|
||||
// Menu controls
|
||||
void menu_up(Menu* menu);
|
||||
void menu_down(Menu* menu);
|
||||
void menu_ok(Menu* menu);
|
||||
void menu_back(Menu* menu);
|
||||
void menu_exit(Menu* menu);
|
||||
@ -1,65 +0,0 @@
|
||||
#include "menu_event.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define MENU_MESSAGE_MQUEUE_SIZE 8
|
||||
|
||||
struct MenuEvent {
|
||||
osMessageQueueId_t mqueue;
|
||||
};
|
||||
|
||||
void MenuEventimeout_callback(void* arg) {
|
||||
MenuEvent* menu_event = arg;
|
||||
MenuMessage message;
|
||||
message.type = MenuMessageTypeIdle;
|
||||
osMessageQueuePut(menu_event->mqueue, &message, 0, osWaitForever);
|
||||
}
|
||||
|
||||
MenuEvent* menu_event_alloc() {
|
||||
MenuEvent* menu_event = furi_alloc(sizeof(MenuEvent));
|
||||
menu_event->mqueue = osMessageQueueNew(MENU_MESSAGE_MQUEUE_SIZE, sizeof(MenuMessage), NULL);
|
||||
furi_check(menu_event->mqueue);
|
||||
return menu_event;
|
||||
}
|
||||
|
||||
void menu_event_free(MenuEvent* menu_event) {
|
||||
furi_assert(menu_event);
|
||||
furi_check(osMessageQueueDelete(menu_event->mqueue) == osOK);
|
||||
free(menu_event);
|
||||
}
|
||||
|
||||
void menu_event_activity_notify(MenuEvent* menu_event) {
|
||||
furi_assert(menu_event);
|
||||
}
|
||||
|
||||
MenuMessage menu_event_next(MenuEvent* menu_event) {
|
||||
furi_assert(menu_event);
|
||||
MenuMessage message;
|
||||
while(osMessageQueueGet(menu_event->mqueue, &message, NULL, osWaitForever) != osOK) {
|
||||
};
|
||||
return message;
|
||||
}
|
||||
|
||||
void menu_event_input_callback(InputEvent* input_event, void* context) {
|
||||
MenuEvent* menu_event = context;
|
||||
MenuMessage message;
|
||||
|
||||
if(input_event->type != InputTypeShort) return;
|
||||
|
||||
if(input_event->key == InputKeyUp) {
|
||||
message.type = MenuMessageTypeUp;
|
||||
} else if(input_event->key == InputKeyDown) {
|
||||
message.type = MenuMessageTypeDown;
|
||||
} else if(input_event->key == InputKeyOk) {
|
||||
message.type = MenuMessageTypeOk;
|
||||
} else if(input_event->key == InputKeyBack) {
|
||||
message.type = MenuMessageTypeBack;
|
||||
} else {
|
||||
message.type = MenuMessageTypeUnknown;
|
||||
}
|
||||
|
||||
osMessageQueuePut(menu_event->mqueue, &message, 0, osWaitForever);
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <input/input.h>
|
||||
|
||||
typedef enum {
|
||||
MenuMessageTypeUp = 0x00,
|
||||
MenuMessageTypeDown = 0x01,
|
||||
MenuMessageTypeLeft = 0x02,
|
||||
MenuMessageTypeRight = 0x03,
|
||||
MenuMessageTypeOk = 0x04,
|
||||
MenuMessageTypeBack = 0x05,
|
||||
MenuMessageTypeIdle = 0x06,
|
||||
MenuMessageTypeUnknown = 0xFF,
|
||||
} MenuMessageType;
|
||||
|
||||
typedef struct {
|
||||
MenuMessageType type;
|
||||
void* data;
|
||||
} MenuMessage;
|
||||
|
||||
typedef struct MenuEvent MenuEvent;
|
||||
|
||||
MenuEvent* menu_event_alloc();
|
||||
|
||||
void menu_event_free(MenuEvent* menu_event);
|
||||
|
||||
void menu_event_activity_notify(MenuEvent* menu_event);
|
||||
|
||||
MenuMessage menu_event_next(MenuEvent* menu_event);
|
||||
|
||||
void menu_event_input_callback(InputEvent* input_event, void* context);
|
||||
@ -1,135 +0,0 @@
|
||||
#include "menu_item.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <furi.h>
|
||||
|
||||
struct MenuItem {
|
||||
MenuItemType type;
|
||||
|
||||
const char* label;
|
||||
IconAnimation* icon;
|
||||
|
||||
size_t position;
|
||||
size_t window_position;
|
||||
MenuItem* parent;
|
||||
void* data;
|
||||
|
||||
// callback related
|
||||
MenuItemCallback callback;
|
||||
void* callback_context;
|
||||
};
|
||||
|
||||
MenuItem* menu_item_alloc() {
|
||||
MenuItem* menu_item = furi_alloc(sizeof(MenuItem));
|
||||
return menu_item;
|
||||
}
|
||||
|
||||
MenuItem* menu_item_alloc_menu(const char* label, IconAnimation* icon) {
|
||||
MenuItem* menu_item = menu_item_alloc();
|
||||
|
||||
menu_item->type = MenuItemTypeMenu;
|
||||
menu_item->label = label;
|
||||
menu_item->icon = icon;
|
||||
|
||||
MenuItemArray_t* items = furi_alloc(sizeof(MenuItemArray_t));
|
||||
MenuItemArray_init(*items);
|
||||
menu_item->data = items;
|
||||
|
||||
return menu_item;
|
||||
}
|
||||
|
||||
MenuItem* menu_item_alloc_function(
|
||||
const char* label,
|
||||
IconAnimation* icon,
|
||||
MenuItemCallback callback,
|
||||
void* context) {
|
||||
MenuItem* menu_item = menu_item_alloc();
|
||||
|
||||
menu_item->type = MenuItemTypeFunction;
|
||||
menu_item->label = label;
|
||||
menu_item->icon = icon;
|
||||
menu_item->callback = callback;
|
||||
menu_item->callback_context = context;
|
||||
menu_item->parent = NULL;
|
||||
|
||||
return menu_item;
|
||||
}
|
||||
|
||||
void menu_item_release(MenuItem* menu_item) {
|
||||
furi_assert(menu_item);
|
||||
if(menu_item->type == MenuItemTypeMenu) {
|
||||
//TODO: iterate and release
|
||||
free(menu_item->data);
|
||||
}
|
||||
free(menu_item);
|
||||
}
|
||||
|
||||
MenuItem* menu_item_get_parent(MenuItem* menu_item) {
|
||||
furi_assert(menu_item);
|
||||
return menu_item->parent;
|
||||
}
|
||||
|
||||
void menu_item_subitem_add(MenuItem* menu_item, MenuItem* sub_item) {
|
||||
furi_assert(menu_item);
|
||||
furi_check(menu_item->type == MenuItemTypeMenu);
|
||||
MenuItemArray_t* items = menu_item->data;
|
||||
sub_item->parent = menu_item;
|
||||
MenuItemArray_push_back(*items, sub_item);
|
||||
}
|
||||
|
||||
uint8_t menu_item_get_type(MenuItem* menu_item) {
|
||||
furi_assert(menu_item);
|
||||
return menu_item->type;
|
||||
}
|
||||
|
||||
void menu_item_set_position(MenuItem* menu_item, size_t position) {
|
||||
furi_assert(menu_item);
|
||||
menu_item->position = position;
|
||||
}
|
||||
|
||||
size_t menu_item_get_position(MenuItem* menu_item) {
|
||||
furi_assert(menu_item);
|
||||
return menu_item->position;
|
||||
}
|
||||
|
||||
void menu_item_set_window_position(MenuItem* menu_item, size_t window_position) {
|
||||
furi_assert(menu_item);
|
||||
menu_item->window_position = window_position;
|
||||
}
|
||||
|
||||
size_t menu_item_get_window_position(MenuItem* menu_item) {
|
||||
furi_assert(menu_item);
|
||||
return menu_item->window_position;
|
||||
}
|
||||
|
||||
void menu_item_set_label(MenuItem* menu_item, const char* label) {
|
||||
furi_assert(menu_item);
|
||||
menu_item->label = label;
|
||||
}
|
||||
|
||||
const char* menu_item_get_label(MenuItem* menu_item) {
|
||||
furi_assert(menu_item);
|
||||
return menu_item->label;
|
||||
}
|
||||
|
||||
void menu_item_set_icon(MenuItem* menu_item, IconAnimation* icon) {
|
||||
furi_assert(menu_item);
|
||||
menu_item->icon = icon;
|
||||
}
|
||||
|
||||
IconAnimation* menu_item_get_icon(MenuItem* menu_item) {
|
||||
furi_assert(menu_item);
|
||||
return menu_item->icon;
|
||||
}
|
||||
|
||||
MenuItemArray_t* menu_item_get_subitems(MenuItem* menu_item) {
|
||||
furi_assert(menu_item);
|
||||
furi_check(menu_item->type == MenuItemTypeMenu);
|
||||
return menu_item->data;
|
||||
}
|
||||
|
||||
void menu_item_function_call(MenuItem* menu_item) {
|
||||
furi_assert(menu_item);
|
||||
furi_check(menu_item->type == MenuItemTypeFunction);
|
||||
if(menu_item->callback) menu_item->callback(menu_item->callback_context);
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <m-array.h>
|
||||
#include <gui/icon_animation.h>
|
||||
|
||||
typedef enum {
|
||||
MenuItemTypeMenu = 0x00,
|
||||
MenuItemTypeFunction = 0x01,
|
||||
} MenuItemType;
|
||||
|
||||
typedef struct MenuItem MenuItem;
|
||||
typedef void (*MenuItemCallback)(void* context);
|
||||
|
||||
ARRAY_DEF(MenuItemArray, MenuItem*, M_PTR_OPLIST);
|
||||
|
||||
MenuItem* menu_item_alloc_menu(const char* label, IconAnimation* icon);
|
||||
|
||||
MenuItem* menu_item_alloc_function(
|
||||
const char* label,
|
||||
IconAnimation* icon,
|
||||
MenuItemCallback callback,
|
||||
void* context);
|
||||
|
||||
void menu_item_release(MenuItem* menu_item);
|
||||
|
||||
MenuItem* menu_item_get_parent(MenuItem* menu_item);
|
||||
|
||||
void menu_item_subitem_add(MenuItem* menu_item, MenuItem* sub_item);
|
||||
|
||||
MenuItemType menu_item_get_type(MenuItem* menu_item);
|
||||
|
||||
void menu_item_set_position(MenuItem* menu_item, size_t position);
|
||||
size_t menu_item_get_position(MenuItem* menu_item);
|
||||
|
||||
void menu_item_set_window_position(MenuItem* menu_item, size_t window_position);
|
||||
size_t menu_item_get_window_position(MenuItem* menu_item);
|
||||
|
||||
void menu_item_set_label(MenuItem* menu_item, const char* label);
|
||||
const char* menu_item_get_label(MenuItem* menu_item);
|
||||
|
||||
void menu_item_set_icon(MenuItem* menu_item, IconAnimation* icon);
|
||||
IconAnimation* menu_item_get_icon(MenuItem* menu_item);
|
||||
|
||||
MenuItemArray_t* menu_item_get_subitems(MenuItem* menu_item);
|
||||
|
||||
void menu_item_function_call(MenuItem* menu_item);
|
||||
@ -27,7 +27,7 @@ void nfc_cli_detect(Cli* cli, string_t args, void* context) {
|
||||
if(dev_cnt > 0) {
|
||||
printf("Found %d devices\r\n", dev_cnt);
|
||||
for(uint8_t i = 0; i < dev_cnt; i++) {
|
||||
printf("%d found: %s ", i + 1, nfc_get_dev_type(dev_list[i].type));
|
||||
printf("%d found: %s ", i + 1, nfc_get_rfal_type(dev_list[i].type));
|
||||
if(dev_list[i].type == RFAL_NFC_LISTEN_TYPE_NFCA) {
|
||||
printf("type: %s, ", nfc_get_nfca_type(dev_list[i].dev.nfca.type));
|
||||
}
|
||||
|
||||
@ -6,19 +6,33 @@
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include "nfc_worker.h"
|
||||
|
||||
static inline const char* nfc_get_dev_type(rfalNfcDevType type) {
|
||||
static inline const char* nfc_get_rfal_type(rfalNfcDevType type) {
|
||||
if(type == RFAL_NFC_LISTEN_TYPE_NFCA) {
|
||||
return "NFC-A may be:";
|
||||
return "NFC-A";
|
||||
} else if(type == RFAL_NFC_LISTEN_TYPE_NFCB) {
|
||||
return "NFC-B may be:";
|
||||
return "NFC-B";
|
||||
} else if(type == RFAL_NFC_LISTEN_TYPE_NFCF) {
|
||||
return "NFC-F may be:";
|
||||
return "NFC-F";
|
||||
} else if(type == RFAL_NFC_LISTEN_TYPE_NFCV) {
|
||||
return "NFC-V may be:";
|
||||
return "NFC-V";
|
||||
} else if(type == RFAL_NFC_LISTEN_TYPE_ST25TB) {
|
||||
return "NFC-ST25TB may be:";
|
||||
return "NFC-ST25TB";
|
||||
} else if(type == RFAL_NFC_LISTEN_TYPE_AP2P) {
|
||||
return "NFC-AP2P may be:";
|
||||
return "NFC-AP2P";
|
||||
} else {
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char* nfc_get_dev_type(NfcDeviceType type) {
|
||||
if(type == NfcDeviceNfca) {
|
||||
return "NFC-A may be:";
|
||||
} else if(type == NfcDeviceNfcb) {
|
||||
return "NFC-B may be:";
|
||||
} else if(type == NfcDeviceNfcf) {
|
||||
return "NFC-F may be:";
|
||||
} else if(type == NfcDeviceNfcv) {
|
||||
return "NFC-V may be:";
|
||||
} else {
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ void nfc_scene_card_menu_submenu_callback(void* context, uint32_t index) {
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
|
||||
}
|
||||
|
||||
const void nfc_scene_card_menu_on_enter(void* context) {
|
||||
void nfc_scene_card_menu_on_enter(void* context) {
|
||||
Nfc* nfc = (Nfc*)context;
|
||||
Submenu* submenu = nfc->submenu;
|
||||
|
||||
@ -41,7 +41,7 @@ const void nfc_scene_card_menu_on_enter(void* context) {
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
|
||||
}
|
||||
|
||||
const bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event) {
|
||||
bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = (Nfc*)context;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
@ -78,7 +78,7 @@ const bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event)
|
||||
return false;
|
||||
}
|
||||
|
||||
const void nfc_scene_card_menu_on_exit(void* context) {
|
||||
void nfc_scene_card_menu_on_exit(void* context) {
|
||||
Nfc* nfc = (Nfc*)context;
|
||||
|
||||
submenu_clean(nfc->submenu);
|
||||
|
||||
@ -66,7 +66,7 @@ void nfc_scene_delete_on_enter(void* context) {
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
}
|
||||
|
||||
const bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) {
|
||||
bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = (Nfc*)context;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
@ -85,7 +85,7 @@ const bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const void nfc_scene_delete_on_exit(void* context) {
|
||||
void nfc_scene_delete_on_exit(void* context) {
|
||||
Nfc* nfc = (Nfc*)context;
|
||||
|
||||
widget_clear(nfc->widget);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user