From 1afa3f7ef3ca955ee435c8436de888d4296dda26 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 15 May 2024 18:22:54 +0300 Subject: [PATCH] Merge remote-tracking branch 'OFW/dev' into dev --- .vscode/example/clangd/extensions.json | 5 +- .../example/cpptools/c_cpp_properties.json | 6 +- .../infrared/resources/infrared/assets/ac.ir | 32 ++ applications/services/gui/elements.h | 24 +- applications/services/gui/modules/submenu.c | 39 +++ applications/services/gui/modules/submenu.h | 20 +- applications/settings/about/about.c | 10 +- .../bt_settings_scene_forget_dev_confirm.c | 6 +- .../scenes/bt_settings_scene_start.c | 2 +- .../desktop_settings/desktop_settings_app.c | 4 + .../desktop_settings/desktop_settings_app.h | 1 + .../scenes/desktop_settings_scene_pin_menu.c | 7 +- .../scenes/desktop_settings_scene_pin_setup.c | 1 + .../desktop_settings_scene_pin_setup_done.c | 1 + .../desktop_settings_view_pin_setup_howto2.c | 4 +- .../power_settings_app/power_settings_app.h | 2 + .../scenes/power_settings_scene_config.h | 1 + .../scenes/power_settings_scene_power_off.c | 10 +- .../scenes/power_settings_scene_reboot.c | 12 +- .../power_settings_scene_reboot_confirm.c | 66 +++++ .../scenes/storage_settings_scene_benchmark.c | 28 +- ...storage_settings_scene_benchmark_confirm.c | 70 +++++ .../scenes/storage_settings_scene_config.h | 1 + .../storage_settings_scene_factory_reset.c | 8 +- .../storage_settings_scene_format_confirm.c | 4 +- .../storage_settings_scene_formatting.c | 15 +- .../storage_settings_scene_internal_info.c | 2 +- .../scenes/storage_settings_scene_sd_info.c | 30 +- .../scenes/storage_settings_scene_start.c | 2 +- .../external/L1_Akira_128x64/frame_0.png | Bin 0 -> 1832 bytes .../external/L1_Akira_128x64/frame_1.png | Bin 0 -> 1888 bytes .../external/L1_Akira_128x64/frame_10.png | Bin 0 -> 1733 bytes .../external/L1_Akira_128x64/frame_11.png | Bin 0 -> 1601 bytes .../external/L1_Akira_128x64/frame_12.png | Bin 0 -> 1385 bytes .../external/L1_Akira_128x64/frame_13.png | Bin 0 -> 1815 bytes .../external/L1_Akira_128x64/frame_14.png | Bin 0 -> 1701 bytes .../external/L1_Akira_128x64/frame_15.png | Bin 0 -> 1409 bytes .../external/L1_Akira_128x64/frame_16.png | Bin 0 -> 1369 bytes .../external/L1_Akira_128x64/frame_17.png | Bin 0 -> 1398 bytes .../external/L1_Akira_128x64/frame_18.png | Bin 0 -> 1360 bytes .../external/L1_Akira_128x64/frame_19.png | Bin 0 -> 1330 bytes .../external/L1_Akira_128x64/frame_2.png | Bin 0 -> 1836 bytes .../external/L1_Akira_128x64/frame_20.png | Bin 0 -> 1077 bytes .../external/L1_Akira_128x64/frame_21.png | Bin 0 -> 1276 bytes .../external/L1_Akira_128x64/frame_22.png | Bin 0 -> 1688 bytes .../external/L1_Akira_128x64/frame_23.png | Bin 0 -> 1984 bytes .../external/L1_Akira_128x64/frame_24.png | Bin 0 -> 2015 bytes .../external/L1_Akira_128x64/frame_25.png | Bin 0 -> 1812 bytes .../external/L1_Akira_128x64/frame_26.png | Bin 0 -> 1395 bytes .../external/L1_Akira_128x64/frame_27.png | Bin 0 -> 1426 bytes .../external/L1_Akira_128x64/frame_28.png | Bin 0 -> 2053 bytes .../external/L1_Akira_128x64/frame_29.png | Bin 0 -> 1891 bytes .../external/L1_Akira_128x64/frame_3.png | Bin 0 -> 1908 bytes .../external/L1_Akira_128x64/frame_30.png | Bin 0 -> 1951 bytes .../external/L1_Akira_128x64/frame_31.png | Bin 0 -> 1911 bytes .../external/L1_Akira_128x64/frame_32.png | Bin 0 -> 1726 bytes .../external/L1_Akira_128x64/frame_33.png | Bin 0 -> 1923 bytes .../external/L1_Akira_128x64/frame_34.png | Bin 0 -> 1126 bytes .../external/L1_Akira_128x64/frame_35.png | Bin 0 -> 1555 bytes .../external/L1_Akira_128x64/frame_4.png | Bin 0 -> 1989 bytes .../external/L1_Akira_128x64/frame_5.png | Bin 0 -> 2000 bytes .../external/L1_Akira_128x64/frame_6.png | Bin 0 -> 1869 bytes .../external/L1_Akira_128x64/frame_7.png | Bin 0 -> 1868 bytes .../external/L1_Akira_128x64/frame_8.png | Bin 0 -> 1891 bytes .../external/L1_Akira_128x64/frame_9.png | Bin 0 -> 1817 bytes .../dolphin/external/L1_Akira_128x64/meta.txt | 14 + assets/dolphin/external/manifest.txt | 7 + assets/icons/Dolphin/DolphinWait_61x59.png | Bin 2023 -> 0 bytes ...h_55x52.png => LoadingHourglass_24x24.png} | Bin 3898 -> 3650 bytes assets/icons/Settings/dolph_cry_49x54.png | Bin 0 -> 973 bytes assets/icons/Settings/qr_benchmark_25x25.png | Bin 0 -> 395 bytes lib/ble_profile/extra_profiles/hid_profile.c | 2 +- lib/flipper_application/elf/elf_file.c | 5 + .../mf_desfire/mf_desfire_poller_i.c | 2 +- scripts/map_analyse_upload.py | 86 ++++++ scripts/map_mariadb_insert.py | 139 --------- scripts/map_parser.py | 274 ------------------ targets/f18/api_symbols.csv | 4 +- targets/f18/furi_hal/furi_hal.c | 2 +- targets/f7/api_symbols.csv | 4 +- targets/f7/ble_glue/gap.c | 58 +++- targets/f7/ble_glue/profiles/serial_profile.c | 2 +- targets/f7/furi_hal/furi_hal.c | 2 +- 83 files changed, 531 insertions(+), 483 deletions(-) create mode 100644 applications/settings/power_settings_app/scenes/power_settings_scene_reboot_confirm.c create mode 100644 applications/settings/storage_settings/scenes/storage_settings_scene_benchmark_confirm.c create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_0.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_1.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_10.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_11.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_12.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_13.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_14.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_15.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_16.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_17.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_18.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_19.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_2.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_20.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_21.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_22.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_23.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_24.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_25.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_26.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_27.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_28.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_29.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_3.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_30.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_31.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_32.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_33.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_34.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_35.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_4.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_5.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_6.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_7.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_8.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/frame_9.png create mode 100755 assets/dolphin/external/L1_Akira_128x64/meta.txt delete mode 100644 assets/icons/Dolphin/DolphinWait_61x59.png rename assets/icons/Settings/{Cry_dolph_55x52.png => LoadingHourglass_24x24.png} (70%) create mode 100644 assets/icons/Settings/dolph_cry_49x54.png create mode 100644 assets/icons/Settings/qr_benchmark_25x25.png create mode 100755 scripts/map_analyse_upload.py delete mode 100755 scripts/map_mariadb_insert.py delete mode 100755 scripts/map_parser.py diff --git a/.vscode/example/clangd/extensions.json b/.vscode/example/clangd/extensions.json index daab417cd..4f24dd7b5 100644 --- a/.vscode/example/clangd/extensions.json +++ b/.vscode/example/clangd/extensions.json @@ -8,7 +8,8 @@ "amiralizadeh9480.cpp-helper", "marus25.cortex-debug", "zxh404.vscode-proto3", - "augustocdias.tasks-shell-input" + "augustocdias.tasks-shell-input", + "rioj7.command-variable" ], // List of extensions recommended by VS Code that should not be recommended for users of this workspace. "unwantedRecommendations": [ @@ -16,4 +17,4 @@ "ms-vscode.cpptools", "ms-vscode.cmake-tools" ] -} +} \ No newline at end of file diff --git a/.vscode/example/cpptools/c_cpp_properties.json b/.vscode/example/cpptools/c_cpp_properties.json index 3f8d15a5d..245d44ca1 100644 --- a/.vscode/example/cpptools/c_cpp_properties.json +++ b/.vscode/example/cpptools/c_cpp_properties.json @@ -2,7 +2,7 @@ "configurations": [ { "name": "Win32", - "compilerPath": "${workspaceFolder}/toolchain/x86_64-windows/bin/arm-none-eabi-gcc.exe", + "compilerPath": "${workspaceFolder}/toolchain/current/bin/arm-none-eabi-gcc.exe", "intelliSenseMode": "gcc-arm", "compileCommands": "${workspaceFolder}/build/latest/compile_commands.json", "cStandard": "gnu23", @@ -10,7 +10,7 @@ }, { "name": "Linux", - "compilerPath": "${workspaceFolder}/toolchain/x86_64-linux/bin/arm-none-eabi-gcc", + "compilerPath": "${workspaceFolder}/toolchain/current/bin/arm-none-eabi-gcc", "intelliSenseMode": "gcc-arm", "compileCommands": "${workspaceFolder}/build/latest/compile_commands.json", "cStandard": "gnu23", @@ -18,7 +18,7 @@ }, { "name": "Mac", - "compilerPath": "${workspaceFolder}/toolchain/x86_64-darwin/bin/arm-none-eabi-gcc", + "compilerPath": "${workspaceFolder}/toolchain/current/bin/arm-none-eabi-gcc", "intelliSenseMode": "gcc-arm", "compileCommands": "${workspaceFolder}/build/latest/compile_commands.json", "cStandard": "gnu23", diff --git a/applications/main/infrared/resources/infrared/assets/ac.ir b/applications/main/infrared/resources/infrared/assets/ac.ir index 38db599b2..a007b12b7 100644 --- a/applications/main/infrared/resources/infrared/assets/ac.ir +++ b/applications/main/infrared/resources/infrared/assets/ac.ir @@ -1021,3 +1021,35 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 3302 1640 404 423 407 420 410 1212 437 390 440 1234 405 395 435 392 438 415 415 1207 432 1242 407 420 410 391 439 414 405 1243 406 1241 408 392 438 415 415 386 433 393 437 390 440 414 405 396 434 419 411 389 441 412 407 420 410 390 440 387 432 1242 407 393 437 390 440 414 405 395 435 392 438 389 430 396 434 1240 409 417 413 414 405 395 435 419 411 1237 412 389 430 396 434 393 437 416 414 387 432 394 436 1212 437 389 441 1234 405 1217 432 1241 408 1213 436 1212 437 1210 439 +# +# Model: Toshiba RAS-2518D +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4349 4437 549 1615 551 1614 551 1614 551 1617 549 531 550 530 551 1615 550 531 550 532 549 530 551 531 550 530 551 1615 550 1614 551 531 550 1615 551 529 552 531 550 530 551 533 548 530 551 530 551 1616 549 1615 550 1616 550 1615 550 1614 551 1616 550 1615 551 1615 550 531 550 531 550 530 551 529 552 530 551 530 551 529 552 530 551 531 550 1615 550 532 549 1615 550 1616 550 531 550 531 550 530 551 530 551 529 552 532 549 530 551 530 551 531 550 529 552 531 550 1615 551 530 551 530 551 530 551 531 550 530 551 531 550 530 551 531 550 531 550 531 550 1616 550 1618 547 532 549 529 552 530 551 1615 551 1615 550 5379 4350 4436 550 1616 549 1615 551 1614 552 1615 550 529 552 530 551 1614 552 530 551 529 552 531 550 531 550 531 550 1614 552 1614 551 530 551 1615 550 530 551 530 551 530 551 530 551 531 550 532 549 1616 549 1615 551 1614 552 1615 550 1614 551 1616 550 1614 552 1615 550 529 552 530 551 530 551 530 551 531 550 531 550 530 551 530 551 531 550 1615 550 530 551 1615 550 1615 551 530 551 530 551 530 551 530 551 530 551 530 551 529 552 530 551 531 550 532 549 530 551 1615 551 531 550 530 551 530 551 530 551 530 551 531 550 531 550 531 550 530 551 531 550 1615 551 1615 551 532 549 531 550 531 550 1616 549 1614 552 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4350 4438 549 1615 551 1614 552 1616 549 1616 550 530 551 531 550 1615 551 530 551 529 552 531 550 530 551 531 550 1614 551 1616 550 531 550 1616 549 530 551 531 550 530 551 529 552 530 551 531 550 1616 549 1616 550 1616 549 1616 550 1615 551 1614 551 1614 552 1615 551 530 551 531 550 530 551 531 550 531 550 529 552 532 549 531 550 530 551 1613 552 530 551 531 550 529 552 532 549 530 551 530 551 531 550 531 550 530 551 530 551 530 551 531 550 530 551 531 550 531 550 1615 551 529 552 530 551 530 551 530 551 530 551 530 551 530 551 530 551 532 549 531 550 531 550 532 549 531 550 531 550 530 551 530 551 5132 4351 4435 552 1616 550 1615 550 1615 551 1613 553 531 550 530 551 1615 550 530 551 531 550 531 550 530 551 532 549 1616 550 1616 549 530 551 1615 551 530 551 531 550 530 551 530 551 530 551 531 550 1615 551 1615 551 1614 551 1615 550 1615 551 1615 550 1615 550 1616 550 530 551 530 551 531 550 532 549 530 551 530 551 531 550 531 550 531 550 1615 550 530 551 530 551 530 551 529 552 531 550 530 551 531 550 531 550 530 551 530 551 531 550 530 551 530 551 530 551 531 550 1616 550 530 551 529 552 530 551 531 550 532 549 530 551 530 551 529 552 531 550 529 552 530 551 530 551 531 550 531 550 529 552 531 550 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4350 4436 550 1617 549 1615 550 1615 550 1617 548 530 551 531 550 1615 551 531 550 531 550 530 551 530 551 531 550 1614 552 1615 550 530 551 1614 551 531 550 531 550 531 550 529 552 532 549 530 551 1617 549 1616 549 1615 551 1619 547 1615 550 1615 550 1616 549 1616 550 530 551 531 550 530 551 530 551 531 550 530 551 529 552 529 552 530 551 1617 548 533 548 1615 551 1613 552 530 551 531 550 531 550 530 551 530 551 532 549 531 550 531 550 530 551 531 550 531 550 531 550 1615 551 531 550 531 550 532 549 531 550 530 551 531 550 533 548 531 550 530 551 1617 548 1616 549 530 551 531 550 532 549 532 549 532 549 5200 4349 4436 550 1615 551 1615 551 1615 550 1616 550 531 550 530 551 1615 551 531 550 530 551 530 551 530 551 530 551 1616 549 1615 551 530 551 1615 551 531 550 531 550 530 551 531 550 531 550 531 550 1615 551 1616 550 1616 550 1615 550 1617 548 1616 549 1616 550 1615 550 531 550 530 551 531 550 531 550 532 549 530 551 531 550 531 550 532 549 1616 550 531 550 1616 550 1615 550 531 550 530 551 531 550 531 550 531 550 531 550 531 550 532 549 532 549 531 550 532 549 531 550 1616 550 531 550 530 551 532 549 532 549 530 551 532 549 531 550 532 549 531 550 1616 549 1617 549 531 550 530 551 531 550 532 549 532 549 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4350 4437 547 1618 548 1620 546 1620 546 1619 547 534 547 535 546 1619 547 534 547 536 545 536 545 535 546 535 546 1619 547 1620 545 534 523 1644 546 535 522 559 546 535 546 534 547 535 546 535 545 1620 546 1620 546 1620 546 1619 547 1619 546 1619 547 1620 545 1620 546 535 546 534 547 537 520 558 523 558 547 534 547 536 521 559 522 559 522 1644 546 535 546 535 522 560 545 536 521 559 522 559 522 558 523 559 522 560 521 559 522 559 522 560 521 559 522 561 520 1644 521 1645 520 559 522 559 522 559 522 559 522 559 522 560 521 560 521 560 521 561 520 559 522 560 521 559 522 559 522 559 522 1644 522 559 522 5341 4349 4439 520 1645 521 1645 521 1646 519 1645 521 560 521 561 520 1645 521 560 521 560 521 559 522 560 521 561 520 1646 520 1645 521 561 520 1645 521 561 520 560 521 560 521 560 521 560 521 561 520 1644 522 1644 522 1645 520 1645 521 1645 521 1645 520 1646 520 1644 522 561 520 560 521 560 521 561 520 560 521 561 520 561 520 561 520 560 521 1646 520 562 519 561 520 561 520 562 519 560 521 560 521 561 520 561 520 560 521 560 521 561 520 560 521 560 521 562 519 1646 520 1645 521 561 520 561 520 561 520 560 521 560 521 561 520 560 521 559 522 560 521 561 520 561 520 560 521 562 519 559 522 1645 521 561 520 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4348 4439 520 1646 520 1646 520 1646 519 1646 520 561 520 561 520 1646 519 561 520 561 520 562 519 562 519 561 520 1646 520 1647 518 563 518 1646 519 562 519 561 520 561 520 562 519 562 519 561 520 1648 517 1647 519 1646 519 1647 519 1646 520 1646 520 1645 520 1647 519 561 520 561 520 562 519 562 519 562 519 562 519 561 520 562 519 561 520 1646 520 562 519 1647 518 1646 520 562 519 560 521 561 520 561 520 561 520 562 519 562 519 560 521 562 519 562 519 560 521 1646 520 1646 520 561 520 562 519 561 520 562 519 561 520 561 520 561 520 561 520 561 520 1647 518 1646 520 562 519 562 519 561 520 1646 520 561 520 5409 4348 4440 519 1645 521 1646 519 1645 521 1645 521 561 520 561 520 1644 522 561 520 561 520 561 520 560 521 562 519 1646 520 1646 520 562 519 1644 522 561 520 561 520 561 520 561 520 561 520 561 520 1646 520 1645 520 1646 520 1645 521 1646 520 1646 520 1644 522 1645 521 560 521 560 521 561 520 561 520 560 521 560 521 561 520 561 520 561 520 1645 521 562 519 1645 521 1645 520 561 520 562 519 561 520 561 520 561 520 560 521 560 521 560 521 560 521 561 520 560 521 1646 520 1646 520 561 520 560 521 559 522 560 521 561 520 561 520 560 521 560 521 560 521 1646 520 1645 520 561 520 560 521 560 521 1645 521 561 520 diff --git a/applications/services/gui/elements.h b/applications/services/gui/elements.h index 8cc912362..8270a9319 100644 --- a/applications/services/gui/elements.h +++ b/applications/services/gui/elements.h @@ -121,7 +121,8 @@ void elements_multiline_text_aligned( /** Draw multiline text * * @param canvas Canvas instance - * @param x, y top left corner coordinates + * @param x top left corner coordinates + * @param y top left corner coordinates * @param text string (possible multiline) */ void elements_multiline_text(Canvas* canvas, int32_t x, int32_t y, const char* text); @@ -129,7 +130,8 @@ void elements_multiline_text(Canvas* canvas, int32_t x, int32_t y, const char* t /** Draw framed multiline text * * @param canvas Canvas instance - * @param x, y top left corner coordinates + * @param x top left corner coordinates + * @param y top left corner coordinates * @param text string (possible multiline) */ void elements_multiline_text_framed(Canvas* canvas, int32_t x, int32_t y, const char* text); @@ -137,8 +139,10 @@ void elements_multiline_text_framed(Canvas* canvas, int32_t x, int32_t y, const /** Draw slightly rounded frame * * @param canvas Canvas instance - * @param x, y top left corner coordinates - * @param width, height size of frame + * @param x top left corner coordinates + * @param y top left corner coordinates + * @param width width of frame + * @param height height of frame */ void elements_slightly_rounded_frame( Canvas* canvas, @@ -150,8 +154,10 @@ void elements_slightly_rounded_frame( /** Draw slightly rounded box * * @param canvas Canvas instance - * @param x, y top left corner coordinates - * @param width, height size of box + * @param x top left corner coordinates + * @param y top left corner coordinates + * @param width height of box + * @param height height of box */ void elements_slightly_rounded_box( Canvas* canvas, @@ -163,8 +169,10 @@ void elements_slightly_rounded_box( /** Draw bold rounded frame * * @param canvas Canvas instance - * @param x, y top left corner coordinates - * @param width, height size of frame + * @param x top left corner coordinates + * @param y top left corner coordinates + * @param width width of frame + * @param height height of frame */ void elements_bold_rounded_frame(Canvas* canvas, int32_t x, int32_t y, size_t width, size_t height); diff --git a/applications/services/gui/modules/submenu.c b/applications/services/gui/modules/submenu.c index a4250c2e1..74f93320f 100644 --- a/applications/services/gui/modules/submenu.c +++ b/applications/services/gui/modules/submenu.c @@ -318,6 +318,26 @@ void submenu_add_lockable_item( true); } +void submenu_change_item_label(Submenu* submenu, uint32_t index, const char* label) { + furi_check(submenu); + furi_check(label); + + with_view_model( + submenu->view, + SubmenuModel * model, + { + SubmenuItemArray_it_t it; + for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it); + SubmenuItemArray_next(it)) { + if(index == SubmenuItemArray_cref(it)->index) { + furi_string_set_str(SubmenuItemArray_cref(it)->label, label); + break; + } + } + }, + true); +} + void submenu_reset(Submenu* submenu) { furi_check(submenu); view_set_orientation(submenu->view, ViewOrientationHorizontal); @@ -335,6 +355,25 @@ void submenu_reset(Submenu* submenu) { true); } +uint32_t submenu_get_selected_item(Submenu* submenu) { + furi_check(submenu); + + uint32_t selected_item_index = 0; + + with_view_model( + submenu->view, + SubmenuModel * model, + { + if(model->position < SubmenuItemArray_size(model->items)) { + const SubmenuItem* item = SubmenuItemArray_cget(model->items, model->position); + selected_item_index = item->index; + } + }, + false); + + return selected_item_index; +} + void submenu_set_selected_item(Submenu* submenu, uint32_t index) { furi_check(submenu); with_view_model( diff --git a/applications/services/gui/modules/submenu.h b/applications/services/gui/modules/submenu.h index 676e99d29..d77f570c2 100644 --- a/applications/services/gui/modules/submenu.h +++ b/applications/services/gui/modules/submenu.h @@ -73,16 +73,32 @@ void submenu_add_lockable_item( bool locked, const char* locked_message); +/** Change label of an existing item + * + * @param submenu Submenu instance + * @param index The index of the item + * @param label The new label + */ +void submenu_change_item_label(Submenu* submenu, uint32_t index, const char* label); + /** Remove all items from submenu * * @param submenu Submenu instance */ void submenu_reset(Submenu* submenu); -/** Set submenu item selector +/** Get submenu selected item index * * @param submenu Submenu instance - * @param index The index + * + * @return Index of the selected item + */ +uint32_t submenu_get_selected_item(Submenu* submenu); + +/** Set submenu selected item by index + * + * @param submenu Submenu instance + * @param index The index of the selected item */ void submenu_set_selected_item(Submenu* submenu, uint32_t index); diff --git a/applications/settings/about/about.c b/applications/settings/about/about.c index 6b7c9f976..1669e335a 100644 --- a/applications/settings/about/about.c +++ b/applications/settings/about/about.c @@ -43,7 +43,7 @@ static DialogMessageButton product_screen(DialogsApp* dialogs, DialogMessage* me static DialogMessageButton address_screen(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; - const char* screen_text = "Flipper Devices Inc\n" + const char* screen_text = "Flipper Devices Inc.\n" "Suite B #551, 2803\n" "Philadelphia Pike, Claymont\n" "DE, USA 19703\n"; @@ -59,7 +59,7 @@ static DialogMessageButton compliance_screen(DialogsApp* dialogs, DialogMessage* DialogMessageButton result; const char* screen_text = "For all compliance\n" - "certificates please visit:\n" + "certificates, please visit:\n" "www.flipp.dev/compliance"; dialog_message_set_text(message, screen_text, 0, 0, AlignLeft, AlignTop); @@ -226,9 +226,11 @@ int32_t about_settings_app(void* p) { while(1) { if(screen_index >= COUNT_OF(about_screens) - 1) { - dialog_message_set_buttons(message, "Back", NULL, NULL); + dialog_message_set_buttons(message, "Prev.", NULL, NULL); + } else if(screen_index == 0) { + dialog_message_set_buttons(message, NULL, NULL, "Next"); } else { - dialog_message_set_buttons(message, "Back", NULL, "Next"); + dialog_message_set_buttons(message, "Prev.", NULL, "Next"); } screen_result = about_screens[screen_index](dialogs, message); diff --git a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c index 150c3ef9f..d341595f8 100644 --- a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c @@ -11,10 +11,10 @@ void bt_settings_scene_forget_dev_confirm_dialog_callback(DialogExResult result, void bt_settings_scene_forget_dev_confirm_on_enter(void* context) { BtSettingsApp* app = context; DialogEx* dialog = app->dialog; - dialog_ex_set_header(dialog, "Unpair All Devices?", 64, 3, AlignCenter, AlignTop); + dialog_ex_set_header(dialog, "Unpair All Devices?", 64, 0, AlignCenter, AlignTop); dialog_ex_set_text( - dialog, "All previous pairings\nwill be lost!", 64, 22, AlignCenter, AlignTop); - dialog_ex_set_left_button_text(dialog, "Back"); + dialog, "All previous pairings\nwill be lost!", 64, 14, AlignCenter, AlignTop); + dialog_ex_set_left_button_text(dialog, "Cancel"); dialog_ex_set_right_button_text(dialog, "Unpair"); dialog_ex_set_context(dialog, app); dialog_ex_set_result_callback(dialog, bt_settings_scene_forget_dev_confirm_dialog_callback); diff --git a/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c index c148f0943..1d72a9e6f 100644 --- a/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c @@ -53,7 +53,7 @@ void bt_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_index(item, BtSettingOff); variable_item_set_current_value_text(item, bt_settings_text[BtSettingOff]); } - variable_item_list_add(var_item_list, "Forget All Paired Devices", 1, NULL, NULL); + variable_item_list_add(var_item_list, "Unpair All Devices", 1, NULL, NULL); variable_item_list_set_enter_callback( var_item_list, bt_settings_scene_start_var_list_enter_callback, app); } else { diff --git a/applications/settings/desktop_settings/desktop_settings_app.c b/applications/settings/desktop_settings/desktop_settings_app.c index 15936bcb8..35ee2a3f1 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.c +++ b/applications/settings/desktop_settings/desktop_settings_app.c @@ -133,6 +133,7 @@ void desktop_settings_app_free(DesktopSettingsApp* app) { extern int32_t desktop_settings_app(void* p) { DesktopSettingsApp* app = desktop_settings_app_alloc(); DESKTOP_SETTINGS_LOAD(&app->settings); + if(p && (strcmp(p, DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG) == 0)) { scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto); } else { @@ -140,6 +141,9 @@ extern int32_t desktop_settings_app(void* p) { } view_dispatcher_run(app->view_dispatcher); + + DESKTOP_SETTINGS_SAVE(&app->settings); desktop_settings_app_free(app); + return 0; } diff --git a/applications/settings/desktop_settings/desktop_settings_app.h b/applications/settings/desktop_settings/desktop_settings_app.h index 76f23e22f..48adb9cfb 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.h +++ b/applications/settings/desktop_settings/desktop_settings_app.h @@ -47,4 +47,5 @@ typedef struct { char device_name[FURI_HAL_VERSION_ARRAY_NAME_LENGTH]; uint8_t menu_idx; + uint32_t pin_menu_idx; } DesktopSettingsApp; diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c index 86e756ede..9fdd68896 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c @@ -44,7 +44,7 @@ void desktop_settings_scene_pin_menu_on_enter(void* context) { } submenu_set_header(app->submenu, "PIN Code Settings"); - submenu_set_selected_item(app->submenu, app->menu_idx); + submenu_set_selected_item(app->submenu, app->pin_menu_idx); view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); } @@ -76,11 +76,16 @@ bool desktop_settings_scene_pin_menu_on_event(void* context, SceneManagerEvent e consumed = true; break; } + } else if(event.type == SceneManagerEventTypeBack) { + submenu_set_selected_item(app->submenu, 0); } + return consumed; } void desktop_settings_scene_pin_menu_on_exit(void* context) { DesktopSettingsApp* app = context; + + app->pin_menu_idx = submenu_get_selected_item(app->submenu); submenu_reset(app->submenu); } diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup.c index 1603aa337..5b8aa8638 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup.c @@ -97,6 +97,7 @@ bool desktop_settings_scene_pin_setup_on_event(void* context, SceneManagerEvent break; } } + return consumed; } diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c index b829f995b..4cef4ba98 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c @@ -27,6 +27,7 @@ void desktop_settings_scene_pin_setup_done_on_enter(void* context) { DESKTOP_SETTINGS_SAVE(&app->settings); NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); notification_message(notification, &sequence_single_vibro); + notification_message(notification, &sequence_blink_green_10); furi_record_close(RECORD_NOTIFICATION); desktop_view_pin_input_set_context(app->pin_input_view, app); diff --git a/applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c b/applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c index b09b0b95f..6148747a2 100644 --- a/applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c +++ b/applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c @@ -24,9 +24,9 @@ static void desktop_settings_view_pin_setup_howto2_draw(Canvas* canvas, void* mo elements_multiline_text_aligned( canvas, 64, - 24, - AlignCenter, + 0, AlignCenter, + AlignTop, "Forgotten PIN can only be\n" "reset with entire device.\n" "Read docs How to reset PIN."); diff --git a/applications/settings/power_settings_app/power_settings_app.h b/applications/settings/power_settings_app/power_settings_app.h index eb9ffcf67..6ee1a0a08 100644 --- a/applications/settings/power_settings_app/power_settings_app.h +++ b/applications/settings/power_settings_app/power_settings_app.h @@ -30,3 +30,5 @@ typedef enum { PowerSettingsAppViewSubmenu, PowerSettingsAppViewDialog, } PowerSettingsAppView; + +typedef enum { RebootTypeDFU, RebootTypeNormal } RebootType; diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_config.h b/applications/settings/power_settings_app/scenes/power_settings_scene_config.h index cc8656dcf..f57071b9b 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_config.h +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_config.h @@ -1,4 +1,5 @@ ADD_SCENE(power_settings, start, Start) ADD_SCENE(power_settings, battery_info, BatteryInfo) ADD_SCENE(power_settings, reboot, Reboot) +ADD_SCENE(power_settings, reboot_confirm, RebootConfirm) ADD_SCENE(power_settings, power_off, PowerOff) diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c index c3f5d5ad8..6cd9c5c67 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c @@ -10,12 +10,12 @@ void power_settings_scene_power_off_on_enter(void* context) { PowerSettingsApp* app = context; DialogEx* dialog = app->dialog; - dialog_ex_set_header(dialog, "Turn OFF Device?", 64, 2, AlignCenter, AlignTop); + dialog_ex_set_header(dialog, "Turn Off Device?", 64, 0, AlignCenter, AlignTop); dialog_ex_set_text( - dialog, " I will be\nwaiting for\n you here...", 78, 16, AlignLeft, AlignTop); - dialog_ex_set_icon(dialog, 21, 13, &I_Cry_dolph_55x52); - dialog_ex_set_left_button_text(dialog, "Back"); - dialog_ex_set_right_button_text(dialog, "OFF"); + dialog, " I will be\nwaiting for\n you here...", 78, 14, AlignLeft, AlignTop); + dialog_ex_set_icon(dialog, 14, 10, &I_dolph_cry_49x54); + dialog_ex_set_left_button_text(dialog, "Cancel"); + dialog_ex_set_right_button_text(dialog, "Power Off"); dialog_ex_set_result_callback(dialog, power_settings_scene_power_off_dialog_callback); dialog_ex_set_context(dialog, app); diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_reboot.c b/applications/settings/power_settings_app/scenes/power_settings_scene_reboot.c index 2d5dedfd4..187d969ed 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_reboot.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_reboot.c @@ -24,7 +24,7 @@ void power_settings_scene_reboot_on_enter(void* context) { app); submenu_add_item( submenu, - "Flipper OS", + "Reboot Flipper", PowerSettingsRebootSubmenuIndexOs, power_settings_scene_reboot_submenu_callback, app); @@ -33,14 +33,18 @@ void power_settings_scene_reboot_on_enter(void* context) { } bool power_settings_scene_reboot_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); + PowerSettingsApp* app = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == PowerSettingsRebootSubmenuIndexDfu) { - power_reboot(PowerBootModeDfu); + scene_manager_set_scene_state( + app->scene_manager, PowerSettingsAppSceneRebootConfirm, RebootTypeDFU); + scene_manager_next_scene(app->scene_manager, PowerSettingsAppSceneRebootConfirm); } else if(event.event == PowerSettingsRebootSubmenuIndexOs) { - power_reboot(PowerBootModeNormal); + scene_manager_set_scene_state( + app->scene_manager, PowerSettingsAppSceneRebootConfirm, RebootTypeNormal); + scene_manager_next_scene(app->scene_manager, PowerSettingsAppSceneRebootConfirm); } consumed = true; } diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_reboot_confirm.c b/applications/settings/power_settings_app/scenes/power_settings_scene_reboot_confirm.c new file mode 100644 index 000000000..62e06de92 --- /dev/null +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_reboot_confirm.c @@ -0,0 +1,66 @@ +#include "../power_settings_app.h" + +void power_settings_scene_reboot_confirm_dialog_callback(DialogExResult result, void* context) { + furi_assert(context); + PowerSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, result); +} + +void power_settings_scene_reboot_confirm_on_enter(void* context) { + PowerSettingsApp* app = context; + DialogEx* dialog = app->dialog; + + RebootType reboot_type = + scene_manager_get_scene_state(app->scene_manager, PowerSettingsAppSceneRebootConfirm); + + if(reboot_type == RebootTypeDFU) { + dialog_ex_set_header(dialog, "Reboot to DFU Mode?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog, + "Needed for device maintenance\nor firmware upgrades", + 64, + 14, + AlignCenter, + AlignTop); + } else if(reboot_type == RebootTypeNormal) { + dialog_ex_set_header(dialog, "Reboot Flipper?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog, "May help with some firmware\n issues", 64, 14, AlignCenter, AlignTop); + } else { + furi_crash("Invalid reboot type"); + } + + dialog_ex_set_left_button_text(dialog, "Cancel"); + dialog_ex_set_right_button_text(dialog, "Reboot"); + + dialog_ex_set_result_callback(dialog, power_settings_scene_reboot_confirm_dialog_callback); + dialog_ex_set_context(dialog, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewDialog); +} + +bool power_settings_scene_reboot_confirm_on_event(void* context, SceneManagerEvent event) { + PowerSettingsApp* app = context; + bool consumed = false; + RebootType reboot_type = + scene_manager_get_scene_state(app->scene_manager, PowerSettingsAppSceneRebootConfirm); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultLeft) { + scene_manager_previous_scene(app->scene_manager); + } else if(event.event == DialogExResultRight) { + if(reboot_type == RebootTypeDFU) { + power_reboot(PowerBootModeDfu); + } else { + power_reboot(PowerBootModeNormal); + } + } + consumed = true; + } + return consumed; +} + +void power_settings_scene_reboot_confirm_on_exit(void* context) { + PowerSettingsApp* app = context; + dialog_ex_reset(app->dialog); +} diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c b/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c index e734c78e0..bfc9ac9c9 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c @@ -1,5 +1,7 @@ #include "../storage_settings.h" #include +#include +#include #define BENCH_DATA_SIZE 4096 #define BENCH_COUNT 6 @@ -86,7 +88,8 @@ static void storage_settings_scene_benchmark(StorageSettings* app) { uint32_t bench_w_speed[BENCH_COUNT] = {0, 0, 0, 0, 0, 0}; uint32_t bench_r_speed[BENCH_COUNT] = {0, 0, 0, 0, 0, 0}; - dialog_ex_set_header(dialog_ex, "Benchmarking...", 64, 32, AlignCenter, AlignCenter); + dialog_ex_set_header(dialog_ex, "Benchmarking...", 74, 32, AlignCenter, AlignCenter); + dialog_ex_set_icon(dialog_ex, 12, 20, &I_LoadingHourglass_24x24); for(size_t i = 0; i < BENCH_COUNT; i++) { if(!storage_settings_scene_bench_write( app->fs_api, bench_size[i], bench_data, &bench_w_speed[i])) @@ -95,6 +98,7 @@ static void storage_settings_scene_benchmark(StorageSettings* app) { if(i > 0) furi_string_cat_printf(app->text_string, "\n"); furi_string_cat_printf(app->text_string, "%ub : W %luK ", bench_size[i], bench_w_speed[i]); dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter); + dialog_ex_set_icon(dialog_ex, 0, 0, NULL); dialog_ex_set_text( dialog_ex, furi_string_get_cstr(app->text_string), 0, 32, AlignLeft, AlignCenter); @@ -110,6 +114,12 @@ static void storage_settings_scene_benchmark(StorageSettings* app) { dialog_ex, furi_string_get_cstr(app->text_string), 0, 32, AlignLeft, AlignCenter); } + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message(notification, &sequence_single_vibro); + notification_message(notification, &sequence_set_green_255); + notification_message(notification, &sequence_success); + furi_record_close(RECORD_NOTIFICATION); + free(bench_data); } @@ -146,11 +156,17 @@ bool storage_settings_scene_benchmark_on_event(void* context, SceneManagerEvent if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { case DialogExResultCenter: - consumed = scene_manager_previous_scene(app->scene_manager); + consumed = scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, StorageSettingsStart); break; } - } else if(event.type == SceneManagerEventTypeBack && sd_status != FSE_OK) { - consumed = true; + } else if(event.type == SceneManagerEventTypeBack) { + if(sd_status == FSE_OK) { + consumed = scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, StorageSettingsStart); + } else { + consumed = true; + } } return consumed; @@ -160,6 +176,10 @@ void storage_settings_scene_benchmark_on_exit(void* context) { StorageSettings* app = context; DialogEx* dialog_ex = app->dialog_ex; + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message(notification, &sequence_reset_green); + furi_record_close(RECORD_NOTIFICATION); + dialog_ex_reset(dialog_ex); furi_string_reset(app->text_string); diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark_confirm.c b/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark_confirm.c new file mode 100644 index 000000000..2f8644761 --- /dev/null +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark_confirm.c @@ -0,0 +1,70 @@ +#include "../storage_settings.h" + +static void + storage_settings_scene_benchmark_confirm_dialog_callback(DialogExResult result, void* context) { + StorageSettings* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, result); +} + +void storage_settings_scene_benchmark_confirm_on_enter(void* context) { + StorageSettings* app = context; + DialogEx* dialog_ex = app->dialog_ex; + + FS_Error sd_status = storage_sd_status(app->fs_api); + + if(sd_status == FSE_NOT_READY) { + dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42); + dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop); + dialog_ex_set_center_button_text(dialog_ex, "Ok"); + } else { + dialog_ex_set_header(dialog_ex, "Benchmark SD Card?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog_ex, + "SD will be tested in SPI\nmode. Learn more:\nr.flipper.net/sd_test", + 0, + 12, + AlignLeft, + AlignTop); + dialog_ex_set_icon(dialog_ex, 103, 12, &I_qr_benchmark_25x25); + dialog_ex_set_left_button_text(dialog_ex, "Cancel"); + dialog_ex_set_right_button_text(dialog_ex, "Benchmark"); + } + + dialog_ex_set_context(dialog_ex, app); + dialog_ex_set_result_callback( + dialog_ex, storage_settings_scene_benchmark_confirm_dialog_callback); + + view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx); +} + +bool storage_settings_scene_benchmark_confirm_on_event(void* context, SceneManagerEvent event) { + StorageSettings* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case DialogExResultLeft: + case DialogExResultCenter: + consumed = scene_manager_previous_scene(app->scene_manager); + break; + case DialogExResultRight: + scene_manager_next_scene(app->scene_manager, StorageSettingsBenchmark); + consumed = true; + break; + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = true; + } + + return consumed; +} + +void storage_settings_scene_benchmark_confirm_on_exit(void* context) { + StorageSettings* app = context; + DialogEx* dialog_ex = app->dialog_ex; + + dialog_ex_reset(dialog_ex); +} diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_config.h b/applications/settings/storage_settings/scenes/storage_settings_scene_config.h index 18e7ba5aa..d6e76894b 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_config.h +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_config.h @@ -5,5 +5,6 @@ ADD_SCENE(storage_settings, format_confirm, FormatConfirm) ADD_SCENE(storage_settings, formatting, Formatting) ADD_SCENE(storage_settings, sd_info, SDInfo) ADD_SCENE(storage_settings, internal_info, InternalInfo) +ADD_SCENE(storage_settings, benchmark_confirm, BenchmarkConfirm) ADD_SCENE(storage_settings, benchmark, Benchmark) ADD_SCENE(storage_settings, factory_reset, FactoryReset) diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c b/applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c index 5832c6589..2d977176a 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c @@ -21,14 +21,14 @@ void storage_settings_scene_factory_reset_on_enter(void* context) { dialog_ex_set_left_button_text(dialog_ex, "Cancel"); dialog_ex_set_right_button_text(dialog_ex, "Erase"); - dialog_ex_set_header(dialog_ex, "Confirm Factory Reset", 64, 10, AlignCenter, AlignCenter); + dialog_ex_set_header(dialog_ex, "Confirm Factory Reset?", 64, 0, AlignCenter, AlignTop); dialog_ex_set_text( dialog_ex, - "Internal storage will be erased\r\nData and settings will be lost!", + "Internal storage will be erased\ndata and settings will be lost!", 64, - 32, + 14, AlignCenter, - AlignCenter); + AlignTop); view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx); } diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c b/applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c index 862f55a46..13b368d3f 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c @@ -20,8 +20,8 @@ void storage_settings_scene_format_confirm_on_enter(void* context) { dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop); dialog_ex_set_center_button_text(dialog_ex, "Ok"); } else { - dialog_ex_set_header(dialog_ex, "Format SD Card?", 64, 10, AlignCenter, AlignCenter); - dialog_ex_set_text(dialog_ex, "All data will be lost!", 64, 32, AlignCenter, AlignCenter); + dialog_ex_set_header(dialog_ex, "Format SD Card?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_text(dialog_ex, "All data will be lost!", 64, 12, AlignCenter, AlignTop); dialog_ex_set_left_button_text(dialog_ex, "Cancel"); dialog_ex_set_right_button_text(dialog_ex, "Format"); } diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c b/applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c index f107aacea..2fb232f14 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c @@ -1,4 +1,6 @@ #include "../storage_settings.h" +#include +#include static const NotificationMessage message_green_165 = { .type = NotificationMessageTypeLedGreen, @@ -31,7 +33,8 @@ void storage_settings_scene_formatting_on_enter(void* context) { FS_Error error; DialogEx* dialog_ex = app->dialog_ex; - dialog_ex_set_header(dialog_ex, "Formatting...", 64, 32, AlignCenter, AlignCenter); + dialog_ex_set_header(dialog_ex, "Formatting...", 70, 32, AlignCenter, AlignCenter); + dialog_ex_set_icon(dialog_ex, 15, 20, &I_LoadingHourglass_24x24); view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx); notification_message_block(app->notification, &sequence_set_formatting_leds); @@ -44,11 +47,17 @@ void storage_settings_scene_formatting_on_enter(void* context) { if(error != FSE_OK) { dialog_ex_set_header(dialog_ex, "Cannot Format SD Card", 64, 10, AlignCenter, AlignCenter); + dialog_ex_set_icon(dialog_ex, 0, 0, NULL); dialog_ex_set_text( dialog_ex, storage_error_get_desc(error), 64, 32, AlignCenter, AlignCenter); } else { dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42); dialog_ex_set_header(dialog_ex, "Format\ncomplete!", 14, 15, AlignLeft, AlignTop); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message(notification, &sequence_single_vibro); + notification_message(notification, &sequence_set_green_255); + notification_message(notification, &sequence_success); + furi_record_close(RECORD_NOTIFICATION); } dialog_ex_set_center_button_text(dialog_ex, "OK"); } @@ -75,5 +84,9 @@ void storage_settings_scene_formatting_on_exit(void* context) { StorageSettings* app = context; DialogEx* dialog_ex = app->dialog_ex; + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message(notification, &sequence_reset_green); + furi_record_close(RECORD_NOTIFICATION); + dialog_ex_reset(dialog_ex); } diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_internal_info.c b/applications/settings/storage_settings/scenes/storage_settings_scene_internal_info.c index f205efc0a..b7620b6e8 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_internal_info.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_internal_info.c @@ -27,7 +27,7 @@ void storage_settings_scene_internal_info_on_enter(void* context) { } else { furi_string_printf( app->text_string, - "Label: %s\nType: LittleFS\n%lu KiB total\n%lu KiB free", + "Name: %s\nType: LittleFS\nTotal: %lu KiB\nFree: %lu KiB", furi_hal_version_get_name_ptr() ? furi_hal_version_get_name_ptr() : "Unknown", (uint32_t)(total_space / 1024), (uint32_t)(free_space / 1024)); diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c b/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c index aa9662a71..cad3fbfaf 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c @@ -27,12 +27,31 @@ void storage_settings_scene_sd_info_on_enter(void* context) { } else { furi_string_printf( app->text_string, - "Label: %s\nType: %s\n%lu KiB total\n%lu KiB free\n" - "%02X%s %s v%i.%i\nSN:%04lX %02i/%i", + "Label: %s\nType: %s\n", sd_info.label, - sd_api_get_fs_type_text(sd_info.fs_type), - sd_info.kb_total, - sd_info.kb_free, + sd_api_get_fs_type_text(sd_info.fs_type)); + + if(sd_info.kb_total < 1024) { + furi_string_cat_printf(app->text_string, "Total: %lu KiB\n", sd_info.kb_total); + } else if(sd_info.kb_total < 1024 * 1024) { + furi_string_cat_printf(app->text_string, "Total: %lu MiB\n", sd_info.kb_total / 1024); + } else { + furi_string_cat_printf( + app->text_string, "Total: %lu GiB\n", sd_info.kb_total / (1024 * 1024)); + } + + if(sd_info.kb_free < 1024) { + furi_string_cat_printf(app->text_string, "Free: %lu KiB\n", sd_info.kb_free); + } else if(sd_info.kb_free < 1024 * 1024) { + furi_string_cat_printf(app->text_string, "Free: %lu MiB\n", sd_info.kb_free / 1024); + } else { + furi_string_cat_printf( + app->text_string, "Free: %lu GiB\n", sd_info.kb_free / (1024 * 1024)); + } + + furi_string_cat_printf( + app->text_string, + "%02X%s %s v%i.%i\nSN:%04lX %02i/%i", sd_info.manufacturer_id, sd_info.oem_id, sd_info.product_name, @@ -41,6 +60,7 @@ void storage_settings_scene_sd_info_on_enter(void* context) { sd_info.product_serial_number, sd_info.manufacturing_month, sd_info.manufacturing_year); + dialog_ex_set_text( dialog_ex, furi_string_get_cstr(app->text_string), 4, 1, AlignLeft, AlignTop); } diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_start.c b/applications/settings/storage_settings/scenes/storage_settings_scene_start.c index 0e667024f..e351a2ef7 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_start.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_start.c @@ -109,7 +109,7 @@ bool storage_settings_scene_start_on_event(void* context, SceneManagerEvent even case StorageSettingsStartSubmenuIndexBenchy: scene_manager_set_scene_state( app->scene_manager, StorageSettingsStart, StorageSettingsStartSubmenuIndexBenchy); - scene_manager_next_scene(app->scene_manager, StorageSettingsBenchmark); + scene_manager_next_scene(app->scene_manager, StorageSettingsBenchmarkConfirm); consumed = true; break; case StorageSettingsStartSubmenuIndexFactoryReset: diff --git a/assets/dolphin/external/L1_Akira_128x64/frame_0.png b/assets/dolphin/external/L1_Akira_128x64/frame_0.png new file mode 100755 index 0000000000000000000000000000000000000000..36c1bbd4994c02762f5b17ea12993524b0f782ee GIT binary patch literal 1832 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Akira_128x64/frame_12.png b/assets/dolphin/external/L1_Akira_128x64/frame_12.png new file mode 100755 index 0000000000000000000000000000000000000000..e3bfd179d9025dc19f60dbe1d2fd5d49e4d4c0ec GIT binary patch literal 1385 zcmV-v1(y1WP)0qEpb6U|QI=(v_{ynWWVXuBv0 zS`dIko9NB5@zi(}H%QQu0At7`IGMTgzN?*3MoG|?0Au_c8pb&L*TFmt-N0^{x03yp zq2x(e6PZSspArdnJc87Z_s}H+83#U^YXCGax}?fn>c{>5D-!MJ!GymL4b zx$?Y;4E-XqUKy_EwIr~r;VL2G?YuJF16(1%uJNNcUAK^_-;McQ%>goYeun4p?vfp3 z$a~f;5_+!W0GV#zbHTyoC@dLoW=@m06FEvWzI$+t04Em$S!~VlmM5@o6}7%4Jw8Q( z$hajAf>6u}u6CSce5BOkJ9>4!%*X$9q8kbY?8H@gv10zGOH~i?(+QBB03py4ysUC8+Go~U zY?XNnnX(?hO4_u^ok+k|eOjSnCX$s;K+EL-H2b!oxmi>`L#OCj#4RA9s1yMr?v~1& zx>ZBY5G=WfY!+4|C=miA1ra0idi@5xr-jr2H?DoXNb>*@BWT9+COc7C4|c|pDv6t< zwY)$I0i=TyYl4>HBLiK3Ap90Ih<8JhWe%eWmN<5@bRWm86J zId$Q>Y3=#O%Jg!fqhM*_#LkW6>0}&ST@rbXf4Y>j)`#CucnK?wMP))E05Lvq&wr>w znxZIBcaC@fbgWER>x1a-9gE6@2(S}B(f&OKu@;7YqVAu{@aLXIKXD$R%_%yTn*dpH z-P-vp4 zTDg8}msq(RCt`-T*)>@&`dSc2)#YETxUx2|zbf zOzmgWf3)7IGBEbFTLhksTL{1ldTU(mXVO3FS7jMM*R{ZkK(FQbcbNcYPFidBy?e*0 zWbAmt^~T8k>wQIFM1an{xCD48&^D)a7t9C5Eqq)a20CVCIvaA!N z-&c{J*+bJkvfq_VURFSUj4S0de zf--}Qtjss`5TIfx5kcF~15_$qI*hF8`B4S52gu3-BL)vMan|>|B2{MW3q23vlutnU%+ophuzjMnk&Xw1ZY*enykPV!sh zf6u-`Lbi59j8$vdwKEZ9B9{%a)hx?(mISbybcxw??%fhtAa=YUul7FP%_8b~qKqZ+ zi}om4YzO%RNGg=$kN{Mzy=$(O33UG;*NJWc7C%!0xPpASZUmk3sxkoLTlB6<1ljGw z8|ST0mw>CtpE>=jw$M4z%>)^TBFbjbski<;KwSep(sz?D*N<=o`N%p|@#(#P37zrB znhOiy^oh5CZ2C0$=+&+g)|#2`-mL->!0AR$|42Wgc`K+SKT7@4{#axsMz3p$AngH8 zUGXfZz4uW=gsh=~5;-#mxrxk{3$NsPb>HgULILb1AF0{ic;rhMJqF2!NnEnE}6^wC`SF;DWBl*%d`B{xDv!LX!`iXvb;N1!$ zaZ6sm1aL?4N3+C!p~9Yi4hK-$WD zvi75GT_J+kpJ#i&=8aOH=~R-IC8FonT6!K*22h)sKat3^%`7Gv&{FW~z4x6NAZohM z?i#J{8JLrg_8zN#GBFs>;RX*7!TUt|myXCE{eEvUEgW`kOp0T6#@_7Q>UbI|<^ySMl|fvx3V{0~2D%Pv99+*eU=t7<%*QiJR%7y-t4oSZi2z zqO}20dB!1Uh;S^;uzXTY2q-DEPgx*FEi61_!K;WtcV@On`b)l2{Y002ovPDHLk FV1jFge-!`# literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Akira_128x64/frame_14.png b/assets/dolphin/external/L1_Akira_128x64/frame_14.png new file mode 100755 index 0000000000000000000000000000000000000000..b36fd051fc724a7f39c4234778a12c8227b7bc7c GIT binary patch literal 1701 zcmV;W23q-vP)cSp`pI#;wpJf)T;4{r(VIhnHwK%aEl@>U zum7DS&}^UQ*$&I;HwM4={(ZHkFSd52cHdpYY-AzVPp+#)HZK;IKXNQNcT)l07JO@Q zqLk)cHG7B;TK@_q@M2_>=#HF$;Dl_dBfz|JzyffPR#S8mT6U-}_s{!S983a~S*{3_p9rhyp}NE_88h~oJr z!0v@!Yu5(9iUdeNt^Qsgx<>Il-sPD03@FkuS{0e`()P)8=)AxA0{NI5JMY2-C$VtQNyQKnXlKNx?ApZzaprU%9 zk_4|%0D3)D0qB*%RV3I_0VI0wk5+(HgsdRJ)(W6i*iTac`B?^$3dpfWNbm{;;JBLv za{Lkb=a?lygsl~TgD>1uW78YGbGsdw}BGKuADuRNjrC*G@{QU5)fXY z03$nW=lK%6RKV|h*Tc#UZ^iCpCs;?rtP(t00U}xVi2KC(Xu*lvME1O6ARlKN1)u_l zlagO~wL4<_YGOz~WwqYg-}z-9Tp?{O5*$xa04?ZI0rLdEQDn~R{C*T%^eG{=k>fq$ ze+Fq!$s&Jb9}eqD+?~50eN1c}*}_M<&U#3g3UEgnupW^j!MdBcC^CL(3fNlhvjnpS zxS9l3@S{FiU+*Ng{0x=$)^a4s66NblYyZ9^>gwH;4ZbFY9(lDVfJjQwkX@4~*Ju2q zc9|5C^0kQ?;0h8TIa{{1+C(Hk`lCKX?X7;{Ci`t5wXSCk_o(?ndW>k^oJI9{zko z$W;n368w?Hr+)k;+eLGwHpseU3tFpx#_mkqzF!h`^=ZHwCo>7&*HRYE*63n+{VbAL zJ|g2}KSvdf<9iCQj8!UuS0M4vWgp9z=p^Cg>yi1PzF2)`=BL?`bJxa-NcZ7yuBrvJ zLM=OnHc9ELRp8CeM_X7gW%0Gl7Pj1uv?bqjW*(Xz;16r(oLrtAei*ueJEk#v%DE>j(-j?`6m1!PkC{ zY*_udZPXvD{S^f)Cy%QD1Zr$4L3M#hS8Io4+3y7lu_Xgm5qxV*tFBoEhyWb3X~AoU z9D}$I8LV2+5kJt4Lz|%5cfNjU0&oLaML>dY1$Sh(X=7BMWM+IJJK8;215|_jA0hx5 z;D#bHM8Tgq3eCc^S8;04?2V+bYV_X62_Oy8HCr$u1*LX>lmu2k>u6`G(MV8c=CO(d zB?RDz3PuhY{7gm8`)3jPL$6iAM+3e(4Yam^034qDqrs0Bd<6-t!fPh@qrOzsJ|315 vz=O6UkcHy?XTf*82Tu_)kYgeCR+HdACUF5(X_P&+00000NkvXXu0mjf(nlKZ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Akira_128x64/frame_15.png b/assets/dolphin/external/L1_Akira_128x64/frame_15.png new file mode 100755 index 0000000000000000000000000000000000000000..33607328a0f29b835feab64cb0b46b83318d3766 GIT binary patch literal 1409 zcmV-{1%CR8P)e98^ajQlW6s&*$uaYq|E2W!qT@9>1$;Cfkpdd$@O^p%pNu(u!S}kZZF^_~ zH~>(*SFzFiBlrU0J?dLPKDFoKQ%o1rP+O9Js6Dl=;YY#I23d|5eGVP*X)<%2v<>6!EC2=D{yRh1*g zs`4qMXb>IS05*X$07?1Sqa(&qkp`LsZh++d7?RSX5h7~@Uw8vO?&BPwAg%`h??shK z*IVKMbI!Rd_r3C%fh3E1x@W!xu0)KWIlu;B#e0>G=dBn3x3&lUG;jm3@@ROMlv+_4 zL<|S8-2hJKt49CQzkP!p42(Req$+tnF`%eipDDlJcdzY$2=sKapp21W+L{Y-G@cIIaD^5sZ|7$fpi35pyg%Bi}8Dw z$8&Nuc+&*e0K)S*GzUQ74i6xaIQpI>!}3xIE7gItM-Jan!S4*u6Fg73M-@%Ozvs-g z=;4JF0r2`A0e;|V-L81=s^Ve2$UKb$NGajt4Z!2|^m7UHpeZ5b?#ZkdL6blPDNkDu zYa(*-HHOs^_R66$qtyi9<01gZ@HORn>uW4WlWQ48H0MW&C6!+q0X*Jx3Q@0zIV;T- zx+aF_cLF@JR;vS;3-dlA0!Vee*GR2H7W3;u+K`YXq;xLGQ#*mt?pb#Jj*%l`WB7aK zxRa;7a92)~$#w7|hN!I(l{E$v0q}uEMrlK<@+wBW2f8oM;Y7kL*WS(Kb}Gxec2xFO z4)ChV&*WFU2InMN6pE}D;T@`+@rGp}H3W=EUd|v=0aPenTSLQJwt_ufF$q=5*NiXh zrA1_wl|A3U0#>}DqWdQ0eM=5kidqI!?fHP0F9W>?j)56?W?k;uqX;cUQCYkDDKEhR zBITPlm&#Zre9|1t7$wrDkwHpoHLOH|M@ImuoQi;(>pjp)SYB;$mdZ^1rxjdXe!(J< zo&o*Rc%xNcUh`bKwpSL0Reeb3LuF`lBT>LpDn9O6fF~?xS+S=kw`5rR(8BvlDH<^X zNd|{U89=sv_wEhL^zJ!IG`YD}x6oQwi=xX-tFd4J*%XlN+-1Wwy(i*rs}X_D%@}kk z9~cj)0we>X_eh^EhDI^;diU&xB*zo zul6Xd!l*KW2)FR;btjG*z}?EnkgAPcMu6u?`_bm^!VwN&<^0{s=ho2_U{xlb3L}C$ z4d4;~CCsb*>O;}k{z}7&lb_ks9k{&}tkwEfXKY7Q;0PPOwiKe(<#)E4J>d3p{8@n3 zBj=P~RTW5kS@FI|3-~U0&*xSH&<={b7f{8jC1_C~qdbtY(+IL>@dyq;af%VcdMoQ% zE!s%)%5yImRupso{xh|sDL@PV0IQUWlv?R1aO@}uqJAR{BZh7=@=p znBfQ&O4p1h@?0Y-$C4?X?{N~i0xe>Aq6&O|H#p;zTGP>LiIYL)bJza>A;Uzi^@JQ@ P00000NkvXXu0mjfC})%l literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Akira_128x64/frame_16.png b/assets/dolphin/external/L1_Akira_128x64/frame_16.png new file mode 100755 index 0000000000000000000000000000000000000000..e115834eff3b0d2b3240dbf798a3152ba8995eef GIT binary patch literal 1369 zcmV-f1*ZCmP)sPYz&!?@)fv%Ha7f)sirJ z&4VXHokOyT>nPk>T*}D-{=nPy^&Uz-Kl7t~0%}WC z4sqh+00dt6ECv7FyL1H5JOClniXJF5hpyMZqF(i6Aay%W_z<0~#MgBqa@suYXgU#~ z_{7&a2nqZ})9=zX%?8bm9!Qbox<5PwWRfrG)%0;_J%N_w54Ek753K?s89939djxuG zX`MmOK5AnGAK3?p*5DEHt)76S7q!pQ)mj}?+55gf#{pV)6_tx7Z&BM?$?6s z0DaIM#~yGUR=DSK+C6O@Z+;{qpz@L~I3M_)y9db`WOI1rqv5!ve3P!VJYP%n2{}O6 zqBns>K1xJun3awY$(b_R!yF+U<)IFMzHm#v7m*4egLrEV4UcIASzS9mFY^Uz(AICw z+($X?V;VuxBu$g+A%>p&(Z&tpFe4oI5fK1=;S$+BiRYiTYG8MJD1lG>w zYood-EV8t7AIU>nqk!AzC>uI}R>D#AX2p>bF%r@xIf>wwq&4CMnhuWb9KhQ_y}Bix zXrBOW!&X^1f$*mkfBfsZi5m{|bBtb*g(`0)bI_*d-8~!rr$2cc5Mi9~G zIR=_1j7ls=6l_~W0FV5vjNPj!5GSDWo)cu%@FOerE&2c+`Pn=rDY9GcGd4& zIN6dN({ljye9c^sj@q#!m2DZew&V#>^f=fS5nv>&=8t!#M@jQqw2|f&GFRKxi2iER z@;?a42>)KGWf7^Zo4sRcB&gqe5P4TtayqpNusVL$?$NqxUa#Kk;aj`42S~+$tQpng z>6Dyrc?8hFOEZZ~FX`fQJcpP8E&q^gYOR)#i1_n!*)Rfl4}d0TS>d(?=n;aDXg!A~ b?>YTHjQTa#bKt=W00000NkvXXu0mjfk)(|v literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Akira_128x64/frame_17.png b/assets/dolphin/external/L1_Akira_128x64/frame_17.png new file mode 100755 index 0000000000000000000000000000000000000000..8e5fa20d8d9290e70540c179fdc8f96d97588e30 GIT binary patch literal 1398 zcmV-+1&R8JP)}C~+Ah#sF zN-4D*d@LTj1t_JITI(5ijw4t7p>oC-9j|giz(?a2A>d#izIF@nsi+lmyw`P2mE#z| zVl>60ijB^X;?WqKasF6qE!TCG>$;xz-X+b?lL=pRAFw z6j{-|%IC1cLUcR^Fagr(*OV`vXared;3R>`&|E)-ru3+X$S{Hj72=F@a*n1TsitqQ zMU_c;4dno})|wUj-f8TGMu(qt?RCV1a5^*>h~ne`$)JkYl+NeVIXwm-@kVhz4NL~F z1llzl5yPRFZUDRE@m=x&3EJt8;!jK`IR+xp*`xzsBPrc}I|I%|v?s9T~ zRG_EWI{iHfAb;i=V|#x305?N#2;r}56#~CM2CxC6vA<`oW(emCA&Zm0QP(fg;gAf3 zH=&-dmy-je!#dGi;ZfyB+RT=G4JQOlgcgf6ckE$Q`JxQvcmNezhV8A9TD{V#*LX;< zas?!Zkml4{Pa{Yg+eJHhG=^I1Xq=@q=sn*X54=~xFUvDXd@0@I0T@P3ljh!~xhT*u z!Fh8zG@KmZ7wCqe%BKqRPN{~T>OhlGoA(tZ{;r5*&t4)Xm3k2)u)GHf&7iq##{-mj z>4ehkRea+YS!d+{R!I1013*s~q6EItJRy|kXfkAbWy_O%ToQ=JdOAPiA-oT1&~-h@ zp9hKmM|pr~Ohqe#Qn0UQc}5YHFPd0N^iw^6=h}85dg|Mno{E6;r10jVw0f(9wwA7Y zdk-*r>b*_2nk)z}WbQ~GLE=g2nVrCB7j1O?j4=}HwM5RlXjjgoFLenc@@YJxv>N_- z0J;g0Q4^|{Ue<_iMsE^E=Su78o<toUg?kMhqnu*_zaNWZ?g1&p4G48>4mL-van~zLp0a@seEzFd71o^^Jza8+v2FT8$@?pdtLUwIc?dUA1qo0yN{{bF^m`U6h_@ z7-*g_E3ur}s@dKMLN+@q1ZavxjDW89j3BFqU!j_<2H+__`$Sn4MwSsoatlP(+1O?P ztCdfoRU5O6fUeQxvDVJS@*Kd6`LmTT@v#(u^y;N&y-Hkd0Nejd7^?jA?AOTlBel9V zx5tQ=pZ;s{4@0b4-|LLc@Cs7GM(;g^SatczRx<)ko~@q+P&bMwKdUOxWa&A5krwbO zMD}Ny0azO)&27lasx??%U_^P1#!Mr~*o*Bs0LRvh7ARWSy93eyLmhnVB*ND>b zXj=Pw+b@J@usjChRoe5_kc?AnO-HLGi3X=H-Ir3zH^}-5)7n9zod5s;07*qoM6N<$ Ef;@4W3IG5A literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Akira_128x64/frame_18.png b/assets/dolphin/external/L1_Akira_128x64/frame_18.png new file mode 100755 index 0000000000000000000000000000000000000000..6a658e0e22b8218d5fb63e3cf0d494e7e32f7e9a GIT binary patch literal 1360 zcmV-W1+V&vP)(MQB7Gv%uQv)%nO@_0n=arTL32Il=_?R1oHBrm5Dm@Q zyl%Ch6vGB^cb?yQ2@^~!Q18t~`!}BVi)&d!xAk!U4 zXT@Mf>~=JBZK78Ed@^BA`m*Fyg&lC2H)QwNC3e`q*Vw z3iyUsNggFoh2h`1&*&MV`$a(S@1u)AZ?4Dly`&D7pCL&Uz;C^2;TLaELYb6ACLk@ zl^=Sb3iprC5=C@x_ZoP(0gNbrB){UlxCp03ipYMWT*adeAgg@5eiV@oV20wgJvcm? z3g&qD{<(!&%GdIJbdOBkV#;g}vVf>p%%*zrT8~Fd?+PUO5iY`u#}Gl@2aYkL>!9l+ z-c3`KBzt9d^j0-Mv?Y_)YBDl;n2Ri$9I5Ejioqyx<;7b>0I8jdfLog_4o6^lwRB%r zw8)-i>j;qP!#&VEm+tMAg`p`+%KM&HFdGH5T4!4W@buxMRk5chw`5r7(87DB8yRIY zDYi5K%Y#N`b27d4MYPc5)>>?#wXa5-hf^bb8^GQCKR5n5=)lc-)=GPN3N+7t(%KP& zp2AhYMiGEhzEv4}<$C2=hT*LfR=(7liLHnr&%>UT0whIxi~z5A#;rAgmGf6CpW8=MfQPH+zRN1{ z3Io{c{{rq%e&1KBF(SRXG&ge}S12FgMg!1#{guj(gz88*)+pTFa2!8>Q@Bk8usHHj zU@sjZbF4jPMu8c?%I+7&EiZ#K?_aea9;J}awQV!b+GVV~=d6Kk69HC2BY&*-1W~0~ z6m5jO^33JD+AF`cw#Vz>8R6e*wKPRa`_A@Q90~gGBZxdJ>G@pV25POJ<~M+InydGo;v^N*Rf?`49B{S( zK!wC(@pXCuiL*!pcsQ;wfLd#|C)<&y{z%F8L)!vL0+F0%={P)>?PPzE5gbfRnAAQv5CA0i^K3&5;~n zBQzpt^^e>YaD62s$U49#^eEr+jw0&4mK(t7xO+?6gOQL#=fwut9)Qb2!0EWZvIXGt z`I)g_nuD9~N(1=I;1uaqK7Oyk(>FQ5#TZdOmBE2CxGV&1f~8dYEvkI4>!-*+2suCo zDY2sjZI|)^k^^*s+iZ)eb4thKr0*8AOu>^uqyYp#l}5jL3OWsMY`jzX+8E0?0MY>5 z&|I^0it#(^#B*{rc*BI0hPLrZ0u6X`c(3v~E)Y>Xo;Ny+da{G?pM+_cX#lGs<@Mf*UWB#xtgUB!4@bDx1FVFk#MuV! z5D~z^L*gZdR-PaJ+JO(pz@*Dmn3G{9_29DJu^K!pEim=kl85cpUT=-B9Fp_e5PgYMH<_M-m z+_<%AM9x!i;>L0KI1eDDQxR}udy6k4uuiqbS*CCk_>e+SPaf`pc5><5URfAc_Q9QO zPa4V9Ge1HLX-0~ zlxT8et!kk)uePrq&vms2h*a)I-8AhJIc>cjfsP&7v)+(+Z5AMT71~Gorpfinvkb$_ z6IKz+DJ1aLLXc;(XN3Ssksc$!<1HhYmBY_e*_8%hDSu~%G0O-d+`_ZgS-8#sb}Ju4 z$~IOR0Ujf*M;ki>*KhzU=C4*hH;<-(p39vPC9X1nYyV$>SNYRjqQdqgxw_Q1$B0)c zAK(%L&~kmLGS;3IUV&Rn#2srT+}m*FU4T|2=R(A+tUy{zkK!UF;9cPM=ePlA8^!f) zn8m6kaA{j)trD7yl|~TRiz`Ba2Rj(Cgtnp4d94y{qxiDiuk! zBI}xIbl4NL-y?|p8($6+983eX)K5ysC_ELy5somW+^QH)z>%07*qoM6N<$g6YzBj{pDw literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Akira_128x64/frame_2.png b/assets/dolphin/external/L1_Akira_128x64/frame_2.png new file mode 100755 index 0000000000000000000000000000000000000000..9f4cc1fb9e43970818b801fce43ff942f343544f GIT binary patch literal 1836 zcmV+{2h;e8P)4cNdK zHvKrp829mq2k!U(x8pdD$NS?rqyU4aC^4_S3%eL zpX&ID+h6vNC;C?Qgwg${IDX>CzInHyuhMVz*fU&#b-bN(9LJOM{g^A_pCjPiY|znP zrS9nvU+h)qTYOU1ds~0VxChQ&$g2FR1MT=~<|DfQl&nX#jv*9*E3Cja!aL!Ws4|9H z$^P zvfW~yMb&mo(Z%el%wcD}mwOegGU@3Lf|!BGF}sh3s`IA}c0RlJPVC&H+Zes)-MeAi zvu=j3*zGf4Io{4Fkaz3JaI*^3R|aKK6-HP=<%sEIz48H2q*G}@t2I_<@3u0)s`I)% zJql1Ypx6VUXv3}Z?EEN@tlVjrofFx4mh;DWAp@L2V<W<`f+stLw9UFfjq-yZahehZi4+`9Bp*P2MoL{B)SDg7C zVH>E{04kmBfa+*AcZ_;_mlb==ao)}h{wRyEy4O=j7yxPwP^pAbp_Yw{99(Vjs#)g8 zvb9FRmDyhHCU&-;H+v(a)ofXHK9hx>U36MmH7Sq{s?BTtBz-QFhHaL1xR$wzK<9{VHuWT%3{xSKEkfLB`XFq=22}N!!dwjP*qWv0Z`Vf z%)*X~GqKb8eq44ORQB5M!_U0vzq$(0LcGj~aiRrgmAIAvD(5qn%bc(5%e?RR#xsO} zt>9-0_Fvu*uOL%yjkYqGb0T?aJ#j|nNBiQPfU?)x!2&m1Uo@B5Yy-?6we}0v0BoV1 z;tQQsD5}}3)t}k2)A=l5qq7-}XV)UVNPcI}n0*$I0hnRvJ)>@ahF4KxfZdsIGl)48 z>16wt*@gB_trO5|1y2mn$#!NW%68-^^uEJ3_PvE#9I>otdNMXb`qe_#kBzP|WaEzG z=zkdG9js8_>M;@>D|U{BpU03nrh5*rCK5v=TMe-L{jNO+STRs>8XM1Si*))m_`-~_ zL+1R<79WJF^H+j|?U@>=OAOFKXR%Qc8D%BQcvMi@dlj2e(5Ts$$yVl0JypkBgg+3n z8yTH<QSc9D!Ybi1ZA7mnt`!xhs8g(2t=8= z>U<j1&M! zX!MH4&hPz~z0PEzd1sxeUT1Val5MY_Q6!?EvQB1W=D4ZrDF7m8TA`=|$QBw=9qX;L zP07Z4DpVY+IwA{rn{mt;=m_zSR8V1^IsSgXKmT%Q=1kSu%&^QTKc;#<&N-++%HDSo zu@P$-{q-rw%Tg1e3+bYM%$Df6);QbW=@CT084}|>T ad+;B*3PK3uO;xo30000V7AP9i5)AxU6&yQ|$h(!dHMkC)$$97|EBFN2j z698lgw&ivA1qAmJ3a~k$0GqRn0_2>db!Zbr5O|VvhLqA?ErbGW%`z?^@Zkk;zaR2y zgElGWoKs47Uq^5g;Q}`2bwq=~o^)G)yZwB${c%$MeEx8}k9M2(qX1YDqTQ#x6oPWT zR*Hh>bH)Jq&nI9d2%b-*ftz!w1Rh{3aJ+Co+_qk|^P-)jO_{OvM;vf;seR@shmY7| zF5Z$(Ek%%=dk_KUTvTHKN=PZCi))Zjx`YTY`*C*x7SpaFQcXX~RKC@IDL_jo*bP{e zAcQ{`XCX);6snI#2}1buaasUQIIXC^r35%=4B^kk#RXuqUcPTpQ-Tow+*GrGGe{7) zg_>}85dI9soSwF70Wh53uN~V#_)p-p09;g$wFX-Tu2z80E61?3fe&9nAlq57l4Iqs z!5cuH(`$tqt9O190C4+3^httRK$Lvf;B6HW7YGLHAleNq(YS&U%3;}c3}k$zfdF#f zEiDjPgrl`EVUcW{L7)YXNS~@cYAt}Kg1HJ_3jXTZ+&TJ7Gl*7;YYg}q0?l<$V@SV$ z=9aqO5q$lBfYFhAYfl>Y8~y!dHFDLqoR^{Z1#tacz-U^#j2?p5T*E4OxqveZ@Dl#f zV!uqimyA<`8{-MW_u!NPpa()m!EFRdSA9=l+Pa3FDd&8M3upzB(m>NIqgWZ~oJg!7 zg{ctKlR4DN#+v?;>snd(miyqg@>%5oCJV4W0!Cc~b>67)pF(dyOl4NBvDkkTH$o6K zZT4^rUXk*z@SN`&u2}%4CLFCx;8?jDIC}o2*g#kb8Ej(1N-aX9=H{Fm#?0sCg4?Wx8Py{q-ou+g)zfZN;3%` zz*q;UQH$&;LEp6!? z4PSc=)G_?sJ*$vYm?y3LgHr*d8#JRH8j;c}9AWb=RRY{s8niu8_yB|ju=w%bG)r=R z?RahSwF2RS#HX2k3>gGc?*aY?dgW_$BwZetDu+jzd1c{BAY%aSLj}~tw_JuLI2(!7 v>O5TmzW!Z2{%lQ=H0N2eieE9?^?2|PZ#c2xfG9xh00000NkvXXu0mjf8sF>g literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Akira_128x64/frame_21.png b/assets/dolphin/external/L1_Akira_128x64/frame_21.png new file mode 100755 index 0000000000000000000000000000000000000000..727511100d4b48408d45459ebde3814370e6dc61 GIT binary patch literal 1276 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Akira_128x64/frame_25.png b/assets/dolphin/external/L1_Akira_128x64/frame_25.png new file mode 100755 index 0000000000000000000000000000000000000000..26c944d16e6917c7e73b40e072cb8f87a5ca8afc GIT binary patch literal 1812 zcmV+v2kZEWP)FbI@0dH+}T{W-rmNzk=z*$ggg@GhVeEc5Jx@V~nr0ncrpmwavW$YSH{JuIu{t zS$~(tA^)zBT{$MR$Wva`HKQBS6aD^{yV|;TVSVuE4oBCB+zd1s^S7$2)Rkdc?U}6K z-Vvp&31cSHvd&H_)vzm1>mS)n+A~{5Z0Tw-faWUJc4Z6@p~!51tv$1QrtEG5+uiBf zX6x4bwL?oQ?)!$Hd0&eMvWWUmOXk|wqDyq2)hTja=gQlBp59iUGy}+J)0<#9hDZCF z@~2&d1x^17-=CQ)WF!=GkIgi2y$6K^{NO5Q7i=0;g{Y6;i5WECj@Cr>S^L5?kfqjD zi1v~27GI8wev7(*6NpChECVod(Oe^Zxd3ZWO*zyqs<5?Snq_BptTmBxXKMYtXO{5X zC)$bLPM0#QXN%6!dsT>es8#rSjqGHktT$m=UuM@;P(3q-wWG(t292&L9Q0AB7;RvqqcRuJve2lOf?z zCkxLHM%pv7w4IHDG$}I@LomTK(reY}?M(urX|J8PP_nP*t3AJpCXJzB*`Co^gS9B0 z%;=(KA$595`m-pVg=QELde{1(2|}&`>Ecna#~9W#Sc@-pLC-7Oc^l3f+roX{{%K%F zkQN7IqU!aYw$RdGnV7`dZE_54b6C&7#wil0os@^>RrZm3q3d;B}^|&$_l{SPik;4iEz(ePAI-!pa8N zMal$TD=asJ{HMG>zsq|X8)(d9>5F8u&xKIQ6!KZ#*}*H7Vht*~Q>+Dv#2uD9u*P75 z%IbxUjD20#zr&!23s{Amgq0#``lDSgVJ)s_M3K0n6W*qQ8Av7wT!mJ+0If}Sw|jn= zWf*2ccT;3`?$~+DAQc2g5H*lUABF`ySymvX>4DlupE2upXJ5%USq7kE07P{jZ4^LUVbfqiw3gH_*Q=_Ahz%$2;33uMa8l&qZl(@29jOm#Cx(eL&@lg z0!weKj+((3APtOK^De3ar%2(&+r8StR2nHu;j)i~=z3h&_46>O%hC`|E&god$;;;Q zC$JL;N<*7h+67c1Vq_?iXXVK6UeU)J|MUilAY&kOC9n!?aA&n&Y1k{$SOH@7LT0j$ zlTZWMG5EKFtAVOlOfEm~c?B4hxiD2a4Q#z3f={x_aR#u!rk4x@OlUCxf)*rd3!QNt zn*L9~5e7gDgcx8~hP4ogVIqYcR7A=agMKvwumz8bNywIIQ58@^izhOANBVZh46Xkk zKtpwEb>I4HVU>o4^7LJ!Wn5A>+8KZe>a5?#i231p&vFS7cd@q7>Iu7G*s9Y#90>xg zMQ5UM6eoDQ68UOvTut9>q#ubVES!i79+d@XKn$`Iq(xFZ`MW0{)g+!2`LFnB9blqr zf;6@mgEIs+8(@O6CgwH-@6pvtlG4@}Sh?HTc93~j- zOk2@7l~qO$M;XBKt+GvLUA@9bS7ZXHe`S{8;V1({^IDH?cPej4PIS^U{i?Er3WNXucke&7^$QvNd_u7@cS_fNuf> zy?QnF`FBJoE%no6v;Ee-RSOVdAMIWjQZYbvjXLXttvud~@;#&Lpx|!i%U~-AtT2S# zhh<91NG6-(|7JtjIt@H!sTG-k*WU8i9gN37JL#;(_Hpoz5kzu*b|ThE)B2n^Vr8(C zHdf*zrQpcJM}je}poC;wN5oeDDm=vyL;oK@B9hXreWEz+YOd?&zk>PXfla4eGYI^Y z@7sYF`A5g;4hl~kEScjE#Q?hK>GTe&D!D$%2&&7-UoPM&KB)`4!D`3+uxY3zd$(%% zI+1EjoHVlk&7i$EjTmH}`S@GG-OvpBv9Dt_=oK}B$Gj1=i_RV2IkxpazzG~RfFsA7 z#KF>T7(x04;aV;tBD?d>*!~_MO2o1NhXg={T^G1tR(Bl&xXbEK)_b`;fVx zDgrU`ciu|Mz7vr}cct~QE}$zt-5s;)^pQbE*Jz)T(?E}YPECS!)LRJ@xy!#l>Gs%p zOJ@PrBxGN#^u}Ldy$uu@rwW~iMa)aB8nktgWBdVN8OYy+^XKXS0000jrtF>jn?bq(JPpkdZn>t0fYVOLHz{PUkYoU>Gk!0!jNsN>U`Y;e60%4-&D(jA zVgNUugs0*s(_&9O4J*7rZ!)$E;W+?R?(Pjr_~rsHq~-vpk`M6B1sXyPfGYKtQ}ARw z)&l^nNmh3^dX_bUxY&P#D|~t`kmdn2#g-?9a2wI?1K8UgwAL9xx-~zRHF8+2HDaWPfEF)rI{6l{+^LO%RWW(S_Pmg@ ztO~e+{GG&epsrkV^LTwd>sposp6&rOa#s`2p{;>l@xMpbHC+Zuwf!fO&y{#P5z$ba z_5d8YSV^a6|5Cg>;nZu8^l8O^Iwm{>NZ#O9@^=!?m2&ty&v*`4R;U3;$~w^l>?WT} z=&W@{&JFO|UuqKAa60)M36bPWWaDekd{Md#=>&RC_f6zS5pR(`GtX%0$L0(W{8q3Q z20SwGz0ods2l;ruO!Rt@HfFjE9JRci$RJ;G>ocWe>!Nxv5@u_yF?|5*`x8niOmr0a zlx!?7!l0U*Od}XJ09u|%xxfhtAZ?aJkxyfLRHF#X9(f+nqwiq@;98Q>eIBK(dJ;He zkkW~7a!-PHkKG2KK3f(Pv(p36xkpKpvigknu=WthJKl?K1CWv(>7-kW)}ABGLYuoM zdCe&0R={I0ZUg8!`MXcO1uyxUS38}28d4>=4M6K&X)VoNikdM%i~QT5>0-(QNX9_F zw-)EM)(BXQD$U)mB!4y!;AC+a@&NR7a0>G-ECbRSH1bdMVl&{3hl6PVbRe}%>37aM zc(7X8pGZE3VGrPm^_cYC$zRXue+u~kau{%X0M6|L*!^m$MC9b)Yb0(ZAFodQ znVE;~k?K`70FH>>3>(9nnMRQ>$<@f$!UgxaHttm5{g@sgf_!R$c#I^o*ArM|j*_p< zA?aqPF?jU$j|Y%qpx0Gq#q7v1K;=axniA=|ateujDGZcS{{F9F6p`f5D%@iX)*3zJ zTk=QpAnNflfamaptA<*Iw}K;2i#6Uu%53@Lou_A=nlW%ak_>R+v`0^+2yX>@1Vvf2 zWoTR|(y9n;9tmC}^ThmBjWDi#5$K z0lfY&L?cFmw~?b6fh2df^EP5cQu({f7^31lmPPfF^0$GO;w*A{$8z_MCW@L7tTKj~ zINv=NOn86>k0%&OC`UuVY*LEltTc+WIIrYOe*kG_3K?=Snfd?#002ovPDHLkV1iK$ Bj&uM3 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Akira_128x64/frame_27.png b/assets/dolphin/external/L1_Akira_128x64/frame_27.png new file mode 100755 index 0000000000000000000000000000000000000000..397face78dfc1d6e247aaeccdf0171b4699b41a5 GIT binary patch literal 1426 zcmV;D1#S9?P)A4Fbst}zWszvpI{{;@6=y{8Dwk*kKKx(u=kV;!i){)pNUzw`HfJ~NtE`_(PQTg9T+rMHIJxGZjXOyRD*FPDtO8b8zb3CcVeLX`4q$n$aQ)Sw%Ig9>&uA{!mILe{AHWyaXX`sn391Zgc@D7qpd@(6 z|7I-D0aiZA(jZxCgnT^av|J#?4=fA;J7YfpuK-@RPvhoHb|;Px0lTfAgV*v;TR%os zDg4MjKm-xDe=7NvSfT(cT%%VeCj80S{%S1q04v;QgpjD)2YAlY#!M1et^g+;REzxR^iQYisd- zCV@*VcUe8gM**S_6Ds3!Wn!r*Q+I|>J=t#J2aXB>Rc>Fh_;J0{1xuEH3QDSg3gWz$ z&yh$EIqUAT&j7eH+1)~>IHnbxb^lsZM}l{*Cy?*ibE*?ag=LVH7wP1!B4h{oBeZdz zk^cO19~=+@qL#0Pf!TZ(*%9~ONj|q%?_AqMKxI35wZ)%6eg)|m+pB;K`P_Z8P-P7b zk><~8@}ql|b#4AaNQL{yheH)<_M}ge^CL+z z<`k9WclIet{yI2T=K2p2(tGA=;6a+&0dcnjkAoM z$-sN*>KEMiz3=+~nbJ>6&?;l2MP3DQo$j8)mK-2LzI4dg)n`j2k1}P*KdD9B{+~jl z!_UOv?lC{nmYq_m(~2y_`Tkz^1Q+Mab92XmYi+MM9DFUg`P7PT~jJ`v9K0 zzp_J!y%l|*$wQt@K0vbqc-+5d+>jkvi?<2oNpw#qAD}r0h{XO(mr$D%=LuF=iKL`^ zDhn-VsYL5v2V2KVrJhvR&uaO~b>TU+F2hMtkcjisMe zlaI$pl?l!bXs;uRwj|IJ9y}J3RVL5DSrlU?&(j}~NakeXanbWA%K-r9c|a!iOJj#z zs~joGlCEVBcOa55k9A-f+;uA7i?$q~IwnWJ9n^SF50#d08AQm(YoqJ1D6ti73J^Jf z6MaYuxS(6v0B+!c420gJ9 zyI)#%NnV!Z07*qoM6N<$f;o1pI{*Lx literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Akira_128x64/frame_28.png b/assets/dolphin/external/L1_Akira_128x64/frame_28.png new file mode 100755 index 0000000000000000000000000000000000000000..6ca246f57c4c8e7b8029958e5555b116f27c1536 GIT binary patch literal 2053 zcmV+g2>SPlP) zMJd%;GUr$!1C6m`(LAW_F5~Zz_s#@G5y1EB8ms~og}jbq^rKQ$mpic|(u59>MF8Kg z8UdmOMMkcmBg2mCZ2Fxk;VOO{K*^P>BY>KpqZE|!gs80w)zNntf0t9e$G}7e<-#li zOn7n?^;M9OP}aYKhyy4x|5d!k_*^JD$#c=?d47CGZPr*eGGr0L&ud}z>lok;K7iyf zLYWWS>{gu3L4OF>X$*?Q!9LMn|1EIY+q}D`&)jNW0uIz*``73l$_3V~`%CoX2JNH%0d0+%! zirAk_5k%^uYJ)wkvMUzO){2O%f=GX4Y-CiG0M9-vqd>?3B5y^W$rSG>-O2UJTqG6H$B2?X@ z&Dllt6kRf~=!taBq8ZaEtO6nqpuCS3)P?Fi)d-*%5fNB5TFm|#)IIlYUD-bz06n_| zE>c2azUmz6c$7OkvK8Z_y{hDC6fM>HGd_WSqaoNCq{vFvPIhv+FrXZlqlYzOUo0IOVN$cU+;Mc91ie>_wpuuW8;I4zTl@^{yH zJ@Z$IX9gGnc7=bIpV^5-CzE+>WmB0B8DCKIA|tVt6tjyrA))mgr_vZEBjP7KnEVVB{jeoRj0nAy7Q zoF5zjO=N|l?<_BQg^^J*mY?aAy^EqtG{?7%Z}k{M-xoD<`9tu_0Z<-^LJJzt+OBS# z8kVoj|1IPD)2gIg|DNg3s-p8e$2%i;YKe0tgsWJw`AX%mQ$ErM&CT|`Q#~;p$1&c! z!j6eozv!b=grD#hBXi>r+3=#!1D z8vlynyW^*xsuQROirxb}1uW&*cTvfT=3^x+lf!hWU`6hp^H^O}CtzIZ`14Nt+d3s* zu7<*nDYJTN*66yS$w>v;a+*WO< zVxZm5R^>~bJ@9j*vN3wz>Hy5pcNoIUMBl3hSedRWaupqH**IlDCm1(z0F;02yVXE4 ze4g~OSDZlhE+Vxn%FAvwa7zS;jK2e!jEFVwibN>&t(2EJ8k6&N5_lKkL-K5R^liTH z4*5I4I2Oa#9l%405UWp(ms_bw?z|MMfUV8rF%D44zgKcKGZtdcSB={pk4n=k4i?=7 zJi-C03*32U@QwS3F1k9}F|VNCEx1AoeW_#HpMnnYWjB~R@Q4V&7&7V;ja3c5`qD|2 z`6S1jc*H$`?ZeRd1r4hE6}i5C&kU#bVY+v)>H_Mv6o4))<^s?iwa4reh5aZMSXoFn z&pTn)dn*UZImq>%f_Q5vt>?JU`}k66hD-9nZec zwrrEo4WM_u~$u16-}G*=c4MIsUts-yxn%uN`ZuM3)Z7`llQq z+X&?sA(aiS>b8>LtwN^LSAHG5^Nu~r_mLw!=}K2|=7415g5sv@TH}SZUngEJgnTjk`6B)tZ_pG=o%>+lNm3%7|Qg_h~hU- z@v5}-Qyq*O9iVzuo6U*(XLk%$CqVKPbO>vf&X9iWX~&1WXgn+ebQo~8c8^Z>mDAp3 j?^Q=->8mPZj4}QLk;r=k|9eg|00000NkvXXu0mjffRO9u literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Akira_128x64/frame_29.png b/assets/dolphin/external/L1_Akira_128x64/frame_29.png new file mode 100755 index 0000000000000000000000000000000000000000..8bcc83a1db2c9656942f74f1f632247e78502c92 GIT binary patch literal 1891 zcmV-p2b}ncP)zkwjS# zQ=msnHLMu}uyXwuWw>&BmJ6%l>zV32qGFN^M8b0Bhz8O|M3X) zjlruot{MR%MMa^$0?)Gp7Bq6~U^Mw3;( zNN*&ad@`ci=s3yK+0BV^YjwU_%`&6Ql>bfM?-~9htJbqn>oMzBr3*kFEWhI>GPzXc z->r;0fNOuIfb75}Dz?9y~c%2(tps**=?zvY1j$M~*FPa{gd(g>!`0b1jV@v3+>2Whb{g?Ck6 z#&;z=ns#p#J zSBp)>5NFZpAdkq%uQq}Q6K)n>3g`>Ms@{%K(@{nFUXG6pbSCsFt!0#UFe2>4qce*7 z)zw9kSA1dKR|=>Mik!^oMvb6igq@FvOKN~j2=9Fqo|pnFg^d^Hqc)Anl0^_ZhOI{l z#VDI6!-ywe8UgH;3BsN!A&fm!YJ`esWDzQpGc#tjXhWk?m7hhrop@LQvZ9R%G}AUB z?8ZfOy%y5r=h2AhBQG>u_PIj$T|j>|Of9}xmFsypR{o5KotH&`NY+Z&MVY-+f=Xte zk*1X)3+Gh>UIl6Zna6v1nN{V{J$igrjC%7K&(WNVvNFSs*00hXk;}@n3V(h#kQFpp z4vd6lpVb9K8@<~rlv+Slenxi$v#G?&{`}oQMx5P`gfWX%d2qG&XY&-}AECQrUIovS zz^hP+03-92_wQJYdDwmqFq11gyn`M4&U-(C$onf$`uw{Pov*CKOA3gD!946KJgbs% ze$*0HFtR~AjKI>Hp{dkZRp5HvKM*k@oPC}NS}mY(EP8)+ld+9u1#ne1&nXo7vLbg^ zbdrvORUxuowz7zwxi34*urgF@2K4+W3VQSVc*M#*d3FI&f2&^a#At}klw+T%wFVlK z*FJkRJ>{?RewOBr*4F%$u+>n{0cLY-HT)~7VI>_N7$goTK5k4*g%z)+D z(Sx2$Syj3t^HjP~vHTSV5EWx8aQw`Pp{1G0IZ6xPd2RGZ89+7vN1=Ab5pU;_RrHLW zBIimP-r31z29TX~@8HF}bEWbvKb9U;IV-Y^&SiUW9LHJ%U`l42GJAld%Ag$DL>Qen z8p@+~afU9!@>pU3o|5e|&jMFMIWgSTTW6=18E{XoIt!RGfK18w%+6bF1ZtR8F{8{X zkE@k#G4=BRnUaS9;f-#!ly#nmRGhPv$?3h-egY?+<0q#`1U1#`T z!`L@HR(rqo5U&`D&fZ5220v%(V&1D5)13t#gO&L>oc|k$){2(ALl_2+C!44A{44rv zrO30gkML0VXIF2I#d)5u2Eb0WcIEw11Mp~NMsQVYhp2WRBiB*9w1KL}c?)2;BJAxh zP8EzOKU1oDRrw*pa{#r_$(X-O{@aztJTFvIQKW2@-VBBhf+|XlP7JTc3M0t8HcCI9 z(ox2%P&R-WC1Q?_@|UH*+Hf*uMVM87|IB2%18x`4+Fn7?3u%Qa6p2W$s#rCJDaDtf d{A~Qy_yvDSf*KIVECK)k002ovPDHLkV1h@ln#BMB literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Akira_128x64/frame_3.png b/assets/dolphin/external/L1_Akira_128x64/frame_3.png new file mode 100755 index 0000000000000000000000000000000000000000..7a1d2b36e828c299c37e05c6a3a078abcffc6b5b GIT binary patch literal 1908 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Akira_128x64/frame_31.png b/assets/dolphin/external/L1_Akira_128x64/frame_31.png new file mode 100755 index 0000000000000000000000000000000000000000..4f2deb5c58b47c14558a5183d1a7e9b90b16d71c GIT binary patch literal 1911 zcmV--2Z;EIP)^@RCt`_UG0+NI0#fydH+}T{W(pM9%vdN+nH=vZ8?!8K;m1u z-FqCzaXcQ6aU92B&xzwW#u(#mtOBIxy+%tw3qcI7kB6p(o%<`{3x`>YBUS*tZbB??6f{?^>@ zAxd=&kHgIF9u~{B)%9wNGruE0#W;536TH9bh|j26&$@t!`Ftu}0P;Yu$Io-Fs{Ff^ zaR(xYi9RpY08x&|yc{uBc(6;1%y3xv|5ptoLa7U2eP-tAti@A8zc+WvlF@nF@E3=F z3$+G%MM`DG89UTSk#;P5dVEyndwcoU5jhs^0>;oUW|(5Sf>u2LuOLdRci?dx?>`1b z$PU|65WZ8t&#_5|br*`RN9urq8pxgO_D+rWLZ(pem#qg`)bSy637AD&5rOIUnv?=siHR zxDqw6dtjc)=2Z{NGcZ<&cCn&#rLNiyEX?H^AVZ`$MTsmovUQ3)6+SD7XnLVMdm>Gb zHhZ@M=^mg1FRvlL%=lJ31P>Nvs2m_3dbFM?J$uiLoatz#D7T-tMSu$PtmuovzeS}0 zWroL;s~QOwmzeex*|Pnx!ntAu7@b=YKrtG-L3>b9nm~AmQ;7)dV7pbe(poYjT6Ml% z?erMrzX^;Oc$TLM!Xf|@bXTQr_ZbfSDUF_w9T7b>$f z5bgZ6*=+=tUWcYqL#>2;rH%geFN9RV zLRTiV(gC3NR}UGBTU44Ni${~m%#aniyQ5R;)MNDiji4@A#XUyf02HsUhFFYlmF~s6 zH$d7uLcL1oj&M+DdJNG!%Y2pwhmz22k_=Zk`#cx0Foz8I7IFU%BIG-vfL)Y`?D_ zc9z>GO)oOcC%XqZOZhAK6DgpIZf~uo3^pFoU0J8nT`88o$^cj~rUK`yhLF+BDteEeBIiyT)w`4J25?0ohrB%R+^Kxa&r1)goE2GC*Lr8b zOn(Eu_fYTfG&$3-lt$ zZlEJ{C2a196lWKo3jfICKS}vxkODK&Ry z#;d+louK+Vz*WG#%(1sQ$Nz7<_U($hYQ+ec@pyb9CCI;!IX7z2_43)R+$y5$;a~e0 z|Nk0@o4G>yR;cKZy}9$9Y6Q4el{qW&tn4d16#m&WT4TY^^(_LNmG@T-AflBS!Bwpt zqEj!Z&)s;d0jjweaYfiWyEs*_qWny$>Qm*X%KeI7u*EF&t_rskXQi>o3zbyFl&#Y1 z!lRpZ0TrcUL3gJi>@b4NYgzh@+1yC>5p;phJt xg4kWn+V99mDH4%hRk3OcQ;IJ|`PuwW!GA`&=&$EdWJ3S|002ovPDHLkV1m<}sCWPX literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Akira_128x64/frame_32.png b/assets/dolphin/external/L1_Akira_128x64/frame_32.png new file mode 100755 index 0000000000000000000000000000000000000000..4f59f99555b225edc829f6204cc80a5ba5497da7 GIT binary patch literal 1726 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 JPDHLkV1f?phW-Em literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Akira_128x64/frame_34.png b/assets/dolphin/external/L1_Akira_128x64/frame_34.png new file mode 100755 index 0000000000000000000000000000000000000000..5ffc55f75f3451585d426b352cc99df25d496269 GIT binary patch literal 1126 zcmV-s1eyDZP)USFbIIr@%>-f^P@+L6v8DJ z7XaAzeTQXPfW98=`wjqbIp%@3$9+zr_viYD+m-s9lvx14Eh&p|IR#2>MGY(3x8%zT z@D&<;rC?8>{ndi|AoY))lgjtA0enX*eWhS`tAX1%+_iR|6oNET@o_F8KvO`}UH1$D zsr))t{EyWG9B?kU#uZB;$Qq&&KNkbQ0_KEH-yh!McyGx8N=n2AXi;(+LOr2b00tYv zrD3C6w@m3u*Cus{9>5A7wGj`%je2TO>H7Q8!~^^^>Kw^jo7y@;8bcdY51`GxrI9bK z%xUcb{DzN~3q{G54gcergO_4I7uU5{d@Vhm^dr0!tnC?C6&XqX@V#1Noq_xnR{fMR z*p(k)z!I}(8|k?3Z$dQyE@o5rZ5g?xxXuZkGniiM{dhg&ZB%;zfUDcoglJ6$v2pcO z%o|5NrfCjf{oHdOj#f&0yYc~o9>6OaEu|Xyzo)5;N(jrpNGc>6$mp|7S9UpCnM)m??FdOWuxSTCBH}k^MEO zZ0_X80JL2?sa{W_v|oVV&oCNBGlLPVTFjygF0Hb2u8k1_nL+3?v6e-l=oD;!A1J5y3N8g>>yN4iww=Mh+GnRo=2``u{>cg;>V zfbv5c$LE{^z05wsX73*WFZY^$Z`$e{eg8zB(0?hvDy^Gv=Xk&883HU*K76xiLTLFy zt$Bh00PvwUDqrh$7Y{(;lwUjb<5mlkD>ZWV8ql);KSR4|Z3bPse$=MS>|UTp5~{U% zZU%9gf9XO`XG=*NVd99FIx9a%i~TDAR$%tc&C6f#rhd@g1a`tOjW^87&#sR z&ip-k!hLUdNm$FJ;N;6)GFky_KN1yD zgj9oItxc#cu6zsCzY&DdN}9QI*iH!#qFAp_>@+n13(?}yqz}Ul;9z8ha3XV?0dS=9 s$1_buP%8f(rZ9k1{(Veg0Lgv)16R;j8|j|I0ssI207*qoM6N<$f-x2lJpcdz literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Akira_128x64/frame_35.png b/assets/dolphin/external/L1_Akira_128x64/frame_35.png new file mode 100755 index 0000000000000000000000000000000000000000..9a101f0aa36d323084e06e505fff0b14cfccf8e7 GIT binary patch literal 1555 zcmV+u2JHEXP)Y+s7={Jr(w`|ES>9e$;2P>k<1c~8P|w*q&88ZnCD1k!a$&Xe(#4v;ne|77@q zuX6yNz7a>z()}kJzO`TdUk)&n5-V_pe5r<8_}>gK>3vvVUWttSh|^>o?9mze1(~m^{Cr5o`PfZ7yF^;x5bj_<$J_?lNJ<;&ly*x5E;|a4i{wl7KA$wlb zKh5y7`*@ybJ1T1Ls*RBhhsuhi{7*~wRRQ$Y1!UnxI~SN?;fSor7`px^Mg5A*x%3=B zQ&@6>iqa5WgBtg#hF@JSkIeaZHO6LV+PaLMJo1rpwt^5>%gNxy0nk?s$W2BvLIm0N z4vl)`0gxqA>n`{>PY-SU49YUVtU}TGyq!e(fh5b?aB4h$rhs7RMdw(0UTOTdE?{K_ zSdrF{&7|_9#u2iy_&#b- zE-O+N?lk^+p7&({YmSsDq)qlBKN@A49+`|#b&gPZHX`li!3SvzRA&IR1W3HYTbItG z*P_U<{xPq@2{OjF_Eid$AFAy5Cn;eq$`zpRwRIymL~jhq8WqOpIz*j(=3Jw-b)6H> z20zrm(oIqW@P3$I87qQKF26=GhD}mmh!D~%;!ta=>8zYia0HD5rbOmAlXJ5cXi}b z+~Ig!wpT*A)AZi|W&n;%`j?1_AW=WYq4(l;0U1c7!(#IiXFz07X!5MN zG&^ODe--)Tk<9hr`k*#VWq=H#>D|i&6k3#_nLyLUo8vh)q64CrMWCf|5;Uh#e-KiQ zs&-GyAks#o^Q7yVF5cW(WQ{+rd%FJ#p0YkA zgIF?J1D*v`LnCq(GA#m_{f_IlI`XCcpw~S1FRg2q&vp?9*wBHbcRDW8fmq{u@45dR zx={}cI?goHz89BrE>u3GrexD(b{%^TOieBgy>53-{K78y47`+IDpm#Xnj3b z%$idLX~Uuf7&(BE){|hflB2}^eBFNJR8QE?h6@dIrPCx6^g zb{qIGXe=Kgew>_-wX71f9pFZ2Z7MaKWjyUHfnU?QkJk3HfLq{2G)nBWt^tudKJN^0 zGkD6@fU^In?Ck(Iz&aVU(!F%(7HKiXHnQy!a2vF`?2V<@ve+EpA(;6+Km~RQxEax> zc&!y5Dz%*f9s%@epf@Ip?Envh_gJ|a+X22Dn*)3~{sFEAmOW?D3%LLQ002ovPDHLk FV1m_$>L>sJ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Akira_128x64/frame_4.png b/assets/dolphin/external/L1_Akira_128x64/frame_4.png new file mode 100755 index 0000000000000000000000000000000000000000..ea42d75d60c0de48db304dcdf196d5922e68279e GIT binary patch literal 1989 zcmV;$2RitPP)b7pAL76Bzti91_(i|2>na~qEB-G+e+J)(y!FVw_kBOY`642}><5fuClTOX zhVkU+8qSb*yiJTJJAToR0AMouBm#8!RgJpV+kG9_M^1ez+#~uC0EnUkP8@8PN7ZS% zfnD-Dng6%Jy{4rr)dZqLu~UHeO!NFiA7RJ3%08yad#ckdy{d}ZD9@Z9&AXp&AZ@c( zEq@}dbO z-&3fX(5(Qd-UNC|p&*zp4d`07dh4lqdCxvcJN9~Y?zT_e1L#sx&G1NHZ*4?2u$ePY ze5b0~|g1yCfjiljSs)-;X)yUyF4>5-1>Jre}> z+duocr-N;TD|ZS@FTE$ybISQ5f4&RQ8(0MJ1g(n?^mBr&;nDAE=2x=bbF_A9rq)|ItdX;IRW!&Zs%htoh+HC4PR*!uuPS-!vIuqlD44rrZ07ThJ&piYkayS&<~?1aeNm$tHGET) zB}R2(s8D4}(lm+y9z1866m{#?xt*~sa?C{b+r5!4)8y$Dq0G0}@@EW;3}c0{uo*eB zJK|~GvN6w|C4${u=QFaVgJYr*rV&7|@iJ<BBgmajKmJPQt#{ubWuk z?g7@%dLId0#AF8Q86sG&j*nVKgaBIp+JkhoJUcq>P8m~#{JHFOU03hRAS8f=&#E!9 zGO|d&j}X9vTSo6_PX_NN`h$rQ01-fEM#iAh7-f)XqZLIj%5H6B#`zWzqN&5v6_vZJ zZ#qGdt!Y>Idb_4_&PoGlb<6mB0p0BZzD)rvqiI;L>_;fVII~J8 zYIYU`*&4lWt=+Mk$S?vhfx#%vM7e&B_D8`R(aB~lDka?>pp#5m7JUQ+& z`pP;#)5$Y<6~U@|JYCp+3dye6-Lvo8dw?naOvfJ)%dfg7Bjmsizp2skx{K)>LH&tN zH*3V0ac%91rVwU~+#S2v`kkRyw5CdrLV(J6iPkWNL+hfZawpRwIn4Pioj}>Q{J((= zzs1XbhKQL0cGlYUohc`J#*C~P->G1r5P*TJtTfo@_YPC7#nG&f_-AsW&pTObb(<x&7j*nljWe@=>bX#XZ=jrgv^yr`f z6#z)sq9{1*XUI`mHZaCOLE!0?p-d&?QJ^8GdlVs3l~Q4 zbjCi<@tJNFomPOYANEYqI`j+%Kz4ApfzkU;)>N$vV*tjnCz>x5RNBejnzb&ImFbM0 zQzf7~t492y?DwG1qY6w$C2GlD`gzVk=eLezsDe6cz3nGgM!e{cOHp%Hk=W$CT|V#u^X zvU>r$Wgc2(w#KvVMW#k@Y&$<>j&eW>D4> zL}ka8D7&8Nrr8o>pD|5bXH68a*#r?$D*!aT6XMtmi~0;*B2Yh}XYfx^7J!pUR|#}n)6GU~yLwZL1vvn*{V_;@9ZIJ4P4?`KxT_dpFeWsu0Wy_U) z&gy)+;oL3(OZd)SHfz0SEFa4c)o$i2Y(TQ(ai*)4?LBMpzljrf?+Qwa5HdXY>qb9Q z3L+h?Gb-@R=p1$Z*+j$AC9?P5158oYv$za!dI#hLG*RvB(hsFY60`8dRi&@{5 zolzE`J4jjGJ7I5;VgO7|0g;T1&(z_kXd)098&21Mk)Z%s&n6X%GV&>oKSige9YntY X2l-HT4d<&o00000NkvXXu0mjfqrTcj literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Akira_128x64/frame_5.png b/assets/dolphin/external/L1_Akira_128x64/frame_5.png new file mode 100755 index 0000000000000000000000000000000000000000..c347d3035380bfec476a597479fa0d1a82f5084d GIT binary patch literal 2000 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 Hu0mjfR2qjA literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Akira_128x64/frame_7.png b/assets/dolphin/external/L1_Akira_128x64/frame_7.png new file mode 100755 index 0000000000000000000000000000000000000000..275f2a35770c4d3b88e915969cd713f16e6eb690 GIT binary patch literal 1868 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 Hu0mjf3G!+@ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Akira_128x64/meta.txt b/assets/dolphin/external/L1_Akira_128x64/meta.txt new file mode 100755 index 000000000..4ac9b38b3 --- /dev/null +++ b/assets/dolphin/external/L1_Akira_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 15 +Active frames: 21 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 0 \ No newline at end of file diff --git a/assets/dolphin/external/manifest.txt b/assets/dolphin/external/manifest.txt index d9488cbba..70b2b7159 100644 --- a/assets/dolphin/external/manifest.txt +++ b/assets/dolphin/external/manifest.txt @@ -190,6 +190,13 @@ Min level: 3 Max level: 3 Weight: 5 +Name: L1_Akira_128x64 +Min butthurt: 0 +Max butthurt: 8 +Min level: 1 +Max level: 3 +Weight: 5 + Name: L3_Fireplace_128x64 Min butthurt: 0 Max butthurt: 13 diff --git a/assets/icons/Dolphin/DolphinWait_61x59.png b/assets/icons/Dolphin/DolphinWait_61x59.png deleted file mode 100644 index 423e079199b00df0d910981caf8944cbaf8ee67e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2023 zcmbVN2~ZPP7!Hc#RVu|&R6N!I%3-nxkdW1AfgnT)&=3`rikr>mC`oqNT?mIZ73u(L zrQQXsTAVP$bgCRqVL%3}R4ZPzma(V>JPRlyt(Mwx+HOKfI~`kFcXs!^eeZkUfB##O zlo0DNW!4lPkLMwelPS4T$~}uGjpN?2soqDpVKNn$%J6tor`sPlUipC;Jl=#i45}11 zMG=qUq)CWrNHrnMF;N_v$6K;2hr;j-f(6us&R~}EhnidYfI%bWuL)N`3M!h=8{+b4 zA~`QXh39495)FUZQea6A$`P0d76WojMl*xvNcj$4l$+a^K|bJsuo+T*q+KA8qDTUw zNtyseLP&r^5CVuLLRb_QCW00L2!uc&6b{0O02ZN87z&F4=f&rw(HbqPlr4A4;=ZJO zJE3n9Bn4xk2i;ixRy=n$^KLBdFw2s6uYSlET-yrfXL z;LoKsnOtawjmhRTa@zJ>G^5I;2vA8dWEPDRG1;6%zcIxqJ;{=cp8N+pT-z>dC^VWT zFqWiMBxxKARMHp=fWSfo2wY<@Ye)+dWS8PRK*%tbkn*{x!2$^3ZWV%{P&f+1AuxnO z&?r>F<$(rcvHu1pH3n_&3!xeu)snOc(Gwi$zvRUzj3KqG1*3^b z9p~;B<{ii>584ZM)DH0PCOY>1Qru&3u4CAzu2#i;xSAbd<~khBwX$#Sj?d@u##!XD zNR@tb=h}6&`}|35K||KN=gwBmj&-Xj(w;uMx-L?`(_*_ZqL8ard_B`EtMjXyZhc;> zZF)8CG-7Mu_=mE#!jz);z6%T5S~mVv@At*=X|?Ol?+v-67}*3~ z(I*yd*!+Hl{6bH0ad%YF(4l>;^=h9m2|jf9+!^TVd3?C0_k*j|7SExD#fc+65!1f++)=)#z9VKCyt!)o1FiS^CqLF68$Nlu@R-*F`KK+GldJrn z-6KW!*!*?i?t>eHLo#YCZFefNm-}Sy+HzW#(kJAQ% zu3Sm`#Ai<6U{7oT)is=0nUQ{99C7-Y$5MCXl>F$xVZhye@tt~FtDW0WISrk6aF6fm zc|x z#r8!%cqXJshqsp~m3WISwlJh}Z|BwFsa3Puno2spY&@ROyQ9E+zRBvfE9A|~$kXqv z6B}Ob_YXK*dq(uIvE9}XXM5*fnIhFsF*9M3Y+v$?f)8s}ZTskiwP8;Zmba>-dq;Q2 swOjp-{igh?qWDbj&x1Zp}@CP3WTf1pBX89+MzD8nvO|4+g31aR2}S diff --git a/assets/icons/Settings/Cry_dolph_55x52.png b/assets/icons/Settings/LoadingHourglass_24x24.png similarity index 70% rename from assets/icons/Settings/Cry_dolph_55x52.png rename to assets/icons/Settings/LoadingHourglass_24x24.png index 86d9db1b497cd9e49bb62987cb35075b4bc7a410..9c49dcad1cb6060c4057ad5c44d67f440c2a91b1 100644 GIT binary patch delta 422 zcmdlbcSuIDGr-TCmrII^fq{Y7)59eQNK1e)2Q!eozVhs;jf&-5j2e??a`kB#>Lwc) z7$}%q>RBcyC!3_CS{myp7#SED=^Gg98=B}Enphc_TNxQm_T?^_tiz);c^7x~WIkR6 zeq%jj1r6WC(@vVoXfTjNL2^%w0_l%nc1)ogJM_ja}WG%`J=!jVz6g zEtM21atnNYtz7bxOLJ56N<3X`m4G68DVZr&P`wto^iHoSkw4cEn{ pO$PgN{;}_HzIpl9d4&=d35G^5hRi9SpH2l?;pyt^U8eBS^7 delta 696 zcmX>kvrA5~Gr-TCmrII^fq{Y7)59eQNSgz(2?sNfl$;oFYolU07h~DvnOuEp2D-@x z1_lb|mU=0PNr}lxNjeHf21bVZ1}6H3Cb|X&R>qcACMJ{pxl1O$;ZB*nhg;L7z}MHx zzbG?3GcPg6B|o_|H#M)s)5TV)BDX*CnUiJFPK_^myVa^ud6XN>+|9>De^w8xxAobJJ#WAGf)|P3v z`Hm=XxXk?j|D~_l-ED`@Hm1CFp8ZHLP{OpE_li$WhwqX}g~vj^|IU?<-sg5(fvPv+mW0F?cs@P-Z_VT$$NG{hH`nVv zlUXCaFKOP6gLCvACkH&!sy`rF_L%=!(v$w{^*^3oN|~~gdE1i<1+O@bw2yXO77kgf z#DDEo?1Ytqd#-*JoKmzopr06iN2s{jB1 diff --git a/assets/icons/Settings/dolph_cry_49x54.png b/assets/icons/Settings/dolph_cry_49x54.png new file mode 100644 index 0000000000000000000000000000000000000000..351a849b09030fd4628bbbf582503dfb83672c59 GIT binary patch literal 973 zcmV;;12X)HP)^74TPjRsUxkj1>*;PV6t7eFDw(WuIv6mwruS4`RwBiLhH>T z{w$4%m|Dg4qvN>VK1(Upyp@rywexF>C)i^7{(8MCrLVy9Z9!%5NH{TpXdrs6H9UHa z5tNL;i)}WJG~PVpYsanUTl7ke$dvP#V@%tpH;=Rut}S|{M%YEIJL9co;dDw;^F6Cx z$C{R$Km$;4PdV&`Rz~sBj04qO|QCb*yOzu=3VLgIgDU4W=?ZS-np$Do9 zhIg?90##V7#>C3X=H0*(UG*tA$;#mqvEK`;^KJx|I80`*G(HKtxvHb0M2oTx^GQRZ zjXW8l6~GHGuao=%-Y?aPPG+T!&!Mk8ZIfTy+8p>pnN1$ zM{-HmvWmISe<{7O+N*>!( z{e2b19v&n4Ah0~v;G(bJalS_mL-X5?9s$fNN;B$hE5mjbdn8;*TyJgW1^nJ450hI^ zo!E@ATh?HQ*?1H}tuGE3Eu!jT*EN1~XI;q723Df=ndO_!B`g%U7L7XY*jel{Lzy>v z!!x=);vDO+1%M3qrZ$DTrql1s--KqG{?#)hgFUPmL_Q`q<_79m{qTrHKj{%mgL>gvQdo~O z(*W-T_vTa&OKbIFhvCq_Mf4)ulx$Add!@Pg$M`GA@@)IQc1CSRm`6`|Vg#N+4Ab5K v&c;UCdJm6;)7|(ljL0hGuP2^DR(t*eG-K{iyaDCP00000NkvXXu0mjfItI*` literal 0 HcmV?d00001 diff --git a/assets/icons/Settings/qr_benchmark_25x25.png b/assets/icons/Settings/qr_benchmark_25x25.png new file mode 100644 index 0000000000000000000000000000000000000000..c5f9df119582ba1625b5c40d869150d72e49bf37 GIT binary patch literal 395 zcmV;60d)R}P)P000>X1^@s6#OZ}&00009a7bBm000XU z000XU0RWnu7ytkO0drDELIAGL9O(c600d`2O+f$vv5yPj=?b%4XtA$ZUrb~kgF?s8V;qp}O#oaJ%lv*EAWmF>5n1cxby zZ@WK8Co29`v?;A*8@aB&yK3c{ZjAdo-yow0t#gmg;IW%dkZh1tUj>h|n{q#{*|F34 z*{Pe2!F^X5K6B(u(0zn1Pbpp zTxys%i6%2vhazbe_HAZVefNc^`O$7OBU_K!K=C@rx_buffer, sizeof(uint8_t)); } else { FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size - 1); diff --git a/scripts/map_analyse_upload.py b/scripts/map_analyse_upload.py new file mode 100755 index 000000000..38d961879 --- /dev/null +++ b/scripts/map_analyse_upload.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 + +import os +import requests +import argparse +import subprocess + +# usage: +# COMMIT_HASH, COMMIT_MSG, BRANCH_NAME, +# PULL_ID(optional), PULL_NAME(optional) must be set as envs +# maybe from sctipts/get_env.py +# other args must be set via command line args + + +class AnalyseRequest: + def __init__(self): + self.commit_hash = os.environ["COMMIT_HASH"] + self.commit_msg = os.environ["COMMIT_MSG"] + self.branch_name = os.environ["BRANCH_NAME"] + self.pull_id = os.getenv("PULL_ID", default=None) + self.pull_name = os.getenv("PULL_NAME", default=None) + + def get_payload(self): + return vars(self) + + +class AnalyseUploader: + def __init__(self): + self.args = self.parse_args() + + @staticmethod + def get_sections_size(elf_file) -> dict: + ret = dict() + all_sizes = subprocess.check_output( + ["arm-none-eabi-size", "-A", elf_file], shell=False + ) + all_sizes = all_sizes.splitlines() + + sections_to_keep = (".text", ".rodata", ".data", ".bss", ".free_flash") + for line in all_sizes: + line = line.decode("utf-8") + parts = line.split() + if len(parts) != 3: + continue + section, size, _ = parts + if section not in sections_to_keep: + continue + section_size_payload_name = ( + section[1:] if section.startswith(".") else section + ) + section_size_payload_name += "_size" + ret[section_size_payload_name] = size + return ret + + @staticmethod + def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("--elf_file", help="Firmware ELF file", required=True) + parser.add_argument("--map_file", help="Firmware MAP file", required=True) + parser.add_argument( + "--analyser_token", help="Analyser auth token", required=True + ) + parser.add_argument( + "--analyser_url", help="Analyser analyse url", required=True + ) + args = parser.parse_args() + return args + + def upload_analyse_request(self): + payload = AnalyseRequest().get_payload() | self.get_sections_size( + self.args.elf_file + ) + headers = {"Authorization": f"Bearer {self.args.analyser_token}"} + file = {"map_file": open(self.args.map_file, "rb")} + response = requests.post( + self.args.analyser_url, data=payload, files=file, headers=headers + ) + if not response.ok: + raise Exception( + f"Failed to upload map file, code: {response.status_code}, reason: {response.text}" + ) + + +if __name__ == "__main__": + analyzer = AnalyseUploader() + analyzer.upload_analyse_request() diff --git a/scripts/map_mariadb_insert.py b/scripts/map_mariadb_insert.py deleted file mode 100755 index a4c9ed5c7..000000000 --- a/scripts/map_mariadb_insert.py +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/env python3 - -# Requiremets: -# mariadb==1.1.6 - -from datetime import datetime -import argparse -import mariadb -import sys -import os - - -def parseArgs(): - parser = argparse.ArgumentParser() - parser.add_argument("db_user", help="MariaDB user") - parser.add_argument("db_pass", help="MariaDB password") - parser.add_argument("db_host", help="MariaDB hostname") - parser.add_argument("db_port", type=int, help="MariaDB port") - parser.add_argument("db_name", help="MariaDB database") - parser.add_argument("report_file", help="Report file(.map.all)") - args = parser.parse_args() - return args - - -def mariadbConnect(args): - try: - conn = mariadb.connect( - user=args.db_user, - password=args.db_pass, - host=args.db_host, - port=args.db_port, - database=args.db_name, - ) - except mariadb.Error as e: - print(f"Error connecting to MariaDB: {e}") - sys.exit(1) - return conn - - -def parseEnv(): - outArr = [] - outArr.append(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) - outArr.append(os.getenv("COMMIT_HASH", default=None)) - outArr.append(os.getenv("COMMIT_MSG", default=None)) - outArr.append(os.getenv("BRANCH_NAME", default=None)) - outArr.append(os.getenv("BSS_SIZE", default=None)) - outArr.append(os.getenv("TEXT_SIZE", default=None)) - outArr.append(os.getenv("RODATA_SIZE", default=None)) - outArr.append(os.getenv("DATA_SIZE", default=None)) - outArr.append(os.getenv("FREE_FLASH_SIZE", default=None)) - outArr.append(os.getenv("PULL_ID", default=None)) - outArr.append(os.getenv("PULL_NAME", default=None)) - return outArr - - -def createTables(cur, conn): - headerTable = "CREATE TABLE IF NOT EXISTS `header` ( \ - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, \ - `datetime` datetime NOT NULL, \ - `commit` varchar(40) NOT NULL, \ - `commit_msg` text NOT NULL, \ - `branch_name` text NOT NULL, \ - `bss_size` int(10) unsigned NOT NULL, \ - `text_size` int(10) unsigned NOT NULL, \ - `rodata_size` int(10) unsigned NOT NULL, \ - `data_size` int(10) unsigned NOT NULL, \ - `free_flash_size` int(10) unsigned NOT NULL, \ - `pullrequest_id` int(10) unsigned DEFAULT NULL, \ - `pullrequest_name` text DEFAULT NULL, \ - PRIMARY KEY (`id`), \ - KEY `header_id_index` (`id`) )" - dataTable = "CREATE TABLE IF NOT EXISTS `data` ( \ - `header_id` int(10) unsigned NOT NULL, \ - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, \ - `section` text NOT NULL, \ - `address` text NOT NULL, \ - `size` int(10) unsigned NOT NULL, \ - `name` text NOT NULL, \ - `lib` text NOT NULL, \ - `obj_name` text NOT NULL, \ - PRIMARY KEY (`id`), \ - KEY `data_id_index` (`id`), \ - KEY `data_header_id_index` (`header_id`), \ - CONSTRAINT `data_header_id_foreign` FOREIGN KEY (`header_id`) REFERENCES `header` (`id`) )" - cur.execute(headerTable) - cur.execute(dataTable) - conn.commit() - - -def insertHeader(data, cur, conn): - query = "INSERT INTO `header` ( \ - datetime, commit, commit_msg, branch_name, bss_size, text_size, \ - rodata_size, data_size, free_flash_size, pullrequest_id, pullrequest_name) \ - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" - cur.execute(query, data) - conn.commit() - return cur.lastrowid - - -def parseFile(fileObj, headerID): - arr = [] - fileLines = fileObj.readlines() - for line in fileLines: - lineArr = [] - tempLineArr = line.split("\t") - lineArr.append(headerID) - lineArr.append(tempLineArr[0]) # section - lineArr.append(int(tempLineArr[2], 16)) # address hex - lineArr.append(int(tempLineArr[3])) # size - lineArr.append(tempLineArr[4]) # name - lineArr.append(tempLineArr[5]) # lib - lineArr.append(tempLineArr[6]) # obj_name - arr.append(tuple(lineArr)) - return arr - - -def insertData(data, cur, conn): - query = "INSERT INTO `data` ( \ - header_id, section, address, size, \ - name, lib, obj_name) \ - VALUES (?, ?, ?, ?, ? ,?, ?)" - cur.executemany(query, data) - conn.commit() - - -def main(): - args = parseArgs() - dbConn = mariadbConnect(args) - reportFile = open(args.report_file) - dbCurs = dbConn.cursor() - createTables(dbCurs, dbConn) - headerID = insertHeader(parseEnv(), dbCurs, dbConn) - insertData(parseFile(reportFile, headerID), dbCurs, dbConn) - reportFile.close() - dbCurs.close() - - -if __name__ == "__main__": - main() diff --git a/scripts/map_parser.py b/scripts/map_parser.py deleted file mode 100755 index 1efc4fe82..000000000 --- a/scripts/map_parser.py +++ /dev/null @@ -1,274 +0,0 @@ -#!/usr/bin/env python3 - -# Requiremets: -# cxxfilt==0.3.0 - -# Most part of this code written by Lars-Dominik Braun https://github.com/PromyLOPh/linkermapviz -# and distributes under MIT licence - -# Copyright (c) 2017 Lars-Dominik Braun -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -import sys -import re -import os -from typing import TextIO -from cxxfilt import demangle - - -class Objectfile: - def __init__(self, section: str, offset: int, size: int, comment: str): - self.section = section.strip() - self.offset = offset - self.size = size - self.path = (None, None) - self.basepath = None - - if comment: - self.path = re.match(r"^(.+?)(?:\(([^\)]+)\))?$", comment).groups() - self.basepath = os.path.basename(self.path[0]) - - self.children = [] - - def __repr__(self) -> str: - return f"" - - -def update_children_size(children: list[list], subsection_size: int) -> list: - # set subsection size to an only child - if len(children) == 1: - children[0][1] = subsection_size - return children - - rest_size = subsection_size - - for index in range(1, len(children)): - if rest_size > 0: - # current size = current address - previous child address - child_size = children[index][0] - children[index - 1][0] - rest_size -= child_size - children[index - 1][1] = child_size - - # if there is rest size, set it to the last child element - if rest_size > 0: - children[-1][1] = rest_size - - return children - - -def parse_sections(file_name: str) -> list: - """ - Quick&Dirty parsing for GNU ld’s linker map output, needs LANG=C, because - some messages are localized. - """ - - sections = [] - with open(file_name, "r") as file: - # skip until memory map is found - found = False - - while True: - line = file.readline() - if not line: - break - if line.strip() == "Memory Configuration": - found = True - break - - if not found: - raise Exception(f"Memory configuration is not found in the{input_file}") - - # long section names result in a linebreak afterwards - sectionre = re.compile( - "(?P
.+?|.{14,}\n)[ ]+0x(?P[0-9a-f]+)[ ]+0x(?P[0-9a-f]+)(?:[ ]+(?P.+))?\n+", - re.I, - ) - subsectionre = re.compile( - "[ ]{16}0x(?P[0-9a-f]+)[ ]+(?P.+)\n+", re.I - ) - s = file.read() - pos = 0 - - while True: - m = sectionre.match(s, pos) - if not m: - # skip that line - try: - nextpos = s.index("\n", pos) + 1 - pos = nextpos - continue - except ValueError: - break - - pos = m.end() - section = m.group("section") - v = m.group("offset") - offset = int(v, 16) if v is not None else None - v = m.group("size") - size = int(v, 16) if v is not None else None - comment = m.group("comment") - - if section != "*default*" and size > 0: - of = Objectfile(section, offset, size, comment) - - if section.startswith(" "): - children = [] - sections[-1].children.append(of) - - while True: - m = subsectionre.match(s, pos) - if not m: - break - pos = m.end() - offset, function = m.groups() - offset = int(offset, 16) - if sections and sections[-1].children: - children.append([offset, 0, function]) - - if children: - children = update_children_size( - children=children, subsection_size=of.size - ) - - sections[-1].children[-1].children.extend(children) - - else: - sections.append(of) - - return sections - - -def get_subsection_name(section_name: str, subsection: Objectfile) -> str: - subsection_split_names = subsection.section.split(".") - if subsection.section.startswith("."): - subsection_split_names = subsection_split_names[1:] - - return ( - f".{subsection_split_names[1]}" - if len(subsection_split_names) > 2 - else section_name - ) - - -def write_subsection( - section_name: str, - subsection_name: str, - address: str, - size: int, - demangled_name: str, - module_name: str, - file_name: str, - mangled_name: str, - write_file_object: TextIO, -) -> None: - write_file_object.write( - f"{section_name}\t" - f"{subsection_name}\t" - f"{address}\t" - f"{size}\t" - f"{demangled_name}\t" - f"{module_name}\t" - f"{file_name}\t" - f"{mangled_name}\n" - ) - - -def save_subsection( - section_name: str, subsection: Objectfile, write_file_object: TextIO -) -> None: - subsection_name = get_subsection_name(section_name, subsection) - module_name = subsection.path[0] - file_name = subsection.path[1] - - if not file_name: - file_name, module_name = module_name, "" - - if not subsection.children: - address = f"{subsection.offset:x}" - size = subsection.size - mangled_name = ( - "" - if subsection.section == section_name - else subsection.section.split(".")[-1] - ) - demangled_name = demangle(mangled_name) if mangled_name else mangled_name - - write_subsection( - section_name=section_name, - subsection_name=subsection_name, - address=address, - size=size, - demangled_name=demangled_name, - module_name=module_name, - file_name=file_name, - mangled_name=mangled_name, - write_file_object=write_file_object, - ) - return - - for subsection_child in subsection.children: - address = f"{subsection_child[0]:x}" - size = subsection_child[1] - mangled_name = subsection_child[2] - demangled_name = demangle(mangled_name) - - write_subsection( - section_name=section_name, - subsection_name=subsection_name, - address=address, - size=size, - demangled_name=demangled_name, - module_name=module_name, - file_name=file_name, - mangled_name=mangled_name, - write_file_object=write_file_object, - ) - - -def save_section(section: Objectfile, write_file_object: TextIO) -> None: - section_name = section.section - for subsection in section.children: - save_subsection( - section_name=section_name, - subsection=subsection, - write_file_object=write_file_object, - ) - - -def save_parsed_data(parsed_data: list[Objectfile], output_file_name: str) -> None: - with open(output_file_name, "w") as write_file_object: - for section in parsed_data: - if section.children: - save_section(section=section, write_file_object=write_file_object) - - -if __name__ == "__main__": - if len(sys.argv) < 3: - raise Exception(f"Usage: {sys.argv[0]} ") - - input_file = sys.argv[1] - output_file = sys.argv[2] - - parsed_sections = parse_sections(input_file) - - if parsed_sections is None: - raise Exception(f"Memory configuration is not {input_file}") - - save_parsed_data(parsed_sections, output_file) diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 3cee044aa..492539d46 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,61.2,, +Version,+,61.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2554,7 +2554,9 @@ Function,-,strxfrm,size_t,"char*, const char*, size_t" Function,-,strxfrm_l,size_t,"char*, const char*, size_t, locale_t" Function,+,submenu_add_item,void,"Submenu*, const char*, uint32_t, SubmenuItemCallback, void*" Function,+,submenu_alloc,Submenu*, +Function,+,submenu_change_item_label,void,"Submenu*, uint32_t, const char*" Function,+,submenu_free,void,Submenu* +Function,+,submenu_get_selected_item,uint32_t,Submenu* Function,+,submenu_get_view,View*,Submenu* Function,+,submenu_reset,void,Submenu* Function,+,submenu_set_header,void,"Submenu*, const char*" diff --git a/targets/f18/furi_hal/furi_hal.c b/targets/f18/furi_hal/furi_hal.c index 6cfc939b8..247c2ee2d 100644 --- a/targets/f18/furi_hal/furi_hal.c +++ b/targets/f18/furi_hal/furi_hal.c @@ -17,6 +17,7 @@ void furi_hal_init_early(void) { furi_hal_i2c_init_early(); furi_hal_light_init(); furi_hal_rtc_init_early(); + furi_hal_version_init(); } void furi_hal_deinit_early(void) { @@ -39,7 +40,6 @@ void furi_hal_init(void) { furi_hal_interrupt_init(); furi_hal_flash_init(); furi_hal_resources_init(); - furi_hal_version_init(); furi_hal_spi_config_init(); furi_hal_spi_dma_init(); furi_hal_speaker_init(); diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 218166100..08f700bd2 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,61.2,, +Version,+,61.3,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -3426,7 +3426,9 @@ Function,+,subghz_worker_stop,void,SubGhzWorker* Function,+,submenu_add_item,void,"Submenu*, const char*, uint32_t, SubmenuItemCallback, void*" Function,+,submenu_add_lockable_item,void,"Submenu*, const char*, uint32_t, SubmenuItemCallback, void*, _Bool, const char*" Function,+,submenu_alloc,Submenu*, +Function,+,submenu_change_item_label,void,"Submenu*, uint32_t, const char*" Function,+,submenu_free,void,Submenu* +Function,+,submenu_get_selected_item,uint32_t,Submenu* Function,+,submenu_get_view,View*,Submenu* Function,+,submenu_reset,void,Submenu* Function,+,submenu_set_header,void,"Submenu*, const char*" diff --git a/targets/f7/ble_glue/gap.c b/targets/f7/ble_glue/gap.c index 5a95cff26..bdef259e3 100644 --- a/targets/f7/ble_glue/gap.c +++ b/targets/f7/ble_glue/gap.c @@ -40,6 +40,8 @@ typedef struct { FuriThread* thread; FuriMessageQueue* command_queue; bool enable_adv; + bool is_secure; + uint8_t negotiation_round; } Gap; typedef enum { @@ -87,17 +89,46 @@ static void gap_verify_connection_parameters(Gap* gap) { // Send connection parameters request update if necessary GapConnectionParamsRequest* params = &gap->config->conn_param; - if(params->conn_int_min > gap->connection_params.conn_interval || - params->conn_int_max < gap->connection_params.conn_interval) { - FURI_LOG_W(TAG, "Unsupported connection interval. Request connection parameters update"); + + // Desired max connection interval depends on how many negotiation rounds we had in the past + // In the first negotiation round we want connection interval to be minimum + // If platform disagree then we request wider range + uint16_t connection_interval_max = gap->negotiation_round ? params->conn_int_max : + params->conn_int_min; + + // We do care about lower connection interval bound a lot: if it's lower than 30ms 2nd core will not allow us to use flash controller + bool negotiation_failed = params->conn_int_min > gap->connection_params.conn_interval; + + // We don't care about upper bound till connection become secure + if(gap->is_secure) { + negotiation_failed |= connection_interval_max < gap->connection_params.conn_interval; + } + + if(negotiation_failed) { + FURI_LOG_W( + TAG, + "Connection interval doesn't suite us. Trying to negotiate, round %u", + gap->negotiation_round + 1); if(aci_l2cap_connection_parameter_update_req( gap->service.connection_handle, params->conn_int_min, - params->conn_int_max, + connection_interval_max, gap->connection_params.slave_latency, gap->connection_params.supervisor_timeout)) { FURI_LOG_E(TAG, "Failed to request connection parameters update"); + // The other side is not in the mood + // But we are open to try it again + gap->negotiation_round = 0; + } else { + gap->negotiation_round++; } + } else { + FURI_LOG_I( + TAG, + "Connection interval suits us. Spent %u rounds to negotiate", + gap->negotiation_round); + // Looks like the other side is open to negotiation + gap->negotiation_round = 0; } } @@ -112,9 +143,9 @@ BleEventFlowStatus ble_event_app_notification(void* pckt) { event_pckt = (hci_event_pckt*)((hci_uart_pckt*)pckt)->data; - if(gap) { - furi_mutex_acquire(gap->state_mutex, FuriWaitForever); - } + furi_check(gap); + furi_mutex_acquire(gap->state_mutex, FuriWaitForever); + switch(event_pckt->evt) { case HCI_DISCONNECTION_COMPLETE_EVT_CODE: { hci_disconnection_complete_event_rp0* disconnection_complete_event = @@ -125,6 +156,8 @@ BleEventFlowStatus ble_event_app_notification(void* pckt) { FURI_LOG_I( TAG, "Disconnect from client. Reason: %02X", disconnection_complete_event->Reason); } + gap->is_secure = false; + gap->negotiation_round = 0; // Enterprise sleep furi_delay_us(666 + 666); if(gap->enable_adv) { @@ -232,6 +265,7 @@ BleEventFlowStatus ble_event_app_notification(void* pckt) { case ACI_GAP_SLAVE_SECURITY_INITIATED_VSEVT_CODE: FURI_LOG_D(TAG, "Slave security initiated"); + gap->is_secure = true; break; case ACI_GAP_BOND_LOST_VSEVT_CODE: @@ -293,9 +327,9 @@ BleEventFlowStatus ble_event_app_notification(void* pckt) { default: break; } - if(gap) { - furi_mutex_release(gap->state_mutex); - } + + furi_mutex_release(gap->state_mutex); + return BleEventFlowEnable; } @@ -539,6 +573,10 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) { gap->thread = furi_thread_alloc_ex("BleGapDriver", 1024, gap_app, gap); furi_thread_start(gap->thread); + // Set initial state + gap->is_secure = false; + gap->negotiation_round = 0; + uint8_t adv_service_uid[2]; gap->service.adv_svc_uuid_len = 1; adv_service_uid[0] = gap->config->adv_service_uuid & 0xff; diff --git a/targets/f7/ble_glue/profiles/serial_profile.c b/targets/f7/ble_glue/profiles/serial_profile.c index 165b81330..118a76e8c 100644 --- a/targets/f7/ble_glue/profiles/serial_profile.c +++ b/targets/f7/ble_glue/profiles/serial_profile.c @@ -47,7 +47,7 @@ static GapConfig serial_template_config = { .pairing_method = GapPairingPinCodeShow, .conn_param = { .conn_int_min = 0x18, // AN5289: 4.7, we need at least 25ms + advertisement, which is 30 ms - .conn_int_max = 0x18, // 30 ms + .conn_int_max = 0x24, // 45 ms .slave_latency = 0, .supervisor_timeout = 0, }}; diff --git a/targets/f7/furi_hal/furi_hal.c b/targets/f7/furi_hal/furi_hal.c index 3686658f7..7e713a08d 100644 --- a/targets/f7/furi_hal/furi_hal.c +++ b/targets/f7/furi_hal/furi_hal.c @@ -17,6 +17,7 @@ void furi_hal_init_early(void) { furi_hal_i2c_init_early(); furi_hal_light_init(); furi_hal_rtc_init_early(); + furi_hal_version_init(); } void furi_hal_deinit_early(void) { @@ -39,7 +40,6 @@ void furi_hal_init(void) { furi_hal_interrupt_init(); furi_hal_flash_init(); furi_hal_resources_init(); - furi_hal_version_init(); furi_hal_spi_config_init(); furi_hal_spi_dma_init(); furi_hal_ibutton_init();