Merge branch 'release-candidate' into release
This commit is contained in:
		
						commit
						d294b5e256
					
				
							
								
								
									
										1
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
								
							@ -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
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										16
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							@ -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
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -36,3 +36,6 @@ CMakeLists.txt
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# bundle output
 | 
					# bundle output
 | 
				
			||||||
dist
 | 
					dist
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# kde
 | 
				
			||||||
 | 
					.directory
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							@ -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
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										52
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								Makefile
									
									
									
									
									
								
							@ -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    ================"
 | 
				
			||||||
 | 
				
			|||||||
@ -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)
 | 
				
			||||||
 | 
				
			|||||||
@ -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)
 | 
				
			||||||
 | 
				
			|||||||
@ -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
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -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
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
				
			|||||||
@ -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)
 | 
				
			||||||
@ -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
 | 
				
			||||||
 | 
				
			|||||||
@ -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");
 | 
				
			||||||
 | 
				
			|||||||
@ -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(
 | 
				
			||||||
 | 
				
			|||||||
@ -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] = "*",
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -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");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -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"
 | 
				
			||||||
 | 
				
			|||||||
@ -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, ...) {
 | 
				
			||||||
 | 
				
			|||||||
@ -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,
 | 
				
			||||||
 | 
				
			|||||||
@ -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) {
 | 
				
			||||||
 | 
				
			|||||||
@ -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]);
 | 
				
			||||||
 | 
				
			|||||||
@ -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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -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 {
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -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());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -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");
 | 
				
			||||||
 | 
				
			|||||||
@ -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) {
 | 
					 | 
				
			||||||
            scene_manager_set_scene_state(
 | 
					 | 
				
			||||||
                desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER);
 | 
					 | 
				
			||||||
            scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
        furi_hal_rtc_reset_flag(FuriHalRtcFlagLock);
 | 
					        furi_hal_rtc_reset_flag(FuriHalRtcFlagLock);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) {
 | 
				
			||||||
 | 
					        if(!loader_is_locked(desktop->loader)) {
 | 
				
			||||||
 | 
					            desktop_auto_lock_arm(desktop);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        desktop_lock(desktop);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(desktop_is_first_start()) {
 | 
					    if(desktop_is_first_start()) {
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
 | 
				
			|||||||
@ -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) {
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -38,4 +38,5 @@ typedef enum {
 | 
				
			|||||||
    // Global events
 | 
					    // Global events
 | 
				
			||||||
    DesktopGlobalBeforeAppStarted,
 | 
					    DesktopGlobalBeforeAppStarted,
 | 
				
			||||||
    DesktopGlobalAfterAppFinished,
 | 
					    DesktopGlobalAfterAppFinished,
 | 
				
			||||||
 | 
					    DesktopGlobalAutoLock,
 | 
				
			||||||
} DesktopEvent;
 | 
					} DesktopEvent;
 | 
				
			||||||
 | 
				
			|||||||
@ -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) {
 | 
				
			||||||
 | 
					            while(count-- > 0) {
 | 
				
			||||||
                debug_view->callback(DesktopDebugEventWrongDeed, debug_view->context);
 | 
					                debug_view->callback(DesktopDebugEventWrongDeed, debug_view->context);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        } else if(event->key == InputKeyRight) {
 | 
					        } else if(event->key == InputKeyRight) {
 | 
				
			||||||
 | 
					            while(count-- > 0) {
 | 
				
			||||||
                debug_view->callback(DesktopDebugEventDeed, debug_view->context);
 | 
					                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 {
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
 | 
				
			|||||||
@ -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,52 +83,55 @@ 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((now < m->hint_icon_expire_at) && !m->pin_locked) {
 | 
					    } else if(view_state == DesktopViewLockedStateLockedHintShown) {
 | 
				
			||||||
        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) {
 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        if(m->unlocked_hint) {
 | 
					 | 
				
			||||||
        canvas_set_font(canvas, FontPrimary);
 | 
					        canvas_set_font(canvas, FontPrimary);
 | 
				
			||||||
        elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Unlocked");
 | 
					        elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Unlocked");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
View* desktop_view_locked_get_view(DesktopViewLocked* locked_view) {
 | 
					View* desktop_view_locked_get_view(DesktopViewLocked* locked_view) {
 | 
				
			||||||
@ -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;
 | 
					 | 
				
			||||||
    bool locked_with_pin = false;
 | 
					 | 
				
			||||||
    bool doors_closing = false;
 | 
					 | 
				
			||||||
    uint32_t press_time = xTaskGetTickCount();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
    DesktopViewLockedModel* model = view_get_model(locked_view->view);
 | 
					    DesktopViewLockedModel* model = view_get_model(locked_view->view);
 | 
				
			||||||
        bool changed = false;
 | 
					    if(model->view_state == DesktopViewLockedStateUnlockedHintShown &&
 | 
				
			||||||
        locked = model->locked;
 | 
					       event->type == InputTypePress) {
 | 
				
			||||||
        locked_with_pin = model->pin_locked;
 | 
					        model->view_state = DesktopViewLockedStateUnlocked;
 | 
				
			||||||
        doors_closing = model->doors_closing;
 | 
					        is_changed = true;
 | 
				
			||||||
        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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
 | 
				
			|||||||
@ -52,7 +52,10 @@ typedef enum {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    DolphinDeedU2fAuthorized,
 | 
					    DolphinDeedU2fAuthorized,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    DolphinDeedMAX
 | 
					    DolphinDeedMAX,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DolphinDeedTestLeft,
 | 
				
			||||||
 | 
					    DolphinDeedTestRight,
 | 
				
			||||||
} DolphinDeed;
 | 
					} DolphinDeed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
 | 
				
			|||||||
@ -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];
 | 
				
			||||||
 | 
				
			|||||||
@ -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,
 | 
				
			||||||
 | 
				
			|||||||
@ -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()},
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
 | 
				
			|||||||
@ -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)) {
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										51
									
								
								applications/ibutton/scene/ibutton_scene_exit_confirm.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								applications/ibutton/scene/ibutton_scene_exit_confirm.cpp
									
									
									
									
									
										Normal 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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										9
									
								
								applications/ibutton/scene/ibutton_scene_exit_confirm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								applications/ibutton/scene/ibutton_scene_exit_confirm.h
									
									
									
									
									
										Normal 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;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -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) {
 | 
				
			||||||
 | 
				
			|||||||
@ -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();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -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();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -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;
 | 
				
			||||||
@ -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();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										51
									
								
								applications/ibutton/scene/ibutton_scene_retry_confirm.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								applications/ibutton/scene/ibutton_scene_retry_confirm.cpp
									
									
									
									
									
										Normal 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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										9
									
								
								applications/ibutton/scene/ibutton_scene_retry_confirm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								applications/ibutton/scene/ibutton_scene_retry_confirm.h
									
									
									
									
									
										Normal 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;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -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});
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -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,
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
 | 
				
			|||||||
@ -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));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
 | 
				
			|||||||
@ -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) {
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -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());
 | 
				
			||||||
 | 
				
			|||||||
@ -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,
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										59
									
								
								applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.cpp
									
									
									
									
									
										Normal 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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										13
									
								
								applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.h
									
									
									
									
									
										Normal 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);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -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;
 | 
				
			||||||
@ -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);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										59
									
								
								applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.cpp
									
									
									
									
									
										Normal 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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										13
									
								
								applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.h
									
									
									
									
									
										Normal 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);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -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});
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -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) {
 | 
				
			||||||
 | 
					        application = loader_find_application_by_name_in_list(
 | 
				
			||||||
 | 
					            name, FLIPPER_SETTINGS_APPS, FLIPPER_SETTINGS_APPS_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_SYSTEM_APPS, FLIPPER_SYSTEM_APPS_COUNT);
 | 
				
			||||||
            application = &FLIPPER_PLUGINS[i];
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) {
 | 
					 | 
				
			||||||
        if(strcmp(name, FLIPPER_SETTINGS_APPS[i].name) == 0) {
 | 
					 | 
				
			||||||
            application = &FLIPPER_SETTINGS_APPS[i];
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
 | 
					 | 
				
			||||||
        for(size_t i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) {
 | 
					 | 
				
			||||||
            if(strcmp(name, FLIPPER_DEBUG_APPS[i].name) == 0) {
 | 
					 | 
				
			||||||
                application = &FLIPPER_DEBUG_APPS[i];
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) && !application) {
 | 
				
			||||||
 | 
					        application = loader_find_application_by_name_in_list(
 | 
				
			||||||
 | 
					            name, FLIPPER_DEBUG_APPS, FLIPPER_DEBUG_APPS_COUNT);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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,
 | 
				
			||||||
 | 
				
			|||||||
@ -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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -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));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -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));
 | 
				
			||||||
 | 
				
			|||||||
@ -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(
 | 
				
			||||||
 | 
				
			|||||||
@ -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(
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
 | 
				
			|||||||
@ -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(
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -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
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user