Merge remote-tracking branch 'origin/dev' into release-candidate
4
.github/workflows/build.yml
vendored
@ -58,7 +58,7 @@ jobs:
|
||||
- name: 'Bundle scripts'
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
run: |
|
||||
tar czpf artifacts/flipper-z-any-scripts-${SUFFIX}.tgz scripts
|
||||
tar czpf "artifacts/flipper-z-any-scripts-${SUFFIX}.tgz" scripts
|
||||
|
||||
- name: 'Build the firmware'
|
||||
run: |
|
||||
@ -73,6 +73,8 @@ jobs:
|
||||
./fbt TARGET_HW=$TARGET_HW fap_dist
|
||||
tar czpf "artifacts/flipper-z-${TARGET}-debugapps-${SUFFIX}.tgz" \
|
||||
-C dist/${TARGET}-*/apps/Debug .
|
||||
tar czpf "artifacts/flipper-z-${TARGET}-appsymbols-${SUFFIX}.tgz" \
|
||||
-C dist/${TARGET}-*/debug_elf .
|
||||
done
|
||||
|
||||
- name: "Check for uncommitted changes"
|
||||
|
||||
7
.github/workflows/unit_tests.yml
vendored
@ -31,7 +31,7 @@ jobs:
|
||||
if: success()
|
||||
timeout-minutes: 10
|
||||
run: |
|
||||
./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1
|
||||
./fbt flash SWD_TRANSPORT_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1
|
||||
|
||||
- name: 'Wait for flipper and format ext'
|
||||
id: format_ext
|
||||
@ -45,9 +45,10 @@ jobs:
|
||||
- name: 'Copy assets and unit data, reboot and wait for flipper'
|
||||
id: copy
|
||||
if: steps.format_ext.outcome == 'success'
|
||||
timeout-minutes: 5
|
||||
timeout-minutes: 7
|
||||
run: |
|
||||
source scripts/toolchain/fbtenv.sh
|
||||
python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}}
|
||||
python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} -f send assets/resources /ext
|
||||
python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} -f send assets/unit_tests /ext/unit_tests
|
||||
python3 scripts/power.py -p ${{steps.device.outputs.flipper}} reboot
|
||||
@ -64,4 +65,4 @@ jobs:
|
||||
- name: 'Check GDB output'
|
||||
if: failure()
|
||||
run: |
|
||||
./fbt gdb_trace_all OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1
|
||||
./fbt gdb_trace_all SWD_TRANSPORT_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1
|
||||
|
||||
2
.github/workflows/updater_test.yml
vendored
@ -64,7 +64,7 @@ jobs:
|
||||
- name: 'Flash last release'
|
||||
if: failure()
|
||||
run: |
|
||||
./fbt flash OPENOCD_ADAPTER_SERIAL=${{steps.device.outputs.stlink}} FORCE=1
|
||||
./fbt flash SWD_TRANSPORT_SERIAL=${{steps.device.outputs.stlink}} FORCE=1
|
||||
|
||||
- name: 'Wait for flipper and format ext'
|
||||
if: failure()
|
||||
|
||||
9
.vscode/.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
./c_cpp_properties.json
|
||||
./launch.json
|
||||
./settings.json
|
||||
./tasks.json
|
||||
/c_cpp_properties.json
|
||||
/extensions.json
|
||||
/launch.json
|
||||
/settings.json
|
||||
/tasks.json
|
||||
|
||||
19
.vscode/example/clangd/extensions.json
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
||||
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
||||
// List of extensions which should be recommended for users of this workspace.
|
||||
"recommendations": [
|
||||
"ms-python.black-formatter",
|
||||
"llvm-vs-code-extensions.vscode-clangd",
|
||||
"amiralizadeh9480.cpp-helper",
|
||||
"marus25.cortex-debug",
|
||||
"zxh404.vscode-proto3",
|
||||
"augustocdias.tasks-shell-input"
|
||||
],
|
||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||
"unwantedRecommendations": [
|
||||
"twxs.cmake",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.cmake-tools"
|
||||
]
|
||||
}
|
||||
@ -13,6 +13,7 @@
|
||||
],
|
||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||
"unwantedRecommendations": [
|
||||
"llvm-vs-code-extensions.vscode-clangd",
|
||||
"twxs.cmake",
|
||||
"ms-vscode.cmake-tools"
|
||||
]
|
||||
9
.vscode/example/settings.json
vendored
@ -21,5 +21,10 @@
|
||||
"SConscript": "python",
|
||||
"SConstruct": "python",
|
||||
"*.fam": "python",
|
||||
}
|
||||
}
|
||||
},
|
||||
"clangd.arguments": [
|
||||
// We might be able to tighten this a bit more to only include the correct toolchain.
|
||||
"--query-driver=**",
|
||||
"--compile-commands-dir=${workspaceFolder}/build/latest"
|
||||
]
|
||||
}
|
||||
16
.vscode/example/tasks.json
vendored
@ -16,29 +16,17 @@
|
||||
"command": "./fbt"
|
||||
},
|
||||
{
|
||||
"label": "[Release] Flash (ST-Link)",
|
||||
"label": "[Release] Flash (SWD)",
|
||||
"group": "build",
|
||||
"type": "shell",
|
||||
"command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 flash"
|
||||
},
|
||||
{
|
||||
"label": "[Debug] Flash (ST-Link)",
|
||||
"label": "[Debug] Flash (SWD)",
|
||||
"group": "build",
|
||||
"type": "shell",
|
||||
"command": "./fbt FORCE=1 flash"
|
||||
},
|
||||
{
|
||||
"label": "[Release] Flash (blackmagic)",
|
||||
"group": "build",
|
||||
"type": "shell",
|
||||
"command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 flash_blackmagic"
|
||||
},
|
||||
{
|
||||
"label": "[Debug] Flash (blackmagic)",
|
||||
"group": "build",
|
||||
"type": "shell",
|
||||
"command": "./fbt FORCE=1 flash_blackmagic"
|
||||
},
|
||||
{
|
||||
"label": "[Release] Flash (JLink)",
|
||||
"group": "build",
|
||||
|
||||
30
SConstruct
@ -45,6 +45,7 @@ distenv = coreenv.Clone(
|
||||
],
|
||||
ENV=os.environ,
|
||||
UPDATE_BUNDLE_DIR="dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}",
|
||||
VSCODE_LANG_SERVER=ARGUMENTS.get("LANG_SERVER", "cpptools"),
|
||||
)
|
||||
|
||||
firmware_env = distenv.AddFwProject(
|
||||
@ -184,27 +185,15 @@ copro_dist = distenv.CoproBuilder(
|
||||
distenv.AlwaysBuild(copro_dist)
|
||||
distenv.Alias("copro_dist", copro_dist)
|
||||
|
||||
firmware_flash = distenv.AddOpenOCDFlashTarget(firmware_env)
|
||||
|
||||
firmware_flash = distenv.AddFwFlashTarget(firmware_env)
|
||||
distenv.Alias("flash", firmware_flash)
|
||||
|
||||
# To be implemented in fwflash.py
|
||||
firmware_jflash = distenv.AddJFlashTarget(firmware_env)
|
||||
distenv.Alias("jflash", firmware_jflash)
|
||||
|
||||
firmware_bm_flash = distenv.PhonyTarget(
|
||||
"flash_blackmagic",
|
||||
"$GDB $GDBOPTS $SOURCES $GDBFLASH",
|
||||
source=firmware_env["FW_ELF"],
|
||||
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
|
||||
GDBREMOTE="${BLACKMAGIC_ADDR}",
|
||||
GDBFLASH=[
|
||||
"-ex",
|
||||
"load",
|
||||
"-ex",
|
||||
"quit",
|
||||
],
|
||||
)
|
||||
|
||||
gdb_backtrace_all_threads = distenv.PhonyTarget(
|
||||
distenv.PhonyTarget(
|
||||
"gdb_trace_all",
|
||||
"$GDB $GDBOPTS $SOURCES $GDBFLASH",
|
||||
source=firmware_env["FW_ELF"],
|
||||
@ -348,7 +337,14 @@ distenv.PhonyTarget(
|
||||
)
|
||||
|
||||
# Prepare vscode environment
|
||||
vscode_dist = distenv.Install("#.vscode", distenv.Glob("#.vscode/example/*"))
|
||||
VSCODE_LANG_SERVER = cmd_environment["LANG_SERVER"]
|
||||
vscode_dist = distenv.Install(
|
||||
"#.vscode",
|
||||
[
|
||||
distenv.Glob("#.vscode/example/*.json"),
|
||||
distenv.Glob(f"#.vscode/example/{VSCODE_LANG_SERVER}/*.json"),
|
||||
],
|
||||
)
|
||||
distenv.Precious(vscode_dist)
|
||||
distenv.NoClean(vscode_dist)
|
||||
distenv.Alias("vscode_dist", vscode_dist)
|
||||
|
||||
@ -33,9 +33,6 @@ Applications for main Flipper menu.
|
||||
- `nfc` - NFC application, HF rfid, EMV and etc
|
||||
- `subghz` - SubGhz application, 433 fobs and etc
|
||||
- `u2f` - U2F Application
|
||||
- `clock` - Clock application
|
||||
- `music_player` - Music player app (demo)
|
||||
- `snake_game` - Snake game application
|
||||
|
||||
|
||||
## services
|
||||
|
||||
@ -174,7 +174,7 @@ bool WIEGAND::DoWiegandConversion() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Handle validation failure case!
|
||||
// TODO FL-3490: Handle validation failure case!
|
||||
} else if(4 == _bitCount) {
|
||||
// 4-bit Wiegand codes have no data integrity check so we just
|
||||
// read the LOW nibble.
|
||||
|
||||
@ -56,7 +56,6 @@ static void subghz_test_packet_rx_callback(bool level, uint32_t duration, void*
|
||||
subghz_decoder_princeton_for_testing_parse(instance->decoder, level, duration);
|
||||
}
|
||||
|
||||
//todo
|
||||
static void subghz_test_packet_rx_pt_callback(SubGhzDecoderPrinceton* parser, void* context) {
|
||||
UNUSED(parser);
|
||||
furi_assert(context);
|
||||
|
||||
@ -3,6 +3,7 @@ App(
|
||||
apptype=FlipperAppType.STARTUP,
|
||||
entry_point="unit_tests_on_system_start",
|
||||
cdefines=["APP_UNIT_TESTS"],
|
||||
requires=["system_settings"],
|
||||
provides=["delay_test"],
|
||||
order=100,
|
||||
)
|
||||
|
||||
@ -26,7 +26,7 @@ void test_furi_memmgr() {
|
||||
mu_assert_int_eq(66, ((uint8_t*)ptr)[i]);
|
||||
}
|
||||
|
||||
// TODO: fix realloc to copy only old size, and write testcase that leftover of reallocated memory is zero-initialized
|
||||
// TODO FL-3492: fix realloc to copy only old size, and write testcase that leftover of reallocated memory is zero-initialized
|
||||
free(ptr);
|
||||
|
||||
// allocate and zero-initialize array (calloc)
|
||||
|
||||
@ -69,7 +69,7 @@ MU_TEST(mu_test_furi_string_mem) {
|
||||
mu_check(string != NULL);
|
||||
mu_check(!furi_string_empty(string));
|
||||
|
||||
// TODO: how to test furi_string_reserve?
|
||||
// TODO FL-3493: how to test furi_string_reserve?
|
||||
|
||||
// test furi_string_reset
|
||||
furi_string_reset(string);
|
||||
|
||||
@ -311,7 +311,7 @@ MU_TEST(test_bit_lib_test_parity) {
|
||||
}
|
||||
|
||||
MU_TEST(test_bit_lib_remove_bit_every_nth) {
|
||||
// TODO: more tests
|
||||
// TODO FL-3494: more tests
|
||||
uint8_t data_i[1] = {0b00001111};
|
||||
uint8_t data_o[1] = {0b00011111};
|
||||
size_t length;
|
||||
|
||||
@ -330,7 +330,12 @@ bool subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestType type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FuriHalCortexTimer timer = furi_hal_cortex_timer_get(30000000);
|
||||
|
||||
while(!furi_hal_subghz_is_async_tx_complete()) {
|
||||
if(furi_hal_cortex_timer_is_expired(timer)) {
|
||||
return false;
|
||||
}
|
||||
furi_delay_ms(10);
|
||||
}
|
||||
furi_hal_subghz_stop_async_tx();
|
||||
|
||||
@ -90,7 +90,7 @@ void unit_tests_cli(Cli* cli, FuriString* args, void* context) {
|
||||
Loader* loader = furi_record_open(RECORD_LOADER);
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
// TODO: lock device while test running
|
||||
// TODO FL-3491: lock device while test running
|
||||
if(loader_is_locked(loader)) {
|
||||
printf("RPC: stop all applications to run tests\r\n");
|
||||
notification_message(notification, &sequence_blink_magenta_100);
|
||||
|
||||
@ -333,8 +333,7 @@ bool subghz_device_cc1101_ext_rx_pipe_not_empty() {
|
||||
(CC1101_STATUS_RXBYTES) | CC1101_BURST,
|
||||
(uint8_t*)status);
|
||||
furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);
|
||||
// TODO: you can add a buffer overflow flag if needed
|
||||
if(status->NUM_RXBYTES > 0) {
|
||||
if((status->NUM_RXBYTES > 0) || (status->RXFIFO_OVERFLOW == 0)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
||||
@ -11,9 +11,6 @@ App(
|
||||
"subghz",
|
||||
"bad_usb",
|
||||
"u2f",
|
||||
"clock",
|
||||
"music_player",
|
||||
"snake_game",
|
||||
"archive",
|
||||
"main_apps_on_start",
|
||||
],
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
App(
|
||||
appid="clock",
|
||||
name="Clock",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="clock_app",
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
fap_icon="clock.png",
|
||||
fap_category="Tools",
|
||||
)
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
@ -1,136 +0,0 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <locale/locale.h>
|
||||
|
||||
typedef enum {
|
||||
ClockEventTypeTick,
|
||||
ClockEventTypeKey,
|
||||
} ClockEventType;
|
||||
|
||||
typedef struct {
|
||||
ClockEventType type;
|
||||
InputEvent input;
|
||||
} ClockEvent;
|
||||
|
||||
typedef struct {
|
||||
FuriString* buffer;
|
||||
FuriHalRtcDateTime datetime;
|
||||
LocaleTimeFormat timeformat;
|
||||
LocaleDateFormat dateformat;
|
||||
} ClockData;
|
||||
|
||||
typedef struct {
|
||||
FuriMutex* mutex;
|
||||
FuriMessageQueue* queue;
|
||||
ClockData* data;
|
||||
} Clock;
|
||||
|
||||
static void clock_input_callback(InputEvent* input_event, FuriMessageQueue* queue) {
|
||||
furi_assert(queue);
|
||||
ClockEvent event = {.type = ClockEventTypeKey, .input = *input_event};
|
||||
furi_message_queue_put(queue, &event, FuriWaitForever);
|
||||
}
|
||||
|
||||
static void clock_render_callback(Canvas* canvas, void* ctx) {
|
||||
Clock* clock = ctx;
|
||||
if(furi_mutex_acquire(clock->mutex, 200) != FuriStatusOk) {
|
||||
return;
|
||||
}
|
||||
|
||||
ClockData* data = clock->data;
|
||||
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
locale_format_time(data->buffer, &data->datetime, data->timeformat, true);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, 28, AlignCenter, AlignCenter, furi_string_get_cstr(data->buffer));
|
||||
|
||||
// Special case to cover missing glyphs in FontBigNumbers
|
||||
if(data->timeformat == LocaleTimeFormat12h) {
|
||||
size_t time_width = canvas_string_width(canvas, furi_string_get_cstr(data->buffer));
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
64 + (time_width / 2) - 10,
|
||||
31,
|
||||
AlignLeft,
|
||||
AlignCenter,
|
||||
(data->datetime.hour > 11) ? "PM" : "AM");
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
locale_format_date(data->buffer, &data->datetime, data->dateformat, "/");
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, 42, AlignCenter, AlignTop, furi_string_get_cstr(data->buffer));
|
||||
|
||||
furi_mutex_release(clock->mutex);
|
||||
}
|
||||
|
||||
static void clock_tick(void* ctx) {
|
||||
furi_assert(ctx);
|
||||
FuriMessageQueue* queue = ctx;
|
||||
ClockEvent event = {.type = ClockEventTypeTick};
|
||||
// It's OK to loose this event if system overloaded
|
||||
furi_message_queue_put(queue, &event, 0);
|
||||
}
|
||||
|
||||
int32_t clock_app(void* p) {
|
||||
UNUSED(p);
|
||||
Clock* clock = malloc(sizeof(Clock));
|
||||
clock->data = malloc(sizeof(ClockData));
|
||||
clock->data->buffer = furi_string_alloc();
|
||||
|
||||
clock->queue = furi_message_queue_alloc(8, sizeof(ClockEvent));
|
||||
clock->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
|
||||
furi_hal_rtc_get_datetime(&clock->data->datetime);
|
||||
clock->data->timeformat = locale_get_time_format();
|
||||
clock->data->dateformat = locale_get_date_format();
|
||||
|
||||
// Set ViewPort callbacks
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, clock_render_callback, clock);
|
||||
view_port_input_callback_set(view_port, clock_input_callback, clock->queue);
|
||||
|
||||
FuriTimer* timer = furi_timer_alloc(clock_tick, FuriTimerTypePeriodic, clock->queue);
|
||||
|
||||
// Open GUI and register view_port
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
furi_timer_start(timer, 100);
|
||||
|
||||
// Main loop
|
||||
ClockEvent event;
|
||||
for(bool processing = true; processing;) {
|
||||
furi_check(furi_message_queue_get(clock->queue, &event, FuriWaitForever) == FuriStatusOk);
|
||||
furi_mutex_acquire(clock->mutex, FuriWaitForever);
|
||||
if(event.type == ClockEventTypeKey) {
|
||||
if(event.input.type == InputTypeShort && event.input.key == InputKeyBack) {
|
||||
processing = false;
|
||||
}
|
||||
} else if(event.type == ClockEventTypeTick) {
|
||||
furi_hal_rtc_get_datetime(&clock->data->datetime);
|
||||
}
|
||||
|
||||
furi_mutex_release(clock->mutex);
|
||||
view_port_update(view_port);
|
||||
}
|
||||
|
||||
furi_timer_free(timer);
|
||||
view_port_enabled_set(view_port, false);
|
||||
gui_remove_view_port(gui, view_port);
|
||||
view_port_free(view_port);
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
furi_message_queue_free(clock->queue);
|
||||
furi_mutex_free(clock->mutex);
|
||||
|
||||
furi_string_free(clock->data->buffer);
|
||||
|
||||
free(clock->data);
|
||||
free(clock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -19,7 +19,7 @@ void gpio_scene_usb_uart_on_enter(void* context) {
|
||||
uint32_t prev_state = scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUart);
|
||||
if(prev_state == 0) {
|
||||
scene_usb_uart = malloc(sizeof(SceneUsbUartBridge));
|
||||
scene_usb_uart->cfg.vcp_ch = 0; // TODO: settings load
|
||||
scene_usb_uart->cfg.vcp_ch = 0; // TODO FL-3495: settings load
|
||||
scene_usb_uart->cfg.uart_ch = 0;
|
||||
scene_usb_uart->cfg.flow_pins = 0;
|
||||
scene_usb_uart->cfg.baudrate_mode = 0;
|
||||
|
||||
@ -85,7 +85,7 @@ static void infrared_cli_print_usage(void) {
|
||||
printf("\tir decode <input_file> [<output_file>]\r\n");
|
||||
printf("\tir universal <remote_name> <signal_name>\r\n");
|
||||
printf("\tir universal list <remote_name>\r\n");
|
||||
// TODO: Do not hardcode universal remote names
|
||||
// TODO FL-3496: Do not hardcode universal remote names
|
||||
printf("\tAvailable universal remotes: tv audio ac projector\r\n");
|
||||
}
|
||||
|
||||
@ -211,7 +211,7 @@ static bool infrared_cli_decode_raw_signal(
|
||||
|
||||
size_t i;
|
||||
for(i = 0; i < raw_signal->timings_size; ++i) {
|
||||
// TODO: Any infrared_check_decoder_ready() magic?
|
||||
// TODO FL-3523: Any infrared_check_decoder_ready() magic?
|
||||
const InfraredMessage* message = infrared_decode(decoder, level, raw_signal->timings[i]);
|
||||
|
||||
if(message) {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#include "../infrared_i.h"
|
||||
|
||||
#include "common/infrared_scene_universal_common.h"
|
||||
#include <furi_hal_rtc.h>
|
||||
|
||||
void infrared_scene_universal_ac_on_enter(void* context) {
|
||||
infrared_scene_universal_common_on_enter(context);
|
||||
@ -18,24 +19,26 @@ void infrared_scene_universal_ac_on_enter(void* context) {
|
||||
i,
|
||||
0,
|
||||
0,
|
||||
3,
|
||||
22,
|
||||
&I_Off_25x27,
|
||||
&I_Off_hvr_25x27,
|
||||
6,
|
||||
15,
|
||||
&I_power_19x20,
|
||||
&I_power_hover_19x20,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
button_panel_add_icon(button_panel, 4, 37, &I_power_text_24x5);
|
||||
infrared_brute_force_add_record(brute_force, i++, "Off");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
0,
|
||||
36,
|
||||
22,
|
||||
&I_Dehumidify_25x27,
|
||||
&I_Dehumidify_hvr_25x27,
|
||||
39,
|
||||
15,
|
||||
&I_dry_19x20,
|
||||
&I_dry_hover_19x20,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
button_panel_add_icon(button_panel, 41, 37, &I_dry_text_15x5);
|
||||
infrared_brute_force_add_record(brute_force, i++, "Dh");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
@ -43,9 +46,9 @@ void infrared_scene_universal_ac_on_enter(void* context) {
|
||||
0,
|
||||
1,
|
||||
3,
|
||||
59,
|
||||
&I_CoolHi_25x27,
|
||||
&I_CoolHi_hvr_25x27,
|
||||
49,
|
||||
&I_max_24x23,
|
||||
&I_max_hover_24x23,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "Cool_hi");
|
||||
@ -54,39 +57,71 @@ void infrared_scene_universal_ac_on_enter(void* context) {
|
||||
i,
|
||||
1,
|
||||
1,
|
||||
36,
|
||||
59,
|
||||
&I_HeatHi_25x27,
|
||||
&I_HeatHi_hvr_25x27,
|
||||
37,
|
||||
49,
|
||||
&I_max_24x23,
|
||||
&I_max_hover_24x23,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "Heat_hi");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
91,
|
||||
&I_CoolLo_25x27,
|
||||
&I_CoolLo_hvr_25x27,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
if(furi_hal_rtc_get_locale_units() == FuriHalRtcLocaleUnitsMetric) {
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
100,
|
||||
&I_celsius_24x23,
|
||||
&I_celsius_hover_24x23,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
} else {
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
100,
|
||||
&I_fahren_24x23,
|
||||
&I_fahren_hover_24x23,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
}
|
||||
infrared_brute_force_add_record(brute_force, i++, "Cool_lo");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
2,
|
||||
36,
|
||||
91,
|
||||
&I_HeatLo_25x27,
|
||||
&I_HeatLo_hvr_25x27,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
|
||||
if(furi_hal_rtc_get_locale_units() == FuriHalRtcLocaleUnitsMetric) {
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
2,
|
||||
37,
|
||||
100,
|
||||
&I_celsius_24x23,
|
||||
&I_celsius_hover_24x23,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
} else {
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
2,
|
||||
37,
|
||||
100,
|
||||
&I_fahren_24x23,
|
||||
&I_fahren_hover_24x23,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
}
|
||||
infrared_brute_force_add_record(brute_force, i++, "Heat_lo");
|
||||
|
||||
button_panel_add_label(button_panel, 6, 10, FontPrimary, "AC remote");
|
||||
button_panel_add_icon(button_panel, 0, 60, &I_cool_30x51);
|
||||
button_panel_add_icon(button_panel, 34, 60, &I_heat_30x51);
|
||||
|
||||
button_panel_add_label(button_panel, 4, 10, FontPrimary, "AC remote");
|
||||
|
||||
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical);
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
|
||||
|
||||
@ -18,82 +18,88 @@ void infrared_scene_universal_audio_on_enter(void* context) {
|
||||
i,
|
||||
0,
|
||||
0,
|
||||
3,
|
||||
11,
|
||||
&I_Power_25x27,
|
||||
&I_Power_hvr_25x27,
|
||||
6,
|
||||
13,
|
||||
&I_power_19x20,
|
||||
&I_power_hover_19x20,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
button_panel_add_icon(button_panel, 4, 35, &I_power_text_24x5);
|
||||
infrared_brute_force_add_record(brute_force, i++, "Power");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
0,
|
||||
36,
|
||||
11,
|
||||
&I_Mute_25x27,
|
||||
&I_Mute_hvr_25x27,
|
||||
39,
|
||||
13,
|
||||
&I_mute_19x20,
|
||||
&I_mute_hover_19x20,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
button_panel_add_icon(button_panel, 39, 35, &I_mute_text_19x5);
|
||||
infrared_brute_force_add_record(brute_force, i++, "Mute");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
0,
|
||||
1,
|
||||
3,
|
||||
41,
|
||||
&I_Play_25x27,
|
||||
&I_Play_hvr_25x27,
|
||||
6,
|
||||
42,
|
||||
&I_play_19x20,
|
||||
&I_play_hover_19x20,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
button_panel_add_icon(button_panel, 6, 64, &I_play_text_19x5);
|
||||
infrared_brute_force_add_record(brute_force, i++, "Play");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
1,
|
||||
36,
|
||||
41,
|
||||
&I_Pause_25x27,
|
||||
&I_Pause_hvr_25x27,
|
||||
0,
|
||||
2,
|
||||
6,
|
||||
71,
|
||||
&I_pause_19x20,
|
||||
&I_pause_hover_19x20,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
button_panel_add_icon(button_panel, 4, 93, &I_pause_text_23x5);
|
||||
infrared_brute_force_add_record(brute_force, i++, "Pause");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
71,
|
||||
&I_TrackPrev_25x27,
|
||||
&I_TrackPrev_hvr_25x27,
|
||||
6,
|
||||
101,
|
||||
&I_prev_19x20,
|
||||
&I_prev_hover_19x20,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
button_panel_add_icon(button_panel, 6, 123, &I_prev_text_19x5);
|
||||
infrared_brute_force_add_record(brute_force, i++, "Prev");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
2,
|
||||
36,
|
||||
71,
|
||||
&I_TrackNext_25x27,
|
||||
&I_TrackNext_hvr_25x27,
|
||||
3,
|
||||
39,
|
||||
101,
|
||||
&I_next_19x20,
|
||||
&I_next_hover_19x20,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
button_panel_add_icon(button_panel, 39, 123, &I_next_text_19x6);
|
||||
infrared_brute_force_add_record(brute_force, i++, "Next");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
0,
|
||||
3,
|
||||
3,
|
||||
101,
|
||||
&I_Vol_down_25x27,
|
||||
&I_Vol_down_hvr_25x27,
|
||||
1,
|
||||
2,
|
||||
37,
|
||||
77,
|
||||
&I_voldown_24x21,
|
||||
&I_voldown_hover_24x21,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "Vol_dn");
|
||||
@ -101,16 +107,17 @@ void infrared_scene_universal_audio_on_enter(void* context) {
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
3,
|
||||
36,
|
||||
101,
|
||||
&I_Vol_up_25x27,
|
||||
&I_Vol_up_hvr_25x27,
|
||||
1,
|
||||
37,
|
||||
43,
|
||||
&I_volup_24x21,
|
||||
&I_volup_hover_24x21,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "Vol_up");
|
||||
|
||||
button_panel_add_label(button_panel, 1, 8, FontPrimary, "Mus. remote");
|
||||
button_panel_add_label(button_panel, 1, 10, FontPrimary, "Mus. remote");
|
||||
button_panel_add_icon(button_panel, 34, 56, &I_vol_ac_text_30x30);
|
||||
|
||||
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical);
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
|
||||
|
||||
@ -11,59 +11,62 @@ void infrared_scene_universal_projector_on_enter(void* context) {
|
||||
|
||||
infrared_brute_force_set_db_filename(brute_force, EXT_PATH("infrared/assets/projector.ir"));
|
||||
|
||||
button_panel_reserve(button_panel, 2, 2);
|
||||
button_panel_reserve(button_panel, 2, 3);
|
||||
uint32_t i = 0;
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
0,
|
||||
0,
|
||||
3,
|
||||
19,
|
||||
&I_Power_25x27,
|
||||
&I_Power_hvr_25x27,
|
||||
6,
|
||||
23,
|
||||
&I_power_19x20,
|
||||
&I_power_hover_19x20,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
button_panel_add_icon(button_panel, 4, 45, &I_power_text_24x5);
|
||||
infrared_brute_force_add_record(brute_force, i++, "Power");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
0,
|
||||
36,
|
||||
19,
|
||||
&I_Mute_25x27,
|
||||
&I_Mute_hvr_25x27,
|
||||
39,
|
||||
23,
|
||||
&I_mute_19x20,
|
||||
&I_mute_hover_19x20,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
button_panel_add_icon(button_panel, 39, 45, &I_mute_text_19x5);
|
||||
infrared_brute_force_add_record(brute_force, i++, "Mute");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
0,
|
||||
1,
|
||||
3,
|
||||
66,
|
||||
&I_Vol_up_25x27,
|
||||
&I_Vol_up_hvr_25x27,
|
||||
20,
|
||||
59,
|
||||
&I_volup_24x21,
|
||||
&I_volup_hover_24x21,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "Vol_up");
|
||||
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
1,
|
||||
36,
|
||||
66,
|
||||
&I_Vol_down_25x27,
|
||||
&I_Vol_down_hvr_25x27,
|
||||
0,
|
||||
2,
|
||||
20,
|
||||
93,
|
||||
&I_voldown_24x21,
|
||||
&I_voldown_hover_24x21,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "Vol_dn");
|
||||
|
||||
button_panel_add_label(button_panel, 2, 11, FontPrimary, "Proj. remote");
|
||||
button_panel_add_label(button_panel, 17, 62, FontSecondary, "Volume");
|
||||
button_panel_add_label(button_panel, 3, 11, FontPrimary, "Proj. remote");
|
||||
button_panel_add_icon(button_panel, 17, 72, &I_vol_ac_text_30x30);
|
||||
|
||||
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical);
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
|
||||
|
||||
@ -18,77 +18,82 @@ void infrared_scene_universal_tv_on_enter(void* context) {
|
||||
i,
|
||||
0,
|
||||
0,
|
||||
3,
|
||||
19,
|
||||
&I_Power_25x27,
|
||||
&I_Power_hvr_25x27,
|
||||
6,
|
||||
16,
|
||||
&I_power_19x20,
|
||||
&I_power_hover_19x20,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
button_panel_add_icon(button_panel, 4, 38, &I_power_text_24x5);
|
||||
infrared_brute_force_add_record(brute_force, i++, "Power");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
0,
|
||||
36,
|
||||
19,
|
||||
&I_Mute_25x27,
|
||||
&I_Mute_hvr_25x27,
|
||||
39,
|
||||
16,
|
||||
&I_mute_19x20,
|
||||
&I_mute_hover_19x20,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
button_panel_add_icon(button_panel, 39, 38, &I_mute_text_19x5);
|
||||
|
||||
button_panel_add_icon(button_panel, 0, 66, &I_ch_text_31x34);
|
||||
button_panel_add_icon(button_panel, 35, 66, &I_vol_tv_text_29x34);
|
||||
|
||||
infrared_brute_force_add_record(brute_force, i++, "Mute");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
1,
|
||||
38,
|
||||
53,
|
||||
&I_volup_24x21,
|
||||
&I_volup_hover_24x21,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
|
||||
infrared_brute_force_add_record(brute_force, i++, "Vol_up");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
0,
|
||||
1,
|
||||
3,
|
||||
66,
|
||||
&I_Vol_up_25x27,
|
||||
&I_Vol_up_hvr_25x27,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "Vol_up");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
1,
|
||||
36,
|
||||
66,
|
||||
&I_Up_25x27,
|
||||
&I_Up_hvr_25x27,
|
||||
53,
|
||||
&I_ch_up_24x21,
|
||||
&I_ch_up_hover_24x21,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "Ch_next");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
98,
|
||||
&I_Vol_down_25x27,
|
||||
&I_Vol_down_hvr_25x27,
|
||||
38,
|
||||
91,
|
||||
&I_voldown_24x21,
|
||||
&I_voldown_hover_24x21,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "Vol_dn");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
36,
|
||||
98,
|
||||
&I_Down_25x27,
|
||||
&I_Down_hvr_25x27,
|
||||
3,
|
||||
91,
|
||||
&I_ch_down_24x21,
|
||||
&I_ch_down_hover_24x21,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "Ch_prev");
|
||||
|
||||
button_panel_add_label(button_panel, 6, 11, FontPrimary, "TV remote");
|
||||
button_panel_add_label(button_panel, 9, 64, FontSecondary, "Vol");
|
||||
button_panel_add_label(button_panel, 43, 64, FontSecondary, "Ch");
|
||||
button_panel_add_label(button_panel, 5, 10, FontPrimary, "TV remote");
|
||||
|
||||
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical);
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
App(
|
||||
appid="music_player",
|
||||
name="Music Player",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="music_player_app",
|
||||
requires=[
|
||||
"gui",
|
||||
"dialogs",
|
||||
],
|
||||
stack_size=2 * 1024,
|
||||
order=20,
|
||||
fap_icon="icons/music_10px.png",
|
||||
fap_category="Media",
|
||||
fap_icon_assets="icons",
|
||||
fap_libs=["music_worker"],
|
||||
)
|
||||
|
Before Width: | Height: | Size: 142 B |
@ -1,372 +0,0 @@
|
||||
#include <music_worker/music_worker.h>
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <music_player_icons.h>
|
||||
#include <gui/gui.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
#define TAG "MusicPlayer"
|
||||
|
||||
#define MUSIC_PLAYER_APP_EXTENSION "*"
|
||||
|
||||
#define MUSIC_PLAYER_SEMITONE_HISTORY_SIZE 4
|
||||
|
||||
typedef struct {
|
||||
uint8_t semitone_history[MUSIC_PLAYER_SEMITONE_HISTORY_SIZE];
|
||||
uint8_t duration_history[MUSIC_PLAYER_SEMITONE_HISTORY_SIZE];
|
||||
|
||||
uint8_t volume;
|
||||
uint8_t semitone;
|
||||
uint8_t dots;
|
||||
uint8_t duration;
|
||||
float position;
|
||||
} MusicPlayerModel;
|
||||
|
||||
typedef struct {
|
||||
MusicPlayerModel* model;
|
||||
FuriMutex** model_mutex;
|
||||
|
||||
FuriMessageQueue* input_queue;
|
||||
|
||||
ViewPort* view_port;
|
||||
Gui* gui;
|
||||
|
||||
MusicWorker* worker;
|
||||
} MusicPlayer;
|
||||
|
||||
static const float MUSIC_PLAYER_VOLUMES[] = {0, .25, .5, .75, 1};
|
||||
|
||||
static const char* semitone_to_note(int8_t semitone) {
|
||||
switch(semitone) {
|
||||
case 0:
|
||||
return "C";
|
||||
case 1:
|
||||
return "C#";
|
||||
case 2:
|
||||
return "D";
|
||||
case 3:
|
||||
return "D#";
|
||||
case 4:
|
||||
return "E";
|
||||
case 5:
|
||||
return "F";
|
||||
case 6:
|
||||
return "F#";
|
||||
case 7:
|
||||
return "G";
|
||||
case 8:
|
||||
return "G#";
|
||||
case 9:
|
||||
return "A";
|
||||
case 10:
|
||||
return "A#";
|
||||
case 11:
|
||||
return "B";
|
||||
default:
|
||||
return "--";
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_white_note(uint8_t semitone, uint8_t id) {
|
||||
switch(semitone) {
|
||||
case 0:
|
||||
if(id == 0) return true;
|
||||
break;
|
||||
case 2:
|
||||
if(id == 1) return true;
|
||||
break;
|
||||
case 4:
|
||||
if(id == 2) return true;
|
||||
break;
|
||||
case 5:
|
||||
if(id == 3) return true;
|
||||
break;
|
||||
case 7:
|
||||
if(id == 4) return true;
|
||||
break;
|
||||
case 9:
|
||||
if(id == 5) return true;
|
||||
break;
|
||||
case 11:
|
||||
if(id == 6) return true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_black_note(uint8_t semitone, uint8_t id) {
|
||||
switch(semitone) {
|
||||
case 1:
|
||||
if(id == 0) return true;
|
||||
break;
|
||||
case 3:
|
||||
if(id == 1) return true;
|
||||
break;
|
||||
case 6:
|
||||
if(id == 3) return true;
|
||||
break;
|
||||
case 8:
|
||||
if(id == 4) return true;
|
||||
break;
|
||||
case 10:
|
||||
if(id == 5) return true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void render_callback(Canvas* canvas, void* ctx) {
|
||||
MusicPlayer* music_player = ctx;
|
||||
furi_check(furi_mutex_acquire(music_player->model_mutex, FuriWaitForever) == FuriStatusOk);
|
||||
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 0, 12, "MusicPlayer");
|
||||
|
||||
uint8_t x_pos = 0;
|
||||
uint8_t y_pos = 24;
|
||||
const uint8_t white_w = 10;
|
||||
const uint8_t white_h = 40;
|
||||
|
||||
const int8_t black_x = 6;
|
||||
const int8_t black_y = -5;
|
||||
const uint8_t black_w = 8;
|
||||
const uint8_t black_h = 32;
|
||||
|
||||
// white keys
|
||||
for(size_t i = 0; i < 7; i++) {
|
||||
if(is_white_note(music_player->model->semitone, i)) {
|
||||
canvas_draw_box(canvas, x_pos + white_w * i, y_pos, white_w + 1, white_h);
|
||||
} else {
|
||||
canvas_draw_frame(canvas, x_pos + white_w * i, y_pos, white_w + 1, white_h);
|
||||
}
|
||||
}
|
||||
|
||||
// black keys
|
||||
for(size_t i = 0; i < 7; i++) {
|
||||
if(i != 2 && i != 6) {
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_box(
|
||||
canvas, x_pos + white_w * i + black_x, y_pos + black_y, black_w + 1, black_h);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
if(is_black_note(music_player->model->semitone, i)) {
|
||||
canvas_draw_box(
|
||||
canvas, x_pos + white_w * i + black_x, y_pos + black_y, black_w + 1, black_h);
|
||||
} else {
|
||||
canvas_draw_frame(
|
||||
canvas, x_pos + white_w * i + black_x, y_pos + black_y, black_w + 1, black_h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// volume view_port
|
||||
x_pos = 124;
|
||||
y_pos = 0;
|
||||
const uint8_t volume_h =
|
||||
(64 / (COUNT_OF(MUSIC_PLAYER_VOLUMES) - 1)) * music_player->model->volume;
|
||||
canvas_draw_frame(canvas, x_pos, y_pos, 4, 64);
|
||||
canvas_draw_box(canvas, x_pos, y_pos + (64 - volume_h), 4, volume_h);
|
||||
|
||||
// note stack view_port
|
||||
x_pos = 73;
|
||||
y_pos = 0; //-V1048
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_frame(canvas, x_pos, y_pos, 49, 64);
|
||||
canvas_draw_line(canvas, x_pos + 28, 0, x_pos + 28, 64);
|
||||
|
||||
char duration_text[16];
|
||||
for(uint8_t i = 0; i < MUSIC_PLAYER_SEMITONE_HISTORY_SIZE; i++) {
|
||||
if(music_player->model->duration_history[i] == 0xFF) {
|
||||
snprintf(duration_text, 15, "--");
|
||||
} else {
|
||||
snprintf(duration_text, 15, "%d", music_player->model->duration_history[i]);
|
||||
}
|
||||
|
||||
if(i == 0) {
|
||||
canvas_draw_box(canvas, x_pos, y_pos + 48, 49, 16);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
canvas_draw_str(
|
||||
canvas,
|
||||
x_pos + 4,
|
||||
64 - 16 * i - 3,
|
||||
semitone_to_note(music_player->model->semitone_history[i]));
|
||||
canvas_draw_str(canvas, x_pos + 31, 64 - 16 * i - 3, duration_text);
|
||||
canvas_draw_line(canvas, x_pos, 64 - 16 * i, x_pos + 48, 64 - 16 * i);
|
||||
}
|
||||
|
||||
furi_mutex_release(music_player->model_mutex);
|
||||
}
|
||||
|
||||
static void input_callback(InputEvent* input_event, void* ctx) {
|
||||
MusicPlayer* music_player = ctx;
|
||||
if(input_event->type == InputTypeShort) {
|
||||
furi_message_queue_put(music_player->input_queue, input_event, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void music_worker_callback(
|
||||
uint8_t semitone,
|
||||
uint8_t dots,
|
||||
uint8_t duration,
|
||||
float position,
|
||||
void* context) {
|
||||
MusicPlayer* music_player = context;
|
||||
furi_check(furi_mutex_acquire(music_player->model_mutex, FuriWaitForever) == FuriStatusOk);
|
||||
|
||||
for(size_t i = 0; i < MUSIC_PLAYER_SEMITONE_HISTORY_SIZE - 1; i++) {
|
||||
size_t r = MUSIC_PLAYER_SEMITONE_HISTORY_SIZE - 1 - i;
|
||||
music_player->model->duration_history[r] = music_player->model->duration_history[r - 1];
|
||||
music_player->model->semitone_history[r] = music_player->model->semitone_history[r - 1];
|
||||
}
|
||||
|
||||
semitone = (semitone == 0xFF) ? 0xFF : semitone % 12;
|
||||
|
||||
music_player->model->semitone = semitone;
|
||||
music_player->model->dots = dots;
|
||||
music_player->model->duration = duration;
|
||||
music_player->model->position = position;
|
||||
|
||||
music_player->model->semitone_history[0] = semitone;
|
||||
music_player->model->duration_history[0] = duration;
|
||||
|
||||
furi_mutex_release(music_player->model_mutex);
|
||||
view_port_update(music_player->view_port);
|
||||
}
|
||||
|
||||
void music_player_clear(MusicPlayer* instance) {
|
||||
memset(instance->model->duration_history, 0xff, MUSIC_PLAYER_SEMITONE_HISTORY_SIZE);
|
||||
memset(instance->model->semitone_history, 0xff, MUSIC_PLAYER_SEMITONE_HISTORY_SIZE);
|
||||
music_worker_clear(instance->worker);
|
||||
}
|
||||
|
||||
MusicPlayer* music_player_alloc() {
|
||||
MusicPlayer* instance = malloc(sizeof(MusicPlayer));
|
||||
|
||||
instance->model = malloc(sizeof(MusicPlayerModel));
|
||||
instance->model->volume = 3;
|
||||
|
||||
instance->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
|
||||
instance->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||
|
||||
instance->worker = music_worker_alloc();
|
||||
music_worker_set_volume(instance->worker, MUSIC_PLAYER_VOLUMES[instance->model->volume]);
|
||||
music_worker_set_callback(instance->worker, music_worker_callback, instance);
|
||||
|
||||
music_player_clear(instance);
|
||||
|
||||
instance->view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(instance->view_port, render_callback, instance);
|
||||
view_port_input_callback_set(instance->view_port, input_callback, instance);
|
||||
|
||||
// Open GUI and register view_port
|
||||
instance->gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void music_player_free(MusicPlayer* instance) {
|
||||
gui_remove_view_port(instance->gui, instance->view_port);
|
||||
furi_record_close(RECORD_GUI);
|
||||
view_port_free(instance->view_port);
|
||||
|
||||
music_worker_free(instance->worker);
|
||||
|
||||
furi_message_queue_free(instance->input_queue);
|
||||
|
||||
furi_mutex_free(instance->model_mutex);
|
||||
|
||||
free(instance->model);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
int32_t music_player_app(void* p) {
|
||||
MusicPlayer* music_player = music_player_alloc();
|
||||
|
||||
FuriString* file_path;
|
||||
file_path = furi_string_alloc();
|
||||
|
||||
do {
|
||||
if(p && strlen(p)) {
|
||||
furi_string_set(file_path, (const char*)p);
|
||||
} else {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
storage_common_migrate(
|
||||
storage, EXT_PATH("music_player"), STORAGE_APP_DATA_PATH_PREFIX);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
furi_string_set(file_path, STORAGE_APP_DATA_PATH_PREFIX);
|
||||
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(
|
||||
&browser_options, MUSIC_PLAYER_APP_EXTENSION, &I_music_10px);
|
||||
browser_options.hide_ext = false;
|
||||
browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX;
|
||||
|
||||
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
bool res = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options);
|
||||
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
|
||||
if(!res) {
|
||||
FURI_LOG_E(TAG, "No file selected");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!music_worker_load(music_player->worker, furi_string_get_cstr(file_path))) {
|
||||
FURI_LOG_E(TAG, "Unable to load file");
|
||||
break;
|
||||
}
|
||||
|
||||
music_worker_start(music_player->worker);
|
||||
|
||||
InputEvent input;
|
||||
while(furi_message_queue_get(music_player->input_queue, &input, FuriWaitForever) ==
|
||||
FuriStatusOk) {
|
||||
furi_check(
|
||||
furi_mutex_acquire(music_player->model_mutex, FuriWaitForever) == FuriStatusOk);
|
||||
|
||||
if(input.key == InputKeyBack) {
|
||||
furi_mutex_release(music_player->model_mutex);
|
||||
break;
|
||||
} else if(input.key == InputKeyUp) {
|
||||
if(music_player->model->volume < COUNT_OF(MUSIC_PLAYER_VOLUMES) - 1)
|
||||
music_player->model->volume++;
|
||||
music_worker_set_volume(
|
||||
music_player->worker, MUSIC_PLAYER_VOLUMES[music_player->model->volume]);
|
||||
} else if(input.key == InputKeyDown) {
|
||||
if(music_player->model->volume > 0) music_player->model->volume--;
|
||||
music_worker_set_volume(
|
||||
music_player->worker, MUSIC_PLAYER_VOLUMES[music_player->model->volume]);
|
||||
}
|
||||
|
||||
furi_mutex_release(music_player->model_mutex);
|
||||
view_port_update(music_player->view_port);
|
||||
}
|
||||
|
||||
music_worker_stop(music_player->worker);
|
||||
if(p && strlen(p)) break; // Exit instead of going to browser if launched with arg
|
||||
music_player_clear(music_player);
|
||||
} while(1);
|
||||
|
||||
furi_string_free(file_path);
|
||||
music_player_free(music_player);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -3,6 +3,8 @@
|
||||
|
||||
void nfc_scene_file_select_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
nfc_device_data_clear(&nfc->dev->dev_data);
|
||||
|
||||
// Process file_select return
|
||||
nfc_device_set_loading_callback(nfc->dev, nfc_show_loading_popup, nfc);
|
||||
if(!furi_string_size(nfc->dev->load_path)) {
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
App(
|
||||
appid="snake_game",
|
||||
name="Snake Game",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="snake_game_app",
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=30,
|
||||
fap_icon="snake_10px.png",
|
||||
fap_category="Games",
|
||||
)
|
||||
|
Before Width: | Height: | Size: 158 B |
@ -1,434 +0,0 @@
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
#include <stdlib.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
typedef struct {
|
||||
// +-----x
|
||||
// |
|
||||
// |
|
||||
// y
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
} Point;
|
||||
|
||||
typedef enum {
|
||||
GameStateLife,
|
||||
|
||||
// https://melmagazine.com/en-us/story/snake-nokia-6110-oral-history-taneli-armanto
|
||||
// Armanto: While testing the early versions of the game, I noticed it was hard
|
||||
// to control the snake upon getting close to and edge but not crashing — especially
|
||||
// in the highest speed levels. I wanted the highest level to be as fast as I could
|
||||
// possibly make the device "run," but on the other hand, I wanted to be friendly
|
||||
// and help the player manage that level. Otherwise it might not be fun to play. So
|
||||
// I implemented a little delay. A few milliseconds of extra time right before
|
||||
// the player crashes, during which she can still change the directions. And if
|
||||
// she does, the game continues.
|
||||
GameStateLastChance,
|
||||
|
||||
GameStateGameOver,
|
||||
} GameState;
|
||||
|
||||
// Note: do not change without purpose. Current values are used in smart
|
||||
// orthogonality calculation in `snake_game_get_turn_snake`.
|
||||
typedef enum {
|
||||
DirectionUp,
|
||||
DirectionRight,
|
||||
DirectionDown,
|
||||
DirectionLeft,
|
||||
} Direction;
|
||||
|
||||
#define MAX_SNAKE_LEN 253
|
||||
|
||||
typedef struct {
|
||||
Point points[MAX_SNAKE_LEN];
|
||||
uint16_t len;
|
||||
Direction currentMovement;
|
||||
Direction nextMovement; // if backward of currentMovement, ignore
|
||||
Point fruit;
|
||||
GameState state;
|
||||
FuriMutex* mutex;
|
||||
} SnakeState;
|
||||
|
||||
typedef enum {
|
||||
EventTypeTick,
|
||||
EventTypeKey,
|
||||
} EventType;
|
||||
|
||||
typedef struct {
|
||||
EventType type;
|
||||
InputEvent input;
|
||||
} SnakeEvent;
|
||||
|
||||
const NotificationSequence sequence_fail = {
|
||||
&message_vibro_on,
|
||||
|
||||
&message_note_ds4,
|
||||
&message_delay_10,
|
||||
&message_sound_off,
|
||||
&message_delay_10,
|
||||
|
||||
&message_note_ds4,
|
||||
&message_delay_10,
|
||||
&message_sound_off,
|
||||
&message_delay_10,
|
||||
|
||||
&message_note_ds4,
|
||||
&message_delay_10,
|
||||
&message_sound_off,
|
||||
&message_delay_10,
|
||||
|
||||
&message_vibro_off,
|
||||
NULL,
|
||||
};
|
||||
|
||||
const NotificationSequence sequence_eat = {
|
||||
&message_note_c7,
|
||||
&message_delay_50,
|
||||
&message_sound_off,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void snake_game_render_callback(Canvas* const canvas, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
const SnakeState* snake_state = ctx;
|
||||
|
||||
furi_mutex_acquire(snake_state->mutex, FuriWaitForever);
|
||||
|
||||
// Frame
|
||||
canvas_draw_frame(canvas, 0, 0, 128, 64);
|
||||
|
||||
// Fruit
|
||||
Point f = snake_state->fruit;
|
||||
f.x = f.x * 4 + 1;
|
||||
f.y = f.y * 4 + 1;
|
||||
canvas_draw_rframe(canvas, f.x, f.y, 6, 6, 2);
|
||||
|
||||
// Snake
|
||||
for(uint16_t i = 0; i < snake_state->len; i++) {
|
||||
Point p = snake_state->points[i];
|
||||
p.x = p.x * 4 + 2;
|
||||
p.y = p.y * 4 + 2;
|
||||
canvas_draw_box(canvas, p.x, p.y, 4, 4);
|
||||
}
|
||||
|
||||
// Game Over banner
|
||||
if(snake_state->state == GameStateGameOver) {
|
||||
// Screen is 128x64 px
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_box(canvas, 34, 20, 62, 24);
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_frame(canvas, 34, 20, 62, 24);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 37, 31, "Game Over");
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
char buffer[12];
|
||||
snprintf(buffer, sizeof(buffer), "Score: %u", snake_state->len - 7U);
|
||||
canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignBottom, buffer);
|
||||
}
|
||||
|
||||
furi_mutex_release(snake_state->mutex);
|
||||
}
|
||||
|
||||
static void snake_game_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
|
||||
furi_assert(event_queue);
|
||||
|
||||
SnakeEvent event = {.type = EventTypeKey, .input = *input_event};
|
||||
furi_message_queue_put(event_queue, &event, FuriWaitForever);
|
||||
}
|
||||
|
||||
static void snake_game_update_timer_callback(FuriMessageQueue* event_queue) {
|
||||
furi_assert(event_queue);
|
||||
|
||||
SnakeEvent event = {.type = EventTypeTick};
|
||||
furi_message_queue_put(event_queue, &event, 0);
|
||||
}
|
||||
|
||||
static void snake_game_init_game(SnakeState* const snake_state) {
|
||||
Point p[] = {{8, 6}, {7, 6}, {6, 6}, {5, 6}, {4, 6}, {3, 6}, {2, 6}};
|
||||
memcpy(snake_state->points, p, sizeof(p)); //-V1086
|
||||
|
||||
snake_state->len = 7;
|
||||
|
||||
snake_state->currentMovement = DirectionRight;
|
||||
|
||||
snake_state->nextMovement = DirectionRight;
|
||||
|
||||
Point f = {18, 6};
|
||||
snake_state->fruit = f;
|
||||
|
||||
snake_state->state = GameStateLife;
|
||||
}
|
||||
|
||||
static Point snake_game_get_new_fruit(SnakeState const* const snake_state) {
|
||||
// 1 bit for each point on the playing field where the snake can turn
|
||||
// and where the fruit can appear
|
||||
uint16_t buffer[8];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
uint8_t empty = 8 * 16;
|
||||
|
||||
for(uint16_t i = 0; i < snake_state->len; i++) {
|
||||
Point p = snake_state->points[i];
|
||||
|
||||
if(p.x % 2 != 0 || p.y % 2 != 0) {
|
||||
continue;
|
||||
}
|
||||
p.x /= 2;
|
||||
p.y /= 2;
|
||||
|
||||
buffer[p.y] |= 1 << p.x;
|
||||
empty--;
|
||||
}
|
||||
// Bit set if snake use that playing field
|
||||
|
||||
uint16_t newFruit = rand() % empty;
|
||||
|
||||
// Skip random number of _empty_ fields
|
||||
for(uint8_t y = 0; y < 8; y++) {
|
||||
for(uint16_t x = 0, mask = 1; x < 16; x += 1, mask <<= 1) {
|
||||
if((buffer[y] & mask) == 0) {
|
||||
if(newFruit == 0) {
|
||||
Point p = {
|
||||
.x = x * 2,
|
||||
.y = y * 2,
|
||||
};
|
||||
return p;
|
||||
}
|
||||
newFruit--;
|
||||
}
|
||||
}
|
||||
}
|
||||
// We will never be here
|
||||
Point p = {0, 0};
|
||||
return p;
|
||||
}
|
||||
|
||||
static bool snake_game_collision_with_frame(Point const next_step) {
|
||||
// if x == 0 && currentMovement == left then x - 1 == 255 ,
|
||||
// so check only x > right border
|
||||
return next_step.x > 30 || next_step.y > 14;
|
||||
}
|
||||
|
||||
static bool
|
||||
snake_game_collision_with_tail(SnakeState const* const snake_state, Point const next_step) {
|
||||
for(uint16_t i = 0; i < snake_state->len; i++) {
|
||||
Point p = snake_state->points[i];
|
||||
if(p.x == next_step.x && p.y == next_step.y) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static Direction snake_game_get_turn_snake(SnakeState const* const snake_state) {
|
||||
// Sum of two `Direction` lies between 0 and 6, odd values indicate orthogonality.
|
||||
bool is_orthogonal = (snake_state->currentMovement + snake_state->nextMovement) % 2 == 1;
|
||||
return is_orthogonal ? snake_state->nextMovement : snake_state->currentMovement;
|
||||
}
|
||||
|
||||
static Point snake_game_get_next_step(SnakeState const* const snake_state) {
|
||||
Point next_step = snake_state->points[0];
|
||||
switch(snake_state->currentMovement) {
|
||||
// +-----x
|
||||
// |
|
||||
// |
|
||||
// y
|
||||
case DirectionUp:
|
||||
next_step.y--;
|
||||
break;
|
||||
case DirectionRight:
|
||||
next_step.x++;
|
||||
break;
|
||||
case DirectionDown:
|
||||
next_step.y++;
|
||||
break;
|
||||
case DirectionLeft:
|
||||
next_step.x--;
|
||||
break;
|
||||
}
|
||||
return next_step;
|
||||
}
|
||||
|
||||
static void snake_game_move_snake(SnakeState* const snake_state, Point const next_step) {
|
||||
memmove(snake_state->points + 1, snake_state->points, snake_state->len * sizeof(Point));
|
||||
snake_state->points[0] = next_step;
|
||||
}
|
||||
|
||||
static void
|
||||
snake_game_process_game_step(SnakeState* const snake_state, NotificationApp* notification) {
|
||||
if(snake_state->state == GameStateGameOver) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool can_turn = (snake_state->points[0].x % 2 == 0) && (snake_state->points[0].y % 2 == 0);
|
||||
if(can_turn) {
|
||||
snake_state->currentMovement = snake_game_get_turn_snake(snake_state);
|
||||
}
|
||||
|
||||
Point next_step = snake_game_get_next_step(snake_state);
|
||||
|
||||
bool crush = snake_game_collision_with_frame(next_step);
|
||||
if(crush) {
|
||||
if(snake_state->state == GameStateLife) {
|
||||
snake_state->state = GameStateLastChance;
|
||||
return;
|
||||
} else if(snake_state->state == GameStateLastChance) {
|
||||
snake_state->state = GameStateGameOver;
|
||||
notification_message_block(notification, &sequence_fail);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if(snake_state->state == GameStateLastChance) {
|
||||
snake_state->state = GameStateLife;
|
||||
}
|
||||
}
|
||||
|
||||
crush = snake_game_collision_with_tail(snake_state, next_step);
|
||||
if(crush) {
|
||||
snake_state->state = GameStateGameOver;
|
||||
notification_message_block(notification, &sequence_fail);
|
||||
return;
|
||||
}
|
||||
|
||||
bool eatFruit = (next_step.x == snake_state->fruit.x) && (next_step.y == snake_state->fruit.y);
|
||||
if(eatFruit) {
|
||||
snake_state->len++;
|
||||
if(snake_state->len >= MAX_SNAKE_LEN) {
|
||||
snake_state->state = GameStateGameOver;
|
||||
notification_message_block(notification, &sequence_fail);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
snake_game_move_snake(snake_state, next_step);
|
||||
|
||||
if(eatFruit) {
|
||||
snake_state->fruit = snake_game_get_new_fruit(snake_state);
|
||||
notification_message(notification, &sequence_eat);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t snake_game_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SnakeEvent));
|
||||
|
||||
SnakeState* snake_state = malloc(sizeof(SnakeState));
|
||||
snake_game_init_game(snake_state);
|
||||
|
||||
snake_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
|
||||
if(!snake_state->mutex) {
|
||||
FURI_LOG_E("SnakeGame", "cannot create mutex\r\n");
|
||||
free(snake_state);
|
||||
return 255;
|
||||
}
|
||||
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, snake_game_render_callback, snake_state);
|
||||
view_port_input_callback_set(view_port, snake_game_input_callback, event_queue);
|
||||
|
||||
FuriTimer* timer =
|
||||
furi_timer_alloc(snake_game_update_timer_callback, FuriTimerTypePeriodic, event_queue);
|
||||
furi_timer_start(timer, furi_kernel_get_tick_frequency() / 4);
|
||||
|
||||
// Open GUI and register view_port
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
notification_message_block(notification, &sequence_display_backlight_enforce_on);
|
||||
|
||||
dolphin_deed(DolphinDeedPluginGameStart);
|
||||
|
||||
SnakeEvent event;
|
||||
for(bool processing = true; processing;) {
|
||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
|
||||
|
||||
furi_mutex_acquire(snake_state->mutex, FuriWaitForever);
|
||||
|
||||
if(event_status == FuriStatusOk) {
|
||||
// press events
|
||||
if(event.type == EventTypeKey) {
|
||||
if(event.input.type == InputTypePress) {
|
||||
switch(event.input.key) {
|
||||
case InputKeyUp:
|
||||
snake_state->nextMovement = DirectionUp;
|
||||
break;
|
||||
case InputKeyDown:
|
||||
snake_state->nextMovement = DirectionDown;
|
||||
break;
|
||||
case InputKeyRight:
|
||||
snake_state->nextMovement = DirectionRight;
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
snake_state->nextMovement = DirectionLeft;
|
||||
break;
|
||||
case InputKeyOk:
|
||||
if(snake_state->state == GameStateGameOver) {
|
||||
snake_game_init_game(snake_state);
|
||||
}
|
||||
break;
|
||||
case InputKeyBack:
|
||||
processing = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if(event.type == EventTypeTick) {
|
||||
snake_game_process_game_step(snake_state, notification);
|
||||
}
|
||||
} else {
|
||||
// event timeout
|
||||
}
|
||||
|
||||
view_port_update(view_port);
|
||||
furi_mutex_release(snake_state->mutex);
|
||||
}
|
||||
|
||||
// Return backlight to normal state
|
||||
notification_message(notification, &sequence_display_backlight_enforce_auto);
|
||||
|
||||
furi_timer_free(timer);
|
||||
view_port_enabled_set(view_port, false);
|
||||
gui_remove_view_port(gui, view_port);
|
||||
furi_record_close(RECORD_GUI);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
furi_mutex_free(snake_state->mutex);
|
||||
free(snake_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Screen is 128x64 px
|
||||
// (4 + 4) * 16 - 4 + 2 + 2border == 128
|
||||
// (4 + 4) * 8 - 4 + 2 + 2border == 64
|
||||
// Game field from point{x: 0, y: 0} to point{x: 30, y: 14}.
|
||||
// The snake turns only in even cells - intersections.
|
||||
// ┌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┐
|
||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
|
||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
|
||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
|
||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
|
||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
|
||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
|
||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
|
||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
|
||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
|
||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
|
||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
|
||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
|
||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
|
||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
|
||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
|
||||
// └╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘
|
||||
@ -334,7 +334,6 @@ static void subghz_txrx_tx_stop(SubGhzTxRx* instance) {
|
||||
}
|
||||
subghz_txrx_idle(instance);
|
||||
subghz_txrx_speaker_off(instance);
|
||||
//Todo: Show message
|
||||
}
|
||||
|
||||
FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance) {
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
|
||||
#define TAG "SubGhzCreateProtocolKey"
|
||||
|
||||
bool subghz_txrx_gen_data_protocol(
|
||||
SubGhzProtocolStatus subghz_txrx_gen_data_protocol(
|
||||
void* context,
|
||||
const char* preset_name,
|
||||
uint32_t frequency,
|
||||
@ -23,30 +23,29 @@ bool subghz_txrx_gen_data_protocol(
|
||||
furi_assert(context);
|
||||
SubGhzTxRx* instance = context;
|
||||
|
||||
bool res = false;
|
||||
SubGhzProtocolStatus ret = SubGhzProtocolStatusOk;
|
||||
|
||||
subghz_txrx_set_preset(instance, preset_name, frequency, NULL, 0);
|
||||
instance->decoder_result =
|
||||
subghz_receiver_search_decoder_base_by_name(instance->receiver, protocol_name);
|
||||
|
||||
if(instance->decoder_result == NULL) {
|
||||
//TODO: Error
|
||||
// furi_string_set(error_str, "Protocol not\nfound!");
|
||||
// scene_manager_next_scene(scene_manager, SubGhzSceneShowErrorSub);
|
||||
FURI_LOG_E(TAG, "Protocol not found!");
|
||||
return false;
|
||||
ret = SubGhzProtocolStatusErrorProtocolNotFound;
|
||||
return ret;
|
||||
}
|
||||
|
||||
do {
|
||||
Stream* fff_data_stream = flipper_format_get_raw_stream(instance->fff_data);
|
||||
stream_clean(fff_data_stream);
|
||||
if(subghz_protocol_decoder_base_serialize(
|
||||
instance->decoder_result, instance->fff_data, instance->preset) !=
|
||||
SubGhzProtocolStatusOk) {
|
||||
ret = subghz_protocol_decoder_base_serialize(
|
||||
instance->decoder_result, instance->fff_data, instance->preset);
|
||||
if(ret != SubGhzProtocolStatusOk) {
|
||||
FURI_LOG_E(TAG, "Unable to serialize");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_update_uint32(instance->fff_data, "Bit", &bit, 1)) {
|
||||
ret = SubGhzProtocolStatusErrorParserOthers;
|
||||
FURI_LOG_E(TAG, "Unable to update Bit");
|
||||
break;
|
||||
}
|
||||
@ -56,15 +55,15 @@ bool subghz_txrx_gen_data_protocol(
|
||||
key_data[sizeof(uint64_t) - i - 1] = (key >> (i * 8)) & 0xFF;
|
||||
}
|
||||
if(!flipper_format_update_hex(instance->fff_data, "Key", key_data, sizeof(uint64_t))) {
|
||||
ret = SubGhzProtocolStatusErrorParserOthers;
|
||||
FURI_LOG_E(TAG, "Unable to update Key");
|
||||
break;
|
||||
}
|
||||
res = true;
|
||||
} while(false);
|
||||
return res;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool subghz_txrx_gen_data_protocol_and_te(
|
||||
SubGhzProtocolStatus subghz_txrx_gen_data_protocol_and_te(
|
||||
SubGhzTxRx* instance,
|
||||
const char* preset_name,
|
||||
uint32_t frequency,
|
||||
@ -73,18 +72,18 @@ bool subghz_txrx_gen_data_protocol_and_te(
|
||||
uint32_t bit,
|
||||
uint32_t te) {
|
||||
furi_assert(instance);
|
||||
bool ret = false;
|
||||
if(subghz_txrx_gen_data_protocol(instance, preset_name, frequency, protocol_name, key, bit)) {
|
||||
SubGhzProtocolStatus ret =
|
||||
subghz_txrx_gen_data_protocol(instance, preset_name, frequency, protocol_name, key, bit);
|
||||
if(ret == SubGhzProtocolStatusOk) {
|
||||
if(!flipper_format_update_uint32(instance->fff_data, "TE", (uint32_t*)&te, 1)) {
|
||||
ret = SubGhzProtocolStatusErrorParserOthers;
|
||||
FURI_LOG_E(TAG, "Unable to update Te");
|
||||
} else {
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool subghz_txrx_gen_keeloq_protocol(
|
||||
SubGhzProtocolStatus subghz_txrx_gen_keeloq_protocol(
|
||||
SubGhzTxRx* instance,
|
||||
const char* name_preset,
|
||||
uint32_t frequency,
|
||||
@ -94,7 +93,7 @@ bool subghz_txrx_gen_keeloq_protocol(
|
||||
uint16_t cnt) {
|
||||
furi_assert(instance);
|
||||
|
||||
bool ret = false;
|
||||
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
|
||||
serial &= 0x0FFFFFFF;
|
||||
instance->transmitter =
|
||||
subghz_transmitter_alloc_init(instance->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME);
|
||||
@ -108,13 +107,13 @@ bool subghz_txrx_gen_keeloq_protocol(
|
||||
cnt,
|
||||
name_sysmem,
|
||||
instance->preset);
|
||||
ret = true;
|
||||
ret = SubGhzProtocolStatusOk;
|
||||
}
|
||||
subghz_transmitter_free(instance->transmitter);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool subghz_txrx_gen_secplus_v2_protocol(
|
||||
SubGhzProtocolStatus subghz_txrx_gen_secplus_v2_protocol(
|
||||
SubGhzTxRx* instance,
|
||||
const char* name_preset,
|
||||
uint32_t frequency,
|
||||
@ -123,10 +122,11 @@ bool subghz_txrx_gen_secplus_v2_protocol(
|
||||
uint32_t cnt) {
|
||||
furi_assert(instance);
|
||||
|
||||
bool ret = false;
|
||||
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
|
||||
instance->transmitter =
|
||||
subghz_transmitter_alloc_init(instance->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME);
|
||||
subghz_txrx_set_preset(instance, name_preset, frequency, NULL, 0);
|
||||
|
||||
if(instance->transmitter) {
|
||||
subghz_protocol_secplus_v2_create_data(
|
||||
subghz_transmitter_get_protocol_instance(instance->transmitter),
|
||||
@ -135,30 +135,27 @@ bool subghz_txrx_gen_secplus_v2_protocol(
|
||||
btn,
|
||||
cnt,
|
||||
instance->preset);
|
||||
ret = true;
|
||||
ret = SubGhzProtocolStatusOk;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool subghz_txrx_gen_secplus_v1_protocol(
|
||||
SubGhzProtocolStatus subghz_txrx_gen_secplus_v1_protocol(
|
||||
SubGhzTxRx* instance,
|
||||
const char* name_preset,
|
||||
uint32_t frequency) {
|
||||
furi_assert(instance);
|
||||
|
||||
bool ret = false;
|
||||
uint32_t serial = (uint32_t)rand();
|
||||
while(!subghz_protocol_secplus_v1_check_fixed(serial)) {
|
||||
serial = (uint32_t)rand();
|
||||
}
|
||||
if(subghz_txrx_gen_data_protocol(
|
||||
instance,
|
||||
name_preset,
|
||||
frequency,
|
||||
SUBGHZ_PROTOCOL_SECPLUS_V1_NAME,
|
||||
(uint64_t)serial << 32 | 0xE6000000,
|
||||
42)) {
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
return subghz_txrx_gen_data_protocol(
|
||||
instance,
|
||||
name_preset,
|
||||
frequency,
|
||||
SUBGHZ_PROTOCOL_SECPLUS_V1_NAME,
|
||||
(uint64_t)serial << 32 | 0xE6000000,
|
||||
42);
|
||||
}
|
||||
|
||||
@ -11,9 +11,9 @@
|
||||
* @param protocol_name Name of protocol
|
||||
* @param key Key
|
||||
* @param bit Bit
|
||||
* @return bool True if success
|
||||
* @return SubGhzProtocolStatus
|
||||
*/
|
||||
bool subghz_txrx_gen_data_protocol(
|
||||
SubGhzProtocolStatus subghz_txrx_gen_data_protocol(
|
||||
void* context,
|
||||
const char* preset_name,
|
||||
uint32_t frequency,
|
||||
@ -31,9 +31,9 @@ bool subghz_txrx_gen_data_protocol(
|
||||
* @param key Key
|
||||
* @param bit Bit
|
||||
* @param te Te
|
||||
* @return bool True if success
|
||||
* @return SubGhzProtocolStatus
|
||||
*/
|
||||
bool subghz_txrx_gen_data_protocol_and_te(
|
||||
SubGhzProtocolStatus subghz_txrx_gen_data_protocol_and_te(
|
||||
SubGhzTxRx* instance,
|
||||
const char* preset_name,
|
||||
uint32_t frequency,
|
||||
@ -52,9 +52,9 @@ bool subghz_txrx_gen_data_protocol_and_te(
|
||||
* @param serial Serial number
|
||||
* @param btn Button
|
||||
* @param cnt Counter
|
||||
* @return bool True if success
|
||||
* @return SubGhzProtocolStatus
|
||||
*/
|
||||
bool subghz_txrx_gen_keeloq_protocol(
|
||||
SubGhzProtocolStatus subghz_txrx_gen_keeloq_protocol(
|
||||
SubGhzTxRx* instance,
|
||||
const char* name_preset,
|
||||
uint32_t frequency,
|
||||
@ -72,9 +72,9 @@ bool subghz_txrx_gen_keeloq_protocol(
|
||||
* @param serial Serial number
|
||||
* @param btn Button
|
||||
* @param cnt Counter
|
||||
* @return bool True if success
|
||||
* @return SubGhzProtocolStatus
|
||||
*/
|
||||
bool subghz_txrx_gen_secplus_v2_protocol(
|
||||
SubGhzProtocolStatus subghz_txrx_gen_secplus_v2_protocol(
|
||||
SubGhzTxRx* instance,
|
||||
const char* name_preset,
|
||||
uint32_t frequency,
|
||||
@ -88,9 +88,9 @@ bool subghz_txrx_gen_secplus_v2_protocol(
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @param name_preset Name of preset
|
||||
* @param frequency Frequency in Hz
|
||||
* @return bool True if success
|
||||
* @return SubGhzProtocolStatus
|
||||
*/
|
||||
bool subghz_txrx_gen_secplus_v1_protocol(
|
||||
SubGhzProtocolStatus subghz_txrx_gen_secplus_v1_protocol(
|
||||
SubGhzTxRx* instance,
|
||||
const char* name_preset,
|
||||
uint32_t frequency);
|
||||
@ -1,6 +1,7 @@
|
||||
#include "../subghz_i.h"
|
||||
#include <lib/toolbox/value_index.h>
|
||||
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>
|
||||
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
|
||||
|
||||
enum SubGhzRadioSettingIndex {
|
||||
SubGhzRadioSettingIndexDevice,
|
||||
@ -17,16 +18,29 @@ const uint32_t radio_device_value[RADIO_DEVICE_COUNT] = {
|
||||
SubGhzRadioDeviceTypeExternalCC1101,
|
||||
};
|
||||
|
||||
const char* const radio_device_name[RADIO_DEVICE_COUNT] = {
|
||||
SUBGHZ_DEVICE_CC1101_INT_NAME,
|
||||
SUBGHZ_DEVICE_CC1101_EXT_NAME,
|
||||
};
|
||||
|
||||
static uint8_t subghz_scene_radio_settings_next_index_connect_ext_device(
|
||||
SubGhz* subghz,
|
||||
uint8_t current_index) {
|
||||
uint8_t index = 0;
|
||||
for(index = current_index; index < RADIO_DEVICE_COUNT; index++) {
|
||||
if(subghz_txrx_radio_device_is_external_connected(subghz->txrx, radio_device_name[index])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(index == RADIO_DEVICE_COUNT) index = 0;
|
||||
return index;
|
||||
}
|
||||
|
||||
static void subghz_scene_radio_settings_set_device(VariableItem* item) {
|
||||
SubGhz* subghz = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
if(!subghz_txrx_radio_device_is_external_connected(
|
||||
subghz->txrx, SUBGHZ_DEVICE_CC1101_EXT_NAME) &&
|
||||
radio_device_value[index] == SubGhzRadioDeviceTypeExternalCC1101) {
|
||||
//ToDo correct if there is more than 1 module
|
||||
index = 0;
|
||||
}
|
||||
uint8_t current_index = variable_item_get_current_value_index(item);
|
||||
uint8_t index =
|
||||
subghz_scene_radio_settings_next_index_connect_ext_device(subghz, current_index);
|
||||
variable_item_set_current_value_text(item, radio_device_text[index]);
|
||||
subghz_txrx_radio_device_set(subghz->txrx, radio_device_value[index]);
|
||||
}
|
||||
@ -37,9 +51,11 @@ void subghz_scene_radio_settings_on_enter(void* context) {
|
||||
uint8_t value_index;
|
||||
|
||||
uint8_t value_count_device = RADIO_DEVICE_COUNT;
|
||||
|
||||
if(subghz_txrx_radio_device_get(subghz->txrx) == SubGhzRadioDeviceTypeInternal &&
|
||||
!subghz_txrx_radio_device_is_external_connected(subghz->txrx, SUBGHZ_DEVICE_CC1101_EXT_NAME))
|
||||
!subghz_scene_radio_settings_next_index_connect_ext_device(subghz, 1))
|
||||
value_count_device = 1;
|
||||
|
||||
item = variable_item_list_add(
|
||||
subghz->variable_item_list,
|
||||
"Module",
|
||||
|
||||
@ -23,7 +23,7 @@ static bool subghz_scene_receiver_info_update_parser(void* context) {
|
||||
if(subghz_txrx_load_decoder_by_name_protocol(
|
||||
subghz->txrx,
|
||||
subghz_history_get_protocol_name(subghz->history, subghz->idx_menu_chosen))) {
|
||||
//todo we are trying to deserialize without checking for errors, since it is assumed that we just received this chignal
|
||||
// we are trying to deserialize without checking for errors, since it is assumed that we just received this chignal
|
||||
subghz_protocol_decoder_base_deserialize(
|
||||
subghz_txrx_get_decoder(subghz->txrx),
|
||||
subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen));
|
||||
|
||||
@ -118,7 +118,7 @@ void subghz_scene_set_type_on_enter(void* context) {
|
||||
|
||||
bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
||||
SubGhz* subghz = context;
|
||||
bool generated_protocol = false;
|
||||
SubGhzProtocolStatus generated_protocol = SubGhzProtocolStatusError;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
uint32_t key = (uint32_t)rand();
|
||||
@ -174,7 +174,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
||||
case SubmenuIndexDoorHan_433_92:
|
||||
generated_protocol = subghz_txrx_gen_keeloq_protocol(
|
||||
subghz->txrx, "AM650", 433920000, "DoorHan", key, 0x2, 0x0003);
|
||||
if(!generated_protocol) {
|
||||
if(generated_protocol != SubGhzProtocolStatusOk) {
|
||||
furi_string_set(
|
||||
subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
|
||||
@ -183,7 +183,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
||||
case SubmenuIndexDoorHan_315_00:
|
||||
generated_protocol = subghz_txrx_gen_keeloq_protocol(
|
||||
subghz->txrx, "AM650", 315000000, "DoorHan", key, 0x2, 0x0003);
|
||||
if(!generated_protocol) {
|
||||
if(generated_protocol != SubGhzProtocolStatusOk) {
|
||||
furi_string_set(
|
||||
subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
|
||||
@ -216,7 +216,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneSetType, event.event);
|
||||
|
||||
if(generated_protocol) {
|
||||
if(generated_protocol == SubGhzProtocolStatusOk) {
|
||||
subghz_file_name_clear(subghz);
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
|
||||
return true;
|
||||
|
||||
@ -134,7 +134,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) {
|
||||
SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
|
||||
|
||||
if(!strcmp(furi_string_get_cstr(temp_str), "CUSTOM")) {
|
||||
//Todo add Custom_preset_module
|
||||
//TODO FL-3551: add Custom_preset_module
|
||||
//delete preset if it already exists
|
||||
subghz_setting_delete_custom_preset(setting, furi_string_get_cstr(temp_str));
|
||||
//load custom preset from file
|
||||
@ -289,10 +289,13 @@ bool subghz_save_protocol_to_file(
|
||||
if(!storage_simply_remove(storage, dev_file_name)) {
|
||||
break;
|
||||
}
|
||||
//ToDo check Write
|
||||
stream_seek(flipper_format_stream, 0, StreamOffsetFromStart);
|
||||
stream_save_to_file(flipper_format_stream, storage, dev_file_name, FSOM_CREATE_ALWAYS);
|
||||
|
||||
if(storage_common_stat(storage, dev_file_name, NULL) != FSE_OK) {
|
||||
break;
|
||||
}
|
||||
|
||||
saved = true;
|
||||
} while(0);
|
||||
furi_string_free(file_dir);
|
||||
|
||||
@ -51,6 +51,12 @@ extern const size_t FLIPPER_ON_SYSTEM_START_COUNT;
|
||||
extern const FlipperInternalApplication FLIPPER_SYSTEM_APPS[];
|
||||
extern const size_t FLIPPER_SYSTEM_APPS_COUNT;
|
||||
|
||||
/* Debug apps
|
||||
* Can only be spawned by loader by name
|
||||
*/
|
||||
extern const FlipperInternalApplication FLIPPER_DEBUG_APPS[];
|
||||
extern const size_t FLIPPER_DEBUG_APPS_COUNT;
|
||||
|
||||
extern const FlipperInternalApplication FLIPPER_ARCHIVE;
|
||||
|
||||
/* Settings list
|
||||
|
||||
@ -128,8 +128,8 @@ static bool bubble_animation_input_callback(InputEvent* event, void* context) {
|
||||
|
||||
if(event->key == InputKeyRight) {
|
||||
/* Right button reserved for animation activation, so consume */
|
||||
consumed = true;
|
||||
if(event->type == InputTypeShort) {
|
||||
consumed = true;
|
||||
if(animation_view->interact_callback) {
|
||||
animation_view->interact_callback(animation_view->interact_callback_context);
|
||||
}
|
||||
|
||||
@ -65,8 +65,8 @@ static bool one_shot_view_input(InputEvent* event, void* context) {
|
||||
if(!consumed) {
|
||||
if(event->key == InputKeyRight) {
|
||||
/* Right button reserved for animation activation, so consume */
|
||||
consumed = true;
|
||||
if(event->type == InputTypeShort) {
|
||||
consumed = true;
|
||||
if(view->interact_callback) {
|
||||
view->interact_callback(view->interact_callback_context);
|
||||
}
|
||||
|
||||
@ -101,7 +101,7 @@ static void desktop_clock_draw_callback(Canvas* canvas, void* context) {
|
||||
char buffer[20];
|
||||
snprintf(buffer, sizeof(buffer), "%02u:%02u", hour, desktop->time_minute);
|
||||
|
||||
// ToDo: never do that, may cause visual glitches
|
||||
// TODO FL-3515: never do that, may cause visual glitches
|
||||
view_port_set_width(
|
||||
desktop->clock_viewport,
|
||||
canvas_string_width(canvas, buffer) - 1 + (desktop->time_minute % 10 == 1));
|
||||
@ -126,7 +126,7 @@ static bool desktop_custom_event_callback(void* context, uint32_t event) {
|
||||
return true;
|
||||
case DesktopGlobalAfterAppFinished:
|
||||
animation_manager_load_and_continue_animation(desktop->animation_manager);
|
||||
// TODO: Implement a message mechanism for loading settings and (optionally)
|
||||
// TODO FL-3497: Implement a message mechanism for loading settings and (optionally)
|
||||
// locking and unlocking
|
||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||
|
||||
@ -223,7 +223,6 @@ void desktop_lock(Desktop* 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_backlight_off_delay_1000);
|
||||
|
||||
DesktopStatus status = {.locked = true};
|
||||
furi_pubsub_publish(desktop->status_pubsub, &status);
|
||||
@ -381,12 +380,7 @@ Desktop* desktop_alloc() {
|
||||
}
|
||||
gui_add_view_port(desktop->gui, desktop->stealth_mode_icon_viewport, GuiLayerStatusBarLeft);
|
||||
|
||||
// Special case: autostart application is already running
|
||||
desktop->loader = furi_record_open(RECORD_LOADER);
|
||||
if(loader_is_locked(desktop->loader) &&
|
||||
animation_manager_is_animation_loaded(desktop->animation_manager)) {
|
||||
animation_manager_unload_and_stall_animation(desktop->animation_manager);
|
||||
}
|
||||
|
||||
desktop->notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
desktop->app_start_stop_subscription = furi_pubsub_subscribe(
|
||||
@ -477,6 +471,12 @@ int32_t desktop_srv(void* p) {
|
||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneFault);
|
||||
}
|
||||
|
||||
// Special case: autostart application is already running
|
||||
if(loader_is_locked(desktop->loader) &&
|
||||
animation_manager_is_animation_loaded(desktop->animation_manager)) {
|
||||
animation_manager_unload_and_stall_animation(desktop->animation_manager);
|
||||
}
|
||||
|
||||
view_dispatcher_run(desktop->view_dispatcher);
|
||||
|
||||
furi_crash("That was unexpected");
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
#include <toolbox/saved_struct.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
#define DESKTOP_SETTINGS_VER (9)
|
||||
#define DESKTOP_SETTINGS_VER (10)
|
||||
|
||||
#define DESKTOP_SETTINGS_PATH INT_PATH(DESKTOP_SETTINGS_FILE_NAME)
|
||||
#define DESKTOP_SETTINGS_MAGIC (0x17)
|
||||
@ -36,6 +36,22 @@
|
||||
#define MIN_PIN_SIZE 4
|
||||
#define MAX_APP_LENGTH 128
|
||||
|
||||
typedef enum {
|
||||
FavoriteAppLeftShort = 0,
|
||||
FavoriteAppLeftLong,
|
||||
FavoriteAppRightShort,
|
||||
FavoriteAppRightLong,
|
||||
FavoriteAppNumber,
|
||||
} FavoriteAppShortcut;
|
||||
|
||||
typedef enum {
|
||||
DummyAppLeft = 0,
|
||||
DummyAppRight,
|
||||
DummyAppDown,
|
||||
DummyAppOk,
|
||||
DummyAppNumber,
|
||||
} DummyAppShortcut;
|
||||
|
||||
typedef struct {
|
||||
InputKey data[MAX_PIN_SIZE];
|
||||
uint8_t length;
|
||||
@ -46,10 +62,10 @@ typedef struct {
|
||||
} FavoriteApp;
|
||||
|
||||
typedef struct {
|
||||
FavoriteApp favorite_primary;
|
||||
FavoriteApp favorite_secondary;
|
||||
PinCode pin_code;
|
||||
uint32_t auto_lock_delay_ms;
|
||||
uint8_t dummy_mode;
|
||||
uint8_t display_clock;
|
||||
FavoriteApp favorite_apps[FavoriteAppNumber];
|
||||
FavoriteApp dummy_apps[DummyAppNumber];
|
||||
} DesktopSettings;
|
||||
|
||||
@ -87,6 +87,10 @@ bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) {
|
||||
desktop_unlock(desktop);
|
||||
consumed = true;
|
||||
break;
|
||||
case DesktopLockedEventDoorsClosed:
|
||||
notification_message(desktop->notification, &sequence_display_backlight_off);
|
||||
consumed = true;
|
||||
break;
|
||||
case DesktopLockedEventUpdate:
|
||||
if(desktop_view_locked_is_locked_hint_visible(desktop->locked_view)) {
|
||||
notification_message(desktop->notification, &sequence_display_backlight_off);
|
||||
|
||||
@ -12,10 +12,6 @@
|
||||
|
||||
#define TAG "DesktopSrv"
|
||||
|
||||
#define MUSIC_PLAYER_APP EXT_PATH("/apps/Media/music_player.fap")
|
||||
#define SNAKE_GAME_APP EXT_PATH("/apps/Games/snake_game.fap")
|
||||
#define CLOCK_APP EXT_PATH("/apps/Tools/clock.fap")
|
||||
|
||||
static void desktop_scene_main_new_idle_animation_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Desktop* desktop = context;
|
||||
@ -65,8 +61,15 @@ static void
|
||||
}
|
||||
#endif
|
||||
|
||||
static void desktop_scene_main_open_app_or_profile(Desktop* desktop, const char* path) {
|
||||
if(loader_start_with_gui_error(desktop->loader, path, NULL) != LoaderStatusOk) {
|
||||
static void desktop_scene_main_open_app_or_profile(Desktop* desktop, FavoriteApp* application) {
|
||||
bool load_ok = false;
|
||||
if(strlen(application->name_or_path) > 0) {
|
||||
if(loader_start(desktop->loader, application->name_or_path, NULL, NULL) ==
|
||||
LoaderStatusOk) {
|
||||
load_ok = true;
|
||||
}
|
||||
}
|
||||
if(!load_ok) {
|
||||
loader_start(desktop->loader, "Passport", NULL, NULL);
|
||||
}
|
||||
}
|
||||
@ -115,6 +118,11 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
|
||||
consumed = true;
|
||||
} break;
|
||||
|
||||
case DesktopMainEventLock:
|
||||
desktop_lock(desktop);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
case DesktopMainEventOpenLockMenu:
|
||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLockMenu);
|
||||
consumed = true;
|
||||
@ -138,16 +146,31 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
|
||||
break;
|
||||
}
|
||||
|
||||
case DesktopMainEventOpenFavoritePrimary:
|
||||
case DesktopMainEventOpenFavoriteLeftShort:
|
||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||
desktop_scene_main_start_favorite(desktop, &desktop->settings.favorite_primary);
|
||||
desktop_scene_main_start_favorite(
|
||||
desktop, &desktop->settings.favorite_apps[FavoriteAppLeftShort]);
|
||||
consumed = true;
|
||||
break;
|
||||
case DesktopMainEventOpenFavoriteSecondary:
|
||||
case DesktopMainEventOpenFavoriteLeftLong:
|
||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||
desktop_scene_main_start_favorite(desktop, &desktop->settings.favorite_secondary);
|
||||
desktop_scene_main_start_favorite(
|
||||
desktop, &desktop->settings.favorite_apps[FavoriteAppLeftLong]);
|
||||
consumed = true;
|
||||
break;
|
||||
case DesktopMainEventOpenFavoriteRightShort:
|
||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||
desktop_scene_main_start_favorite(
|
||||
desktop, &desktop->settings.favorite_apps[FavoriteAppRightShort]);
|
||||
consumed = true;
|
||||
break;
|
||||
case DesktopMainEventOpenFavoriteRightLong:
|
||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||
desktop_scene_main_start_favorite(
|
||||
desktop, &desktop->settings.favorite_apps[FavoriteAppRightLong]);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
case DesktopAnimationEventCheckAnimation:
|
||||
animation_manager_check_blocking_process(desktop->animation_manager);
|
||||
consumed = true;
|
||||
@ -158,26 +181,31 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
|
||||
break;
|
||||
case DesktopAnimationEventInteractAnimation:
|
||||
if(!animation_manager_interact_process(desktop->animation_manager)) {
|
||||
loader_start(desktop->loader, "Passport", NULL, NULL);
|
||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||
if(!desktop->settings.dummy_mode) {
|
||||
desktop_scene_main_open_app_or_profile(
|
||||
desktop, &desktop->settings.favorite_apps[FavoriteAppRightShort]);
|
||||
} else {
|
||||
desktop_scene_main_open_app_or_profile(
|
||||
desktop, &desktop->settings.dummy_apps[DummyAppRight]);
|
||||
}
|
||||
}
|
||||
consumed = true;
|
||||
break;
|
||||
case DesktopMainEventOpenPassport: {
|
||||
loader_start(desktop->loader, "Passport", NULL, NULL);
|
||||
|
||||
case DesktopDummyEventOpenLeft:
|
||||
desktop_scene_main_open_app_or_profile(
|
||||
desktop, &desktop->settings.dummy_apps[DummyAppLeft]);
|
||||
break;
|
||||
}
|
||||
case DesktopMainEventOpenGame: {
|
||||
desktop_scene_main_open_app_or_profile(desktop, SNAKE_GAME_APP);
|
||||
case DesktopDummyEventOpenDown:
|
||||
desktop_scene_main_open_app_or_profile(
|
||||
desktop, &desktop->settings.dummy_apps[DummyAppDown]);
|
||||
break;
|
||||
}
|
||||
case DesktopMainEventOpenClock: {
|
||||
desktop_scene_main_open_app_or_profile(desktop, CLOCK_APP);
|
||||
case DesktopDummyEventOpenOk:
|
||||
desktop_scene_main_open_app_or_profile(
|
||||
desktop, &desktop->settings.dummy_apps[DummyAppOk]);
|
||||
break;
|
||||
}
|
||||
case DesktopMainEventOpenMusicPlayer: {
|
||||
desktop_scene_main_open_app_or_profile(desktop, MUSIC_PLAYER_APP);
|
||||
break;
|
||||
}
|
||||
|
||||
case DesktopLockedEventUpdate:
|
||||
desktop_view_locked_update(desktop->locked_view);
|
||||
consumed = true;
|
||||
|
||||
@ -1,22 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
DesktopMainEventLock,
|
||||
DesktopMainEventOpenLockMenu,
|
||||
DesktopMainEventOpenArchive,
|
||||
DesktopMainEventOpenFavoritePrimary,
|
||||
DesktopMainEventOpenFavoriteSecondary,
|
||||
DesktopMainEventOpenFavoriteLeftShort,
|
||||
DesktopMainEventOpenFavoriteLeftLong,
|
||||
DesktopMainEventOpenFavoriteRightShort,
|
||||
DesktopMainEventOpenFavoriteRightLong,
|
||||
DesktopMainEventOpenMenu,
|
||||
DesktopMainEventOpenDebug,
|
||||
DesktopMainEventOpenPassport,
|
||||
DesktopMainEventOpenPowerOff,
|
||||
|
||||
DesktopMainEventOpenGame,
|
||||
DesktopMainEventOpenClock,
|
||||
DesktopMainEventOpenMusicPlayer,
|
||||
DesktopDummyEventOpenLeft,
|
||||
DesktopDummyEventOpenDown,
|
||||
DesktopDummyEventOpenOk,
|
||||
|
||||
DesktopLockedEventUnlocked,
|
||||
DesktopLockedEventUpdate,
|
||||
DesktopLockedEventShowPinInput,
|
||||
DesktopLockedEventDoorsClosed,
|
||||
|
||||
DesktopPinInputEventResetWrongPinLabel,
|
||||
DesktopPinInputEventUnlocked,
|
||||
|
||||
@ -99,6 +99,7 @@ void desktop_view_locked_update(DesktopViewLocked* locked_view) {
|
||||
|
||||
if(view_state == DesktopViewLockedStateDoorsClosing &&
|
||||
!desktop_view_locked_doors_move(model)) {
|
||||
locked_view->callback(DesktopLockedEventDoorsClosed, locked_view->context);
|
||||
model->view_state = DesktopViewLockedStateLocked;
|
||||
} else if(view_state == DesktopViewLockedStateLockedHintShown) {
|
||||
model->view_state = DesktopViewLockedStateLocked;
|
||||
|
||||
@ -59,28 +59,32 @@ bool desktop_main_input_callback(InputEvent* event, void* context) {
|
||||
} else if(event->key == InputKeyDown) {
|
||||
main_view->callback(DesktopMainEventOpenArchive, main_view->context);
|
||||
} else if(event->key == InputKeyLeft) {
|
||||
main_view->callback(DesktopMainEventOpenFavoritePrimary, main_view->context);
|
||||
main_view->callback(DesktopMainEventOpenFavoriteLeftShort, main_view->context);
|
||||
}
|
||||
// Right key is handled by animation manager
|
||||
// Right key short is handled by animation manager
|
||||
} else if(event->type == InputTypeLong) {
|
||||
if(event->key == InputKeyDown) {
|
||||
if(event->key == InputKeyUp) {
|
||||
main_view->callback(DesktopMainEventLock, main_view->context);
|
||||
} else if(event->key == InputKeyDown) {
|
||||
main_view->callback(DesktopMainEventOpenDebug, main_view->context);
|
||||
} else if(event->key == InputKeyLeft) {
|
||||
main_view->callback(DesktopMainEventOpenFavoriteSecondary, main_view->context);
|
||||
main_view->callback(DesktopMainEventOpenFavoriteLeftLong, main_view->context);
|
||||
} else if(event->key == InputKeyRight) {
|
||||
main_view->callback(DesktopMainEventOpenFavoriteRightLong, main_view->context);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyOk) {
|
||||
main_view->callback(DesktopMainEventOpenGame, main_view->context);
|
||||
main_view->callback(DesktopDummyEventOpenOk, main_view->context);
|
||||
} else if(event->key == InputKeyUp) {
|
||||
main_view->callback(DesktopMainEventOpenLockMenu, main_view->context);
|
||||
} else if(event->key == InputKeyDown) {
|
||||
main_view->callback(DesktopMainEventOpenMusicPlayer, main_view->context);
|
||||
main_view->callback(DesktopDummyEventOpenDown, main_view->context);
|
||||
} else if(event->key == InputKeyLeft) {
|
||||
main_view->callback(DesktopMainEventOpenClock, main_view->context);
|
||||
main_view->callback(DesktopDummyEventOpenLeft, main_view->context);
|
||||
}
|
||||
// Right key is handled by animation manager
|
||||
// Right key short is handled by animation manager
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -236,11 +236,11 @@ static size_t
|
||||
}
|
||||
|
||||
if(len_px > px_left) {
|
||||
uint8_t excess_symbols_approximately =
|
||||
roundf((float)(len_px - px_left) / ((float)len_px / (float)text_size));
|
||||
size_t excess_symbols_approximately =
|
||||
ceilf((float)(len_px - px_left) / ((float)len_px / (float)text_size));
|
||||
// reduce to 5 to be sure dash fit, and next line will be at least 5 symbols long
|
||||
if(excess_symbols_approximately > 0) {
|
||||
excess_symbols_approximately = MAX(excess_symbols_approximately, 5);
|
||||
excess_symbols_approximately = MAX(excess_symbols_approximately, 5u);
|
||||
result = text_size - excess_symbols_approximately - 1;
|
||||
} else {
|
||||
result = text_size;
|
||||
|
||||
@ -29,6 +29,9 @@ typedef struct {
|
||||
const Icon* name_selected;
|
||||
} IconElement;
|
||||
|
||||
LIST_DEF(IconList, IconElement, M_POD_OPLIST)
|
||||
#define M_OPL_IconList_t() LIST_OPLIST(IconList)
|
||||
|
||||
typedef struct ButtonItem {
|
||||
uint32_t index;
|
||||
ButtonItemCallback callback;
|
||||
@ -47,6 +50,7 @@ struct ButtonPanel {
|
||||
|
||||
typedef struct {
|
||||
ButtonMatrix_t button_matrix;
|
||||
IconList_t icons;
|
||||
LabelList_t labels;
|
||||
uint16_t reserve_x;
|
||||
uint16_t reserve_y;
|
||||
@ -103,7 +107,6 @@ void button_panel_reserve(ButtonPanel* button_panel, size_t reserve_x, size_t re
|
||||
ButtonArray_t* array = ButtonMatrix_get(model->button_matrix, i);
|
||||
ButtonArray_init(*array);
|
||||
ButtonArray_reserve(*array, reserve_x);
|
||||
// TODO: do we need to clear allocated memory of ptr-s to ButtonItem ??
|
||||
}
|
||||
LabelList_init(model->labels);
|
||||
},
|
||||
@ -147,6 +150,7 @@ void button_panel_reset(ButtonPanel* button_panel) {
|
||||
model->selected_item_x = 0;
|
||||
model->selected_item_y = 0;
|
||||
LabelList_reset(model->labels);
|
||||
IconList_reset(model->icons);
|
||||
ButtonMatrix_reset(model->button_matrix);
|
||||
},
|
||||
true);
|
||||
@ -209,9 +213,17 @@ static void button_panel_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
for
|
||||
M_EACH(icon, model->icons, IconList_t) {
|
||||
canvas_draw_icon(canvas, icon->x, icon->y, icon->name);
|
||||
}
|
||||
|
||||
for(size_t x = 0; x < model->reserve_x; ++x) {
|
||||
for(size_t y = 0; y < model->reserve_y; ++y) {
|
||||
ButtonItem* button_item = *button_panel_get_item(model, x, y);
|
||||
if(!button_item) {
|
||||
continue;
|
||||
}
|
||||
const Icon* icon_name = button_item->icon.name;
|
||||
if((model->selected_item_x == x) && (model->selected_item_y == y)) {
|
||||
icon_name = button_item->icon.name_selected;
|
||||
@ -407,3 +419,24 @@ void button_panel_add_label(
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
// Draw an icon but don't make it a button.
|
||||
void button_panel_add_icon(
|
||||
ButtonPanel* button_panel,
|
||||
uint16_t x,
|
||||
uint16_t y,
|
||||
const Icon* icon_name) {
|
||||
furi_assert(button_panel);
|
||||
|
||||
with_view_model( //-V773
|
||||
button_panel->view,
|
||||
ButtonPanelModel * model,
|
||||
{
|
||||
IconElement* icon = IconList_push_raw(model->icons);
|
||||
icon->x = x;
|
||||
icon->y = y;
|
||||
icon->name = icon_name;
|
||||
icon->name_selected = icon_name;
|
||||
},
|
||||
true);
|
||||
}
|
||||
@ -100,6 +100,19 @@ void button_panel_add_label(
|
||||
Font font,
|
||||
const char* label_str);
|
||||
|
||||
/** Add a non-button icon to button_panel module.
|
||||
*
|
||||
* @param button_panel ButtonPanel instance
|
||||
* @param x x-coordinate to place icon
|
||||
* @param y y-coordinate to place icon
|
||||
* @param icon_name name of the icon to draw
|
||||
*/
|
||||
void button_panel_add_icon(
|
||||
ButtonPanel* button_panel,
|
||||
uint16_t x,
|
||||
uint16_t y,
|
||||
const Icon* icon_name);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -272,7 +272,7 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e
|
||||
} else if(view_dispatcher->navigation_event_callback) {
|
||||
// Dispatch navigation event
|
||||
if(!view_dispatcher->navigation_event_callback(view_dispatcher->event_context)) {
|
||||
// TODO: should we allow view_dispatcher to stop without navigation_event_callback?
|
||||
// TODO FL-3514: should we allow view_dispatcher to stop without navigation_event_callback?
|
||||
view_dispatcher_stop(view_dispatcher);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
#include "gui.h"
|
||||
#include "gui_i.h"
|
||||
|
||||
// TODO add mutex to view_port ops
|
||||
// TODO FL-3498: add mutex to view_port ops
|
||||
|
||||
_Static_assert(ViewPortOrientationMAX == 4, "Incorrect ViewPortOrientation count");
|
||||
_Static_assert(
|
||||
|
||||
@ -10,6 +10,19 @@
|
||||
|
||||
static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!");
|
||||
|
||||
#ifdef APP_UNIT_TESTS
|
||||
constexpr HashtableApiInterface mock_elf_api_interface{
|
||||
{
|
||||
.api_version_major = 0,
|
||||
.api_version_minor = 0,
|
||||
.resolver_callback = &elf_resolve_from_hashtable,
|
||||
},
|
||||
.table_cbegin = nullptr,
|
||||
.table_cend = nullptr,
|
||||
};
|
||||
|
||||
const ElfApiInterface* const firmware_api_interface = &mock_elf_api_interface;
|
||||
#else
|
||||
constexpr HashtableApiInterface elf_api_interface{
|
||||
{
|
||||
.api_version_major = (elf_api_version >> 16),
|
||||
@ -19,10 +32,10 @@ constexpr HashtableApiInterface elf_api_interface{
|
||||
.table_cbegin = elf_api_table.cbegin(),
|
||||
.table_cend = elf_api_table.cend(),
|
||||
};
|
||||
|
||||
const ElfApiInterface* const firmware_api_interface = &elf_api_interface;
|
||||
#endif
|
||||
|
||||
extern "C" void furi_hal_info_get_api_version(uint16_t* major, uint16_t* minor) {
|
||||
*major = elf_api_interface.api_version_major;
|
||||
*minor = elf_api_interface.api_version_minor;
|
||||
*major = firmware_api_interface->api_version_major;
|
||||
*minor = firmware_api_interface->api_version_minor;
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
#include "loader.h"
|
||||
#include "core/core_defines.h"
|
||||
#include "loader_i.h"
|
||||
#include <applications.h>
|
||||
#include <storage/storage.h>
|
||||
@ -61,7 +62,7 @@ LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const
|
||||
dialog_message_free(message);
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
} else if(status == LoaderStatusErrorUnknownApp || status == LoaderStatusErrorInternal) {
|
||||
// TODO: we have many places where we can emit a double start, ex: desktop, menu
|
||||
// TODO FL-3522: we have many places where we can emit a double start, ex: desktop, menu
|
||||
// so i prefer to not show LoaderStatusErrorAppStarted error message for now
|
||||
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
DialogMessage* message = dialog_message_alloc();
|
||||
@ -186,18 +187,24 @@ static FlipperInternalApplication const* loader_find_application_by_name_in_list
|
||||
}
|
||||
|
||||
static const FlipperInternalApplication* loader_find_application_by_name(const char* name) {
|
||||
const FlipperInternalApplication* application = NULL;
|
||||
application = loader_find_application_by_name_in_list(name, FLIPPER_APPS, FLIPPER_APPS_COUNT);
|
||||
if(!application) {
|
||||
application = loader_find_application_by_name_in_list(
|
||||
name, FLIPPER_SETTINGS_APPS, FLIPPER_SETTINGS_APPS_COUNT);
|
||||
}
|
||||
if(!application) {
|
||||
application = loader_find_application_by_name_in_list(
|
||||
name, FLIPPER_SYSTEM_APPS, FLIPPER_SYSTEM_APPS_COUNT);
|
||||
const struct {
|
||||
const FlipperInternalApplication* list;
|
||||
const uint32_t count;
|
||||
} lists[] = {
|
||||
{FLIPPER_SETTINGS_APPS, FLIPPER_SETTINGS_APPS_COUNT},
|
||||
{FLIPPER_SYSTEM_APPS, FLIPPER_SYSTEM_APPS_COUNT},
|
||||
{FLIPPER_DEBUG_APPS, FLIPPER_DEBUG_APPS_COUNT},
|
||||
};
|
||||
|
||||
for(size_t i = 0; i < COUNT_OF(lists); i++) {
|
||||
const FlipperInternalApplication* application =
|
||||
loader_find_application_by_name_in_list(name, lists[i].list, lists[i].count);
|
||||
if(application) {
|
||||
return application;
|
||||
}
|
||||
}
|
||||
|
||||
return application;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void loader_start_app_thread(Loader* loader, FlipperInternalApplicationFlag flags) {
|
||||
@ -253,9 +260,7 @@ static void loader_log_status_error(
|
||||
furi_string_vprintf(error_message, format, args);
|
||||
FURI_LOG_E(TAG, "Status [%d]: %s", status, furi_string_get_cstr(error_message));
|
||||
} else {
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
FURI_LOG_E(TAG, "Status [%d]: %s", status, furi_string_get_cstr(tmp));
|
||||
furi_string_free(tmp);
|
||||
FURI_LOG_E(TAG, "Status [%d]", status);
|
||||
}
|
||||
}
|
||||
|
||||
@ -490,7 +495,9 @@ int32_t loader_srv(void* p) {
|
||||
FLIPPER_ON_SYSTEM_START[i]();
|
||||
}
|
||||
|
||||
if(FLIPPER_AUTORUN_APP_NAME && strlen(FLIPPER_AUTORUN_APP_NAME)) {
|
||||
if((furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) && FLIPPER_AUTORUN_APP_NAME &&
|
||||
strlen(FLIPPER_AUTORUN_APP_NAME)) {
|
||||
FURI_LOG_I(TAG, "Starting autorun app: %s", FLIPPER_AUTORUN_APP_NAME);
|
||||
loader_do_start_by_name(loader, FLIPPER_AUTORUN_APP_NAME, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ static void loader_cli_info(Loader* loader) {
|
||||
if(!loader_is_locked(loader)) {
|
||||
printf("No application is running\r\n");
|
||||
} else {
|
||||
// TODO: print application name ???
|
||||
// TODO FL-3513: print application name ???
|
||||
printf("Application is running\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) {
|
||||
if(power->state == PowerStateCharging) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
// TODO: replace -1 magic for uint8_t with re-framing
|
||||
// TODO FL-3510: replace -1 magic for uint8_t with re-framing
|
||||
canvas_draw_icon(canvas, 8, -1, &I_Charging_lightning_mask_9x10);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_icon(canvas, 8, -1, &I_Charging_lightning_9x10);
|
||||
|
||||
@ -279,7 +279,7 @@ static void rpc_system_gui_start_virtual_display_process(const PB_Main* request,
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: consider refactoring
|
||||
// TODO FL-3511: consider refactoring
|
||||
// Using display framebuffer size as an XBM buffer size is like comparing apples and oranges
|
||||
// Glad they both are 1024 for now
|
||||
size_t buffer_size = canvas_get_buffer_size(rpc_gui->gui->canvas);
|
||||
|
||||
@ -399,7 +399,7 @@ static FS_Error storage_process_common_fs_info(
|
||||
}
|
||||
|
||||
/****************** Raw SD API ******************/
|
||||
// TODO think about implementing a custom storage API to split that kind of api linkage
|
||||
// TODO FL-3521: think about implementing a custom storage API to split that kind of api linkage
|
||||
#include "storages/storage_ext.h"
|
||||
|
||||
static FS_Error storage_process_sd_format(Storage* app) {
|
||||
|
||||
@ -100,7 +100,7 @@ FS_Error sd_unmount_card(StorageData* storage) {
|
||||
storage->status = StorageStatusNotReady;
|
||||
error = FR_DISK_ERR;
|
||||
|
||||
// TODO do i need to close the files?
|
||||
// TODO FL-3522: do i need to close the files?
|
||||
f_mount(0, sd_data->path, 0);
|
||||
|
||||
return storage_ext_parse_error(error);
|
||||
|
||||
@ -1,17 +1,19 @@
|
||||
#include "../desktop_settings_app.h"
|
||||
#include "applications.h"
|
||||
#include "desktop_settings_scene.h"
|
||||
#include "desktop_settings_scene_i.h"
|
||||
#include <flipper_application/flipper_application.h>
|
||||
#include <storage/storage.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
|
||||
#define APPS_COUNT (FLIPPER_APPS_COUNT + FLIPPER_EXTERNAL_APPS_COUNT)
|
||||
|
||||
#define EXTERNAL_BROWSER_NAME ("Apps")
|
||||
#define EXTERNAL_BROWSER_INDEX (APPS_COUNT + 1)
|
||||
#define DEFAULT_INDEX (0)
|
||||
#define EXTERNAL_BROWSER_NAME ("Apps Menu (Default)")
|
||||
#define PASSPORT_NAME ("Passport (Default)")
|
||||
|
||||
#define EXTERNAL_APPLICATION_INDEX (1)
|
||||
#define EXTERNAL_APPLICATION_NAME ("[Select App]")
|
||||
#define EXTERNAL_APPLICATION_INDEX (APPS_COUNT + 2)
|
||||
|
||||
#define PRESELECTED_SPECIAL 0xffffffff
|
||||
|
||||
@ -55,28 +57,32 @@ void desktop_settings_scene_favorite_on_enter(void* context) {
|
||||
Submenu* submenu = app->submenu;
|
||||
submenu_reset(submenu);
|
||||
|
||||
uint32_t primary_favorite =
|
||||
uint32_t favorite_id =
|
||||
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
||||
uint32_t pre_select_item = PRESELECTED_SPECIAL;
|
||||
FavoriteApp* curr_favorite_app = primary_favorite ? &app->settings.favorite_primary :
|
||||
&app->settings.favorite_secondary;
|
||||
FavoriteApp* curr_favorite_app = NULL;
|
||||
bool is_dummy_app = false;
|
||||
bool default_passport = false;
|
||||
|
||||
for(size_t i = 0; i < APPS_COUNT; i++) {
|
||||
const char* name = favorite_fap_get_app_name(i);
|
||||
|
||||
submenu_add_item(submenu, name, i, desktop_settings_scene_favorite_submenu_callback, app);
|
||||
|
||||
// Select favorite item in submenu
|
||||
if(!strcmp(name, curr_favorite_app->name_or_path)) {
|
||||
pre_select_item = i;
|
||||
if((favorite_id & SCENE_STATE_SET_DUMMY_APP) == 0) {
|
||||
furi_assert(favorite_id < FavoriteAppNumber);
|
||||
curr_favorite_app = &app->settings.favorite_apps[favorite_id];
|
||||
if(favorite_id == FavoriteAppRightShort) {
|
||||
default_passport = true;
|
||||
}
|
||||
} else {
|
||||
favorite_id &= ~(SCENE_STATE_SET_DUMMY_APP);
|
||||
furi_assert(favorite_id < DummyAppNumber);
|
||||
curr_favorite_app = &app->settings.dummy_apps[favorite_id];
|
||||
is_dummy_app = true;
|
||||
default_passport = true;
|
||||
}
|
||||
|
||||
// Special case: Application browser
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
EXTERNAL_BROWSER_NAME,
|
||||
EXTERNAL_BROWSER_INDEX,
|
||||
default_passport ? (PASSPORT_NAME) : (EXTERNAL_BROWSER_NAME),
|
||||
DEFAULT_INDEX,
|
||||
desktop_settings_scene_favorite_submenu_callback,
|
||||
app);
|
||||
|
||||
@ -88,16 +94,29 @@ void desktop_settings_scene_favorite_on_enter(void* context) {
|
||||
desktop_settings_scene_favorite_submenu_callback,
|
||||
app);
|
||||
|
||||
if(!is_dummy_app) {
|
||||
for(size_t i = 0; i < APPS_COUNT; i++) {
|
||||
const char* name = favorite_fap_get_app_name(i);
|
||||
|
||||
submenu_add_item(
|
||||
submenu, name, i + 2, desktop_settings_scene_favorite_submenu_callback, app);
|
||||
|
||||
// Select favorite item in submenu
|
||||
if(!strcmp(name, curr_favorite_app->name_or_path)) {
|
||||
pre_select_item = i + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(pre_select_item == PRESELECTED_SPECIAL) {
|
||||
if(curr_favorite_app->name_or_path[0] == '\0') {
|
||||
pre_select_item = EXTERNAL_BROWSER_INDEX;
|
||||
pre_select_item = DEFAULT_INDEX;
|
||||
} else {
|
||||
pre_select_item = EXTERNAL_APPLICATION_INDEX;
|
||||
}
|
||||
}
|
||||
|
||||
submenu_set_header(
|
||||
submenu, primary_favorite ? "Primary favorite app:" : "Secondary favorite app:");
|
||||
submenu_set_header(submenu, is_dummy_app ? ("Dummy Mode app:") : ("Favorite app:"));
|
||||
submenu_set_selected_item(submenu, pre_select_item); // If set during loop, visual glitch.
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu);
|
||||
@ -108,13 +127,20 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e
|
||||
bool consumed = false;
|
||||
FuriString* temp_path = furi_string_alloc_set_str(EXT_PATH("apps"));
|
||||
|
||||
uint32_t primary_favorite =
|
||||
uint32_t favorite_id =
|
||||
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
||||
FavoriteApp* curr_favorite_app = primary_favorite ? &app->settings.favorite_primary :
|
||||
&app->settings.favorite_secondary;
|
||||
FavoriteApp* curr_favorite_app = NULL;
|
||||
if((favorite_id & SCENE_STATE_SET_DUMMY_APP) == 0) {
|
||||
furi_assert(favorite_id < FavoriteAppNumber);
|
||||
curr_favorite_app = &app->settings.favorite_apps[favorite_id];
|
||||
} else {
|
||||
favorite_id &= ~(SCENE_STATE_SET_DUMMY_APP);
|
||||
furi_assert(favorite_id < DummyAppNumber);
|
||||
curr_favorite_app = &app->settings.dummy_apps[favorite_id];
|
||||
}
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == EXTERNAL_BROWSER_INDEX) {
|
||||
if(event.event == DEFAULT_INDEX) {
|
||||
curr_favorite_app->name_or_path[0] = '\0';
|
||||
consumed = true;
|
||||
} else if(event.event == EXTERNAL_APPLICATION_INDEX) {
|
||||
@ -142,7 +168,8 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e
|
||||
consumed = true;
|
||||
}
|
||||
} else {
|
||||
const char* name = favorite_fap_get_app_name(event.event);
|
||||
size_t app_index = event.event - 2;
|
||||
const char* name = favorite_fap_get_app_name(app_index);
|
||||
if(name) strncpy(curr_favorite_app->name_or_path, name, MAX_APP_LENGTH);
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
@ -5,3 +5,6 @@
|
||||
|
||||
#define SCENE_STATE_PIN_ERROR_MISMATCH (0)
|
||||
#define SCENE_STATE_PIN_ERROR_WRONG (1)
|
||||
|
||||
#define SCENE_STATE_SET_FAVORITE_APP (0)
|
||||
#define SCENE_STATE_SET_DUMMY_APP (1 << 8)
|
||||
|
||||
@ -3,15 +3,24 @@
|
||||
|
||||
#include "../desktop_settings_app.h"
|
||||
#include "desktop_settings_scene.h"
|
||||
#include "desktop_settings_scene_i.h"
|
||||
|
||||
#define SCENE_EVENT_SELECT_FAVORITE_PRIMARY 0
|
||||
#define SCENE_EVENT_SELECT_FAVORITE_SECONDARY 1
|
||||
#define SCENE_EVENT_SELECT_PIN_SETUP 2
|
||||
#define SCENE_EVENT_SELECT_AUTO_LOCK_DELAY 3
|
||||
#define SCENE_EVENT_SELECT_CLOCK_DISPLAY 4
|
||||
typedef enum {
|
||||
DesktopSettingsPinSetup = 0,
|
||||
DesktopSettingsAutoLockDelay,
|
||||
DesktopSettingsClockDisplay,
|
||||
DesktopSettingsFavoriteLeftShort,
|
||||
DesktopSettingsFavoriteLeftLong,
|
||||
DesktopSettingsFavoriteRightShort,
|
||||
DesktopSettingsFavoriteRightLong,
|
||||
DesktopSettingsDummyLeft,
|
||||
DesktopSettingsDummyRight,
|
||||
DesktopSettingsDummyDown,
|
||||
DesktopSettingsDummyOk,
|
||||
} DesktopSettingsEntry;
|
||||
|
||||
#define AUTO_LOCK_DELAY_COUNT 6
|
||||
const char* const auto_lock_delay_text[AUTO_LOCK_DELAY_COUNT] = {
|
||||
static const char* const auto_lock_delay_text[AUTO_LOCK_DELAY_COUNT] = {
|
||||
"OFF",
|
||||
"30s",
|
||||
"60s",
|
||||
@ -19,8 +28,7 @@ const char* const auto_lock_delay_text[AUTO_LOCK_DELAY_COUNT] = {
|
||||
"5min",
|
||||
"10min",
|
||||
};
|
||||
|
||||
const uint32_t auto_lock_delay_value[AUTO_LOCK_DELAY_COUNT] =
|
||||
static const uint32_t auto_lock_delay_value[AUTO_LOCK_DELAY_COUNT] =
|
||||
{0, 30000, 60000, 120000, 300000, 600000};
|
||||
|
||||
#define CLOCK_ENABLE_COUNT 2
|
||||
@ -59,10 +67,6 @@ void desktop_settings_scene_start_on_enter(void* context) {
|
||||
VariableItem* item;
|
||||
uint8_t value_index;
|
||||
|
||||
variable_item_list_add(variable_item_list, "Primary Favorite App", 1, NULL, NULL);
|
||||
|
||||
variable_item_list_add(variable_item_list, "Secondary Favorite App", 1, NULL, NULL);
|
||||
|
||||
variable_item_list_add(variable_item_list, "PIN Setup", 1, NULL, NULL);
|
||||
|
||||
item = variable_item_list_add(
|
||||
@ -72,8 +76,6 @@ void desktop_settings_scene_start_on_enter(void* context) {
|
||||
desktop_settings_scene_start_auto_lock_delay_changed,
|
||||
app);
|
||||
|
||||
variable_item_list_set_enter_callback(
|
||||
variable_item_list, desktop_settings_scene_start_var_list_enter_callback, app);
|
||||
value_index = value_index_uint32(
|
||||
app->settings.auto_lock_delay_ms, auto_lock_delay_value, AUTO_LOCK_DELAY_COUNT);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
@ -91,6 +93,19 @@ void desktop_settings_scene_start_on_enter(void* context) {
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, clock_enable_text[value_index]);
|
||||
|
||||
variable_item_list_add(variable_item_list, "Favorite App - Left Short", 1, NULL, NULL);
|
||||
variable_item_list_add(variable_item_list, "Favorite App - Left Long", 1, NULL, NULL);
|
||||
variable_item_list_add(variable_item_list, "Favorite App - Right Short", 1, NULL, NULL);
|
||||
variable_item_list_add(variable_item_list, "Favorite App - Right Long", 1, NULL, NULL);
|
||||
|
||||
variable_item_list_add(variable_item_list, "Dummy Mode App - Left", 1, NULL, NULL);
|
||||
variable_item_list_add(variable_item_list, "Dummy Mode App - Right", 1, NULL, NULL);
|
||||
variable_item_list_add(variable_item_list, "Dummy Mode App - Down", 1, NULL, NULL);
|
||||
variable_item_list_add(variable_item_list, "Dummy Mode App - Ok", 1, NULL, NULL);
|
||||
|
||||
variable_item_list_set_enter_callback(
|
||||
variable_item_list, desktop_settings_scene_start_var_list_enter_callback, app);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewVarItemList);
|
||||
}
|
||||
|
||||
@ -100,25 +115,72 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
switch(event.event) {
|
||||
case SCENE_EVENT_SELECT_FAVORITE_PRIMARY:
|
||||
scene_manager_set_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite, 1);
|
||||
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
||||
consumed = true;
|
||||
break;
|
||||
case SCENE_EVENT_SELECT_FAVORITE_SECONDARY:
|
||||
scene_manager_set_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite, 0);
|
||||
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
||||
consumed = true;
|
||||
break;
|
||||
case SCENE_EVENT_SELECT_PIN_SETUP:
|
||||
case DesktopSettingsPinSetup:
|
||||
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinMenu);
|
||||
consumed = true;
|
||||
break;
|
||||
case SCENE_EVENT_SELECT_AUTO_LOCK_DELAY:
|
||||
case SCENE_EVENT_SELECT_CLOCK_DISPLAY:
|
||||
consumed = true;
|
||||
|
||||
case DesktopSettingsFavoriteLeftShort:
|
||||
scene_manager_set_scene_state(
|
||||
app->scene_manager,
|
||||
DesktopSettingsAppSceneFavorite,
|
||||
SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftShort);
|
||||
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
||||
break;
|
||||
case DesktopSettingsFavoriteLeftLong:
|
||||
scene_manager_set_scene_state(
|
||||
app->scene_manager,
|
||||
DesktopSettingsAppSceneFavorite,
|
||||
SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftLong);
|
||||
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
||||
break;
|
||||
case DesktopSettingsFavoriteRightShort:
|
||||
scene_manager_set_scene_state(
|
||||
app->scene_manager,
|
||||
DesktopSettingsAppSceneFavorite,
|
||||
SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightShort);
|
||||
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
||||
break;
|
||||
case DesktopSettingsFavoriteRightLong:
|
||||
scene_manager_set_scene_state(
|
||||
app->scene_manager,
|
||||
DesktopSettingsAppSceneFavorite,
|
||||
SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightLong);
|
||||
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
||||
break;
|
||||
|
||||
case DesktopSettingsDummyLeft:
|
||||
scene_manager_set_scene_state(
|
||||
app->scene_manager,
|
||||
DesktopSettingsAppSceneFavorite,
|
||||
SCENE_STATE_SET_DUMMY_APP | DummyAppLeft);
|
||||
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
||||
break;
|
||||
case DesktopSettingsDummyRight:
|
||||
scene_manager_set_scene_state(
|
||||
app->scene_manager,
|
||||
DesktopSettingsAppSceneFavorite,
|
||||
SCENE_STATE_SET_DUMMY_APP | DummyAppRight);
|
||||
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
||||
break;
|
||||
case DesktopSettingsDummyDown:
|
||||
scene_manager_set_scene_state(
|
||||
app->scene_manager,
|
||||
DesktopSettingsAppSceneFavorite,
|
||||
SCENE_STATE_SET_DUMMY_APP | DummyAppDown);
|
||||
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
||||
break;
|
||||
case DesktopSettingsDummyOk:
|
||||
scene_manager_set_scene_state(
|
||||
app->scene_manager,
|
||||
DesktopSettingsAppSceneFavorite,
|
||||
SCENE_STATE_SET_DUMMY_APP | DummyAppOk);
|
||||
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ bool updater_scene_main_on_event(void* context, SceneManagerEvent event) {
|
||||
break;
|
||||
|
||||
case UpdaterCustomEventSdUnmounted:
|
||||
// TODO: error out, stop worker (it's probably dead actually)
|
||||
// TODO FL-3499: error out, stop worker (it's probably dead actually)
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
BIN
assets/icons/Infrared/back_btn_10x8.png
Normal file
|
After Width: | Height: | Size: 141 B |
BIN
assets/icons/Infrared/celsius_24x23.png
Normal file
|
After Width: | Height: | Size: 257 B |
BIN
assets/icons/Infrared/celsius_hover_24x23.png
Normal file
|
After Width: | Height: | Size: 204 B |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 2.9 KiB |