Merge branch 'release-candidate' into release

This commit is contained in:
Aleksandr Kutuzov 2022-04-18 16:21:59 +03:00
commit d294b5e256
No known key found for this signature in database
GPG Key ID: 0D0011717914BBCD
546 changed files with 7245 additions and 12409 deletions

1
.github/CODEOWNERS vendored
View File

@ -25,6 +25,7 @@ applications/sd-filesystem/** @skotopes @DrZlo13
applications/subghz/** @skotopes @DrZlo13 applications/subghz/** @skotopes @DrZlo13
applications/template/** @skotopes @DrZlo13 applications/template/** @skotopes @DrZlo13
applications/tests/** @skotopes @DrZlo13 applications/tests/** @skotopes @DrZlo13
applications/updater/** @skotopes @DrZlo13 @hedger
# Assets and asset generator # Assets and asset generator
assets/** @skotopes @DrZlo13 assets/** @skotopes @DrZlo13

View File

@ -86,7 +86,7 @@ jobs:
set -e set -e
for TARGET in ${TARGETS} for TARGET in ${TARGETS}
do do
make TARGET=${TARGET} ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} make updater_package TARGET=${TARGET} ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }}
done done
- name: 'Move upload files' - name: 'Move upload files'
@ -100,6 +100,20 @@ jobs:
mv dist/${TARGET}/* artifacts/ mv dist/${TARGET}/* artifacts/
done done
- name: 'Bundle self-update package'
if: ${{ !github.event.pull_request.head.repo.fork }}
uses: ./.github/actions/docker
with:
run: |
set -e
for UPDATEBUNDLE in artifacts/*/
do
BUNDLE_NAME=`echo $UPDATEBUNDLE | cut -d'/' -f2`
echo Packaging ${BUNDLE_NAME}
tar czpf artifacts/flipper-z-${BUNDLE_NAME}.tgz -C artifacts ${BUNDLE_NAME}
rm -rf artifacts/${BUNDLE_NAME}
done
- name: 'Bundle resources' - name: 'Bundle resources'
if: ${{ !github.event.pull_request.head.repo.fork }} if: ${{ !github.event.pull_request.head.repo.fork }}
run: | run: |

3
.gitignore vendored
View File

@ -36,3 +36,6 @@ CMakeLists.txt
# bundle output # bundle output
dist dist
# kde
.directory

3
.gitmodules vendored
View File

@ -19,3 +19,6 @@
[submodule "lib/FreeRTOS-Kernel"] [submodule "lib/FreeRTOS-Kernel"]
path = lib/FreeRTOS-Kernel path = lib/FreeRTOS-Kernel
url = https://github.com/FreeRTOS/FreeRTOS-Kernel.git url = https://github.com/FreeRTOS/FreeRTOS-Kernel.git
[submodule "lib/microtar"]
path = lib/microtar
url = https://github.com/amachronic/microtar.git

View File

@ -1,16 +1,16 @@
PROJECT_ROOT := $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST))))) PROJECT_ROOT := $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST)))))
include $(PROJECT_ROOT)/make/git.mk
COPRO_DIR := $(PROJECT_ROOT)/lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x COPRO_DIR := $(PROJECT_ROOT)/lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x
PROJECT_SOURCE_DIRECTORIES := \ PROJECT_SOURCE_DIRECTORIES := \
$(PROJECT_ROOT)/applications \ $(PROJECT_ROOT)/applications \
$(PROJECT_ROOT)/bootloader/src \
$(PROJECT_ROOT)/bootloader/targets \
$(PROJECT_ROOT)/core \ $(PROJECT_ROOT)/core \
$(PROJECT_ROOT)/firmware/targets \ $(PROJECT_ROOT)/firmware/targets \
$(PROJECT_ROOT)/lib/app-template \ $(PROJECT_ROOT)/lib/app-template \
$(PROJECT_ROOT)/lib/app-scened-template \ $(PROJECT_ROOT)/lib/app-scened-template \
$(PROJECT_ROOT)/lib/common-api \ $(PROJECT_ROOT)/lib/common-api \
$(PROJECT_ROOT)/lib/cyfral \
$(PROJECT_ROOT)/lib/drivers \ $(PROJECT_ROOT)/lib/drivers \
$(PROJECT_ROOT)/lib/flipper_file \ $(PROJECT_ROOT)/lib/flipper_file \
$(PROJECT_ROOT)/lib/infrared \ $(PROJECT_ROOT)/lib/infrared \
@ -34,18 +34,18 @@ endif
include $(PROJECT_ROOT)/make/defaults.mk include $(PROJECT_ROOT)/make/defaults.mk
.PHONY: all .PHONY: all
all: bootloader_all firmware_all all: firmware_all
@$(PROJECT_ROOT)/scripts/dist.sh @$(PROJECT_ROOT)/scripts/dist.py copy -t $(TARGET) -p firmware -s $(DIST_SUFFIX)
.PHONY: whole .PHONY: whole
whole: flash_radio bootloader_flash firmware_flash whole: flash_radio firmware_flash
.PHONY: clean .PHONY: clean
clean: bootloader_clean firmware_clean clean: firmware_clean updater_clean
@rm -rf $(PROJECT_ROOT)/dist/$(TARGET) @rm -rf $(PROJECT_ROOT)/dist/$(TARGET)
.PHONY: flash .PHONY: flash
flash: bootloader_flash firmware_flash flash: firmware_flash
.PHONY: debug .PHONY: debug
debug: debug:
@ -60,36 +60,38 @@ wipe:
@$(PROJECT_ROOT)/scripts/flash.py wipe @$(PROJECT_ROOT)/scripts/flash.py wipe
@$(PROJECT_ROOT)/scripts/ob.py set @$(PROJECT_ROOT)/scripts/ob.py set
.PHONY: bootloader_all
bootloader_all:
@$(MAKE) -C $(PROJECT_ROOT)/bootloader -j$(NPROCS) all
.PHONY: firmware_all .PHONY: firmware_all
firmware_all: firmware_all:
@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) all @$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) all
.PHONY: bootloader_clean
bootloader_clean:
@$(MAKE) -C $(PROJECT_ROOT)/bootloader -j$(NPROCS) clean
.PHONY: firmware_clean .PHONY: firmware_clean
firmware_clean: firmware_clean:
@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) clean @$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) clean
.PHONY: bootloader_flash
bootloader_flash:
ifeq ($(FORCE), 1)
@rm $(PROJECT_ROOT)/bootloader/.obj/f*/flash || true
endif
@$(MAKE) -C $(PROJECT_ROOT)/bootloader -j$(NPROCS) flash
.PHONY: firmware_flash .PHONY: firmware_flash
firmware_flash: firmware_flash:
ifeq ($(FORCE), 1) ifeq ($(FORCE), 1)
@rm $(PROJECT_ROOT)/firmware/.obj/f*/flash || true @rm $(PROJECT_ROOT)/firmware/.obj/f*-firmware/flash || true
endif endif
@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) flash @$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) flash
.PHONY: updater
updater:
@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) RAM_EXEC=1 all
.PHONY: updater_clean
updater_clean:
@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) RAM_EXEC=1 clean
.PHONY: updater_debug
updater_debug:
@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) RAM_EXEC=1 debug
.PHONY: updater_package
updater_package: firmware_all updater
@$(PROJECT_ROOT)/scripts/dist.py copy -t $(TARGET) -p firmware updater -s $(DIST_SUFFIX) --bundlever "$(VERSION_STRING)"
.PHONY: flash_radio .PHONY: flash_radio
flash_radio: flash_radio:
@$(PROJECT_ROOT)/scripts/flash.py core2radio 0x080D7000 $(COPRO_DIR)/stm32wb5x_BLE_Stack_light_fw.bin @$(PROJECT_ROOT)/scripts/flash.py core2radio 0x080D7000 $(COPRO_DIR)/stm32wb5x_BLE_Stack_light_fw.bin
@ -100,7 +102,7 @@ flash_radio_fus:
@echo @echo
@echo "================ DON'T DO IT ================" @echo "================ DON'T DO IT ================"
@echo "= Flashing FUS is going to erase secure enclave =" @echo "= Flashing FUS is going to erase secure enclave ="
@echo "= You will loose ability to use encrypted assets =" @echo "= You will lose ability to use encrypted assets ="
@echo "= type 'find / -exec rm -rf {} \;' =" @echo "= type 'find / -exec rm -rf {} \;' ="
@echo "= In case if you still want to continue =" @echo "= In case if you still want to continue ="
@echo "================ JUST DON'T ================" @echo "================ JUST DON'T ================"

View File

@ -11,17 +11,16 @@ Our goal is to create nice and clean code with good documentation, to make it a
[Get Latest Firmware from Update Server](https://update.flipperzero.one/) [Get Latest Firmware from Update Server](https://update.flipperzero.one/)
Flipper Zero's firmware consists of three components: Flipper Zero's firmware consists of two components:
- Core2 firmware set - proprietary components by ST: FUS + radio stack. FUS is flashed at factory and you should never update it. - Core2 firmware set - proprietary components by ST: FUS + radio stack. FUS is flashed at factory and you should never update it.
- Core1 Bootloader - controls basic hardware initialization and loads firmware.
- Core1 Firmware - HAL + OS + Drivers + Applications. - Core1 Firmware - HAL + OS + Drivers + Applications.
All 3 of them must be flashed in order described. They both must be flashed in order described.
## With STLink ## With STLink
### Core1 Bootloader + Firmware ### Core1 Firmware
Prerequisites: Prerequisites:
@ -144,7 +143,6 @@ make whole
- `applications` - Applications and services used in firmware - `applications` - Applications and services used in firmware
- `assets` - Assets used by applications and services - `assets` - Assets used by applications and services
- `bootloader` - Bootloader source code
- `core` - Furi Core: os level primitives and helpers - `core` - Furi Core: os level primitives and helpers
- `debug` - Debug tool: GDB-plugins, SVD-file and etc - `debug` - Debug tool: GDB-plugins, SVD-file and etc
- `docker` - Docker image sources (used for firmware build automation) - `docker` - Docker image sources (used for firmware build automation)

View File

@ -35,7 +35,6 @@ Main goal for 1.0.0 is to provide first stable version for both Users and Develo
## What we're planning to implement in 1.0.0 ## What we're planning to implement in 1.0.0
- Updating firmware from SD (work in progress, almost done)
- Loading applications from SD (tested as PoC, work scheduled for Q2) - Loading applications from SD (tested as PoC, work scheduled for Q2)
- More protocols (gathering feedback) - More protocols (gathering feedback)
- User documentation (work in progress) - User documentation (work in progress)

View File

@ -34,6 +34,7 @@
- `system` - System settings, tools and API - `system` - System settings, tools and API
- `tests` - Unit tests and etc - `tests` - Unit tests and etc
- `u2f` - U2F Application - `u2f` - U2F Application
- `updater` - Update service & application
- `application.c` - Firmware application list source - `application.c` - Firmware application list source
- `application.h` - Firmware application list header - `application.h` - Firmware application list header

View File

@ -82,11 +82,12 @@ static DialogMessageButton hw_version_screen(DialogsApp* dialogs, DialogMessage*
string_cat_printf( string_cat_printf(
buffer, buffer,
"%d.F%dB%dC%d %s\n", "%d.F%dB%dC%d %s %s\n",
furi_hal_version_get_hw_version(), furi_hal_version_get_hw_version(),
furi_hal_version_get_hw_target(), furi_hal_version_get_hw_target(),
furi_hal_version_get_hw_body(), furi_hal_version_get_hw_body(),
furi_hal_version_get_hw_connect(), furi_hal_version_get_hw_connect(),
furi_hal_version_get_hw_region_name(),
my_name ? my_name : "Unknown"); my_name ? my_name : "Unknown");
string_cat_printf(buffer, "Serial number:\n"); string_cat_printf(buffer, "Serial number:\n");
@ -116,9 +117,10 @@ static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage*
} else { } else {
string_cat_printf( string_cat_printf(
buffer, buffer,
"%s [%s]\n%s [%s]\n[%d] %s", "%s [%s]\n%s%s [%s]\n[%d] %s",
version_get_version(ver), version_get_version(ver),
version_get_builddate(ver), version_get_builddate(ver),
version_get_dirty_flag(ver) ? "[!] " : "",
version_get_githash(ver), version_get_githash(ver),
version_get_gitbranchnum(ver), version_get_gitbranchnum(ver),
version_get_target(ver), version_get_target(ver),
@ -135,36 +137,6 @@ static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage*
return result; return result;
} }
static DialogMessageButton bootloader_version_screen(DialogsApp* dialogs, DialogMessage* message) {
DialogMessageButton result;
string_t buffer;
string_init(buffer);
const Version* ver = furi_hal_version_get_bootloader_version();
if(!ver) {
string_cat_printf(buffer, "No info\n");
} else {
string_cat_printf(
buffer,
"%s [%s]\n%s [%s]\n[%d] %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[] = { const AboutDialogScreen about_screens[] = {
product_screen, product_screen,
compliance_screen, compliance_screen,
@ -172,8 +144,7 @@ const AboutDialogScreen about_screens[] = {
icon1_screen, icon1_screen,
icon2_screen, icon2_screen,
hw_version_screen, hw_version_screen,
fw_version_screen, fw_version_screen};
bootloader_version_screen};
const size_t about_screens_count = sizeof(about_screens) / sizeof(AboutDialogScreen); const size_t about_screens_count = sizeof(about_screens) / sizeof(AboutDialogScreen);

View File

@ -14,6 +14,7 @@ extern int32_t notification_srv(void* p);
extern int32_t power_srv(void* p); extern int32_t power_srv(void* p);
extern int32_t storage_srv(void* p); extern int32_t storage_srv(void* p);
extern int32_t desktop_srv(void* p); extern int32_t desktop_srv(void* p);
extern int32_t updater_srv(void* p);
// Apps // Apps
extern int32_t accessor_app(void* p); extern int32_t accessor_app(void* p);
@ -58,6 +59,7 @@ extern void storage_on_system_start();
extern void subghz_on_system_start(); extern void subghz_on_system_start();
extern void power_on_system_start(); extern void power_on_system_start();
extern void unit_tests_on_system_start(); extern void unit_tests_on_system_start();
extern void updater_on_system_start();
// Settings // Settings
extern int32_t notification_settings_app(void* p); extern int32_t notification_settings_app(void* p);
@ -91,6 +93,9 @@ const FlipperApplication FLIPPER_SERVICES[] = {
#endif #endif
#ifdef SRV_DESKTOP #ifdef SRV_DESKTOP
#ifdef SRV_UPDATER
#error SRV_UPDATER and SRV_DESKTOP are mutually exclusive!
#endif
{.app = desktop_srv, .name = "DesktopSrv", .stack_size = 2048, .icon = NULL}, {.app = desktop_srv, .name = "DesktopSrv", .stack_size = 2048, .icon = NULL},
#endif #endif
@ -117,9 +122,27 @@ const FlipperApplication FLIPPER_SERVICES[] = {
#ifdef SRV_STORAGE #ifdef SRV_STORAGE
{.app = storage_srv, .name = "StorageSrv", .stack_size = 3072, .icon = NULL}, {.app = storage_srv, .name = "StorageSrv", .stack_size = 3072, .icon = NULL},
#endif #endif
#ifdef SRV_UPDATER
#ifdef SRV_DESKTOP
#error SRV_UPDATER and SRV_DESKTOP are mutually exclusive!
#endif
{.app = updater_srv, .name = "UpdaterSrv", .stack_size = 2048, .icon = NULL},
#endif
}; };
const size_t FLIPPER_SERVICES_COUNT = sizeof(FLIPPER_SERVICES) / sizeof(FlipperApplication); const size_t FLIPPER_SERVICES_COUNT = COUNT_OF(FLIPPER_SERVICES);
const FlipperApplication FLIPPER_SYSTEM_APPS[] = {
#ifdef APP_UPDATER
#ifdef SRV_UPDATER
#error APP_UPDATER and SRV_UPDATER are mutually exclusive!
#endif
{.app = updater_srv, .name = "UpdaterApp", .stack_size = 2048, .icon = NULL},
#endif
};
const size_t FLIPPER_SYSTEM_APPS_COUNT = COUNT_OF(FLIPPER_SYSTEM_APPS);
// Main menu APP // Main menu APP
const FlipperApplication FLIPPER_APPS[] = { const FlipperApplication FLIPPER_APPS[] = {
@ -158,7 +181,7 @@ const FlipperApplication FLIPPER_APPS[] = {
}; };
const size_t FLIPPER_APPS_COUNT = sizeof(FLIPPER_APPS) / sizeof(FlipperApplication); const size_t FLIPPER_APPS_COUNT = COUNT_OF(FLIPPER_APPS);
// On system start hooks // On system start hooks
const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[] = { const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[] = {
@ -199,15 +222,18 @@ const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[] = {
#ifdef APP_UNIT_TESTS #ifdef APP_UNIT_TESTS
unit_tests_on_system_start, unit_tests_on_system_start,
#endif #endif
#ifdef APP_UPDATER
updater_on_system_start,
#endif
}; };
const size_t FLIPPER_ON_SYSTEM_START_COUNT = const size_t FLIPPER_ON_SYSTEM_START_COUNT = COUNT_OF(FLIPPER_ON_SYSTEM_START);
sizeof(FLIPPER_ON_SYSTEM_START) / sizeof(FlipperOnStartHook);
// Plugin menu // Plugin menu
const FlipperApplication FLIPPER_PLUGINS[] = { const FlipperApplication FLIPPER_PLUGINS[] = {
#ifdef APP_BLE_HID #ifdef APP_BLE_HID
{.app = bt_hid_app, .name = "Bluetooth remote", .stack_size = 1024, .icon = NULL}, {.app = bt_hid_app, .name = "Bluetooth Remote", .stack_size = 1024, .icon = NULL},
#endif #endif
#ifdef APP_MUSIC_PLAYER #ifdef APP_MUSIC_PLAYER
@ -219,7 +245,7 @@ const FlipperApplication FLIPPER_PLUGINS[] = {
#endif #endif
}; };
const size_t FLIPPER_PLUGINS_COUNT = sizeof(FLIPPER_PLUGINS) / sizeof(FlipperApplication); const size_t FLIPPER_PLUGINS_COUNT = COUNT_OF(FLIPPER_PLUGINS);
// Plugin menu // Plugin menu
const FlipperApplication FLIPPER_DEBUG_APPS[] = { const FlipperApplication FLIPPER_DEBUG_APPS[] = {
@ -244,7 +270,7 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = {
#endif #endif
#ifdef APP_USB_MOUSE #ifdef APP_USB_MOUSE
{.app = usb_mouse_app, .name = "USB Mouse demo", .stack_size = 1024, .icon = NULL}, {.app = usb_mouse_app, .name = "USB Mouse Demo", .stack_size = 1024, .icon = NULL},
#endif #endif
#ifdef APP_UART_ECHO #ifdef APP_UART_ECHO
@ -280,7 +306,7 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = {
#endif #endif
}; };
const size_t FLIPPER_DEBUG_APPS_COUNT = sizeof(FLIPPER_DEBUG_APPS) / sizeof(FlipperApplication); const size_t FLIPPER_DEBUG_APPS_COUNT = COUNT_OF(FLIPPER_DEBUG_APPS);
#ifdef APP_ARCHIVE #ifdef APP_ARCHIVE
const FlipperApplication FLIPPER_ARCHIVE = const FlipperApplication FLIPPER_ARCHIVE =
@ -295,7 +321,7 @@ const FlipperApplication FLIPPER_SETTINGS_APPS[] = {
#ifdef SRV_NOTIFICATION #ifdef SRV_NOTIFICATION
{.app = notification_settings_app, {.app = notification_settings_app,
.name = "LCD and notifications", .name = "LCD and Notifications",
.stack_size = 1024, .stack_size = 1024,
.icon = NULL}, .icon = NULL},
#endif #endif
@ -325,5 +351,4 @@ const FlipperApplication FLIPPER_SETTINGS_APPS[] = {
#endif #endif
}; };
const size_t FLIPPER_SETTINGS_APPS_COUNT = const size_t FLIPPER_SETTINGS_APPS_COUNT = COUNT_OF(FLIPPER_SETTINGS_APPS);
sizeof(FLIPPER_SETTINGS_APPS) / sizeof(FlipperApplication);

View File

@ -42,6 +42,12 @@ extern const size_t FLIPPER_PLUGINS_COUNT;
extern const FlipperApplication FLIPPER_DEBUG_APPS[]; extern const FlipperApplication FLIPPER_DEBUG_APPS[];
extern const size_t FLIPPER_DEBUG_APPS_COUNT; extern const size_t FLIPPER_DEBUG_APPS_COUNT;
/* System apps
* Can only be spawned by loader by name
*/
extern const FlipperApplication FLIPPER_SYSTEM_APPS[];
extern const size_t FLIPPER_SYSTEM_APPS_COUNT;
/* Seperate scene app holder /* Seperate scene app holder
* Spawned by loader * Spawned by loader
*/ */

View File

@ -5,6 +5,16 @@ CFLAGS += -I$(APP_DIR)
C_SOURCES += $(shell find $(APP_DIR) -name "*.c") C_SOURCES += $(shell find $(APP_DIR) -name "*.c")
CPP_SOURCES += $(shell find $(APP_DIR) -name "*.cpp") CPP_SOURCES += $(shell find $(APP_DIR) -name "*.cpp")
RAM_EXEC ?= 0
ifeq ($(RAM_EXEC), 1)
APP_RELEASE = 0
SRV_GUI = 1
SRV_INPUT = 1
SRV_NOTIFICATION = 1
SRV_STORAGE = 1
SRV_UPDATER = 1
APP_UPDATER = 0
endif
APP_RELEASE ?= 1 APP_RELEASE ?= 1
ifeq ($(APP_RELEASE), 1) ifeq ($(APP_RELEASE), 1)
@ -18,13 +28,13 @@ SRV_INPUT = 1
SRV_LOADER = 1 SRV_LOADER = 1
SRV_NOTIFICATION = 1 SRV_NOTIFICATION = 1
SRV_POWER = 1 SRV_POWER = 1
SRV_RPC = 1 SRV_RPC = 1
SRV_STORAGE = 1 SRV_STORAGE = 1
# Apps # Apps
SRV_DESKTOP = 1 SRV_DESKTOP = 1
APP_ARCHIVE = 1 APP_ARCHIVE = 1
APP_GPIO = 1 APP_GPIO = 1
APP_IBUTTON = 1 APP_IBUTTON = 1
APP_INFRARED = 1 APP_INFRARED = 1
APP_LF_RFID = 1 APP_LF_RFID = 1
@ -32,6 +42,7 @@ APP_NFC = 1
APP_SUBGHZ = 1 APP_SUBGHZ = 1
APP_ABOUT = 1 APP_ABOUT = 1
APP_PASSPORT = 1 APP_PASSPORT = 1
APP_UPDATER = 1
# Plugins # Plugins
APP_MUSIC_PLAYER = 1 APP_MUSIC_PLAYER = 1
@ -223,6 +234,14 @@ CFLAGS += -DAPP_IBUTTON
SRV_GUI = 1 SRV_GUI = 1
endif endif
APP_UPDATER ?= 0
ifeq ($(APP_UPDATER), 1)
CFLAGS += -DAPP_UPDATER
SRV_GUI = 1
SRV_STORAGE = 1
SRV_NOTIFICATION = 1
SRV_INPUT = 1
endif
# Services # Services
# that will start with OS # that will start with OS
@ -245,6 +264,14 @@ SRV_GUI = 1
endif endif
SRV_UPDATER ?= 0
ifeq ($(SRV_UPDATER), 1)
CFLAGS += -DSRV_UPDATER
SRV_STORAGE = 1
SRV_GUI = 1
endif
SRV_DOLPHIN ?= 0 SRV_DOLPHIN ?= 0
ifeq ($(SRV_DOLPHIN), 1) ifeq ($(SRV_DOLPHIN), 1)
CFLAGS += -DSRV_DOLPHIN CFLAGS += -DSRV_DOLPHIN

View File

@ -21,13 +21,23 @@ bool archive_app_is_available(void* context, const char* path) {
ArchiveAppTypeEnum app = archive_get_app_type(path); ArchiveAppTypeEnum app = archive_get_app_type(path);
if(app == ArchiveAppTypeU2f) { if(app == ArchiveAppTypeU2f) {
FileWorker* file_worker = file_worker_alloc(true);
bool file_exists = false; bool file_exists = false;
file_worker_is_file_exist(file_worker, "/any/u2f/key.u2f", &file_exists); Storage* fs_api = furi_record_open("storage");
File* file = storage_file_alloc(fs_api);
file_exists = storage_file_open(file, "/any/u2f/key.u2f", FSAM_READ, FSOM_OPEN_EXISTING);
if(file_exists) { if(file_exists) {
file_worker_is_file_exist(file_worker, "/any/u2f/cnt.u2f", &file_exists); storage_file_close(file);
file_exists =
storage_file_open(file, "/any/u2f/cnt.u2f", FSAM_READ, FSOM_OPEN_EXISTING);
if(file_exists) {
storage_file_close(file);
}
} }
file_worker_free(file_worker);
storage_file_free(file);
furi_record_close("storage");
return file_exists; return file_exists;
} else { } else {
return false; return false;
@ -60,10 +70,10 @@ void archive_app_delete_file(void* context, const char* path) {
bool res = false; bool res = false;
if(app == ArchiveAppTypeU2f) { if(app == ArchiveAppTypeU2f) {
FileWorker* file_worker = file_worker_alloc(true); Storage* fs_api = furi_record_open("storage");
res = file_worker_remove(file_worker, "/any/u2f/key.u2f"); res = (storage_common_remove(fs_api, "/any/u2f/key.u2f") == FSE_OK);
res |= file_worker_remove(file_worker, "/any/u2f/cnt.u2f"); res |= (storage_common_remove(fs_api, "/any/u2f/cnt.u2f") == FSE_OK);
file_worker_free(file_worker); furi_record_close("storage");
if(archive_is_favorite("/app:u2f/U2F Token")) { if(archive_is_favorite("/app:u2f/U2F Token")) {
archive_favorites_delete("/app:u2f/U2F Token"); archive_favorites_delete("/app:u2f/U2F Token");

View File

@ -120,13 +120,12 @@ void archive_file_array_swap(ArchiveBrowserView* browser, int8_t dir) {
ArchiveFile_t_clear(&temp); ArchiveFile_t_clear(&temp);
} else if(model->item_idx == array_size && dir > 0) { } else if(model->item_idx == array_size && dir > 0) {
ArchiveFile_t_init(&temp); ArchiveFile_t_init(&temp);
files_array_pop_at(&temp, model->files, model->item_idx); files_array_pop_at(&temp, model->files, 0);
files_array_push_at(model->files, array_size, temp); files_array_push_at(model->files, array_size, temp);
ArchiveFile_t_clear(&temp); ArchiveFile_t_clear(&temp);
} else { } else {
files_array_swap_at(model->files, model->item_idx, swap_idx); files_array_swap_at(model->files, model->item_idx, swap_idx);
} }
return false; return false;
}); });
} }
@ -392,6 +391,17 @@ void archive_enter_dir(ArchiveBrowserView* browser, string_t name) {
furi_assert(browser); furi_assert(browser);
furi_assert(name); furi_assert(name);
uint8_t browser_depth = 0;
with_view_model(
browser->view, (ArchiveBrowserViewModel * model) {
browser_depth = idx_last_array_size(model->idx_last);
return false;
});
if(browser_depth > BROWSER_DEPTH_MAX) {
return;
}
archive_dir_count_items(browser, string_get_cstr(name)); archive_dir_count_items(browser, string_get_cstr(name));
with_view_model( with_view_model(

View File

@ -4,6 +4,7 @@
#define TAB_RIGHT InputKeyRight //default tab swith direction #define TAB_RIGHT InputKeyRight //default tab swith direction
#define FILE_LIST_BUF_LEN 100 #define FILE_LIST_BUF_LEN 100
#define BROWSER_DEPTH_MAX 8
static const char* tab_default_paths[] = { static const char* tab_default_paths[] = {
[ArchiveTabFavorites] = "/any/favorites", [ArchiveTabFavorites] = "/any/favorites",
@ -25,6 +26,7 @@ static const char* known_ext[] = {
[ArchiveFileTypeInfrared] = ".ir", [ArchiveFileTypeInfrared] = ".ir",
[ArchiveFileTypeBadUsb] = ".txt", [ArchiveFileTypeBadUsb] = ".txt",
[ArchiveFileTypeU2f] = "?", [ArchiveFileTypeU2f] = "?",
[ArchiveFileTypeUpdateManifest] = ".fuf",
[ArchiveFileTypeFolder] = "?", [ArchiveFileTypeFolder] = "?",
[ArchiveFileTypeUnknown] = "*", [ArchiveFileTypeUnknown] = "*",
}; };

View File

@ -4,20 +4,63 @@
#include "archive_apps.h" #include "archive_apps.h"
#include "archive_browser.h" #include "archive_browser.h"
#define ARCHIVE_FAV_FILE_BUF_LEN 32
static bool archive_favorites_read_line(File* file, string_t str_result) {
string_reset(str_result);
uint8_t buffer[ARCHIVE_FAV_FILE_BUF_LEN];
bool result = false;
do {
uint16_t read_count = storage_file_read(file, buffer, ARCHIVE_FAV_FILE_BUF_LEN);
if(storage_file_get_error(file) != FSE_OK) {
return false;
}
for(uint16_t i = 0; i < read_count; i++) {
if(buffer[i] == '\n') {
uint32_t position = storage_file_tell(file);
if(storage_file_get_error(file) != FSE_OK) {
return false;
}
position = position - read_count + i + 1;
storage_file_seek(file, position, true);
if(storage_file_get_error(file) != FSE_OK) {
return false;
}
result = true;
break;
} else {
string_push_back(str_result, buffer[i]);
}
}
if(result || read_count == 0) {
break;
}
} while(true);
return result;
}
uint16_t archive_favorites_count(void* context) { uint16_t archive_favorites_count(void* context) {
furi_assert(context); furi_assert(context);
FileWorker* file_worker = file_worker_alloc(true); Storage* fs_api = furi_record_open("storage");
File* file = storage_file_alloc(fs_api);
string_t buffer; string_t buffer;
string_init(buffer); string_init(buffer);
bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
uint16_t lines = 0; uint16_t lines = 0;
if(result) { if(result) {
while(1) { while(1) {
if(!file_worker_read_until(file_worker, buffer, '\n')) { if(!archive_favorites_read_line(file, buffer)) {
break; break;
} }
if(!string_size(buffer)) { if(!string_size(buffer)) {
@ -27,21 +70,26 @@ uint16_t archive_favorites_count(void* context) {
} }
} }
storage_file_close(file);
string_clear(buffer); string_clear(buffer);
file_worker_close(file_worker); storage_file_free(file);
file_worker_free(file_worker); furi_record_close("storage");
return lines; return lines;
} }
static bool archive_favourites_rescan() { static bool archive_favourites_rescan() {
string_t buffer; string_t buffer;
string_init(buffer); string_init(buffer);
FileWorker* file_worker = file_worker_alloc(true); Storage* fs_api = furi_record_open("storage");
File* file = storage_file_alloc(fs_api);
File* fav_item_file = storage_file_alloc(fs_api);
bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
if(result) { if(result) {
while(1) { while(1) {
if(!file_worker_read_until(file_worker, buffer, '\n')) { if(!archive_favorites_read_line(file, buffer)) {
break; break;
} }
if(!string_size(buffer)) { if(!string_size(buffer)) {
@ -53,10 +101,13 @@ static bool archive_favourites_rescan() {
archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer)); archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer));
} }
} else { } else {
bool file_exists = false; bool file_exists = storage_file_open(
file_worker_is_file_exist(file_worker, string_get_cstr(buffer), &file_exists); fav_item_file, string_get_cstr(buffer), FSAM_READ, FSOM_OPEN_EXISTING);
if(file_exists) { if(file_exists) {
storage_file_close(fav_item_file);
archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer)); archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer));
} else {
storage_file_close(fav_item_file);
} }
} }
} }
@ -64,11 +115,14 @@ static bool archive_favourites_rescan() {
string_clear(buffer); string_clear(buffer);
file_worker_close(file_worker); storage_file_close(file);
file_worker_remove(file_worker, ARCHIVE_FAV_PATH); storage_common_remove(fs_api, ARCHIVE_FAV_PATH);
file_worker_rename(file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); storage_common_rename(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);
storage_common_remove(fs_api, ARCHIVE_FAV_TEMP_PATH);
file_worker_free(file_worker); storage_file_free(file);
storage_file_free(fav_item_file);
furi_record_close("storage");
return result; return result;
} }
@ -77,7 +131,9 @@ bool archive_favorites_read(void* context) {
furi_assert(context); furi_assert(context);
ArchiveBrowserView* browser = context; ArchiveBrowserView* browser = context;
FileWorker* file_worker = file_worker_alloc(true); Storage* fs_api = furi_record_open("storage");
File* file = storage_file_alloc(fs_api);
File* fav_item_file = storage_file_alloc(fs_api);
string_t buffer; string_t buffer;
FileInfo file_info; FileInfo file_info;
@ -88,11 +144,11 @@ bool archive_favorites_read(void* context) {
archive_file_array_rm_all(browser); archive_file_array_rm_all(browser);
bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
if(result) { if(result) {
while(1) { while(1) {
if(!file_worker_read_until(file_worker, buffer, '\n')) { if(!archive_favorites_read_line(file, buffer)) {
break; break;
} }
if(!string_size(buffer)) { if(!string_size(buffer)) {
@ -107,13 +163,15 @@ bool archive_favorites_read(void* context) {
need_refresh = true; need_refresh = true;
} }
} else { } else {
bool file_exists = false; bool file_exists = storage_file_open(
file_worker_is_file_exist(file_worker, string_get_cstr(buffer), &file_exists); fav_item_file, string_get_cstr(buffer), FSAM_READ, FSOM_OPEN_EXISTING);
if(file_exists) { if(file_exists) {
storage_common_stat(fs_api, string_get_cstr(buffer), &file_info);
storage_file_close(fav_item_file);
archive_add_file_item(browser, &file_info, string_get_cstr(buffer)); archive_add_file_item(browser, &file_info, string_get_cstr(buffer));
file_count++; file_count++;
} else { } else {
storage_file_close(fav_item_file);
need_refresh = true; need_refresh = true;
} }
} }
@ -121,9 +179,11 @@ bool archive_favorites_read(void* context) {
string_reset(buffer); string_reset(buffer);
} }
} }
storage_file_close(file);
string_clear(buffer); string_clear(buffer);
file_worker_close(file_worker); storage_file_free(file);
file_worker_free(file_worker); storage_file_free(fav_item_file);
furi_record_close("storage");
archive_set_item_count(browser, file_count); archive_set_item_count(browser, file_count);
@ -143,12 +203,14 @@ bool archive_favorites_delete(const char* format, ...) {
va_end(args); va_end(args);
string_init(buffer); string_init(buffer);
FileWorker* file_worker = file_worker_alloc(true); Storage* fs_api = furi_record_open("storage");
File* file = storage_file_alloc(fs_api);
bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
if(result) { if(result) {
while(1) { while(1) {
if(!file_worker_read_until(file_worker, buffer, '\n')) { if(!archive_favorites_read_line(file, buffer)) {
break; break;
} }
if(!string_size(buffer)) { if(!string_size(buffer)) {
@ -164,11 +226,13 @@ bool archive_favorites_delete(const char* format, ...) {
string_clear(buffer); string_clear(buffer);
string_clear(filename); string_clear(filename);
file_worker_close(file_worker); storage_file_close(file);
file_worker_remove(file_worker, ARCHIVE_FAV_PATH); storage_common_remove(fs_api, ARCHIVE_FAV_PATH);
file_worker_rename(file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); storage_common_rename(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);
storage_common_remove(fs_api, ARCHIVE_FAV_TEMP_PATH);
file_worker_free(file_worker); storage_file_free(file);
furi_record_close("storage");
return result; return result;
} }
@ -182,14 +246,15 @@ bool archive_is_favorite(const char* format, ...) {
va_end(args); va_end(args);
string_init(buffer); string_init(buffer);
FileWorker* file_worker = file_worker_alloc(true); Storage* fs_api = furi_record_open("storage");
File* file = storage_file_alloc(fs_api);
bool found = false; bool found = false;
bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
if(result) { if(result) {
while(1) { while(1) {
if(!file_worker_read_until(file_worker, buffer, '\n')) { if(!archive_favorites_read_line(file, buffer)) {
break; break;
} }
if(!string_size(buffer)) { if(!string_size(buffer)) {
@ -202,10 +267,11 @@ bool archive_is_favorite(const char* format, ...) {
} }
} }
storage_file_close(file);
string_clear(buffer); string_clear(buffer);
string_clear(filename); string_clear(filename);
file_worker_close(file_worker); storage_file_free(file);
file_worker_free(file_worker); furi_record_close("storage");
return found; return found;
} }
@ -214,7 +280,8 @@ bool archive_favorites_rename(const char* src, const char* dst) {
furi_assert(src); furi_assert(src);
furi_assert(dst); furi_assert(dst);
FileWorker* file_worker = file_worker_alloc(true); Storage* fs_api = furi_record_open("storage");
File* file = storage_file_alloc(fs_api);
string_t path; string_t path;
string_t buffer; string_t buffer;
@ -223,11 +290,11 @@ bool archive_favorites_rename(const char* src, const char* dst) {
string_init(path); string_init(path);
string_printf(path, "%s", src); string_printf(path, "%s", src);
bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
if(result) { if(result) {
while(1) { while(1) {
if(!file_worker_read_until(file_worker, buffer, '\n')) { if(!archive_favorites_read_line(file, buffer)) {
break; break;
} }
if(!string_size(buffer)) { if(!string_size(buffer)) {
@ -244,11 +311,13 @@ bool archive_favorites_rename(const char* src, const char* dst) {
string_clear(buffer); string_clear(buffer);
string_clear(path); string_clear(path);
file_worker_close(file_worker); storage_file_close(file);
file_worker_remove(file_worker, ARCHIVE_FAV_PATH); storage_common_remove(fs_api, ARCHIVE_FAV_PATH);
file_worker_rename(file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); storage_common_rename(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);
storage_common_remove(fs_api, ARCHIVE_FAV_TEMP_PATH);
file_worker_free(file_worker); storage_file_free(file);
furi_record_close("storage");
return result; return result;
} }
@ -263,15 +332,18 @@ void archive_favorites_save(void* context) {
furi_assert(context); furi_assert(context);
ArchiveBrowserView* browser = context; ArchiveBrowserView* browser = context;
FileWorker* file_worker = file_worker_alloc(true); Storage* fs_api = furi_record_open("storage");
File* file = storage_file_alloc(fs_api);
for(size_t i = 0; i < archive_file_get_array_size(browser); i++) { for(size_t i = 0; i < archive_file_get_array_size(browser); i++) {
ArchiveFile_t* item = archive_get_file_at(browser, i); ArchiveFile_t* item = archive_get_file_at(browser, i);
archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(item->name)); archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(item->name));
} }
file_worker_remove(file_worker, ARCHIVE_FAV_PATH); storage_common_remove(fs_api, ARCHIVE_FAV_PATH);
file_worker_rename(file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); storage_common_rename(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);
storage_common_remove(fs_api, ARCHIVE_FAV_TEMP_PATH);
file_worker_free(file_worker); storage_file_free(file);
furi_record_close("storage");
} }

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "file_worker.h"
#include <storage/storage.h>
#define ARCHIVE_FAV_PATH "/any/favorites.txt" #define ARCHIVE_FAV_PATH "/any/favorites.txt"
#define ARCHIVE_FAV_TEMP_PATH "/any/favorites.tmp" #define ARCHIVE_FAV_TEMP_PATH "/any/favorites.tmp"

View File

@ -201,18 +201,18 @@ void archive_file_append(const char* path, const char* format, ...) {
string_init_vprintf(string, format, args); string_init_vprintf(string, format, args);
va_end(args); va_end(args);
FileWorker* file_worker = file_worker_alloc(false); Storage* fs_api = furi_record_open("storage");
File* file = storage_file_alloc(fs_api);
if(!file_worker_open(file_worker, path, FSAM_WRITE, FSOM_OPEN_APPEND)) { bool res = storage_file_open(file, path, FSAM_WRITE, FSOM_OPEN_APPEND);
FURI_LOG_E(TAG, "Append open error");
if(res) {
storage_file_write(file, string_get_cstr(string), string_size(string));
} }
if(!file_worker_write(file_worker, string_get_cstr(string), string_size(string))) { storage_file_close(file);
FURI_LOG_E(TAG, "Append write error"); storage_file_free(file);
} furi_record_close("storage");
file_worker_close(file_worker);
file_worker_free(file_worker);
} }
void archive_delete_file(void* context, const char* format, ...) { void archive_delete_file(void* context, const char* format, ...) {

View File

@ -1,6 +1,8 @@
#pragma once #pragma once
#include "file_worker.h"
#include <m-array.h> #include <m-array.h>
#include <m-string.h>
#include <storage/storage.h>
typedef enum { typedef enum {
ArchiveFileTypeIButton, ArchiveFileTypeIButton,
@ -10,6 +12,7 @@ typedef enum {
ArchiveFileTypeInfrared, ArchiveFileTypeInfrared,
ArchiveFileTypeBadUsb, ArchiveFileTypeBadUsb,
ArchiveFileTypeU2f, ArchiveFileTypeU2f,
ArchiveFileTypeUpdateManifest,
ArchiveFileTypeFolder, ArchiveFileTypeFolder,
ArchiveFileTypeUnknown, ArchiveFileTypeUnknown,
ArchiveFileTypeLoading, ArchiveFileTypeLoading,

View File

@ -15,6 +15,7 @@ static const char* flipper_app_name[] = {
[ArchiveFileTypeInfrared] = "Infrared", [ArchiveFileTypeInfrared] = "Infrared",
[ArchiveFileTypeBadUsb] = "Bad USB", [ArchiveFileTypeBadUsb] = "Bad USB",
[ArchiveFileTypeU2f] = "U2F", [ArchiveFileTypeU2f] = "U2F",
[ArchiveFileTypeUpdateManifest] = "UpdaterApp",
}; };
static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selected) { static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selected) {

View File

@ -12,7 +12,8 @@ static const char* ArchiveTabNames[] = {
[ArchiveTabInfrared] = "Infrared", [ArchiveTabInfrared] = "Infrared",
[ArchiveTabBadUsb] = "Bad USB", [ArchiveTabBadUsb] = "Bad USB",
[ArchiveTabU2f] = "U2F", [ArchiveTabU2f] = "U2F",
[ArchiveTabBrowser] = "Browser"}; [ArchiveTabBrowser] = "Browser",
};
static const Icon* ArchiveItemIcons[] = { static const Icon* ArchiveItemIcons[] = {
[ArchiveFileTypeIButton] = &I_ibutt_10px, [ArchiveFileTypeIButton] = &I_ibutt_10px,
@ -22,6 +23,7 @@ static const Icon* ArchiveItemIcons[] = {
[ArchiveFileTypeInfrared] = &I_ir_10px, [ArchiveFileTypeInfrared] = &I_ir_10px,
[ArchiveFileTypeBadUsb] = &I_badusb_10px, [ArchiveFileTypeBadUsb] = &I_badusb_10px,
[ArchiveFileTypeU2f] = &I_u2f_10px, [ArchiveFileTypeU2f] = &I_u2f_10px,
[ArchiveFileTypeUpdateManifest] = &I_update_10px,
[ArchiveFileTypeFolder] = &I_dir_10px, [ArchiveFileTypeFolder] = &I_dir_10px,
[ArchiveFileTypeUnknown] = &I_unknown_10px, [ArchiveFileTypeUnknown] = &I_unknown_10px,
[ArchiveFileTypeLoading] = &I_unknown_10px, // TODO loading icon [ArchiveFileTypeLoading] = &I_unknown_10px, // TODO loading icon
@ -52,6 +54,10 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
ArchiveFile_t* selected = files_array_get(model->files, model->item_idx - model->array_offset); ArchiveFile_t* selected = files_array_get(model->files, model->item_idx - model->array_offset);
if((selected->fav) || (model->tab_idx == ArchiveTabFavorites)) {
string_set_str(menu[1], "Unpin");
}
if(!archive_is_known_app(selected->type)) { if(!archive_is_known_app(selected->type)) {
string_set_str(menu[0], "---"); string_set_str(menu[0], "---");
string_set_str(menu[1], "---"); string_set_str(menu[1], "---");
@ -65,10 +71,6 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
} }
} }
if((selected->fav) || (model->tab_idx == ArchiveTabFavorites)) {
string_set_str(menu[1], "Unpin");
}
for(size_t i = 0; i < MENU_ITEMS; i++) { for(size_t i = 0; i < MENU_ITEMS; i++) {
canvas_draw_str(canvas, 82, 27 + i * 11, string_get_cstr(menu[i])); canvas_draw_str(canvas, 82, 27 + i * 11, string_get_cstr(menu[i]));
string_clear(menu[i]); string_clear(menu[i]);

View File

@ -244,5 +244,7 @@ void bt_on_system_start() {
cli_add_command(cli, "bt", CliCommandFlagDefault, bt_cli, NULL); cli_add_command(cli, "bt", CliCommandFlagDefault, bt_cli, NULL);
furi_record_close("bt"); furi_record_close("bt");
furi_record_close("cli"); furi_record_close("cli");
#else
UNUSED(bt_cli);
#endif #endif
} }

View File

@ -54,7 +54,7 @@ void bt_settings_scene_start_on_enter(void* context) {
variable_item_set_current_value_index(item, BtSettingOff); variable_item_set_current_value_index(item, BtSettingOff);
variable_item_set_current_value_text(item, bt_settings_text[BtSettingOff]); variable_item_set_current_value_text(item, bt_settings_text[BtSettingOff]);
} }
variable_item_list_add(var_item_list, "Forget all paired devices", 1, NULL, NULL); variable_item_list_add(var_item_list, "Forget All Paired Devices", 1, NULL, NULL);
variable_item_list_set_enter_callback( variable_item_list_set_enter_callback(
var_item_list, bt_settings_scene_start_var_list_enter_callback, app); var_item_list, bt_settings_scene_start_var_list_enter_callback, app);
} else { } else {

View File

@ -95,10 +95,11 @@ void cli_motd() {
const Version* firmware_version = furi_hal_version_get_firmware_version(); const Version* firmware_version = furi_hal_version_get_firmware_version();
if(firmware_version) { if(firmware_version) {
printf( printf(
"Firmware version: %s %s (%s built on %s)\r\n", "Firmware version: %s %s (%s%s built on %s)\r\n",
version_get_gitbranch(firmware_version), version_get_gitbranch(firmware_version),
version_get_version(firmware_version), version_get_version(firmware_version),
version_get_githash(firmware_version), version_get_githash(firmware_version),
version_get_dirty_flag(firmware_version) ? "-dirty" : "",
version_get_builddate(firmware_version)); version_get_builddate(firmware_version));
} }
} }
@ -400,6 +401,8 @@ void cli_delete_command(Cli* cli, const char* name) {
int32_t cli_srv(void* p) { int32_t cli_srv(void* p) {
Cli* cli = cli_alloc(); Cli* cli = cli_alloc();
furi_hal_vcp_init();
// Init basic cli commands // Init basic cli commands
cli_commands_init(cli); cli_commands_init(cli);

View File

@ -6,6 +6,7 @@
#include <time.h> #include <time.h>
#include <notification/notification_messages.h> #include <notification/notification_messages.h>
#include <loader/loader.h> #include <loader/loader.h>
#include <stream_buffer.h>
// Close to ISO, `date +'%Y-%m-%d %H:%M:%S %u'` // Close to ISO, `date +'%Y-%m-%d %H:%M:%S %u'`
#define CLI_DATE_FORMAT "%.4d-%.2d-%.2d %.2d:%.2d:%.2d %d" #define CLI_DATE_FORMAT "%.4d-%.2d-%.2d %.2d:%.2d:%.2d %d"
@ -126,11 +127,28 @@ void cli_command_date(Cli* cli, string_t args, void* context) {
} }
} }
#define CLI_COMMAND_LOG_RING_SIZE 2048
#define CLI_COMMAND_LOG_BUFFER_SIZE 64
void cli_command_log_tx_callback(const uint8_t* buffer, size_t size, void* context) {
xStreamBufferSend(context, buffer, size, 0);
}
void cli_command_log(Cli* cli, string_t args, void* context) { void cli_command_log(Cli* cli, string_t args, void* context) {
furi_stdglue_set_global_stdout_callback(cli_stdout_callback); StreamBufferHandle_t ring = xStreamBufferCreate(CLI_COMMAND_LOG_RING_SIZE, 1);
printf("Press any key to stop...\r\n"); uint8_t buffer[CLI_COMMAND_LOG_BUFFER_SIZE];
cli_getc(cli);
furi_stdglue_set_global_stdout_callback(NULL); furi_hal_console_set_tx_callback(cli_command_log_tx_callback, ring);
printf("Press CTRL+C to stop...\r\n");
while(!cli_cmd_interrupt_received(cli)) {
size_t ret = xStreamBufferReceive(ring, buffer, CLI_COMMAND_LOG_BUFFER_SIZE, 50);
cli_write(cli, buffer, ret);
}
furi_hal_console_set_tx_callback(NULL, NULL);
vStreamBufferDelete(ring);
} }
void cli_command_vibro(Cli* cli, string_t args, void* context) { void cli_command_vibro(Cli* cli, string_t args, void* context) {
@ -327,6 +345,7 @@ void cli_command_ps(Cli* cli, string_t args, void* context) {
void cli_command_free(Cli* cli, string_t args, void* context) { void cli_command_free(Cli* cli, string_t args, void* context) {
printf("Free heap size: %d\r\n", memmgr_get_free_heap()); printf("Free heap size: %d\r\n", memmgr_get_free_heap());
printf("Total heap size: %d\r\n", memmgr_get_total_heap());
printf("Minimum heap size: %d\r\n", memmgr_get_minimum_free_heap()); printf("Minimum heap size: %d\r\n", memmgr_get_minimum_free_heap());
printf("Maximum heap block: %d\r\n", memmgr_heap_get_max_free_block()); printf("Maximum heap block: %d\r\n", memmgr_heap_get_max_free_block());
} }

View File

@ -276,7 +276,7 @@ void crypto_cli_store_key(Cli* cli, string_t args) {
string_clear(key_type); string_clear(key_type);
} }
void crypto_cli(Cli* cli, string_t args, void* context) { static void crypto_cli(Cli* cli, string_t args, void* context) {
string_t cmd; string_t cmd;
string_init(cmd); string_init(cmd);
@ -317,5 +317,7 @@ void crypto_on_system_start() {
Cli* cli = furi_record_open("cli"); Cli* cli = furi_record_open("cli");
cli_add_command(cli, "crypto", CliCommandFlagDefault, crypto_cli, NULL); cli_add_command(cli, "crypto", CliCommandFlagDefault, crypto_cli, NULL);
furi_record_close("cli"); furi_record_close("cli");
#else
UNUSED(crypto_cli);
#endif #endif
} }

View File

@ -21,7 +21,7 @@ static void usb_mouse_render_callback(Canvas* canvas, void* ctx) {
canvas_clear(canvas); canvas_clear(canvas);
canvas_set_font(canvas, FontPrimary); canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 0, 10, "USB Mouse demo"); canvas_draw_str(canvas, 0, 10, "USB Mouse Demo");
canvas_set_font(canvas, FontSecondary); canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 0, 63, "Hold [back] to exit"); canvas_draw_str(canvas, 0, 63, "Hold [back] to exit");

View File

@ -1,6 +1,9 @@
#include <storage/storage.h> #include <storage/storage.h>
#include <assets_icons.h> #include <assets_icons.h>
#include <gui/gui.h>
#include <gui/view_stack.h> #include <gui/view_stack.h>
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include <furi.h> #include <furi.h>
#include <furi_hal.h> #include <furi_hal.h>
@ -13,6 +16,10 @@
#include "desktop_i.h" #include "desktop_i.h"
#include "desktop_helpers.h" #include "desktop_helpers.h"
static void desktop_auto_lock_arm(Desktop*);
static void desktop_auto_lock_inhibit(Desktop*);
static void desktop_start_auto_lock_timer(Desktop*);
static void desktop_loader_callback(const void* message, void* context) { static void desktop_loader_callback(const void* message, void* context) {
furi_assert(context); furi_assert(context);
Desktop* desktop = context; Desktop* desktop = context;
@ -37,9 +44,19 @@ static bool desktop_custom_event_callback(void* context, uint32_t event) {
switch(event) { switch(event) {
case DesktopGlobalBeforeAppStarted: case DesktopGlobalBeforeAppStarted:
animation_manager_unload_and_stall_animation(desktop->animation_manager); animation_manager_unload_and_stall_animation(desktop->animation_manager);
desktop_auto_lock_inhibit(desktop);
return true; return true;
case DesktopGlobalAfterAppFinished: case DesktopGlobalAfterAppFinished:
animation_manager_load_and_continue_animation(desktop->animation_manager); animation_manager_load_and_continue_animation(desktop->animation_manager);
// TODO: Implement a message mechanism for loading settings and (optionally)
// locking and unlocking
LOAD_DESKTOP_SETTINGS(&desktop->settings);
desktop_auto_lock_arm(desktop);
return true;
case DesktopGlobalAutoLock:
if(!loader_is_locked(desktop->loader)) {
desktop_lock(desktop);
}
return true; return true;
} }
@ -58,6 +75,63 @@ static void desktop_tick_event_callback(void* context) {
scene_manager_handle_tick_event(app->scene_manager); scene_manager_handle_tick_event(app->scene_manager);
} }
static void desktop_input_event_callback(const void* value, void* context) {
furi_assert(value);
furi_assert(context);
const InputEvent* event = value;
Desktop* desktop = context;
if(event->type == InputTypePress) {
desktop_start_auto_lock_timer(desktop);
}
}
static void desktop_auto_lock_timer_callback(void* context) {
furi_assert(context);
Desktop* desktop = context;
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalAutoLock);
}
static void desktop_start_auto_lock_timer(Desktop* desktop) {
osTimerStart(
desktop->auto_lock_timer, furi_hal_ms_to_ticks(desktop->settings.auto_lock_delay_ms));
}
static void desktop_stop_auto_lock_timer(Desktop* desktop) {
osTimerStop(desktop->auto_lock_timer);
}
static void desktop_auto_lock_arm(Desktop* desktop) {
if(desktop->settings.auto_lock_delay_ms) {
desktop->input_events_subscription = furi_pubsub_subscribe(
desktop->input_events_pubsub, desktop_input_event_callback, desktop);
desktop_start_auto_lock_timer(desktop);
}
}
static void desktop_auto_lock_inhibit(Desktop* desktop) {
desktop_stop_auto_lock_timer(desktop);
if(desktop->input_events_subscription) {
furi_pubsub_unsubscribe(desktop->input_events_pubsub, desktop->input_events_subscription);
desktop->input_events_subscription = NULL;
}
}
void desktop_lock(Desktop* desktop) {
desktop_auto_lock_inhibit(desktop);
scene_manager_set_scene_state(
desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER);
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked);
notification_message(desktop->notification, &sequence_display_off_delay_1000);
}
void desktop_unlock(Desktop* desktop) {
furi_hal_rtc_set_pin_fails(0);
desktop_helpers_unlock_system(desktop);
desktop_view_locked_unlock(desktop->locked_view);
scene_manager_search_and_switch_to_previous_scene(desktop->scene_manager, DesktopSceneMain);
desktop_auto_lock_arm(desktop);
}
Desktop* desktop_alloc() { Desktop* desktop_alloc() {
Desktop* desktop = malloc(sizeof(Desktop)); Desktop* desktop = malloc(sizeof(Desktop));
@ -146,9 +220,17 @@ Desktop* desktop_alloc() {
animation_manager_is_animation_loaded(desktop->animation_manager)) { animation_manager_is_animation_loaded(desktop->animation_manager)) {
animation_manager_unload_and_stall_animation(desktop->animation_manager); animation_manager_unload_and_stall_animation(desktop->animation_manager);
} }
desktop->notification = furi_record_open("notification");
desktop->app_start_stop_subscription = furi_pubsub_subscribe( desktop->app_start_stop_subscription = furi_pubsub_subscribe(
loader_get_pubsub(desktop->loader), desktop_loader_callback, desktop); loader_get_pubsub(desktop->loader), desktop_loader_callback, desktop);
desktop->input_events_pubsub = furi_record_open("input_events");
desktop->input_events_subscription = NULL;
desktop->auto_lock_timer =
osTimerNew(desktop_auto_lock_timer_callback, osTimerOnce, desktop, NULL);
return desktop; return desktop;
} }
@ -157,8 +239,17 @@ void desktop_free(Desktop* desktop) {
furi_pubsub_unsubscribe( furi_pubsub_unsubscribe(
loader_get_pubsub(desktop->loader), desktop->app_start_stop_subscription); loader_get_pubsub(desktop->loader), desktop->app_start_stop_subscription);
if(desktop->input_events_subscription) {
furi_pubsub_unsubscribe(desktop->input_events_pubsub, desktop->input_events_subscription);
desktop->input_events_subscription = NULL;
}
desktop->loader = NULL; desktop->loader = NULL;
desktop->input_events_pubsub = NULL;
furi_record_close("loader"); furi_record_close("loader");
furi_record_close("notification");
furi_record_close("input_events");
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdMain); view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdMain);
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLockMenu); view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLockMenu);
@ -191,6 +282,8 @@ void desktop_free(Desktop* desktop) {
furi_record_close("menu"); furi_record_close("menu");
osTimerDelete(desktop->auto_lock_timer);
free(desktop); free(desktop);
} }
@ -214,14 +307,16 @@ int32_t desktop_srv(void* p) {
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) { if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock) && !desktop->settings.pin_code.length) {
if(desktop->settings.pin_code.length > 0) { furi_hal_rtc_reset_flag(FuriHalRtcFlagLock);
scene_manager_set_scene_state( }
desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER);
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) {
} else { if(!loader_is_locked(desktop->loader)) {
furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); desktop_auto_lock_arm(desktop);
} }
} else {
desktop_lock(desktop);
} }
if(desktop_is_first_start()) { if(desktop_is_first_start()) {

View File

@ -19,6 +19,7 @@
#include <gui/scene_manager.h> #include <gui/scene_manager.h>
#include <loader/loader.h> #include <loader/loader.h>
#include <notification/notification_app.h>
#define STATUS_BAR_Y_SHIFT 13 #define STATUS_BAR_Y_SHIFT 13
@ -59,10 +60,18 @@ struct Desktop {
ViewPort* lock_viewport; ViewPort* lock_viewport;
AnimationManager* animation_manager; AnimationManager* animation_manager;
Loader* loader; Loader* loader;
NotificationApp* notification;
FuriPubSubSubscription* app_start_stop_subscription; FuriPubSubSubscription* app_start_stop_subscription;
FuriPubSub* input_events_pubsub;
FuriPubSubSubscription* input_events_subscription;
osTimerId_t auto_lock_timer;
}; };
Desktop* desktop_alloc(); Desktop* desktop_alloc();
void desktop_free(Desktop* desktop); void desktop_free(Desktop* desktop);
void desktop_lock(Desktop* desktop);
void desktop_unlock(Desktop* desktop);

View File

@ -5,7 +5,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <toolbox/saved_struct.h> #include <toolbox/saved_struct.h>
#define DESKTOP_SETTINGS_VER (1) #define DESKTOP_SETTINGS_VER (2)
#define DESKTOP_SETTINGS_PATH "/int/desktop.settings" #define DESKTOP_SETTINGS_PATH "/int/desktop.settings"
#define DESKTOP_SETTINGS_MAGIC (0x17) #define DESKTOP_SETTINGS_MAGIC (0x17)
#define PIN_MAX_LENGTH 12 #define PIN_MAX_LENGTH 12
@ -39,6 +39,7 @@ typedef struct {
typedef struct { typedef struct {
uint16_t favorite; uint16_t favorite;
PinCode pin_code; PinCode pin_code;
uint32_t auto_lock_delay_ms;
} DesktopSettings; } DesktopSettings;
static inline bool pins_are_equal(const PinCode* pin_code1, const PinCode* pin_code2) { static inline bool pins_are_equal(const PinCode* pin_code1, const PinCode* pin_code2) {

View File

@ -36,12 +36,17 @@ DesktopSettingsApp* desktop_settings_app_alloc() {
app->popup = popup_alloc(); app->popup = popup_alloc();
app->submenu = submenu_alloc(); app->submenu = submenu_alloc();
app->variable_item_list = variable_item_list_alloc();
app->pin_input_view = desktop_view_pin_input_alloc(); app->pin_input_view = desktop_view_pin_input_alloc();
app->pin_setup_howto_view = desktop_settings_view_pin_setup_howto_alloc(); app->pin_setup_howto_view = desktop_settings_view_pin_setup_howto_alloc();
app->pin_setup_howto2_view = desktop_settings_view_pin_setup_howto2_alloc(); app->pin_setup_howto2_view = desktop_settings_view_pin_setup_howto2_alloc();
view_dispatcher_add_view( view_dispatcher_add_view(
app->view_dispatcher, DesktopSettingsAppViewMenu, submenu_get_view(app->submenu)); app->view_dispatcher, DesktopSettingsAppViewMenu, submenu_get_view(app->submenu));
view_dispatcher_add_view(
app->view_dispatcher,
DesktopSettingsAppViewVarItemList,
variable_item_list_get_view(app->variable_item_list));
view_dispatcher_add_view( view_dispatcher_add_view(
app->view_dispatcher, DesktopSettingsAppViewIdPopup, popup_get_view(app->popup)); app->view_dispatcher, DesktopSettingsAppViewIdPopup, popup_get_view(app->popup));
view_dispatcher_add_view( view_dispatcher_add_view(
@ -63,10 +68,12 @@ void desktop_settings_app_free(DesktopSettingsApp* app) {
furi_assert(app); furi_assert(app);
// Variable item list // Variable item list
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewMenu); view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewMenu);
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewVarItemList);
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPopup); view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPopup);
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput); view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput);
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto); view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto);
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto2); view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto2);
variable_item_list_free(app->variable_item_list);
submenu_free(app->submenu); submenu_free(app->submenu);
popup_free(app->popup); popup_free(app->popup);
desktop_view_pin_input_free(app->pin_input_view); desktop_view_pin_input_free(app->pin_input_view);

View File

@ -5,6 +5,7 @@
#include <gui/view_dispatcher.h> #include <gui/view_dispatcher.h>
#include <gui/scene_manager.h> #include <gui/scene_manager.h>
#include <gui/modules/submenu.h> #include <gui/modules/submenu.h>
#include <gui/modules/variable_item_list.h>
#include "desktop_settings.h" #include "desktop_settings.h"
#include "desktop/views/desktop_view_pin_input.h" #include "desktop/views/desktop_view_pin_input.h"
@ -13,6 +14,7 @@
typedef enum { typedef enum {
DesktopSettingsAppViewMenu, DesktopSettingsAppViewMenu,
DesktopSettingsAppViewVarItemList,
DesktopSettingsAppViewIdPopup, DesktopSettingsAppViewIdPopup,
DesktopSettingsAppViewIdPinInput, DesktopSettingsAppViewIdPinInput,
DesktopSettingsAppViewIdPinSetupHowto, DesktopSettingsAppViewIdPinSetupHowto,
@ -25,6 +27,7 @@ typedef struct {
Gui* gui; Gui* gui;
SceneManager* scene_manager; SceneManager* scene_manager;
ViewDispatcher* view_dispatcher; ViewDispatcher* view_dispatcher;
VariableItemList* variable_item_list;
Submenu* submenu; Submenu* submenu;
Popup* popup; Popup* popup;
DesktopViewPinInput* pin_input_view; DesktopViewPinInput* pin_input_view;

View File

@ -1,35 +1,65 @@
#include <applications.h> #include <applications.h>
#include <lib/toolbox/value_index.h>
#include "../desktop_settings_app.h" #include "../desktop_settings_app.h"
#include "desktop_settings_scene.h" #include "desktop_settings_scene.h"
#define SCENE_EVENT_SELECT_FAVORITE 0 #define SCENE_EVENT_SELECT_FAVORITE 0
#define SCENE_EVENT_SELECT_PIN_SETUP 1 #define SCENE_EVENT_SELECT_PIN_SETUP 1
#define SCENE_EVENT_SELECT_AUTO_LOCK_DELAY 2
static void desktop_settings_scene_start_submenu_callback(void* context, uint32_t index) { #define AUTO_LOCK_DELAY_COUNT 6
const char* const auto_lock_delay_text[AUTO_LOCK_DELAY_COUNT] = {
"OFF",
"30s",
"60s",
"2min",
"5min",
"10min",
};
const uint32_t auto_lock_delay_value[AUTO_LOCK_DELAY_COUNT] =
{0, 30000, 60000, 120000, 300000, 600000};
static void desktop_settings_scene_start_var_list_enter_callback(void* context, uint32_t index) {
DesktopSettingsApp* app = context; DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index); view_dispatcher_send_custom_event(app->view_dispatcher, index);
} }
static void desktop_settings_scene_start_auto_lock_delay_changed(VariableItem* item) {
DesktopSettingsApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, auto_lock_delay_text[index]);
app->settings.auto_lock_delay_ms = auto_lock_delay_value[index];
}
void desktop_settings_scene_start_on_enter(void* context) { void desktop_settings_scene_start_on_enter(void* context) {
DesktopSettingsApp* app = context; DesktopSettingsApp* app = context;
Submenu* submenu = app->submenu; VariableItemList* variable_item_list = app->variable_item_list;
submenu_add_item( VariableItem* item;
submenu, uint8_t value_index;
"Favorite App",
SCENE_EVENT_SELECT_FAVORITE, variable_item_list_add(variable_item_list, "Favorite App", 1, NULL, NULL);
desktop_settings_scene_start_submenu_callback,
variable_item_list_add(variable_item_list, "PIN Setup", 1, NULL, NULL);
item = variable_item_list_add(
variable_item_list,
"Auto Lock Time",
AUTO_LOCK_DELAY_COUNT,
desktop_settings_scene_start_auto_lock_delay_changed,
app); app);
submenu_add_item( variable_item_list_set_enter_callback(
submenu, variable_item_list, desktop_settings_scene_start_var_list_enter_callback, app);
"PIN Setup", value_index = value_index_uint32(
SCENE_EVENT_SELECT_PIN_SETUP, app->settings.auto_lock_delay_ms, auto_lock_delay_value, AUTO_LOCK_DELAY_COUNT);
desktop_settings_scene_start_submenu_callback, variable_item_set_current_value_index(item, value_index);
app); variable_item_set_current_value_text(item, auto_lock_delay_text[value_index]);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewVarItemList);
} }
bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent event) { bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent event) {
@ -46,6 +76,9 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinMenu); scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinMenu);
consumed = true; consumed = true;
break; break;
case SCENE_EVENT_SELECT_AUTO_LOCK_DELAY:
consumed = true;
break;
} }
} }
return consumed; return consumed;
@ -53,5 +86,6 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even
void desktop_settings_scene_start_on_exit(void* context) { void desktop_settings_scene_start_on_exit(void* context) {
DesktopSettingsApp* app = context; DesktopSettingsApp* app = context;
submenu_reset(app->submenu); variable_item_list_reset(app->variable_item_list);
SAVE_DESKTOP_SETTINGS(&app->settings);
} }

View File

@ -34,13 +34,13 @@ bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) {
break; break;
case DesktopDebugEventDeed: case DesktopDebugEventDeed:
dolphin_deed(dolphin, DolphinDeedIbuttonEmulate); dolphin_deed(dolphin, DolphinDeedTestRight);
desktop_debug_get_dolphin_data(desktop->debug_view); desktop_debug_get_dolphin_data(desktop->debug_view);
consumed = true; consumed = true;
break; break;
case DesktopDebugEventWrongDeed: case DesktopDebugEventWrongDeed:
dolphin_deed(dolphin, DolphinDeedIbuttonRead); dolphin_deed(dolphin, DolphinDeedTestLeft);
desktop_debug_get_dolphin_data(desktop->debug_view); desktop_debug_get_dolphin_data(desktop->debug_view);
consumed = true; consumed = true;
break; break;

View File

@ -48,17 +48,13 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) {
switch(event.event) { switch(event.event) {
case DesktopLockMenuEventLock: case DesktopLockMenuEventLock:
scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0);
scene_manager_set_scene_state( desktop_lock(desktop);
desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER);
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked);
consumed = true; consumed = true;
break; break;
case DesktopLockMenuEventPinLock: case DesktopLockMenuEventPinLock:
if(desktop->settings.pin_code.length > 0) { if(desktop->settings.pin_code.length > 0) {
furi_hal_rtc_set_flag(FuriHalRtcFlagLock); furi_hal_rtc_set_flag(FuriHalRtcFlagLock);
scene_manager_set_scene_state( desktop_lock(desktop);
desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER);
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked);
} else { } else {
LoaderStatus status = LoaderStatus status =
loader_start(desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG); loader_start(desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG);

View File

@ -81,13 +81,13 @@ bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) { switch(event.event) {
case DesktopLockedEventUnlocked: case DesktopLockedEventUnlocked:
furi_hal_rtc_set_pin_fails(0); desktop_unlock(desktop);
desktop_helpers_unlock_system(desktop);
scene_manager_search_and_switch_to_previous_scene(
desktop->scene_manager, DesktopSceneMain);
consumed = true; consumed = true;
break; break;
case DesktopLockedEventUpdate: case DesktopLockedEventUpdate:
if(desktop_view_locked_is_locked_hint_visible(desktop->locked_view)) {
notification_message(desktop->notification, &sequence_display_off);
}
desktop_view_locked_update(desktop->locked_view); desktop_view_locked_update(desktop->locked_view);
consumed = true; consumed = true;
break; break;

View File

@ -129,16 +129,13 @@ bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) {
consumed = true; consumed = true;
break; break;
case DesktopPinInputEventUnlocked: case DesktopPinInputEventUnlocked:
desktop_view_locked_unlock(desktop->locked_view); desktop_unlock(desktop);
furi_hal_rtc_set_pin_fails(0);
desktop_helpers_unlock_system(desktop);
scene_manager_search_and_switch_to_previous_scene(
desktop->scene_manager, DesktopSceneMain);
consumed = true; consumed = true;
break; break;
case DesktopPinInputEventBack: case DesktopPinInputEventBack:
scene_manager_search_and_switch_to_previous_scene( scene_manager_search_and_switch_to_previous_scene(
desktop->scene_manager, DesktopSceneLocked); desktop->scene_manager, DesktopSceneLocked);
notification_message(desktop->notification, &sequence_display_off);
consumed = true; consumed = true;
break; break;
} }

View File

@ -38,4 +38,5 @@ typedef enum {
// Global events // Global events
DesktopGlobalBeforeAppStarted, DesktopGlobalBeforeAppStarted,
DesktopGlobalAfterAppFinished, DesktopGlobalAfterAppFinished,
DesktopGlobalAutoLock,
} DesktopEvent; } DesktopEvent;

View File

@ -23,7 +23,7 @@ void desktop_debug_render(Canvas* canvas, void* model) {
const Version* ver; const Version* ver;
char buffer[64]; char buffer[64];
static const char* headers[] = {"FW Version info:", "Boot Version info:", "Dolphin info:"}; static const char* headers[] = {"FW Version info:", "Dolphin info:"};
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontPrimary); canvas_set_font(canvas, FontPrimary);
@ -36,16 +36,16 @@ void desktop_debug_render(Canvas* canvas, void* model) {
snprintf( snprintf(
buffer, buffer,
sizeof(buffer), sizeof(buffer),
"HW: %d.F%dB%dC%d %s", "%d.F%dB%dC%d %s %s",
furi_hal_version_get_hw_version(), furi_hal_version_get_hw_version(),
furi_hal_version_get_hw_target(), furi_hal_version_get_hw_target(),
furi_hal_version_get_hw_body(), furi_hal_version_get_hw_body(),
furi_hal_version_get_hw_connect(), furi_hal_version_get_hw_connect(),
furi_hal_version_get_hw_region_name(),
my_name ? my_name : "Unknown"); my_name ? my_name : "Unknown");
canvas_draw_str(canvas, 5, 19 + STATUS_BAR_Y_SHIFT, buffer); canvas_draw_str(canvas, 5, 19 + STATUS_BAR_Y_SHIFT, buffer);
ver = m->screen == DesktopViewStatsBoot ? furi_hal_version_get_bootloader_version() : ver = furi_hal_version_get_firmware_version();
furi_hal_version_get_firmware_version();
if(!ver) { if(!ver) {
canvas_draw_str(canvas, 5, 29 + STATUS_BAR_Y_SHIFT, "No info"); canvas_draw_str(canvas, 5, 29 + STATUS_BAR_Y_SHIFT, "No info");
@ -63,7 +63,8 @@ void desktop_debug_render(Canvas* canvas, void* model) {
snprintf( snprintf(
buffer, buffer,
sizeof(buffer), sizeof(buffer),
"%s [%s]", "%s%s [%s]",
version_get_dirty_flag(ver) ? "[!] " : "",
version_get_githash(ver), version_get_githash(ver),
version_get_gitbranchnum(ver)); version_get_gitbranchnum(ver));
canvas_draw_str(canvas, 5, 39 + STATUS_BAR_Y_SHIFT, buffer); canvas_draw_str(canvas, 5, 39 + STATUS_BAR_Y_SHIFT, buffer);
@ -110,18 +111,15 @@ bool desktop_debug_input(InputEvent* event, void* context) {
DesktopDebugView* debug_view = context; DesktopDebugView* debug_view = context;
if(event->type != InputTypeShort) return false; if(event->type != InputTypeShort && event->type != InputTypeRepeat) {
return false;
}
DesktopViewStatsScreens current = 0; DesktopViewStatsScreens current = 0;
with_view_model( with_view_model(
debug_view->view, (DesktopDebugViewModel * model) { debug_view->view, (DesktopDebugViewModel * model) {
#if SRV_DOLPHIN_STATE_DEBUG == 1
if(event->key == InputKeyDown) { #ifdef SRV_DOLPHIN_STATE_DEBUG
model->screen = (model->screen + 1) % DesktopViewStatsTotalCount;
} else if(event->key == InputKeyUp) {
model->screen = ((model->screen - 1) + DesktopViewStatsTotalCount) %
DesktopViewStatsTotalCount;
}
#else
if((event->key == InputKeyDown) || (event->key == InputKeyUp)) { if((event->key == InputKeyDown) || (event->key == InputKeyUp)) {
model->screen = !model->screen; model->screen = !model->screen;
} }
@ -130,11 +128,16 @@ bool desktop_debug_input(InputEvent* event, void* context) {
return true; return true;
}); });
size_t count = (event->type == InputTypeRepeat) ? 10 : 1;
if(current == DesktopViewStatsMeta) { if(current == DesktopViewStatsMeta) {
if(event->key == InputKeyLeft) { if(event->key == InputKeyLeft) {
debug_view->callback(DesktopDebugEventWrongDeed, debug_view->context); while(count-- > 0) {
debug_view->callback(DesktopDebugEventWrongDeed, debug_view->context);
}
} else if(event->key == InputKeyRight) { } else if(event->key == InputKeyRight) {
debug_view->callback(DesktopDebugEventDeed, debug_view->context); while(count-- > 0) {
debug_view->callback(DesktopDebugEventDeed, debug_view->context);
}
} else if(event->key == InputKeyOk) { } else if(event->key == InputKeyOk) {
debug_view->callback(DesktopDebugEventSaveState, debug_view->context); debug_view->callback(DesktopDebugEventSaveState, debug_view->context);
} else { } else {

View File

@ -11,7 +11,6 @@ typedef void (*DesktopDebugViewCallback)(DesktopEvent event, void* context);
// Debug info // Debug info
typedef enum { typedef enum {
DesktopViewStatsFw, DesktopViewStatsFw,
DesktopViewStatsBoot,
DesktopViewStatsMeta, DesktopViewStatsMeta,
DesktopViewStatsTotalCount, DesktopViewStatsTotalCount,
} DesktopViewStatsScreens; } DesktopViewStatsScreens;

View File

@ -11,6 +11,7 @@
#include "desktop_view_locked.h" #include "desktop_view_locked.h"
#define DOOR_MOVING_INTERVAL_MS (1000 / 16) #define DOOR_MOVING_INTERVAL_MS (1000 / 16)
#define LOCKED_HINT_TIMEOUT_MS (1000)
#define UNLOCKED_HINT_TIMEOUT_MS (2000) #define UNLOCKED_HINT_TIMEOUT_MS (2000)
#define DOOR_OFFSET_START -55 #define DOOR_OFFSET_START -55
@ -32,14 +33,18 @@ struct DesktopViewLocked {
uint32_t lock_lastpress; uint32_t lock_lastpress;
}; };
typedef struct { typedef enum {
uint32_t hint_icon_expire_at; DesktopViewLockedStateUnlocked,
bool unlocked_hint; DesktopViewLockedStateLocked,
bool locked; DesktopViewLockedStateDoorsClosing,
bool pin_locked; DesktopViewLockedStateLockedHintShown,
DesktopViewLockedStateUnlockedHintShown
} DesktopViewLockedState;
typedef struct {
bool pin_locked;
int8_t door_offset; int8_t door_offset;
bool doors_closing; DesktopViewLockedState view_state;
} DesktopViewLockedModel; } DesktopViewLockedModel;
void desktop_view_locked_set_callback( void desktop_view_locked_set_callback(
@ -78,51 +83,54 @@ static bool desktop_view_locked_doors_move(DesktopViewLockedModel* model) {
static void desktop_view_locked_update_hint_icon_timeout(DesktopViewLocked* locked_view) { static void desktop_view_locked_update_hint_icon_timeout(DesktopViewLocked* locked_view) {
DesktopViewLockedModel* model = view_get_model(locked_view->view); DesktopViewLockedModel* model = view_get_model(locked_view->view);
model->hint_icon_expire_at = osKernelGetTickCount() + osKernelGetTickFreq(); const bool change_state = (model->view_state == DesktopViewLockedStateLocked) &&
view_commit_model(locked_view->view, true); !model->pin_locked;
if(change_state) {
model->view_state = DesktopViewLockedStateLockedHintShown;
}
view_commit_model(locked_view->view, change_state);
xTimerChangePeriod(locked_view->timer, pdMS_TO_TICKS(LOCKED_HINT_TIMEOUT_MS), portMAX_DELAY);
} }
void desktop_view_locked_update(DesktopViewLocked* locked_view) { void desktop_view_locked_update(DesktopViewLocked* locked_view) {
bool stop_timer = false;
DesktopViewLockedModel* model = view_get_model(locked_view->view); DesktopViewLockedModel* model = view_get_model(locked_view->view);
if(model->locked) { DesktopViewLockedState view_state = model->view_state;
model->doors_closing = desktop_view_locked_doors_move(model);
stop_timer = !model->doors_closing; if(view_state == DesktopViewLockedStateDoorsClosing &&
} else { !desktop_view_locked_doors_move(model)) {
model->unlocked_hint = false; model->view_state = DesktopViewLockedStateLocked;
stop_timer = true; } else if(view_state == DesktopViewLockedStateLockedHintShown) {
model->view_state = DesktopViewLockedStateLocked;
} else if(view_state == DesktopViewLockedStateUnlockedHintShown) {
model->view_state = DesktopViewLockedStateUnlocked;
} }
view_commit_model(locked_view->view, true); view_commit_model(locked_view->view, true);
if(stop_timer) { if(view_state != DesktopViewLockedStateDoorsClosing) {
xTimerStop(locked_view->timer, portMAX_DELAY); xTimerStop(locked_view->timer, portMAX_DELAY);
} }
} }
static void desktop_view_locked_draw(Canvas* canvas, void* model) { static void desktop_view_locked_draw(Canvas* canvas, void* model) {
DesktopViewLockedModel* m = model; DesktopViewLockedModel* m = model;
uint32_t now = osKernelGetTickCount(); DesktopViewLockedState view_state = m->view_state;
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
if(m->locked) { if(view_state == DesktopViewLockedStateDoorsClosing) {
if(m->doors_closing) { desktop_view_locked_doors_draw(canvas, m);
desktop_view_locked_doors_draw(canvas, m); canvas_set_font(canvas, FontPrimary);
canvas_set_font(canvas, FontPrimary); elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Locked");
elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Locked"); } else if(view_state == DesktopViewLockedStateLockedHintShown) {
} else if((now < m->hint_icon_expire_at) && !m->pin_locked) { canvas_set_font(canvas, FontSecondary);
canvas_set_font(canvas, FontSecondary); elements_bold_rounded_frame(canvas, 14, 2 + STATUS_BAR_Y_SHIFT, 99, 48);
elements_bold_rounded_frame(canvas, 14, 2 + STATUS_BAR_Y_SHIFT, 99, 48); elements_multiline_text(canvas, 65, 20 + STATUS_BAR_Y_SHIFT, "To unlock\npress:");
elements_multiline_text(canvas, 65, 20 + STATUS_BAR_Y_SHIFT, "To unlock\npress:"); canvas_draw_icon(canvas, 65, 36 + STATUS_BAR_Y_SHIFT, &I_Back3_45x8);
canvas_draw_icon(canvas, 65, 36 + STATUS_BAR_Y_SHIFT, &I_Back3_45x8); canvas_draw_icon(canvas, 16, 7 + STATUS_BAR_Y_SHIFT, &I_WarningDolphin_45x42);
canvas_draw_icon(canvas, 16, 7 + STATUS_BAR_Y_SHIFT, &I_WarningDolphin_45x42); canvas_draw_dot(canvas, 17, 61);
canvas_draw_dot(canvas, 17, 61); } else if(view_state == DesktopViewLockedStateUnlockedHintShown) {
} canvas_set_font(canvas, FontPrimary);
} else { elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Unlocked");
if(m->unlocked_hint) {
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Unlocked");
}
} }
} }
@ -134,43 +142,38 @@ View* desktop_view_locked_get_view(DesktopViewLocked* locked_view) {
static bool desktop_view_locked_input(InputEvent* event, void* context) { static bool desktop_view_locked_input(InputEvent* event, void* context) {
furi_assert(event); furi_assert(event);
furi_assert(context); furi_assert(context);
bool is_changed = false;
const uint32_t press_time = xTaskGetTickCount();
DesktopViewLocked* locked_view = context; DesktopViewLocked* locked_view = context;
bool locked = false; DesktopViewLockedModel* model = view_get_model(locked_view->view);
bool locked_with_pin = false; if(model->view_state == DesktopViewLockedStateUnlockedHintShown &&
bool doors_closing = false; event->type == InputTypePress) {
uint32_t press_time = xTaskGetTickCount(); model->view_state = DesktopViewLockedStateUnlocked;
is_changed = true;
{
DesktopViewLockedModel* model = view_get_model(locked_view->view);
bool changed = false;
locked = model->locked;
locked_with_pin = model->pin_locked;
doors_closing = model->doors_closing;
if(!locked && model->unlocked_hint && event->type == InputTypePress) {
model->unlocked_hint = false;
changed = true;
}
view_commit_model(locked_view->view, changed);
} }
const DesktopViewLockedState view_state = model->view_state;
const bool pin_locked = model->pin_locked;
view_commit_model(locked_view->view, is_changed);
if(!locked || doors_closing || (event->type != InputTypeShort)) { if(view_state == DesktopViewLockedStateUnlocked || event->type != InputTypeShort) {
return locked; return view_state != DesktopViewLockedStateUnlocked;
} } else if(view_state == DesktopViewLockedStateLocked && pin_locked) {
if(locked_with_pin) {
locked_view->callback(DesktopLockedEventShowPinInput, locked_view->context); locked_view->callback(DesktopLockedEventShowPinInput, locked_view->context);
} else { } else if(
view_state == DesktopViewLockedStateLocked ||
view_state == DesktopViewLockedStateLockedHintShown) {
if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT) { if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT) {
locked_view->lock_lastpress = press_time; locked_view->lock_lastpress = press_time;
locked_view->lock_count = 0; locked_view->lock_count = 0;
} }
desktop_view_locked_update_hint_icon_timeout(locked_view); desktop_view_locked_update_hint_icon_timeout(locked_view);
if(event->key == InputKeyBack) { if(event->key == InputKeyBack) {
locked_view->lock_lastpress = press_time; locked_view->lock_lastpress = press_time;
locked_view->lock_count++; locked_view->lock_count++;
if(locked_view->lock_count == UNLOCK_CNT) { if(locked_view->lock_count == UNLOCK_CNT) {
desktop_view_locked_unlock(locked_view);
locked_view->callback(DesktopLockedEventUnlocked, locked_view->context); locked_view->callback(DesktopLockedEventUnlocked, locked_view->context);
} }
} else { } else {
@ -180,7 +183,7 @@ static bool desktop_view_locked_input(InputEvent* event, void* context) {
locked_view->lock_lastpress = press_time; locked_view->lock_lastpress = press_time;
} }
return locked; return true;
} }
DesktopViewLocked* desktop_view_locked_alloc() { DesktopViewLocked* desktop_view_locked_alloc() {
@ -189,7 +192,6 @@ DesktopViewLocked* desktop_view_locked_alloc() {
locked_view->timer = locked_view->timer =
xTimerCreate(NULL, 1000 / 16, pdTRUE, locked_view, locked_view_timer_callback); xTimerCreate(NULL, 1000 / 16, pdTRUE, locked_view, locked_view_timer_callback);
locked_view->view = view_alloc();
view_allocate_model(locked_view->view, ViewModelTypeLocking, sizeof(DesktopViewLockedModel)); view_allocate_model(locked_view->view, ViewModelTypeLocking, sizeof(DesktopViewLockedModel));
view_set_context(locked_view->view, locked_view); view_set_context(locked_view->view, locked_view);
view_set_draw_callback(locked_view->view, desktop_view_locked_draw); view_set_draw_callback(locked_view->view, desktop_view_locked_draw);
@ -207,7 +209,8 @@ void desktop_view_locked_free(DesktopViewLocked* locked_view) {
void desktop_view_locked_close_doors(DesktopViewLocked* locked_view) { void desktop_view_locked_close_doors(DesktopViewLocked* locked_view) {
DesktopViewLockedModel* model = view_get_model(locked_view->view); DesktopViewLockedModel* model = view_get_model(locked_view->view);
model->doors_closing = true; furi_assert(model->view_state == DesktopViewLockedStateLocked);
model->view_state = DesktopViewLockedStateDoorsClosing;
model->door_offset = DOOR_OFFSET_START; model->door_offset = DOOR_OFFSET_START;
view_commit_model(locked_view->view, true); view_commit_model(locked_view->view, true);
xTimerChangePeriod(locked_view->timer, pdMS_TO_TICKS(DOOR_MOVING_INTERVAL_MS), portMAX_DELAY); xTimerChangePeriod(locked_view->timer, pdMS_TO_TICKS(DOOR_MOVING_INTERVAL_MS), portMAX_DELAY);
@ -215,19 +218,24 @@ void desktop_view_locked_close_doors(DesktopViewLocked* locked_view) {
void desktop_view_locked_lock(DesktopViewLocked* locked_view, bool pin_locked) { void desktop_view_locked_lock(DesktopViewLocked* locked_view, bool pin_locked) {
DesktopViewLockedModel* model = view_get_model(locked_view->view); DesktopViewLockedModel* model = view_get_model(locked_view->view);
model->locked = true; furi_assert(model->view_state == DesktopViewLockedStateUnlocked);
model->view_state = DesktopViewLockedStateLocked;
model->pin_locked = pin_locked; model->pin_locked = pin_locked;
view_commit_model(locked_view->view, true); view_commit_model(locked_view->view, true);
} }
void desktop_view_locked_unlock(DesktopViewLocked* locked_view) { void desktop_view_locked_unlock(DesktopViewLocked* locked_view) {
furi_assert(locked_view);
locked_view->lock_count = 0; locked_view->lock_count = 0;
DesktopViewLockedModel* model = view_get_model(locked_view->view); DesktopViewLockedModel* model = view_get_model(locked_view->view);
model->locked = false; model->view_state = DesktopViewLockedStateUnlockedHintShown;
model->pin_locked = false; model->pin_locked = false;
model->unlocked_hint = true;
view_commit_model(locked_view->view, true); view_commit_model(locked_view->view, true);
xTimerChangePeriod(locked_view->timer, pdMS_TO_TICKS(UNLOCKED_HINT_TIMEOUT_MS), portMAX_DELAY); xTimerChangePeriod(locked_view->timer, pdMS_TO_TICKS(UNLOCKED_HINT_TIMEOUT_MS), portMAX_DELAY);
} }
bool desktop_view_locked_is_locked_hint_visible(DesktopViewLocked* locked_view) {
DesktopViewLockedModel* model = view_get_model(locked_view->view);
const DesktopViewLockedState view_state = model->view_state;
view_commit_model(locked_view->view, false);
return view_state == DesktopViewLockedStateLockedHintShown;
}

View File

@ -19,3 +19,4 @@ void desktop_view_locked_free(DesktopViewLocked* locked_view);
void desktop_view_locked_lock(DesktopViewLocked* locked_view, bool pin_locked); void desktop_view_locked_lock(DesktopViewLocked* locked_view, bool pin_locked);
void desktop_view_locked_unlock(DesktopViewLocked* locked_view); void desktop_view_locked_unlock(DesktopViewLocked* locked_view);
void desktop_view_locked_close_doors(DesktopViewLocked* locked_view); void desktop_view_locked_close_doors(DesktopViewLocked* locked_view);
bool desktop_view_locked_is_locked_hint_visible(DesktopViewLocked* locked_view);

View File

@ -52,7 +52,10 @@ typedef enum {
DolphinDeedU2fAuthorized, DolphinDeedU2fAuthorized,
DolphinDeedMAX DolphinDeedMAX,
DolphinDeedTestLeft,
DolphinDeedTestRight,
} DolphinDeed; } DolphinDeed;
typedef struct { typedef struct {

View File

@ -126,6 +126,23 @@ uint32_t dolphin_state_xp_to_levelup(uint32_t icounter) {
} }
void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed) { void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed) {
// Special case for testing
if(deed > DolphinDeedMAX) {
if(deed == DolphinDeedTestLeft) {
dolphin_state->data.butthurt =
CLAMP(dolphin_state->data.butthurt + 1, BUTTHURT_MAX, BUTTHURT_MIN);
if(dolphin_state->data.icounter > 0) dolphin_state->data.icounter--;
dolphin_state->data.timestamp = dolphin_state_timestamp();
dolphin_state->dirty = true;
} else if(deed == DolphinDeedTestRight) {
dolphin_state->data.butthurt = BUTTHURT_MIN;
if(dolphin_state->data.icounter < UINT32_MAX) dolphin_state->data.icounter++;
dolphin_state->data.timestamp = dolphin_state_timestamp();
dolphin_state->dirty = true;
}
return;
}
DolphinApp app = dolphin_deed_get_app(deed); DolphinApp app = dolphin_deed_get_app(deed);
int8_t weight_limit = int8_t weight_limit =
dolphin_deed_get_app_limit(app) - dolphin_state->data.icounter_daily_limit[app]; dolphin_deed_get_app_limit(app) - dolphin_state->data.icounter_daily_limit[app];

View File

@ -49,9 +49,9 @@ void gpio_scene_start_on_enter(void* context) {
variable_item_list_set_enter_callback( variable_item_list_set_enter_callback(
var_item_list, gpio_scene_start_var_list_enter_callback, app); var_item_list, gpio_scene_start_var_list_enter_callback, app);
variable_item_list_add(var_item_list, "USB-UART bridge", 0, NULL, NULL); variable_item_list_add(var_item_list, "USB-UART Bridge", 0, NULL, NULL);
variable_item_list_add(var_item_list, "GPIO manual control", 0, NULL, NULL); variable_item_list_add(var_item_list, "GPIO Manual Control", 0, NULL, NULL);
item = variable_item_list_add( item = variable_item_list_add(
var_item_list, var_item_list,

View File

@ -9,7 +9,9 @@
#include "scene/ibutton_scene_read_crc_error.h" #include "scene/ibutton_scene_read_crc_error.h"
#include "scene/ibutton_scene_read_not_key_error.h" #include "scene/ibutton_scene_read_not_key_error.h"
#include "scene/ibutton_scene_read_success.h" #include "scene/ibutton_scene_read_success.h"
#include "scene/ibutton_scene_readed_key_menu.h" #include "scene/ibutton_scene_retry_confirm.h"
#include "scene/ibutton_scene_exit_confirm.h"
#include "scene/ibutton_scene_read_key_menu.h"
#include "scene/ibutton_scene_write.h" #include "scene/ibutton_scene_write.h"
#include "scene/ibutton_scene_write_success.h" #include "scene/ibutton_scene_write_success.h"
#include "scene/ibutton_scene_saved_key_menu.h" #include "scene/ibutton_scene_saved_key_menu.h"
@ -42,7 +44,9 @@ public:
SceneReadNotKeyError, SceneReadNotKeyError,
SceneReadCRCError, SceneReadCRCError,
SceneReadSuccess, SceneReadSuccess,
SceneReadedKeyMenu, SceneRetryConfirm,
SceneExitConfirm,
SceneReadKeyMenu,
SceneWrite, SceneWrite,
SceneWriteSuccess, SceneWriteSuccess,
SceneEmulate, SceneEmulate,
@ -105,7 +109,9 @@ private:
{Scene::SceneReadCRCError, new iButtonSceneReadCRCError()}, {Scene::SceneReadCRCError, new iButtonSceneReadCRCError()},
{Scene::SceneReadNotKeyError, new iButtonSceneReadNotKeyError()}, {Scene::SceneReadNotKeyError, new iButtonSceneReadNotKeyError()},
{Scene::SceneReadSuccess, new iButtonSceneReadSuccess()}, {Scene::SceneReadSuccess, new iButtonSceneReadSuccess()},
{Scene::SceneReadedKeyMenu, new iButtonSceneReadedKeyMenu()}, {Scene::SceneRetryConfirm, new iButtonSceneRetryConfirm()},
{Scene::SceneExitConfirm, new iButtonSceneExitConfirm()},
{Scene::SceneReadKeyMenu, new iButtonSceneReadKeyMenu()},
{Scene::SceneWrite, new iButtonSceneWrite()}, {Scene::SceneWrite, new iButtonSceneWrite()},
{Scene::SceneWriteSuccess, new iButtonSceneWriteSuccess()}, {Scene::SceneWriteSuccess, new iButtonSceneWriteSuccess()},
{Scene::SceneEmulate, new iButtonSceneEmulate()}, {Scene::SceneEmulate, new iButtonSceneEmulate()},

View File

@ -6,8 +6,8 @@
#include <one_wire/ibutton/ibutton_worker.h> #include <one_wire/ibutton/ibutton_worker.h>
#include <one_wire/one_wire_host.h> #include <one_wire/one_wire_host.h>
void ibutton_cli(Cli* cli, string_t args, void* context); static void ibutton_cli(Cli* cli, string_t args, void* context);
void onewire_cli(Cli* cli, string_t args, void* context); static void onewire_cli(Cli* cli, string_t args, void* context);
// app cli function // app cli function
void ibutton_on_system_start() { void ibutton_on_system_start() {
@ -16,6 +16,9 @@ void ibutton_on_system_start() {
cli_add_command(cli, "ikey", CliCommandFlagDefault, ibutton_cli, cli); cli_add_command(cli, "ikey", CliCommandFlagDefault, ibutton_cli, cli);
cli_add_command(cli, "onewire", CliCommandFlagDefault, onewire_cli, cli); cli_add_command(cli, "onewire", CliCommandFlagDefault, onewire_cli, cli);
furi_record_close("cli"); furi_record_close("cli");
#else
UNUSED(ibutton_cli);
UNUSED(onewire_cli);
#endif #endif
} }
@ -236,7 +239,7 @@ void ibutton_cli_emulate(Cli* cli, string_t args) {
ibutton_key_free(key); ibutton_key_free(key);
}; };
void ibutton_cli(Cli* cli, string_t args, void* context) { static void ibutton_cli(Cli* cli, string_t args, void* context) {
string_t cmd; string_t cmd;
string_init(cmd); string_init(cmd);
@ -264,7 +267,7 @@ void onewire_cli_print_usage() {
printf("onewire search\r\n"); printf("onewire search\r\n");
}; };
void onewire_cli_search(Cli* cli) { static void onewire_cli_search(Cli* cli) {
OneWireHost* onewire = onewire_host_alloc(); OneWireHost* onewire = onewire_host_alloc();
uint8_t address[8]; uint8_t address[8];
bool done = false; bool done = false;

View File

@ -22,7 +22,7 @@ void iButtonSceneDeleteConfirm::on_enter(iButtonApp* app) {
app->set_text_store("\e#Delete %s?\e#", ibutton_key_get_name_p(key)); app->set_text_store("\e#Delete %s?\e#", ibutton_key_get_name_p(key));
widget_add_text_box_element( widget_add_text_box_element(
widget, 0, 0, 128, 27, AlignCenter, AlignCenter, app->get_text_store()); widget, 0, 0, 128, 27, AlignCenter, AlignCenter, app->get_text_store());
widget_add_button_element(widget, GuiButtonTypeLeft, "Back", widget_callback, app); widget_add_button_element(widget, GuiButtonTypeLeft, "Cancel", widget_callback, app);
widget_add_button_element(widget, GuiButtonTypeRight, "Delete", widget_callback, app); widget_add_button_element(widget, GuiButtonTypeRight, "Delete", widget_callback, app);
switch(ibutton_key_get_type(key)) { switch(ibutton_key_get_type(key)) {

View File

@ -14,7 +14,7 @@ void iButtonSceneDeleteSuccess::on_enter(iButtonApp* app) {
Popup* popup = view_manager->get_popup(); Popup* popup = view_manager->get_popup();
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
popup_set_text(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
popup_set_callback(popup, popup_callback); popup_set_callback(popup, popup_callback);
popup_set_context(popup, app); popup_set_context(popup, app);

View File

@ -0,0 +1,51 @@
#include "ibutton_scene_exit_confirm.h"
#include "../ibutton_app.h"
static void widget_callback(GuiButtonType result, InputType type, void* context) {
furi_assert(context);
iButtonApp* app = static_cast<iButtonApp*>(context);
iButtonEvent event;
if(type == InputTypeShort) {
event.type = iButtonEvent::Type::EventTypeWidgetButtonResult;
event.payload.widget_button_result = result;
app->get_view_manager()->send_event(&event);
}
}
void iButtonSceneExitConfirm::on_enter(iButtonApp* app) {
iButtonAppViewManager* view_manager = app->get_view_manager();
Widget* widget = view_manager->get_widget();
widget_add_button_element(widget, GuiButtonTypeLeft, "Exit", widget_callback, app);
widget_add_button_element(widget, GuiButtonTypeRight, "Stay", widget_callback, app);
widget_add_string_element(
widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Exit to iButton menu");
widget_add_string_element(
widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost");
view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewWidget);
}
bool iButtonSceneExitConfirm::on_event(iButtonApp* app, iButtonEvent* event) {
bool consumed = false;
if(event->type == iButtonEvent::Type::EventTypeWidgetButtonResult) {
if(event->payload.widget_button_result == GuiButtonTypeLeft) {
app->search_and_switch_to_previous_scene({iButtonApp::Scene::SceneStart});
} else if(event->payload.widget_button_result == GuiButtonTypeRight) {
app->switch_to_previous_scene();
}
consumed = true;
} else if(event->type == iButtonEvent::Type::EventTypeBack) {
consumed = true;
}
return consumed;
}
void iButtonSceneExitConfirm::on_exit(iButtonApp* app) {
iButtonAppViewManager* view_manager = app->get_view_manager();
Widget* widget = view_manager->get_widget();
widget_reset(widget);
}

View File

@ -0,0 +1,9 @@
#pragma once
#include "ibutton_scene_generic.h"
class iButtonSceneExitConfirm : public iButtonScene {
public:
void on_enter(iButtonApp* app) final;
bool on_event(iButtonApp* app, iButtonEvent* event) final;
void on_exit(iButtonApp* app) final;
};

View File

@ -1,18 +1,6 @@
#include "ibutton_scene_info.h" #include "ibutton_scene_info.h"
#include "../ibutton_app.h" #include "../ibutton_app.h"
static void widget_callback(GuiButtonType result, InputType type, void* context) {
furi_assert(context);
iButtonApp* app = static_cast<iButtonApp*>(context);
iButtonEvent event;
if(type == InputTypeShort) {
event.type = iButtonEvent::Type::EventTypeWidgetButtonResult;
event.payload.widget_button_result = result;
app->get_view_manager()->send_event(&event);
}
}
void iButtonSceneInfo::on_enter(iButtonApp* app) { void iButtonSceneInfo::on_enter(iButtonApp* app) {
iButtonAppViewManager* view_manager = app->get_view_manager(); iButtonAppViewManager* view_manager = app->get_view_manager();
Widget* widget = view_manager->get_widget(); Widget* widget = view_manager->get_widget();
@ -21,8 +9,7 @@ void iButtonSceneInfo::on_enter(iButtonApp* app) {
app->set_text_store("%s", ibutton_key_get_name_p(key)); app->set_text_store("%s", ibutton_key_get_name_p(key));
widget_add_text_box_element( widget_add_text_box_element(
widget, 0, 0, 128, 27, AlignCenter, AlignCenter, app->get_text_store()); widget, 0, 0, 128, 28, AlignCenter, AlignCenter, app->get_text_store());
widget_add_button_element(widget, GuiButtonTypeLeft, "Back", widget_callback, app);
switch(ibutton_key_get_type(key)) { switch(ibutton_key_get_type(key)) {
case iButtonKeyDS1990: case iButtonKeyDS1990:
@ -37,36 +24,28 @@ void iButtonSceneInfo::on_enter(iButtonApp* app) {
key_data[6], key_data[6],
key_data[7]); key_data[7]);
widget_add_string_element( widget_add_string_element(
widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Dallas"); widget, 64, 51, AlignCenter, AlignBottom, FontSecondary, "Dallas");
break; break;
case iButtonKeyMetakom: case iButtonKeyMetakom:
app->set_text_store( app->set_text_store(
"%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]); "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
widget_add_string_element( widget_add_string_element(
widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Metakom"); widget, 64, 51, AlignCenter, AlignBottom, FontSecondary, "Metakom");
break; break;
case iButtonKeyCyfral: case iButtonKeyCyfral:
app->set_text_store("%02X %02X", key_data[0], key_data[1]); app->set_text_store("%02X %02X", key_data[0], key_data[1]);
widget_add_string_element( widget_add_string_element(
widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Cyfral"); widget, 64, 51, AlignCenter, AlignBottom, FontSecondary, "Cyfral");
break; break;
} }
widget_add_string_element( widget_add_string_element(
widget, 64, 33, AlignCenter, AlignBottom, FontPrimary, app->get_text_store()); widget, 64, 35, AlignCenter, AlignBottom, FontPrimary, app->get_text_store());
view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewWidget); view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewWidget);
} }
bool iButtonSceneInfo::on_event(iButtonApp* app, iButtonEvent* event) { bool iButtonSceneInfo::on_event(iButtonApp* app, iButtonEvent* event) {
bool consumed = false; bool consumed = false;
if(event->type == iButtonEvent::Type::EventTypeWidgetButtonResult) {
if(event->payload.widget_button_result == GuiButtonTypeLeft) {
app->switch_to_previous_scene();
consumed = true;
}
}
return consumed; return consumed;
} }

View File

@ -34,15 +34,22 @@ bool iButtonSceneRead::on_event(iButtonApp* app, iButtonEvent* event) {
consumed = true; consumed = true;
iButtonKey* key = app->get_key(); iButtonKey* key = app->get_key();
bool success = false;
if(ibutton_key_get_type(key) == iButtonKeyDS1990) { if(ibutton_key_get_type(key) == iButtonKeyDS1990) {
if(!ibutton_key_dallas_crc_is_valid(key)) { if(!ibutton_key_dallas_crc_is_valid(key)) {
app->switch_to_next_scene(iButtonApp::Scene::SceneReadCRCError); app->switch_to_next_scene(iButtonApp::Scene::SceneReadCRCError);
} else if(!ibutton_key_dallas_is_1990_key(key)) { } else if(!ibutton_key_dallas_is_1990_key(key)) {
app->switch_to_next_scene(iButtonApp::Scene::SceneReadNotKeyError); app->switch_to_next_scene(iButtonApp::Scene::SceneReadNotKeyError);
} else { } else {
app->switch_to_next_scene(iButtonApp::Scene::SceneReadSuccess); success = true;
} }
} else { } else {
success = true;
}
if(success) {
app->notify_success();
app->notify_green_on();
DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess);
app->switch_to_next_scene(iButtonApp::Scene::SceneReadSuccess); app->switch_to_next_scene(iButtonApp::Scene::SceneReadSuccess);
} }
} else if(event->type == iButtonEvent::Type::EventTypeTick) { } else if(event->type == iButtonEvent::Type::EventTypeTick) {

View File

@ -47,7 +47,7 @@ bool iButtonSceneReadCRCError::on_event(iButtonApp* app, iButtonEvent* event) {
if(event->type == iButtonEvent::Type::EventTypeDialogResult) { if(event->type == iButtonEvent::Type::EventTypeDialogResult) {
if(event->payload.dialog_result == DialogExResultRight) { if(event->payload.dialog_result == DialogExResultRight) {
app->switch_to_next_scene(iButtonApp::Scene::SceneReadedKeyMenu); app->switch_to_next_scene(iButtonApp::Scene::SceneReadKeyMenu);
} else { } else {
app->switch_to_previous_scene(); app->switch_to_previous_scene();
} }

View File

@ -1,11 +1,10 @@
#include "ibutton_scene_readed_key_menu.h" #include "ibutton_scene_read_key_menu.h"
#include "../ibutton_app.h" #include "../ibutton_app.h"
typedef enum { typedef enum {
SubmenuIndexWrite, SubmenuIndexWrite,
SubmenuIndexEmulate, SubmenuIndexEmulate,
SubmenuIndexSave, SubmenuIndexSave,
SubmenuIndexReadNewKey,
} SubmenuIndex; } SubmenuIndex;
static void submenu_callback(void* context, uint32_t index) { static void submenu_callback(void* context, uint32_t index) {
@ -19,7 +18,7 @@ static void submenu_callback(void* context, uint32_t index) {
app->get_view_manager()->send_event(&event); app->get_view_manager()->send_event(&event);
} }
void iButtonSceneReadedKeyMenu::on_enter(iButtonApp* app) { void iButtonSceneReadKeyMenu::on_enter(iButtonApp* app) {
iButtonAppViewManager* view_manager = app->get_view_manager(); iButtonAppViewManager* view_manager = app->get_view_manager();
Submenu* submenu = view_manager->get_submenu(); Submenu* submenu = view_manager->get_submenu();
@ -28,13 +27,12 @@ void iButtonSceneReadedKeyMenu::on_enter(iButtonApp* app) {
} }
submenu_add_item(submenu, "Save", SubmenuIndexSave, submenu_callback, app); submenu_add_item(submenu, "Save", SubmenuIndexSave, submenu_callback, app);
submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, submenu_callback, app); submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, submenu_callback, app);
submenu_add_item(submenu, "Read new key", SubmenuIndexReadNewKey, submenu_callback, app);
submenu_set_selected_item(submenu, submenu_item_selected); submenu_set_selected_item(submenu, submenu_item_selected);
view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu);
} }
bool iButtonSceneReadedKeyMenu::on_event(iButtonApp* app, iButtonEvent* event) { bool iButtonSceneReadKeyMenu::on_event(iButtonApp* app, iButtonEvent* event) {
bool consumed = false; bool consumed = false;
if(event->type == iButtonEvent::Type::EventTypeMenuSelected) { if(event->type == iButtonEvent::Type::EventTypeMenuSelected) {
@ -49,20 +47,17 @@ bool iButtonSceneReadedKeyMenu::on_event(iButtonApp* app, iButtonEvent* event) {
case SubmenuIndexSave: case SubmenuIndexSave:
app->switch_to_next_scene(iButtonApp::Scene::SceneSaveName); app->switch_to_next_scene(iButtonApp::Scene::SceneSaveName);
break; break;
case SubmenuIndexReadNewKey:
app->search_and_switch_to_previous_scene({iButtonApp::Scene::SceneRead});
break;
} }
consumed = true; consumed = true;
} else if(event->type == iButtonEvent::Type::EventTypeBack) { } else if(event->type == iButtonEvent::Type::EventTypeBack) {
app->search_and_switch_to_previous_scene({iButtonApp::Scene::SceneStart}); app->switch_to_previous_scene();
consumed = true; consumed = true;
} }
return consumed; return consumed;
} }
void iButtonSceneReadedKeyMenu::on_exit(iButtonApp* app) { void iButtonSceneReadKeyMenu::on_exit(iButtonApp* app) {
iButtonAppViewManager* view = app->get_view_manager(); iButtonAppViewManager* view = app->get_view_manager();
Submenu* submenu = view->get_submenu(); Submenu* submenu = view->get_submenu();

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "ibutton_scene_generic.h" #include "ibutton_scene_generic.h"
class iButtonSceneReadedKeyMenu : public iButtonScene { class iButtonSceneReadKeyMenu : public iButtonScene {
public: public:
void on_enter(iButtonApp* app) final; void on_enter(iButtonApp* app) final;
bool on_event(iButtonApp* app, iButtonEvent* event) final; bool on_event(iButtonApp* app, iButtonEvent* event) final;

View File

@ -47,7 +47,7 @@ bool iButtonSceneReadNotKeyError::on_event(iButtonApp* app, iButtonEvent* event)
if(event->type == iButtonEvent::Type::EventTypeDialogResult) { if(event->type == iButtonEvent::Type::EventTypeDialogResult) {
if(event->payload.dialog_result == DialogExResultRight) { if(event->payload.dialog_result == DialogExResultRight) {
app->switch_to_next_scene(iButtonApp::Scene::SceneReadedKeyMenu); app->switch_to_next_scene(iButtonApp::Scene::SceneReadKeyMenu);
} else { } else {
app->switch_to_previous_scene(); app->switch_to_previous_scene();
} }

View File

@ -18,7 +18,6 @@ void iButtonSceneReadSuccess::on_enter(iButtonApp* app) {
DialogEx* dialog_ex = view_manager->get_dialog_ex(); DialogEx* dialog_ex = view_manager->get_dialog_ex();
iButtonKey* key = app->get_key(); iButtonKey* key = app->get_key();
const uint8_t* key_data = ibutton_key_get_data_p(key); const uint8_t* key_data = ibutton_key_get_data_p(key);
DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess);
switch(ibutton_key_get_type(key)) { switch(ibutton_key_get_type(key)) {
case iButtonKeyDS1990: case iButtonKeyDS1990:
@ -45,14 +44,11 @@ void iButtonSceneReadSuccess::on_enter(iButtonApp* app) {
dialog_ex_set_text(dialog_ex, app->get_text_store(), 95, 30, AlignCenter, AlignCenter); dialog_ex_set_text(dialog_ex, app->get_text_store(), 95, 30, AlignCenter, AlignCenter);
dialog_ex_set_left_button_text(dialog_ex, "Retry"); dialog_ex_set_left_button_text(dialog_ex, "Retry");
dialog_ex_set_right_button_text(dialog_ex, "More"); dialog_ex_set_right_button_text(dialog_ex, "More");
dialog_ex_set_icon(dialog_ex, 0, 1, &I_DolphinExcited_64x63); dialog_ex_set_icon(dialog_ex, 0, 1, &I_DolphinReadingSuccess_59x63);
dialog_ex_set_result_callback(dialog_ex, dialog_ex_callback); dialog_ex_set_result_callback(dialog_ex, dialog_ex_callback);
dialog_ex_set_context(dialog_ex, app); dialog_ex_set_context(dialog_ex, app);
view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewDialogEx); view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewDialogEx);
app->notify_success();
app->notify_green_on();
} }
bool iButtonSceneReadSuccess::on_event(iButtonApp* app, iButtonEvent* event) { bool iButtonSceneReadSuccess::on_event(iButtonApp* app, iButtonEvent* event) {
@ -60,11 +56,13 @@ bool iButtonSceneReadSuccess::on_event(iButtonApp* app, iButtonEvent* event) {
if(event->type == iButtonEvent::Type::EventTypeDialogResult) { if(event->type == iButtonEvent::Type::EventTypeDialogResult) {
if(event->payload.dialog_result == DialogExResultRight) { if(event->payload.dialog_result == DialogExResultRight) {
app->switch_to_next_scene(iButtonApp::Scene::SceneReadedKeyMenu); app->switch_to_next_scene(iButtonApp::Scene::SceneReadKeyMenu);
} else { } else {
app->switch_to_previous_scene(); app->switch_to_next_scene(iButtonApp::Scene::SceneRetryConfirm);
} }
consumed = true;
} else if(event->type == iButtonEvent::Type::EventTypeBack) {
app->switch_to_next_scene(iButtonApp::Scene::SceneExitConfirm);
consumed = true; consumed = true;
} }

View File

@ -0,0 +1,51 @@
#include "ibutton_scene_retry_confirm.h"
#include "../ibutton_app.h"
static void widget_callback(GuiButtonType result, InputType type, void* context) {
furi_assert(context);
iButtonApp* app = static_cast<iButtonApp*>(context);
iButtonEvent event;
if(type == InputTypeShort) {
event.type = iButtonEvent::Type::EventTypeWidgetButtonResult;
event.payload.widget_button_result = result;
app->get_view_manager()->send_event(&event);
}
}
void iButtonSceneRetryConfirm::on_enter(iButtonApp* app) {
iButtonAppViewManager* view_manager = app->get_view_manager();
Widget* widget = view_manager->get_widget();
widget_add_button_element(widget, GuiButtonTypeLeft, "Exit", widget_callback, app);
widget_add_button_element(widget, GuiButtonTypeRight, "Stay", widget_callback, app);
widget_add_string_element(
widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Return to reading?");
widget_add_string_element(
widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost");
view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewWidget);
}
bool iButtonSceneRetryConfirm::on_event(iButtonApp* app, iButtonEvent* event) {
bool consumed = false;
if(event->type == iButtonEvent::Type::EventTypeWidgetButtonResult) {
if(event->payload.widget_button_result == GuiButtonTypeLeft) {
app->search_and_switch_to_previous_scene({iButtonApp::Scene::SceneRead});
} else if(event->payload.widget_button_result == GuiButtonTypeRight) {
app->switch_to_previous_scene();
}
consumed = true;
} else if(event->type == iButtonEvent::Type::EventTypeBack) {
consumed = true;
}
return consumed;
}
void iButtonSceneRetryConfirm::on_exit(iButtonApp* app) {
iButtonAppViewManager* view_manager = app->get_view_manager();
Widget* widget = view_manager->get_widget();
widget_reset(widget);
}

View File

@ -0,0 +1,9 @@
#pragma once
#include "ibutton_scene_generic.h"
class iButtonSceneRetryConfirm : public iButtonScene {
public:
void on_enter(iButtonApp* app) final;
bool on_event(iButtonApp* app, iButtonEvent* event) final;
void on_exit(iButtonApp* app) final;
};

View File

@ -49,7 +49,7 @@ bool iButtonSceneSaveName::on_event(iButtonApp* app, iButtonEvent* event) {
app->switch_to_next_scene(iButtonApp::Scene::SceneSaveSuccess); app->switch_to_next_scene(iButtonApp::Scene::SceneSaveSuccess);
} else { } else {
app->search_and_switch_to_previous_scene( app->search_and_switch_to_previous_scene(
{iButtonApp::Scene::SceneReadedKeyMenu, {iButtonApp::Scene::SceneReadKeyMenu,
iButtonApp::Scene::SceneSavedKeyMenu, iButtonApp::Scene::SceneSavedKeyMenu,
iButtonApp::Scene::SceneAddType}); iButtonApp::Scene::SceneAddType});
} }

View File

@ -16,7 +16,7 @@ void iButtonSceneSaveSuccess::on_enter(iButtonApp* app) {
DOLPHIN_DEED(DolphinDeedIbuttonSave); DOLPHIN_DEED(DolphinDeedIbuttonSave);
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
popup_set_text(popup, "Saved!", 13, 22, AlignLeft, AlignBottom); popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
popup_set_callback(popup, popup_callback); popup_set_callback(popup, popup_callback);
popup_set_context(popup, app); popup_set_context(popup, app);
@ -31,7 +31,7 @@ bool iButtonSceneSaveSuccess::on_event(iButtonApp* app, iButtonEvent* event) {
if(event->type == iButtonEvent::Type::EventTypeBack) { if(event->type == iButtonEvent::Type::EventTypeBack) {
app->search_and_switch_to_previous_scene( app->search_and_switch_to_previous_scene(
{iButtonApp::Scene::SceneReadedKeyMenu, {iButtonApp::Scene::SceneReadKeyMenu,
iButtonApp::Scene::SceneSavedKeyMenu, iButtonApp::Scene::SceneSavedKeyMenu,
iButtonApp::Scene::SceneAddType}); iButtonApp::Scene::SceneAddType});
consumed = true; consumed = true;

View File

@ -24,7 +24,7 @@ void iButtonSceneStart::on_enter(iButtonApp* app) {
submenu_add_item(submenu, "Read", SubmenuIndexRead, submenu_callback, app); submenu_add_item(submenu, "Read", SubmenuIndexRead, submenu_callback, app);
submenu_add_item(submenu, "Saved", SubmenuIndexSaved, submenu_callback, app); submenu_add_item(submenu, "Saved", SubmenuIndexSaved, submenu_callback, app);
submenu_add_item(submenu, "Add manually", SubmenuIndexAdd, submenu_callback, app); submenu_add_item(submenu, "Add Manually", SubmenuIndexAdd, submenu_callback, app);
submenu_set_selected_item(submenu, submenu_item_selected); submenu_set_selected_item(submenu, submenu_item_selected);
view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu);

View File

@ -33,7 +33,7 @@ bool iButtonSceneWriteSuccess::on_event(iButtonApp* app, iButtonEvent* event) {
if(event->type == iButtonEvent::Type::EventTypeBack) { if(event->type == iButtonEvent::Type::EventTypeBack) {
app->search_and_switch_to_previous_scene( app->search_and_switch_to_previous_scene(
{iButtonApp::Scene::SceneReadedKeyMenu, iButtonApp::Scene::SceneStart}); {iButtonApp::Scene::SceneReadKeyMenu, iButtonApp::Scene::SceneStart});
consumed = true; consumed = true;
} }

View File

@ -192,5 +192,7 @@ extern "C" void infrared_on_system_start() {
Cli* cli = (Cli*)furi_record_open("cli"); Cli* cli = (Cli*)furi_record_open("cli");
cli_add_command(cli, "ir", CliCommandFlagDefault, infrared_cli_start_ir, NULL); cli_add_command(cli, "ir", CliCommandFlagDefault, infrared_cli_start_ir, NULL);
furi_record_close("cli"); furi_record_close("cli");
#else
UNUSED(infrared_cli_start_ir);
#endif #endif
} }

View File

@ -13,7 +13,7 @@
#include <storage/storage.h> #include <storage/storage.h>
#include "infrared_app.h" #include "infrared_app.h"
static const std::string default_remote_name = "remote"; static const char* default_remote_name = "remote";
std::string InfraredAppRemoteManager::make_full_name( std::string InfraredAppRemoteManager::make_full_name(
const std::string& path, const std::string& path,

View File

@ -23,11 +23,11 @@ void InfraredAppSceneEdit::on_enter(InfraredApp* app) {
InfraredAppViewManager* view_manager = app->get_view_manager(); InfraredAppViewManager* view_manager = app->get_view_manager();
Submenu* submenu = view_manager->get_submenu(); Submenu* submenu = view_manager->get_submenu();
submenu_add_item(submenu, "Add key", SubmenuIndexAddKey, submenu_callback, app); submenu_add_item(submenu, "Add Key", SubmenuIndexAddKey, submenu_callback, app);
submenu_add_item(submenu, "Rename key", SubmenuIndexRenameKey, submenu_callback, app); submenu_add_item(submenu, "Rename Key", SubmenuIndexRenameKey, submenu_callback, app);
submenu_add_item(submenu, "Delete key", SubmenuIndexDeleteKey, submenu_callback, app); submenu_add_item(submenu, "Delete Key", SubmenuIndexDeleteKey, submenu_callback, app);
submenu_add_item(submenu, "Rename remote", SubmenuIndexRenameRemote, submenu_callback, app); submenu_add_item(submenu, "Rename Remote", SubmenuIndexRenameRemote, submenu_callback, app);
submenu_add_item(submenu, "Delete remote", SubmenuIndexDeleteRemote, submenu_callback, app); submenu_add_item(submenu, "Delete Remote", SubmenuIndexDeleteRemote, submenu_callback, app);
submenu_set_selected_item(submenu, submenu_item_selected); submenu_set_selected_item(submenu, submenu_item_selected);
submenu_item_selected = 0; submenu_item_selected = 0;

View File

@ -5,7 +5,6 @@ void InfraredAppSceneEditRenameDone::on_enter(InfraredApp* app) {
Popup* popup = view_manager->get_popup(); Popup* popup = view_manager->get_popup();
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
popup_set_callback(popup, InfraredApp::popup_callback); popup_set_callback(popup, InfraredApp::popup_callback);

View File

@ -21,10 +21,10 @@ void InfraredAppSceneStart::on_enter(InfraredApp* app) {
Submenu* submenu = view_manager->get_submenu(); Submenu* submenu = view_manager->get_submenu();
submenu_add_item( submenu_add_item(
submenu, "Universal library", SubmenuIndexUniversalLibrary, submenu_callback, app); submenu, "Universal Library", SubmenuIndexUniversalLibrary, submenu_callback, app);
submenu_add_item( submenu_add_item(
submenu, "Learn new remote", SubmenuIndexLearnNewRemote, submenu_callback, app); submenu, "Learn New Remote", SubmenuIndexLearnNewRemote, submenu_callback, app);
submenu_add_item(submenu, "Saved remotes", SubmenuIndexSavedRemotes, submenu_callback, app); submenu_add_item(submenu, "Saved Remotes", SubmenuIndexSavedRemotes, submenu_callback, app);
submenu_set_selected_item(submenu, submenu_item_selected); submenu_set_selected_item(submenu, submenu_item_selected);
submenu_item_selected = 0; submenu_item_selected = 0;

View File

@ -50,7 +50,7 @@ static void input_cli_send_print_usage() {
printf("\t\t <type>\t - one of 'press', 'release', 'short', 'long'\r\n"); printf("\t\t <type>\t - one of 'press', 'release', 'short', 'long'\r\n");
} }
void input_cli_send(Cli* cli, string_t args, Input* input) { static void input_cli_send(Cli* cli, string_t args, Input* input) {
InputEvent event; InputEvent event;
string_t key_str; string_t key_str;
string_init(key_str); string_init(key_str);

View File

@ -15,7 +15,7 @@ constexpr uint32_t long_time_high = long_time + jitter_time;
void DecoderEMMarin::reset_state() { void DecoderEMMarin::reset_state() {
ready = false; ready = false;
readed_data = 0; read_data = 0;
manchester_advance( manchester_advance(
manchester_saved_state, ManchesterEventReset, &manchester_saved_state, nullptr); manchester_saved_state, ManchesterEventReset, &manchester_saved_state, nullptr);
} }
@ -26,7 +26,7 @@ bool DecoderEMMarin::read(uint8_t* data, uint8_t data_size) {
if(ready) { if(ready) {
result = true; result = true;
em_marin.decode( em_marin.decode(
reinterpret_cast<const uint8_t*>(&readed_data), sizeof(uint64_t), data, data_size); reinterpret_cast<const uint8_t*>(&read_data), sizeof(uint64_t), data, data_size);
ready = false; ready = false;
} }
@ -59,10 +59,10 @@ void DecoderEMMarin::process_front(bool polarity, uint32_t time) {
manchester_advance(manchester_saved_state, event, &manchester_saved_state, &data); manchester_advance(manchester_saved_state, event, &manchester_saved_state, &data);
if(data_ok) { if(data_ok) {
readed_data = (readed_data << 1) | data; read_data = (read_data << 1) | data;
ready = em_marin.can_be_decoded( ready = em_marin.can_be_decoded(
reinterpret_cast<const uint8_t*>(&readed_data), sizeof(uint64_t)); reinterpret_cast<const uint8_t*>(&read_data), sizeof(uint64_t));
} }
} }
} }

View File

@ -13,7 +13,7 @@ public:
private: private:
void reset_state(); void reset_state();
uint64_t readed_data = 0; uint64_t read_data = 0;
std::atomic<bool> ready; std::atomic<bool> ready;
ManchesterState manchester_saved_state; ManchesterState manchester_saved_state;

View File

@ -78,7 +78,7 @@ void RfidReader::start() {
start_comparator(); start_comparator();
switch_timer_reset(); switch_timer_reset();
last_readed_count = 0; last_read_count = 0;
} }
void RfidReader::start_forced(RfidReader::Type _type) { void RfidReader::start_forced(RfidReader::Type _type) {
@ -97,45 +97,45 @@ void RfidReader::stop() {
bool RfidReader::read(LfrfidKeyType* _type, uint8_t* data, uint8_t data_size, bool switch_enable) { bool RfidReader::read(LfrfidKeyType* _type, uint8_t* data, uint8_t data_size, bool switch_enable) {
bool result = false; bool result = false;
bool something_readed = false; bool something_read = false;
// reading // reading
if(decoder_em.read(data, data_size)) { if(decoder_em.read(data, data_size)) {
*_type = LfrfidKeyType::KeyEM4100; *_type = LfrfidKeyType::KeyEM4100;
something_readed = true; something_read = true;
} }
if(decoder_hid26.read(data, data_size)) { if(decoder_hid26.read(data, data_size)) {
*_type = LfrfidKeyType::KeyH10301; *_type = LfrfidKeyType::KeyH10301;
something_readed = true; something_read = true;
} }
if(decoder_indala.read(data, data_size)) { if(decoder_indala.read(data, data_size)) {
*_type = LfrfidKeyType::KeyI40134; *_type = LfrfidKeyType::KeyI40134;
something_readed = true; something_read = true;
} }
// validation // validation
if(something_readed) { if(something_read) {
switch_timer_reset(); switch_timer_reset();
if(last_readed_type == *_type && memcmp(last_readed_data, data, data_size) == 0) { if(last_read_type == *_type && memcmp(last_read_data, data, data_size) == 0) {
last_readed_count = last_readed_count + 1; last_read_count = last_read_count + 1;
if(last_readed_count > 2) { if(last_read_count > 2) {
result = true; result = true;
} }
} else { } else {
last_readed_type = *_type; last_read_type = *_type;
memcpy(last_readed_data, data, data_size); memcpy(last_read_data, data, data_size);
last_readed_count = 0; last_read_count = 0;
} }
} }
// mode switching // mode switching
if(switch_enable && switch_timer_elapsed()) { if(switch_enable && switch_timer_elapsed()) {
switch_mode(); switch_mode();
last_readed_count = 0; last_read_count = 0;
} }
return result; return result;
@ -152,7 +152,7 @@ bool RfidReader::detect() {
} }
bool RfidReader::any_read() { bool RfidReader::any_read() {
return last_readed_count > 0; return last_read_count > 0;
} }
void RfidReader::start_comparator(void) { void RfidReader::start_comparator(void) {

View File

@ -49,9 +49,9 @@ private:
void switch_timer_reset(); void switch_timer_reset();
void switch_mode(); void switch_mode();
LfrfidKeyType last_readed_type; LfrfidKeyType last_read_type;
uint8_t last_readed_data[LFRFID_KEY_SIZE]; uint8_t last_read_data[LFRFID_KEY_SIZE];
uint8_t last_readed_count; uint8_t last_read_count;
Type type = Type::Normal; Type type = Type::Normal;
}; };

View File

@ -2,7 +2,9 @@
#include "scene/lfrfid_app_scene_start.h" #include "scene/lfrfid_app_scene_start.h"
#include "scene/lfrfid_app_scene_read.h" #include "scene/lfrfid_app_scene_read.h"
#include "scene/lfrfid_app_scene_read_success.h" #include "scene/lfrfid_app_scene_read_success.h"
#include "scene/lfrfid_app_scene_readed_menu.h" #include "scene/lfrfid_app_scene_retry_confirm.h"
#include "scene/lfrfid_app_scene_exit_confirm.h"
#include "scene/lfrfid_app_scene_read_menu.h"
#include "scene/lfrfid_app_scene_write.h" #include "scene/lfrfid_app_scene_write.h"
#include "scene/lfrfid_app_scene_write_success.h" #include "scene/lfrfid_app_scene_write_success.h"
#include "scene/lfrfid_app_scene_emulate.h" #include "scene/lfrfid_app_scene_emulate.h"
@ -48,8 +50,10 @@ void LfRfidApp::run(void* _args) {
} else { } else {
scene_controller.add_scene(SceneType::Start, new LfRfidAppSceneStart()); scene_controller.add_scene(SceneType::Start, new LfRfidAppSceneStart());
scene_controller.add_scene(SceneType::Read, new LfRfidAppSceneRead()); scene_controller.add_scene(SceneType::Read, new LfRfidAppSceneRead());
scene_controller.add_scene(SceneType::RetryConfirm, new LfRfidAppSceneRetryConfirm());
scene_controller.add_scene(SceneType::ExitConfirm, new LfRfidAppSceneExitConfirm());
scene_controller.add_scene(SceneType::ReadSuccess, new LfRfidAppSceneReadSuccess()); scene_controller.add_scene(SceneType::ReadSuccess, new LfRfidAppSceneReadSuccess());
scene_controller.add_scene(SceneType::ReadedMenu, new LfRfidAppSceneReadedMenu()); scene_controller.add_scene(SceneType::ReadKeyMenu, new LfRfidAppSceneReadKeyMenu());
scene_controller.add_scene(SceneType::Write, new LfRfidAppSceneWrite()); scene_controller.add_scene(SceneType::Write, new LfRfidAppSceneWrite());
scene_controller.add_scene(SceneType::WriteSuccess, new LfRfidAppSceneWriteSuccess()); scene_controller.add_scene(SceneType::WriteSuccess, new LfRfidAppSceneWriteSuccess());
scene_controller.add_scene(SceneType::Emulate, new LfRfidAppSceneEmulate()); scene_controller.add_scene(SceneType::Emulate, new LfRfidAppSceneEmulate());

View File

@ -27,13 +27,17 @@ public:
GENERIC_EVENT_ENUM_VALUES, GENERIC_EVENT_ENUM_VALUES,
Next, Next,
MenuSelected, MenuSelected,
Stay,
Retry,
}; };
enum class SceneType : uint8_t { enum class SceneType : uint8_t {
GENERIC_SCENE_ENUM_VALUES, GENERIC_SCENE_ENUM_VALUES,
Read, Read,
ReadSuccess, ReadSuccess,
ReadedMenu, RetryConfirm,
ExitConfirm,
ReadKeyMenu,
Write, Write,
WriteSuccess, WriteSuccess,
Emulate, Emulate,

View File

@ -7,7 +7,7 @@
#include "helpers/rfid_reader.h" #include "helpers/rfid_reader.h"
#include "helpers/rfid_timer_emulator.h" #include "helpers/rfid_timer_emulator.h"
void lfrfid_cli(Cli* cli, string_t args, void* context); static void lfrfid_cli(Cli* cli, string_t args, void* context);
// app cli function // app cli function
extern "C" void lfrfid_on_system_start() { extern "C" void lfrfid_on_system_start() {
@ -15,6 +15,8 @@ extern "C" void lfrfid_on_system_start() {
Cli* cli = static_cast<Cli*>(furi_record_open("cli")); Cli* cli = static_cast<Cli*>(furi_record_open("cli"));
cli_add_command(cli, "rfid", CliCommandFlagDefault, lfrfid_cli, NULL); cli_add_command(cli, "rfid", CliCommandFlagDefault, lfrfid_cli, NULL);
furi_record_close("cli"); furi_record_close("cli");
#else
UNUSED(lfrfid_cli);
#endif #endif
} }
@ -29,7 +31,7 @@ void lfrfid_cli_print_usage() {
printf("\t<key_data> are hex-formatted\r\n"); printf("\t<key_data> are hex-formatted\r\n");
}; };
bool lfrfid_cli_get_key_type(string_t data, LfrfidKeyType* type) { static bool lfrfid_cli_get_key_type(string_t data, LfrfidKeyType* type) {
bool result = false; bool result = false;
if(string_cmp_str(data, "EM4100") == 0 || string_cmp_str(data, "EM-Marin") == 0) { if(string_cmp_str(data, "EM4100") == 0 || string_cmp_str(data, "EM-Marin") == 0) {
@ -46,7 +48,7 @@ bool lfrfid_cli_get_key_type(string_t data, LfrfidKeyType* type) {
return result; return result;
} }
void lfrfid_cli_read(Cli* cli, string_t args) { static void lfrfid_cli_read(Cli* cli, string_t args) {
RfidReader reader; RfidReader reader;
string_t type_string; string_t type_string;
string_init(type_string); string_init(type_string);
@ -97,12 +99,12 @@ void lfrfid_cli_read(Cli* cli, string_t args) {
string_clear(type_string); string_clear(type_string);
} }
void lfrfid_cli_write(Cli* cli, string_t args) { static void lfrfid_cli_write(Cli* cli, string_t args) {
// TODO implement rfid write // TODO implement rfid write
printf("Not implemented :(\r\n"); printf("Not implemented :(\r\n");
} }
void lfrfid_cli_emulate(Cli* cli, string_t args) { static void lfrfid_cli_emulate(Cli* cli, string_t args) {
string_t data; string_t data;
string_init(data); string_init(data);
RfidTimerEmulator emulator; RfidTimerEmulator emulator;
@ -144,7 +146,7 @@ void lfrfid_cli_emulate(Cli* cli, string_t args) {
string_clear(data); string_clear(data);
} }
void lfrfid_cli(Cli* cli, string_t args, void* context) { static void lfrfid_cli(Cli* cli, string_t args, void* context) {
string_t cmd; string_t cmd;
string_init(cmd); string_init(cmd);

View File

@ -73,6 +73,11 @@ bool LfRfidAppSceneDeleteConfirm::on_event(LfRfidApp* app, LfRfidApp::Event* eve
app->delete_key(&app->worker.key); app->delete_key(&app->worker.key);
app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::DeleteSuccess); app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::DeleteSuccess);
consumed = true; consumed = true;
} else if(event->type == LfRfidApp::EventType::Stay) {
app->scene_controller.switch_to_previous_scene();
consumed = true;
} else if(event->type == LfRfidApp::EventType::Back) {
consumed = true;
} }
return consumed; return consumed;
@ -88,7 +93,7 @@ void LfRfidAppSceneDeleteConfirm::on_exit(LfRfidApp* app) {
void LfRfidAppSceneDeleteConfirm::back_callback(void* context) { void LfRfidAppSceneDeleteConfirm::back_callback(void* context) {
LfRfidApp* app = static_cast<LfRfidApp*>(context); LfRfidApp* app = static_cast<LfRfidApp*>(context);
LfRfidApp::Event event; LfRfidApp::Event event;
event.type = LfRfidApp::EventType::Back; event.type = LfRfidApp::EventType::Stay;
app->view_controller.send_event(&event); app->view_controller.send_event(&event);
} }

View File

@ -4,7 +4,7 @@ void LfRfidAppSceneDeleteSuccess::on_enter(LfRfidApp* app, bool need_restore) {
auto popup = app->view_controller.get<PopupVM>(); auto popup = app->view_controller.get<PopupVM>();
popup->set_icon(0, 2, &I_DolphinMafia_115x62); popup->set_icon(0, 2, &I_DolphinMafia_115x62);
popup->set_text("Deleted", 83, 19, AlignLeft, AlignBottom); popup->set_header("Deleted", 83, 19, AlignLeft, AlignBottom);
popup->set_context(app); popup->set_context(app);
popup->set_callback(LfRfidAppSceneDeleteSuccess::timeout_callback); popup->set_callback(LfRfidAppSceneDeleteSuccess::timeout_callback);
popup->set_timeout(1500); popup->set_timeout(1500);

View File

@ -0,0 +1,59 @@
#include "lfrfid_app_scene_exit_confirm.h"
#include "../view/elements/button_element.h"
#include "../view/elements/icon_element.h"
#include "../view/elements/string_element.h"
void LfRfidAppSceneExitConfirm::on_enter(LfRfidApp* app, bool need_restore) {
auto container = app->view_controller.get<ContainerVM>();
auto button = container->add<ButtonElement>();
button->set_type(ButtonElement::Type::Left, "Exit");
button->set_callback(app, LfRfidAppSceneExitConfirm::exit_callback);
button = container->add<ButtonElement>();
button->set_type(ButtonElement::Type::Right, "Stay");
button->set_callback(app, LfRfidAppSceneExitConfirm::stay_callback);
auto line_1 = container->add<StringElement>();
auto line_2 = container->add<StringElement>();
line_1->set_text("Exit to RFID menu?", 64, 19, 128 - 2, AlignCenter, AlignBottom, FontPrimary);
line_2->set_text(
"All unsaved data will be lost", 64, 29, 0, AlignCenter, AlignBottom, FontSecondary);
app->view_controller.switch_to<ContainerVM>();
}
bool LfRfidAppSceneExitConfirm::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
bool consumed = false;
if(event->type == LfRfidApp::EventType::Next) {
app->scene_controller.search_and_switch_to_previous_scene({LfRfidApp::SceneType::Start});
consumed = true;
} else if(event->type == LfRfidApp::EventType::Stay) {
app->scene_controller.switch_to_previous_scene();
consumed = true;
} else if(event->type == LfRfidApp::EventType::Back) {
consumed = true;
}
return consumed;
}
void LfRfidAppSceneExitConfirm::on_exit(LfRfidApp* app) {
app->view_controller.get<ContainerVM>()->clean();
}
void LfRfidAppSceneExitConfirm::exit_callback(void* context) {
LfRfidApp* app = static_cast<LfRfidApp*>(context);
LfRfidApp::Event event;
event.type = LfRfidApp::EventType::Next;
app->view_controller.send_event(&event);
}
void LfRfidAppSceneExitConfirm::stay_callback(void* context) {
LfRfidApp* app = static_cast<LfRfidApp*>(context);
LfRfidApp::Event event;
event.type = LfRfidApp::EventType::Stay;
app->view_controller.send_event(&event);
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "../lfrfid_app.h"
class LfRfidAppSceneExitConfirm : public GenericScene<LfRfidApp> {
public:
void on_enter(LfRfidApp* app, bool need_restore) final;
bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final;
void on_exit(LfRfidApp* app) final;
private:
static void exit_callback(void* context);
static void stay_callback(void* context);
};

View File

@ -1,4 +1,4 @@
#include "lfrfid_app_scene_readed_menu.h" #include "lfrfid_app_scene_read_menu.h"
typedef enum { typedef enum {
SubmenuWrite, SubmenuWrite,
@ -6,7 +6,7 @@ typedef enum {
SubmenuEmulate, SubmenuEmulate,
} SubmenuIndex; } SubmenuIndex;
void LfRfidAppSceneReadedMenu::on_enter(LfRfidApp* app, bool need_restore) { void LfRfidAppSceneReadKeyMenu::on_enter(LfRfidApp* app, bool need_restore) {
auto submenu = app->view_controller.get<SubmenuVM>(); auto submenu = app->view_controller.get<SubmenuVM>();
submenu->add_item("Write", SubmenuWrite, submenu_callback, app); submenu->add_item("Write", SubmenuWrite, submenu_callback, app);
@ -20,7 +20,7 @@ void LfRfidAppSceneReadedMenu::on_enter(LfRfidApp* app, bool need_restore) {
app->view_controller.switch_to<SubmenuVM>(); app->view_controller.switch_to<SubmenuVM>();
} }
bool LfRfidAppSceneReadedMenu::on_event(LfRfidApp* app, LfRfidApp::Event* event) { bool LfRfidAppSceneReadKeyMenu::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
bool consumed = false; bool consumed = false;
if(event->type == LfRfidApp::EventType::MenuSelected) { if(event->type == LfRfidApp::EventType::MenuSelected) {
@ -38,18 +38,18 @@ bool LfRfidAppSceneReadedMenu::on_event(LfRfidApp* app, LfRfidApp::Event* event)
} }
consumed = true; consumed = true;
} else if(event->type == LfRfidApp::EventType::Back) { } else if(event->type == LfRfidApp::EventType::Back) {
app->scene_controller.search_and_switch_to_previous_scene({LfRfidApp::SceneType::Start}); app->scene_controller.switch_to_previous_scene();
consumed = true; consumed = true;
} }
return consumed; return consumed;
} }
void LfRfidAppSceneReadedMenu::on_exit(LfRfidApp* app) { void LfRfidAppSceneReadKeyMenu::on_exit(LfRfidApp* app) {
app->view_controller.get<SubmenuVM>()->clean(); app->view_controller.get<SubmenuVM>()->clean();
} }
void LfRfidAppSceneReadedMenu::submenu_callback(void* context, uint32_t index) { void LfRfidAppSceneReadKeyMenu::submenu_callback(void* context, uint32_t index) {
LfRfidApp* app = static_cast<LfRfidApp*>(context); LfRfidApp* app = static_cast<LfRfidApp*>(context);
LfRfidApp::Event event; LfRfidApp::Event event;

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "../lfrfid_app.h" #include "../lfrfid_app.h"
class LfRfidAppSceneReadedMenu : public GenericScene<LfRfidApp> { class LfRfidAppSceneReadKeyMenu : public GenericScene<LfRfidApp> {
public: public:
void on_enter(LfRfidApp* app, bool need_restore) final; void on_enter(LfRfidApp* app, bool need_restore) final;
bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final;

View File

@ -85,7 +85,13 @@ bool LfRfidAppSceneReadSuccess::on_event(LfRfidApp* app, LfRfidApp::Event* event
bool consumed = false; bool consumed = false;
if(event->type == LfRfidApp::EventType::Next) { if(event->type == LfRfidApp::EventType::Next) {
app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::ReadedMenu); app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::ReadKeyMenu);
consumed = true;
} else if(event->type == LfRfidApp::EventType::Retry) {
app->scene_controller.switch_to_next_scene({LfRfidApp::SceneType::RetryConfirm});
consumed = true;
} else if(event->type == LfRfidApp::EventType::Back) {
app->scene_controller.switch_to_next_scene({LfRfidApp::SceneType::ExitConfirm});
consumed = true; consumed = true;
} }
@ -103,7 +109,7 @@ void LfRfidAppSceneReadSuccess::on_exit(LfRfidApp* app) {
void LfRfidAppSceneReadSuccess::back_callback(void* context) { void LfRfidAppSceneReadSuccess::back_callback(void* context) {
LfRfidApp* app = static_cast<LfRfidApp*>(context); LfRfidApp* app = static_cast<LfRfidApp*>(context);
LfRfidApp::Event event; LfRfidApp::Event event;
event.type = LfRfidApp::EventType::Back; event.type = LfRfidApp::EventType::Retry;
app->view_controller.send_event(&event); app->view_controller.send_event(&event);
} }

View File

@ -0,0 +1,59 @@
#include "lfrfid_app_scene_retry_confirm.h"
#include "../view/elements/button_element.h"
#include "../view/elements/icon_element.h"
#include "../view/elements/string_element.h"
void LfRfidAppSceneRetryConfirm::on_enter(LfRfidApp* app, bool need_restore) {
auto container = app->view_controller.get<ContainerVM>();
auto button = container->add<ButtonElement>();
button->set_type(ButtonElement::Type::Left, "Exit");
button->set_callback(app, LfRfidAppSceneRetryConfirm::exit_callback);
button = container->add<ButtonElement>();
button->set_type(ButtonElement::Type::Right, "Stay");
button->set_callback(app, LfRfidAppSceneRetryConfirm::stay_callback);
auto line_1 = container->add<StringElement>();
auto line_2 = container->add<StringElement>();
line_1->set_text("Return to reading?", 64, 19, 128 - 2, AlignCenter, AlignBottom, FontPrimary);
line_2->set_text(
"All unsaved data will be lost", 64, 29, 0, AlignCenter, AlignBottom, FontSecondary);
app->view_controller.switch_to<ContainerVM>();
}
bool LfRfidAppSceneRetryConfirm::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
bool consumed = false;
if(event->type == LfRfidApp::EventType::Next) {
app->scene_controller.search_and_switch_to_previous_scene({LfRfidApp::SceneType::Read});
consumed = true;
} else if(event->type == LfRfidApp::EventType::Stay) {
app->scene_controller.switch_to_previous_scene();
consumed = true;
} else if(event->type == LfRfidApp::EventType::Back) {
consumed = true;
}
return consumed;
}
void LfRfidAppSceneRetryConfirm::on_exit(LfRfidApp* app) {
app->view_controller.get<ContainerVM>()->clean();
}
void LfRfidAppSceneRetryConfirm::exit_callback(void* context) {
LfRfidApp* app = static_cast<LfRfidApp*>(context);
LfRfidApp::Event event;
event.type = LfRfidApp::EventType::Next;
app->view_controller.send_event(&event);
}
void LfRfidAppSceneRetryConfirm::stay_callback(void* context) {
LfRfidApp* app = static_cast<LfRfidApp*>(context);
LfRfidApp::Event event;
event.type = LfRfidApp::EventType::Stay;
app->view_controller.send_event(&event);
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "../lfrfid_app.h"
class LfRfidAppSceneRetryConfirm : public GenericScene<LfRfidApp> {
public:
void on_enter(LfRfidApp* app, bool need_restore) final;
bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final;
void on_exit(LfRfidApp* app) final;
private:
static void exit_callback(void* context);
static void stay_callback(void* context);
};

View File

@ -42,7 +42,7 @@ bool LfRfidAppSceneSaveName::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveSuccess); app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveSuccess);
} else { } else {
app->scene_controller.search_and_switch_to_previous_scene( app->scene_controller.search_and_switch_to_previous_scene(
{LfRfidApp::SceneType::ReadedMenu}); {LfRfidApp::SceneType::ReadKeyMenu});
} }
} }

View File

@ -8,7 +8,7 @@ void LfRfidAppSceneSaveSuccess::on_enter(LfRfidApp* app, bool need_restore) {
DOLPHIN_DEED(DolphinDeedRfidSave); DOLPHIN_DEED(DolphinDeedRfidSave);
popup->set_icon(32, 5, &I_DolphinNice_96x59); popup->set_icon(32, 5, &I_DolphinNice_96x59);
popup->set_text("Saved!", 13, 22, AlignLeft, AlignBottom); popup->set_header("Saved!", 5, 7, AlignLeft, AlignTop);
popup->set_context(app); popup->set_context(app);
popup->set_callback(LfRfidAppSceneSaveSuccess::timeout_callback); popup->set_callback(LfRfidAppSceneSaveSuccess::timeout_callback);
popup->set_timeout(1500); popup->set_timeout(1500);
@ -22,11 +22,11 @@ bool LfRfidAppSceneSaveSuccess::on_event(LfRfidApp* app, LfRfidApp::Event* event
if(event->type == LfRfidApp::EventType::Back) { if(event->type == LfRfidApp::EventType::Back) {
bool result = app->scene_controller.has_previous_scene( bool result = app->scene_controller.has_previous_scene(
{LfRfidApp::SceneType::ReadedMenu, LfRfidApp::SceneType::SelectKey}); {LfRfidApp::SceneType::ReadKeyMenu, LfRfidApp::SceneType::SelectKey});
if(result) { if(result) {
app->scene_controller.search_and_switch_to_previous_scene( app->scene_controller.search_and_switch_to_previous_scene(
{LfRfidApp::SceneType::ReadedMenu, LfRfidApp::SceneType::SelectKey}); {LfRfidApp::SceneType::ReadKeyMenu, LfRfidApp::SceneType::SelectKey});
} else { } else {
app->scene_controller.search_and_switch_to_another_scene( app->scene_controller.search_and_switch_to_another_scene(
{LfRfidApp::SceneType::SaveType}, LfRfidApp::SceneType::SelectKey); {LfRfidApp::SceneType::SaveType}, LfRfidApp::SceneType::SelectKey);

View File

@ -11,7 +11,7 @@ void LfRfidAppSceneStart::on_enter(LfRfidApp* app, bool need_restore) {
submenu->add_item("Read", SubmenuRead, submenu_callback, app); submenu->add_item("Read", SubmenuRead, submenu_callback, app);
submenu->add_item("Saved", SubmenuSaved, submenu_callback, app); submenu->add_item("Saved", SubmenuSaved, submenu_callback, app);
submenu->add_item("Add manually", SubmenuAddManually, submenu_callback, app); submenu->add_item("Add Manually", SubmenuAddManually, submenu_callback, app);
if(need_restore) { if(need_restore) {
submenu->set_selected_item(submenu_item_selected); submenu->set_selected_item(submenu_item_selected);

View File

@ -18,7 +18,7 @@ bool LfRfidAppSceneWriteSuccess::on_event(LfRfidApp* app, LfRfidApp::Event* even
if(event->type == LfRfidApp::EventType::Back) { if(event->type == LfRfidApp::EventType::Back) {
app->scene_controller.search_and_switch_to_previous_scene( app->scene_controller.search_and_switch_to_previous_scene(
{LfRfidApp::SceneType::ReadedMenu, LfRfidApp::SceneType::SelectKey}); {LfRfidApp::SceneType::ReadKeyMenu, LfRfidApp::SceneType::SelectKey});
consumed = true; consumed = true;
} }

View File

@ -65,39 +65,47 @@ static void loader_cli_print_usage() {
printf("\topen <Application Name:string>\t - Open application by name\r\n"); printf("\topen <Application Name:string>\t - Open application by name\r\n");
} }
static FlipperApplication const* loader_find_application_by_name_in_list(
const char* name,
const FlipperApplication* list,
const uint32_t n_apps) {
for(size_t i = 0; i < n_apps; i++) {
if(strcmp(name, list[i].name) == 0) {
return &list[i];
}
}
return NULL;
}
const FlipperApplication* loader_find_application_by_name(const char* name) { const FlipperApplication* loader_find_application_by_name(const char* name) {
const FlipperApplication* application = NULL; const FlipperApplication* application = NULL;
application = loader_find_application_by_name_in_list(name, FLIPPER_APPS, FLIPPER_APPS_COUNT);
for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { if(!application) {
if(strcmp(name, FLIPPER_APPS[i].name) == 0) { application =
application = &FLIPPER_APPS[i]; loader_find_application_by_name_in_list(name, FLIPPER_PLUGINS, FLIPPER_PLUGINS_COUNT);
}
} }
if(!application) {
for(size_t i = 0; i < FLIPPER_PLUGINS_COUNT; i++) { application = loader_find_application_by_name_in_list(
if(strcmp(name, FLIPPER_PLUGINS[i].name) == 0) { name, FLIPPER_SETTINGS_APPS, FLIPPER_SETTINGS_APPS_COUNT);
application = &FLIPPER_PLUGINS[i];
}
} }
if(!application) {
for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { application = loader_find_application_by_name_in_list(
if(strcmp(name, FLIPPER_SETTINGS_APPS[i].name) == 0) { name, FLIPPER_SYSTEM_APPS, FLIPPER_SYSTEM_APPS_COUNT);
application = &FLIPPER_SETTINGS_APPS[i];
}
} }
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) && !application) {
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { application = loader_find_application_by_name_in_list(
for(size_t i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) { name, FLIPPER_DEBUG_APPS, FLIPPER_DEBUG_APPS_COUNT);
if(strcmp(name, FLIPPER_DEBUG_APPS[i].name) == 0) {
application = &FLIPPER_DEBUG_APPS[i];
}
}
} }
return application; return application;
} }
void loader_cli_open(Cli* cli, string_t args, Loader* instance) { void loader_cli_open(Cli* cli, string_t args, Loader* instance) {
if(loader_is_locked(instance)) {
printf("Can't start, furi application is running");
return;
}
string_t application_name; string_t application_name;
string_init(application_name); string_init(application_name);
@ -149,7 +157,7 @@ void loader_cli_list(Cli* cli, string_t args, Loader* instance) {
} }
} }
void loader_cli(Cli* cli, string_t args, void* _ctx) { static void loader_cli(Cli* cli, string_t args, void* _ctx) {
furi_assert(_ctx); furi_assert(_ctx);
Loader* instance = _ctx; Loader* instance = _ctx;
@ -289,7 +297,9 @@ static Loader* loader_alloc() {
#ifdef SRV_CLI #ifdef SRV_CLI
instance->cli = furi_record_open("cli"); instance->cli = furi_record_open("cli");
cli_add_command(instance->cli, "loader", CliCommandFlagDefault, loader_cli, instance); cli_add_command(instance->cli, "loader", CliCommandFlagParallelSafe, loader_cli, instance);
#else
UNUSED(loader_cli);
#endif #endif
instance->loader_thread = osThreadGetId(); instance->loader_thread = osThreadGetId();
@ -386,7 +396,7 @@ static void loader_build_menu() {
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
menu_add_item( menu_add_item(
loader_instance->primary_menu, loader_instance->primary_menu,
"Debug tools", "Debug Tools",
&A_Debug_14, &A_Debug_14,
i++, i++,
loader_submenu_callback, loader_submenu_callback,

View File

@ -136,5 +136,7 @@ void nfc_on_system_start() {
Cli* cli = furi_record_open("cli"); Cli* cli = furi_record_open("cli");
cli_add_command(cli, "nfc", CliCommandFlagDefault, nfc_cli, NULL); cli_add_command(cli, "nfc", CliCommandFlagDefault, nfc_cli, NULL);
furi_record_close("cli"); furi_record_close("cli");
#else
UNUSED(nfc_cli);
#endif #endif
} }

View File

@ -20,7 +20,7 @@ void nfc_scene_card_menu_on_enter(void* context) {
if(nfc->dev->dev_data.nfc_data.protocol > NfcDeviceProtocolUnknown) { if(nfc->dev->dev_data.nfc_data.protocol > NfcDeviceProtocolUnknown) {
submenu_add_item( submenu_add_item(
submenu, submenu,
"Run compatible app", "Run Compatible App",
SubmenuIndexRunApp, SubmenuIndexRunApp,
nfc_scene_card_menu_submenu_callback, nfc_scene_card_menu_submenu_callback,
nfc); nfc);
@ -34,7 +34,7 @@ void nfc_scene_card_menu_on_enter(void* context) {
submenu_add_item( submenu_add_item(
submenu, "Emulate UID", SubmenuIndexEmulate, nfc_scene_card_menu_submenu_callback, nfc); submenu, "Emulate UID", SubmenuIndexEmulate, nfc_scene_card_menu_submenu_callback, nfc);
submenu_add_item( submenu_add_item(
submenu, "Name and save UID", SubmenuIndexSave, nfc_scene_card_menu_submenu_callback, nfc); submenu, "Save UID", SubmenuIndexSave, nfc_scene_card_menu_submenu_callback, nfc);
submenu_set_selected_item( submenu_set_selected_item(
nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneCardMenu)); nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneCardMenu));

View File

@ -15,11 +15,7 @@ void nfc_scene_mifare_desfire_menu_on_enter(void* context) {
Submenu* submenu = nfc->submenu; Submenu* submenu = nfc->submenu;
submenu_add_item( submenu_add_item(
submenu, submenu, "Save", SubmenuIndexSave, nfc_scene_mifare_desfire_menu_submenu_callback, nfc);
"Name and save",
SubmenuIndexSave,
nfc_scene_mifare_desfire_menu_submenu_callback,
nfc);
submenu_set_selected_item( submenu_set_selected_item(
nfc->submenu, nfc->submenu,
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireMenu)); scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireMenu));

View File

@ -16,7 +16,7 @@ void nfc_scene_mifare_ul_menu_on_enter(void* context) {
Submenu* submenu = nfc->submenu; Submenu* submenu = nfc->submenu;
submenu_add_item( submenu_add_item(
submenu, "Name and save", SubmenuIndexSave, nfc_scene_mifare_ul_menu_submenu_callback, nfc); submenu, "Save", SubmenuIndexSave, nfc_scene_mifare_ul_menu_submenu_callback, nfc);
submenu_add_item( submenu_add_item(
submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_mifare_ul_menu_submenu_callback, nfc); submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_mifare_ul_menu_submenu_callback, nfc);
submenu_set_selected_item( submenu_set_selected_item(

View File

@ -36,7 +36,7 @@ void nfc_scene_saved_menu_on_enter(void* context) {
nfc); nfc);
} }
submenu_add_item( submenu_add_item(
submenu, "Edit UID and name", SubmenuIndexEdit, nfc_scene_saved_menu_submenu_callback, nfc); submenu, "Edit UID and Name", SubmenuIndexEdit, nfc_scene_saved_menu_submenu_callback, nfc);
submenu_add_item( submenu_add_item(
submenu, "Delete", SubmenuIndexDelete, nfc_scene_saved_menu_submenu_callback, nfc); submenu, "Delete", SubmenuIndexDelete, nfc_scene_saved_menu_submenu_callback, nfc);
submenu_add_item( submenu_add_item(

View File

@ -18,7 +18,7 @@ void nfc_scene_scripts_menu_on_enter(void* context) {
submenu_add_item( submenu_add_item(
submenu, submenu,
"Read bank card", "Read Bank Card",
SubmenuIndexBankCard, SubmenuIndexBankCard,
nfc_scene_scripts_menu_submenu_callback, nfc_scene_scripts_menu_submenu_callback,
nfc); nfc);

View File

@ -19,17 +19,16 @@ void nfc_scene_start_on_enter(void* context) {
Submenu* submenu = nfc->submenu; Submenu* submenu = nfc->submenu;
submenu_add_item( submenu_add_item(
submenu, "Read card", SubmenuIndexRead, nfc_scene_start_submenu_callback, nfc); submenu, "Read Card", SubmenuIndexRead, nfc_scene_start_submenu_callback, nfc);
submenu_add_item( submenu_add_item(
submenu, submenu,
"Run special action", "Run Special Action",
SubmenuIndexRunScript, SubmenuIndexRunScript,
nfc_scene_start_submenu_callback, nfc_scene_start_submenu_callback,
nfc); nfc);
submenu_add_item(submenu, "Saved", SubmenuIndexSaved, nfc_scene_start_submenu_callback, nfc);
submenu_add_item( submenu_add_item(
submenu, "Saved cards", SubmenuIndexSaved, nfc_scene_start_submenu_callback, nfc); submenu, "Add Manually", SubmenuIndexAddManualy, nfc_scene_start_submenu_callback, nfc);
submenu_add_item(
submenu, "Add manually", SubmenuIndexAddManualy, nfc_scene_start_submenu_callback, nfc);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
submenu_add_item( submenu_add_item(

View File

@ -1,6 +1,7 @@
#include <furi.h> #include <furi.h>
#include <furi_hal.h> #include <furi_hal.h>
#include <storage/storage.h> #include <storage/storage.h>
#include <input/input.h>
#include "notification.h" #include "notification.h"
#include "notification_messages.h" #include "notification_messages.h"
#include "notification_app.h" #include "notification_app.h"
@ -163,7 +164,6 @@ void notification_process_notification_message(
notification_message = (*message->sequence)[notification_message_index]; notification_message = (*message->sequence)[notification_message_index];
bool led_active = false; bool led_active = false;
uint8_t display_led_lock = 0;
uint8_t led_values[NOTIFICATION_LED_COUNT] = {0x00, 0x00, 0x00}; uint8_t led_values[NOTIFICATION_LED_COUNT] = {0x00, 0x00, 0x00};
bool reset_notifications = true; bool reset_notifications = true;
float speaker_volume_setting = app->settings.speaker_volume; float speaker_volume_setting = app->settings.speaker_volume;
@ -191,18 +191,18 @@ void notification_process_notification_message(
reset_mask |= reset_display_mask; reset_mask |= reset_display_mask;
break; break;
case NotificationMessageTypeLedDisplayLock: case NotificationMessageTypeLedDisplayLock:
furi_assert(display_led_lock < UINT8_MAX); furi_assert(app->display_led_lock < UINT8_MAX);
display_led_lock++; app->display_led_lock++;
if(display_led_lock == 1) { if(app->display_led_lock == 1) {
notification_apply_internal_led_layer( notification_apply_internal_led_layer(
&app->display, &app->display,
notification_message->data.led.value * display_brightness_setting); notification_message->data.led.value * display_brightness_setting);
} }
break; break;
case NotificationMessageTypeLedDisplayUnlock: case NotificationMessageTypeLedDisplayUnlock:
furi_assert(display_led_lock > 0); furi_assert(app->display_led_lock > 0);
display_led_lock--; app->display_led_lock--;
if(display_led_lock == 0) { if(app->display_led_lock == 0) {
notification_apply_internal_led_layer( notification_apply_internal_led_layer(
&app->display, &app->display,
notification_message->data.led.value * display_brightness_setting); notification_message->data.led.value * display_brightness_setting);
@ -416,6 +416,8 @@ static bool notification_save_settings(NotificationApp* app) {
}; };
static void input_event_callback(const void* value, void* context) { static void input_event_callback(const void* value, void* context) {
furi_assert(value);
furi_assert(context);
NotificationApp* app = context; NotificationApp* app = context;
notification_message(app, &sequence_display_on); notification_message(app, &sequence_display_on);
} }

View File

@ -49,6 +49,7 @@ struct NotificationApp {
NotificationLedLayer display; NotificationLedLayer display;
NotificationLedLayer led[NOTIFICATION_LED_COUNT]; NotificationLedLayer led[NOTIFICATION_LED_COUNT];
uint8_t display_led_lock;
NotificationSettings settings; NotificationSettings settings;
}; };

Some files were not shown because too many files have changed in this diff Show More