From a7715704f89990963d33b0c1fcb405de384d8400 Mon Sep 17 00:00:00 2001 From: hakuyoku2011 <92091283+hakuyoku2011@users.noreply.github.com> Date: Tue, 14 May 2024 21:15:30 +0900 Subject: [PATCH 1/4] Infrared: Add Toshiba RAS-2518D (#3635) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Infrared: Add Toshiba RAS-2518D * Infrared: cleanup AC universal remote Co-authored-by: あく --- .../infrared/resources/infrared/assets/ac.ir | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/applications/main/infrared/resources/infrared/assets/ac.ir b/applications/main/infrared/resources/infrared/assets/ac.ir index 4e1735246..68abaf259 100644 --- a/applications/main/infrared/resources/infrared/assets/ac.ir +++ b/applications/main/infrared/resources/infrared/assets/ac.ir @@ -826,3 +826,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 From 5f9b300ab2a55a1a8424c11078e7899d0e25d912 Mon Sep 17 00:00:00 2001 From: gornekich Date: Tue, 14 May 2024 13:34:27 +0100 Subject: [PATCH 2/4] NFC: Mf Desfire fix reading big files (#3616) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * mf desfire: fix incorrect long files reading * nfc app: trim record size for mf desfire render * mf desfire: rework reading long record files * mf desfire: more robust size check Co-authored-by: あく --- .../mf_desfire/mf_desfire_render.c | 24 +++--- .../mf_desfire/mf_desfire_poller_i.c | 77 ++++++++++--------- 2 files changed, 54 insertions(+), 47 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire_render.c b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire_render.c index f8eacd51a..23a1a3b69 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire_render.c +++ b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire_render.c @@ -2,6 +2,8 @@ #include "../iso14443_4a/iso14443_4a_render.h" +#define MF_DESFIRE_RENDER_MAX_RECORD_SIZE (256U) + void nfc_render_mf_desfire_info( const MfDesfireData* data, NfcProtocolFormatType format_type, @@ -212,8 +214,6 @@ void nfc_render_mf_desfire_file_settings_data( uint32_t record_count = 1; uint32_t record_size = 0; - const uint32_t total_size = simple_array_get_count(data->data); - switch(settings->type) { case MfDesfireFileTypeStandard: case MfDesfireFileTypeBackup: @@ -257,17 +257,14 @@ void nfc_render_mf_desfire_file_settings_data( return; } - for(uint32_t rec = 0; rec < record_count; rec++) { - const uint32_t size_offset = rec * record_size; - const uint32_t size_remaining = total_size > size_offset ? total_size - size_offset : 0; + // Limit record size + bool trim_data = record_size > MF_DESFIRE_RENDER_MAX_RECORD_SIZE; + if(trim_data) { + record_size = MF_DESFIRE_RENDER_MAX_RECORD_SIZE; + } - if(size_remaining < record_size) { - furi_string_cat_printf( - str, "record %lu (partial %lu of %lu)\n", rec, size_remaining, record_size); - record_size = size_remaining; - } else { - furi_string_cat_printf(str, "record %lu\n", rec); - } + for(uint32_t rec = 0; rec < record_count; rec++) { + furi_string_cat_printf(str, "record %lu\n", rec); for(uint32_t ch = 0; ch < record_size; ch += 4) { furi_string_cat_printf(str, "%03lx|", ch); @@ -296,6 +293,9 @@ void nfc_render_mf_desfire_file_settings_data( furi_string_push_back(str, '\n'); } + if(trim_data) { + furi_string_cat_str(str, "..."); + } furi_string_push_back(str, '\n'); } diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c index 2b8631849..1dd6c50e1 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c @@ -75,10 +75,10 @@ MfDesfireError mf_desfire_send_chunks( const size_t rx_capacity_remaining = bit_buffer_get_capacity_bytes(rx_buffer) - bit_buffer_get_size_bytes(rx_buffer); - if(rx_size <= rx_capacity_remaining) { + if(rx_size <= rx_capacity_remaining + 1) { bit_buffer_append_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t)); } else { - FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size); + FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size - 1); } } } while(false); @@ -327,36 +327,63 @@ MfDesfireError mf_desfire_poller_read_file_settings_multi( return error; } -MfDesfireError mf_desfire_poller_read_file_data( +static MfDesfireError mf_desfire_poller_read_file( MfDesfirePoller* instance, MfDesfireFileId id, + uint8_t read_cmd, uint32_t offset, size_t size, MfDesfireFileData* data) { furi_check(instance); furi_check(data); - bit_buffer_reset(instance->input_buffer); - bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_READ_DATA); - bit_buffer_append_byte(instance->input_buffer, id); - bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&offset, 3); - bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&size, 3); + MfDesfireError error = MfDesfireErrorNone; + simple_array_init(data->data, size); - MfDesfireError error; + size_t buffer_capacity = bit_buffer_get_capacity_bytes(instance->result_buffer); + uint32_t current_offset = offset; + uint32_t bytes_read = 0; + + while(bytes_read < size) { + size_t bytes_to_read = MIN(buffer_capacity, size - bytes_read); + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, read_cmd); + bit_buffer_append_byte(instance->input_buffer, id); + bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)¤t_offset, 3); + bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&bytes_to_read, 3); - do { error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); - if(error != MfDesfireErrorNone) break; - if(!mf_desfire_file_data_parse(data, instance->result_buffer)) { + size_t bytes_received = bit_buffer_get_size_bytes(instance->result_buffer); + if(bytes_received != bytes_to_read) { + FURI_LOG_W(TAG, "Read %zu out of %zu bytes", bytes_received, bytes_to_read); error = MfDesfireErrorProtocol; + break; } - } while(false); + + uint8_t* file_data = simple_array_get_data(data->data); + bit_buffer_write_bytes(instance->result_buffer, &file_data[current_offset], bytes_to_read); + bytes_read += bytes_to_read; + current_offset += bytes_to_read; + } + + if(error != MfDesfireErrorNone) { + simple_array_reset(data->data); + } return error; } +MfDesfireError mf_desfire_poller_read_file_data( + MfDesfirePoller* instance, + MfDesfireFileId id, + uint32_t offset, + size_t size, + MfDesfireFileData* data) { + return mf_desfire_poller_read_file(instance, id, MF_DESFIRE_CMD_READ_DATA, offset, size, data); +} + MfDesfireError mf_desfire_poller_read_file_value( MfDesfirePoller* instance, MfDesfireFileId id, @@ -389,28 +416,8 @@ MfDesfireError mf_desfire_poller_read_file_records( uint32_t offset, size_t size, MfDesfireFileData* data) { - furi_check(instance); - furi_check(data); - - bit_buffer_reset(instance->input_buffer); - bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_READ_RECORDS); - bit_buffer_append_byte(instance->input_buffer, id); - bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&offset, 3); - bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&size, 3); - - MfDesfireError error; - - do { - error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); - - if(error != MfDesfireErrorNone) break; - - if(!mf_desfire_file_data_parse(data, instance->result_buffer)) { - error = MfDesfireErrorProtocol; - } - } while(false); - - return error; + return mf_desfire_poller_read_file( + instance, id, MF_DESFIRE_CMD_READ_RECORDS, offset, size, data); } MfDesfireError mf_desfire_poller_read_file_data_multi( From a86aeface5a33f9ffdbeceb806a8ba01611cd45c Mon Sep 17 00:00:00 2001 From: Leptopt1los <53914086+Leptopt1los@users.noreply.github.com> Date: Tue, 14 May 2024 17:27:35 +0300 Subject: [PATCH 3/4] electra lfrfid protocol implemented (#3640) --- lib/lfrfid/protocols/lfrfid_protocols.c | 2 + lib/lfrfid/protocols/lfrfid_protocols.h | 1 + lib/lfrfid/protocols/protocol_electra.c | 439 ++++++++++++++++++++++++ lib/lfrfid/protocols/protocol_electra.h | 4 + lib/lfrfid/protocols/protocol_em4100.c | 23 +- 5 files changed, 466 insertions(+), 3 deletions(-) create mode 100644 lib/lfrfid/protocols/protocol_electra.c create mode 100644 lib/lfrfid/protocols/protocol_electra.h diff --git a/lib/lfrfid/protocols/lfrfid_protocols.c b/lib/lfrfid/protocols/lfrfid_protocols.c index a8d0ff280..175d64c2b 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.c +++ b/lib/lfrfid/protocols/lfrfid_protocols.c @@ -1,5 +1,6 @@ #include "lfrfid_protocols.h" #include "protocol_em4100.h" +#include "protocol_electra.h" #include "protocol_h10301.h" #include "protocol_idteck.h" #include "protocol_indala26.h" @@ -22,6 +23,7 @@ const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolEM4100] = &protocol_em4100, [LFRFIDProtocolEM410032] = &protocol_em4100_32, [LFRFIDProtocolEM410016] = &protocol_em4100_16, + [LFRFIDProtocolElectra] = &protocol_electra, [LFRFIDProtocolH10301] = &protocol_h10301, [LFRFIDProtocolIdteck] = &protocol_idteck, [LFRFIDProtocolIndala26] = &protocol_indala26, diff --git a/lib/lfrfid/protocols/lfrfid_protocols.h b/lib/lfrfid/protocols/lfrfid_protocols.h index 64a9fcba2..89aa51c25 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.h +++ b/lib/lfrfid/protocols/lfrfid_protocols.h @@ -11,6 +11,7 @@ typedef enum { LFRFIDProtocolEM4100, LFRFIDProtocolEM410032, LFRFIDProtocolEM410016, + LFRFIDProtocolElectra, LFRFIDProtocolH10301, LFRFIDProtocolIdteck, LFRFIDProtocolIndala26, diff --git a/lib/lfrfid/protocols/protocol_electra.c b/lib/lfrfid/protocols/protocol_electra.c new file mode 100644 index 000000000..b94e60fbe --- /dev/null +++ b/lib/lfrfid/protocols/protocol_electra.c @@ -0,0 +1,439 @@ +/* + * Electra intercom rfid protocol (Romania) + * + * Based on EM4100 protocol implementation from https://github.com/flipperdevices/flipperzero-firmware/blob/dev/lib/lfrfid/protocols/protocol_em4100.c + * + * Copyright 2024 Leptoptilos + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * ------------------------------------------------------------------------------------------------------------------------------ + * PROTOCOL DESCRIPTION: + * ------------------------------------------------------------------------------------------------------------------------------ + * Electra intercom 125 kHz protocol based on 64-bit clock EM4100, but includes some extra data after base EM4100 data (epilogue) + * + * Epilogue size is 64 bits, but only first 16 bits matter. Rest 6 bytes - some filler data, + * that arbitrary change is not validated by the Electra intercoms + * + * There are curently three known types of epilogue: + * - 0x7E71AAAAAAAAAAAA (AA filler) + * - 0x7E71000000000000 (00 filler) + * - 0x0030AAAAAAAAAAAA + * + * First two epilogue bytes may be interpreted as EM4100 data continuation + * Nevertheless, these bytes have correct row parity bits, but have not correct collumn parity + + * For example: 0x7E71AAAAAAAAAAAA epilogue: + * + * In binary: | 0b01111110 | 01110001 | 10101010 | 10101010 | 10101010 | 10101010 | 10101010 | 10101010 | + * In hex: | 0x7E | 71 | AA | AA | AA | AA | AA | AA | + * + * As EM4100 data: + * 0111 1 // 7 + * 1100 0 // C + * 0111 1 // 7 + * 1101 0 // here epilogue filler starts (from second bit) + * 1010 1 // there is no correct raw parity bits anymore + * 0101 0 + * 1010 1 + * 0101 0 + * 1010 // and no correct column parity + */ + +#include "bit_lib/bit_lib.h" +#include +#include +#include +#include +#include "lfrfid_protocols.h" + +#define TAG "ELECTRA" + +typedef uint64_t ElectraDecodedData; + +#define EM_HEADER_POS (55) +#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS) + +#define EM_FIRST_ROW_POS (50) + +#define EM_ROW_COUNT (10) +#define EM_COLUMN_COUNT (4) +#define EM_BITS_PER_ROW_COUNT (EM_COLUMN_COUNT + 1) + +#define EM_COLUMN_POS (4) +#define ELECTRA_STOP_POS (0) +#define ELECTRA_STOP_MASK (0x1LLU << ELECTRA_STOP_POS) + +#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | ELECTRA_STOP_MASK) +#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK) + +#define ELECTRA_DECODED_BASE_DATA_SIZE (5) +#define ELECTRA_ENCODED_BASE_DATA_SIZE (sizeof(ElectraDecodedData)) + +#define ELECTRA_DECODED_EPILOGUE_SIZE (3) +#define ELECTRA_ENCODED_EPILOGUE_SIZE (sizeof(ElectraDecodedData)) + +#define ELECTRA_DECODED_DATA_SIZE (ELECTRA_DECODED_BASE_DATA_SIZE + ELECTRA_DECODED_EPILOGUE_SIZE) +#define ELECTRA_ENCODED_DATA_SIZE (ELECTRA_ENCODED_BASE_DATA_SIZE + ELECTRA_ENCODED_EPILOGUE_SIZE) + +#define ELECTRA_DECODED_DATA_EPILOGUE_START_POS (ELECTRA_DECODED_BASE_DATA_SIZE) + +#define ELECTRA_CLOCK_PER_BIT (64) + +#define ELECTRA_READ_SHORT_TIME (256) +#define ELECTRA_READ_LONG_TIME (512) +#define ELECTRA_READ_JITTER_TIME (100) + +#define ELECTRA_READ_SHORT_TIME_LOW (ELECTRA_READ_SHORT_TIME - ELECTRA_READ_JITTER_TIME) +#define ELECTRA_READ_SHORT_TIME_HIGH (ELECTRA_READ_SHORT_TIME + ELECTRA_READ_JITTER_TIME) +#define ELECTRA_READ_LONG_TIME_LOW (ELECTRA_READ_LONG_TIME - ELECTRA_READ_JITTER_TIME) +#define ELECTRA_READ_LONG_TIME_HIGH (ELECTRA_READ_LONG_TIME + ELECTRA_READ_JITTER_TIME) + +#define EM_ENCODED_DATA_HEADER (0xFF80000000000000ULL) + +typedef struct { + uint8_t data[ELECTRA_DECODED_DATA_SIZE]; + + ElectraDecodedData encoded_base_data; + ElectraDecodedData encoded_epilogue; + + uint8_t encoded_data_index; + bool encoded_polarity; + + ManchesterState decoder_manchester_state; +} ProtocolElectra; + +ProtocolElectra* protocol_electra_alloc(void) { + ProtocolElectra* proto = malloc(sizeof(ProtocolElectra)); + return (void*)proto; +}; + +void protocol_electra_free(ProtocolElectra* proto) { + free(proto); +}; + +uint8_t* protocol_electra_get_data(ProtocolElectra* proto) { + return proto->data; +}; + +static void electra_decode( + const uint8_t* encoded_base_data, + const uint8_t encoded_base_data_size, + const uint8_t* encoded_epilogue, + const uint8_t encoded_epilogue_size, + uint8_t* decoded_data, + const uint8_t decoded_data_size) { + furi_check(decoded_data_size >= ELECTRA_DECODED_DATA_SIZE); + furi_check(encoded_base_data_size >= ELECTRA_ENCODED_BASE_DATA_SIZE); + furi_check(encoded_epilogue_size >= ELECTRA_ENCODED_EPILOGUE_SIZE); + + uint8_t decoded_data_index = 0; + ElectraDecodedData base_data = *((ElectraDecodedData*)(encoded_base_data)); + //ElectraDecodedData epilogue = *((ElectraDecodedData*)(encoded_epilogue)); + + // clean result + memset(decoded_data, 0, decoded_data_size); + + // header + for(uint8_t i = 0; i < 9; i++) { + base_data = base_data << 1; + } + + // nibbles + uint8_t value = 0; + for(uint8_t r = 0; r < EM_ROW_COUNT; r++) { + uint8_t nibble = 0; + for(uint8_t i = 0; i < 5; i++) { + if(i < 4) nibble = (nibble << 1) | (base_data & (1LLU << 63) ? 1 : 0); + base_data = base_data << 1; + } + value = (value << 4) | nibble; + if(r % 2) { + decoded_data[decoded_data_index] |= value; + decoded_data_index++; + value = 0; + } + } + + // copy first 3 bytes of encoded epilogue to decoded data + decoded_data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS] = + encoded_epilogue[ELECTRA_ENCODED_EPILOGUE_SIZE - 1]; + decoded_data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS + 1] = + encoded_epilogue[ELECTRA_ENCODED_EPILOGUE_SIZE - 2]; + decoded_data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS + 2] = + encoded_epilogue[ELECTRA_ENCODED_EPILOGUE_SIZE - 3]; +} + +static bool electra_can_be_decoded( + const uint8_t* encoded_base_data, + const uint8_t encoded_base_data_size, + const uint8_t* encoded_epilogue_data, + const uint8_t encoded_epilogue_data_size) { + furi_check(encoded_base_data_size >= ELECTRA_ENCODED_BASE_DATA_SIZE); + furi_check(encoded_epilogue_data_size >= ELECTRA_ENCODED_EPILOGUE_SIZE); + const ElectraDecodedData* base_data = (ElectraDecodedData*)encoded_base_data; + const ElectraDecodedData* epilogue = (ElectraDecodedData*)encoded_epilogue_data; + + // check electra epilogue. if em4100 header - break + if((*epilogue & EM_ENCODED_DATA_HEADER) == EM_ENCODED_DATA_HEADER) return false; + + // check header and stop bit + if((*base_data & EM_HEADER_AND_STOP_MASK) != EM_HEADER_AND_STOP_DATA) return false; + + // check row parity + for(uint8_t i = 0; i < EM_ROW_COUNT; i++) { + uint8_t parity_sum = 0; + + for(uint8_t j = 0; j < EM_BITS_PER_ROW_COUNT; j++) { + parity_sum += (*base_data >> (EM_FIRST_ROW_POS - i * EM_BITS_PER_ROW_COUNT + j)) & 1; + } + + if((parity_sum % 2)) { + return false; + } + } + + // check columns parity + for(uint8_t i = 0; i < EM_COLUMN_COUNT; i++) { + uint8_t parity_sum = 0; + + for(uint8_t j = 0; j < EM_ROW_COUNT + 1; j++) { + parity_sum += (*base_data >> (EM_COLUMN_POS - i + j * EM_BITS_PER_ROW_COUNT)) & 1; + } + + if((parity_sum % 2)) { + FURI_LOG_D( + TAG, + "Unexpected column parity found. EM4100 data: %016llX", + bit_lib_bytes_to_num_be(encoded_base_data, encoded_base_data_size)); + return false; + } + } + + // encoded_epilogue_data lsb encoded + uint8_t epilogue_filler = encoded_epilogue_data[(ELECTRA_ENCODED_EPILOGUE_SIZE - 1) - 2]; + + for(uint8_t i = 0; i < ((ELECTRA_ENCODED_EPILOGUE_SIZE - 1) - 2); i++) + if(encoded_epilogue_data[i] != epilogue_filler) { + FURI_LOG_D(TAG, "Unexpected epilogue filler found: %016llX", *epilogue); + return false; + } + + return true; +} + +void protocol_electra_decoder_start(ProtocolElectra* proto) { + memset(proto->data, 0, ELECTRA_DECODED_DATA_SIZE); + proto->encoded_base_data = 0; + proto->encoded_epilogue = 0; + + manchester_advance( + proto->decoder_manchester_state, + ManchesterEventReset, + &proto->decoder_manchester_state, + NULL); +}; + +bool protocol_electra_decoder_feed(ProtocolElectra* proto, bool level, uint32_t duration) { + bool result = false; + + ManchesterEvent event = ManchesterEventReset; + + if(duration > ELECTRA_READ_SHORT_TIME_LOW && duration < ELECTRA_READ_SHORT_TIME_HIGH) { + if(!level) { + event = ManchesterEventShortHigh; + } else { + event = ManchesterEventShortLow; + } + } else if(duration > ELECTRA_READ_LONG_TIME_LOW && duration < ELECTRA_READ_LONG_TIME_HIGH) { + if(!level) { + event = ManchesterEventLongHigh; + } else { + event = ManchesterEventLongLow; + } + } + + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + proto->decoder_manchester_state, event, &proto->decoder_manchester_state, &data); + + if(data_ok) { + /* + EM 4100 BASE DATA (64 bit) ELECTRA EPILOGUE (64 bit) + _________________________________ _________________________________ + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | <- new data bit + --------------------------------- --------------------------------- + <- epilogue msb is carry bit to base data + */ + bool carry = proto->encoded_epilogue >> 63 & 0b1; + + proto->encoded_base_data = (proto->encoded_base_data << 1) | carry; + proto->encoded_epilogue = (proto->encoded_epilogue << 1) | data; + + if(electra_can_be_decoded( + (uint8_t*)&proto->encoded_base_data, + ELECTRA_ENCODED_BASE_DATA_SIZE, + (uint8_t*)&proto->encoded_epilogue, + ELECTRA_ENCODED_EPILOGUE_SIZE)) { + electra_decode( + (uint8_t*)&proto->encoded_base_data, + ELECTRA_ENCODED_BASE_DATA_SIZE, + (uint8_t*)&proto->encoded_epilogue, + ELECTRA_ENCODED_EPILOGUE_SIZE, + proto->data, + ELECTRA_DECODED_DATA_SIZE); + result = true; + } + } + } + + return result; +}; + +static void em_write_nibble(bool low_nibble, uint8_t data, ElectraDecodedData* encoded_base_data) { + uint8_t parity_sum = 0; + uint8_t start = 0; + if(!low_nibble) start = 4; + + for(int8_t i = (start + 3); i >= start; i--) { + parity_sum += (data >> i) & 1; + *encoded_base_data = (*encoded_base_data << 1) | ((data >> i) & 1); + } + + *encoded_base_data = (*encoded_base_data << 1) | ((parity_sum % 2) & 1); +} + +bool protocol_electra_encoder_start(ProtocolElectra* proto) { + // header + proto->encoded_base_data = 0b111111111; + + // data + for(uint8_t i = 0; i < ELECTRA_DECODED_BASE_DATA_SIZE; i++) { + em_write_nibble(false, proto->data[i], &proto->encoded_base_data); + em_write_nibble(true, proto->data[i], &proto->encoded_base_data); + } + + // column parity and stop bit + uint8_t parity_sum; + + for(uint8_t c = 0; c < EM_COLUMN_COUNT; c++) { + parity_sum = 0; + for(uint8_t i = 1; i <= EM_ROW_COUNT; i++) { + uint8_t parity_bit = (proto->encoded_base_data >> (i * EM_BITS_PER_ROW_COUNT - 1)) & 1; + parity_sum += parity_bit; + } + proto->encoded_base_data = (proto->encoded_base_data << 1) | ((parity_sum % 2) & 1); + } + + // stop bit + proto->encoded_base_data = (proto->encoded_base_data << 1) | 0; + + proto->encoded_data_index = 0; + proto->encoded_polarity = true; + + // epilogue + proto->encoded_epilogue = (proto->data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS]); + proto->encoded_epilogue <<= 8; + proto->encoded_epilogue |= (proto->data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS + 1]); + + //fill bytes 2-7 by epilogue filler + for(uint8_t i = 2; i < ELECTRA_ENCODED_EPILOGUE_SIZE; i++) { + proto->encoded_epilogue <<= 8; + proto->encoded_epilogue |= proto->data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS + 2]; + } + + return true; +}; + +LevelDuration protocol_electra_encoder_yield(ProtocolElectra* proto) { + bool level; + if(proto->encoded_data_index < 64) + level = (proto->encoded_base_data >> (63 - proto->encoded_data_index)) & 1; + else + level = (proto->encoded_epilogue >> (63 - (proto->encoded_data_index - 64))) & 1; + + uint32_t duration = ELECTRA_CLOCK_PER_BIT / 2; + + if(proto->encoded_polarity) { + proto->encoded_polarity = false; + } else { + level = !level; + + proto->encoded_polarity = true; + proto->encoded_data_index++; + if(proto->encoded_data_index >= 128) { + proto->encoded_data_index = 0; + } + } + + return level_duration_make(level, duration); +}; + +bool protocol_electra_write_data(ProtocolElectra* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + // Correct protocol data by redecoding + protocol_electra_encoder_start(protocol); + electra_decode( + (uint8_t*)&protocol->encoded_base_data, + sizeof(ElectraDecodedData), + (uint8_t*)&protocol->encoded_epilogue, + sizeof(ElectraDecodedData), + protocol->data, + ELECTRA_DECODED_DATA_SIZE); + + protocol_electra_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = + (LFRFID_T5577_MODULATION_MANCHESTER | LFRFID_T5577_BITRATE_RF_64 | + (4 << LFRFID_T5577_MAXBLOCK_SHIFT)); + request->t5577.block[1] = protocol->encoded_base_data >> 32; + request->t5577.block[2] = protocol->encoded_base_data & 0xFFFFFFFF; + request->t5577.block[3] = protocol->encoded_epilogue >> 32; + request->t5577.block[4] = protocol->encoded_epilogue & 0xFFFFFFFF; + request->t5577.blocks_to_write = 5; + result = true; + } + return result; +}; + +void protocol_electra_render_data(ProtocolElectra* protocol, FuriString* result) { + furi_string_printf(result, "Epilogue: %016llX", protocol->encoded_epilogue); +}; + +const ProtocolBase protocol_electra = { + .name = "Electra", + .manufacturer = "Electra Group", + .data_size = ELECTRA_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK | LFRFIDFeaturePSK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_electra_alloc, + .free = (ProtocolFree)protocol_electra_free, + .get_data = (ProtocolGetData)protocol_electra_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_electra_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_electra_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_electra_encoder_start, + .yield = (ProtocolEncoderYield)protocol_electra_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_electra_render_data, + .render_brief_data = (ProtocolRenderData)protocol_electra_render_data, + .write_data = (ProtocolWriteData)protocol_electra_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_electra.h b/lib/lfrfid/protocols/protocol_electra.h new file mode 100644 index 000000000..2c9f79285 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_electra.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_electra; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_em4100.c b/lib/lfrfid/protocols/protocol_em4100.c index 055d06d86..a4a403167 100644 --- a/lib/lfrfid/protocols/protocol_em4100.c +++ b/lib/lfrfid/protocols/protocol_em4100.c @@ -4,6 +4,7 @@ #include "lfrfid_protocols.h" typedef uint64_t EM4100DecodedData; +typedef uint64_t EM4100Epilogue; #define EM_HEADER_POS (55) #define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS) @@ -28,10 +29,13 @@ typedef uint64_t EM4100DecodedData; #define EM_READ_LONG_TIME_BASE (512) #define EM_READ_JITTER_TIME_BASE (100) +#define EM_ENCODED_DATA_HEADER (0xFF80000000000000ULL) + typedef struct { uint8_t data[EM4100_DECODED_DATA_SIZE]; EM4100DecodedData encoded_data; + EM4100Epilogue encoded_epilogue; uint8_t encoded_data_index; bool encoded_polarity; @@ -147,9 +151,16 @@ static void em4100_decode( } } -static bool em4100_can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) { +static bool em4100_can_be_decoded( + const uint8_t* encoded_data, + const uint8_t encoded_data_size, + const uint8_t* encoded_epilogue) { furi_check(encoded_data_size >= EM4100_ENCODED_DATA_SIZE); const EM4100DecodedData* card_data = (EM4100DecodedData*)encoded_data; + const EM4100Epilogue* epilogue = (EM4100Epilogue*)encoded_epilogue; + + // check first 9 bytes on epilogue (to prevent conflict with Electra protocol) + if((*epilogue & EM_ENCODED_DATA_HEADER) != EM_ENCODED_DATA_HEADER) return false; // check header and stop bit if((*card_data & EM_HEADER_AND_STOP_MASK) != EM_HEADER_AND_STOP_DATA) return false; @@ -221,9 +232,15 @@ bool protocol_em4100_decoder_feed(ProtocolEM4100* proto, bool level, uint32_t du proto->decoder_manchester_state, event, &proto->decoder_manchester_state, &data); if(data_ok) { - proto->encoded_data = (proto->encoded_data << 1) | data; + bool carry = proto->encoded_epilogue >> 63 & 0b1; - if(em4100_can_be_decoded((uint8_t*)&proto->encoded_data, sizeof(EM4100DecodedData))) { + proto->encoded_data = (proto->encoded_data << 1) | carry; + proto->encoded_epilogue = (proto->encoded_epilogue << 1) | data; + + if(em4100_can_be_decoded( + (uint8_t*)&proto->encoded_data, + sizeof(EM4100DecodedData), + (uint8_t*)&proto->encoded_epilogue)) { em4100_decode( (uint8_t*)&proto->encoded_data, sizeof(EM4100DecodedData), From e3f95a326bab32c9fd3c3dd66a0fe9ec7b6e9105 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Wed, 15 May 2024 03:58:09 +0900 Subject: [PATCH 4/4] [FL-3797] Settings menu refactoring (#3632) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Settings menu refactoring * Update F18 api * Wording changes * Update certification icon * Desktop: optimize settings save routine, fix navigation lag * Gui: add submenu position getter. Desktop: proper menu index preservation. * Gui: proper index getter for submenu. Desktop: cleaner settings navigation. Co-authored-by: あく --- 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 | 12 +- .../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 | 4 +- .../scenes/desktop_settings_scene_config.h | 3 + .../scenes/desktop_settings_scene_favorite.c | 84 ++++++-- .../scenes/desktop_settings_scene_pin_menu.c | 7 +- .../scenes/desktop_settings_scene_pin_setup.c | 1 + .../desktop_settings_scene_pin_setup_done.c | 1 + ...settings_scene_quick_apps_direction_menu.c | 187 ++++++++++++++++++ .../desktop_settings_scene_quick_apps_menu.c | 80 ++++++++ .../scenes/desktop_settings_scene_start.c | 78 +------- .../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 +- .../About/CertificationChina1_122x47.png | Bin 5215 -> 0 bytes .../About/CertificationChina1_124x47.png | Bin 0 -> 429 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 targets/f18/api_symbols.csv | 4 +- targets/f7/api_symbols.csv | 4 +- 38 files changed, 673 insertions(+), 142 deletions(-) create mode 100644 applications/settings/desktop_settings/scenes/desktop_settings_scene_quick_apps_direction_menu.c create mode 100644 applications/settings/desktop_settings/scenes/desktop_settings_scene_quick_apps_menu.c 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 delete mode 100644 assets/icons/About/CertificationChina1_122x47.png create mode 100644 assets/icons/About/CertificationChina1_124x47.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 diff --git a/applications/services/gui/elements.h b/applications/services/gui/elements.h index 833d5d6ee..54d78420e 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 35916fe23..5b1bdccda 100644 --- a/applications/services/gui/modules/submenu.c +++ b/applications/services/gui/modules/submenu.c @@ -215,6 +215,26 @@ void submenu_add_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); @@ -230,6 +250,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 6ae148eb5..e435f94a2 100644 --- a/applications/services/gui/modules/submenu.h +++ b/applications/services/gui/modules/submenu.h @@ -53,16 +53,32 @@ void submenu_add_item( SubmenuItemCallback callback, void* callback_context); +/** 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 8f0798d9c..973d1f481 100644 --- a/applications/settings/about/about.c +++ b/applications/settings/about/about.c @@ -41,7 +41,7 @@ static DialogMessageButton about_screen_product(DialogsApp* dialogs, DialogMessa static DialogMessageButton about_screen_address(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"; @@ -56,7 +56,7 @@ static DialogMessageButton about_screen_compliance(DialogsApp* dialogs, DialogMe 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); @@ -97,7 +97,7 @@ static DialogMessageButton about_screen_cert_china_0(DialogsApp* dialogs, Dialog static DialogMessageButton about_screen_cert_china_1(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; - dialog_message_set_icon(message, &I_CertificationChina1_122x47, 3, 3); + dialog_message_set_icon(message, &I_CertificationChina1_124x47, 3, 3); dialog_message_set_text( message, furi_hal_version_get_srrc_id(), 55, 11, AlignLeft, AlignBottom); result = dialog_message_show(dialogs, message); @@ -227,9 +227,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 31921b9f3..d72cb95e7 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 @@ -10,10 +10,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 b030656f7..238d866f2 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.c +++ b/applications/settings/desktop_settings/desktop_settings_app.c @@ -92,6 +92,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 { @@ -99,6 +100,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 6f97564c9..1a2c733ed 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.h +++ b/applications/settings/desktop_settings/desktop_settings_app.h @@ -40,5 +40,7 @@ typedef struct { PinCode pincode_buffer; bool pincode_buffer_filled; - uint8_t menu_idx; + uint32_t pin_menu_idx; + uint32_t quick_apps_menu_idx; + uint32_t quick_apps_direction_menu_idx; } DesktopSettingsApp; diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h b/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h index 5bc52172b..52b8829be 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h @@ -9,3 +9,6 @@ ADD_SCENE(desktop_settings, pin_setup, PinSetup) ADD_SCENE(desktop_settings, pin_setup_howto, PinSetupHowto) ADD_SCENE(desktop_settings, pin_setup_howto2, PinSetupHowto2) ADD_SCENE(desktop_settings, pin_setup_done, PinSetupDone) + +ADD_SCENE(desktop_settings, quick_apps_menu, QuickAppsMenu) +ADD_SCENE(desktop_settings, quick_apps_direction_menu, QuickAppsDirectionMenu) \ No newline at end of file diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c index db5d231ff..d77c8adda 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c @@ -9,11 +9,17 @@ #define APPS_COUNT (FLIPPER_APPS_COUNT + FLIPPER_EXTERNAL_APPS_COUNT) #define DEFAULT_INDEX (0) -#define EXTERNAL_BROWSER_NAME ("Apps Menu (Default)") -#define PASSPORT_NAME ("Passport (Default)") +#define EXTERNAL_BROWSER_NAME ("( ) Apps Menu (Default)") +#define EXTERNAL_BROWSER_NAME_SELECTED ("(*) Apps Menu (Default)") +#define PASSPORT_NAME ("( ) Passport (Default)") +#define PASSPORT_NAME_SELECTED ("(*) Passport (Default)") + +#define SELECTED_PREFIX ("(*) ") +#define NOT_SELECTED_PREFIX ("( ) ") #define EXTERNAL_APPLICATION_INDEX (1) -#define EXTERNAL_APPLICATION_NAME ("[Select App]") +#define EXTERNAL_APPLICATION_NAME ("( ) [Select App]") +#define EXTERNAL_APPLICATION_NAME_SELECTED ("(*) [Select App]") #define PRESELECTED_SPECIAL 0xffffffff @@ -61,7 +67,6 @@ void desktop_settings_scene_favorite_on_enter(void* context) { scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite); uint32_t pre_select_item = PRESELECTED_SPECIAL; FavoriteApp* curr_favorite_app = NULL; - bool is_dummy_app = false; bool default_passport = false; if((favorite_id & SCENE_STATE_SET_DUMMY_APP) == 0) { @@ -74,7 +79,6 @@ void desktop_settings_scene_favorite_on_enter(void* context) { favorite_id &= ~(SCENE_STATE_SET_DUMMY_APP); furi_assert(favorite_id < DummyAppNumber); curr_favorite_app = &app->settings.dummy_apps[favorite_id]; - is_dummy_app = true; default_passport = true; } @@ -94,29 +98,76 @@ void desktop_settings_scene_favorite_on_enter(void* context) { desktop_settings_scene_favorite_submenu_callback, app); - if(!is_dummy_app) { - for(size_t i = 0; i < APPS_COUNT; i++) { - const char* name = favorite_fap_get_app_name(i); + FuriString* full_name = furi_string_alloc(); - submenu_add_item( - submenu, name, i + 2, desktop_settings_scene_favorite_submenu_callback, app); + for(size_t i = 0; i < APPS_COUNT; i++) { + const char* name = favorite_fap_get_app_name(i); - // Select favorite item in submenu - if(!strcmp(name, curr_favorite_app->name_or_path)) { - pre_select_item = i + 2; - } + // Add the prefix + furi_string_reset(full_name); + if(!strcmp(name, curr_favorite_app->name_or_path)) { + furi_string_set_str(full_name, SELECTED_PREFIX); + } else { + furi_string_set_str(full_name, NOT_SELECTED_PREFIX); + } + furi_string_cat_str(full_name, name); + + submenu_add_item( + submenu, + furi_string_get_cstr(full_name), + i + 2, + desktop_settings_scene_favorite_submenu_callback, + app); + + // Select favorite item in submenu + if(!strcmp(name, curr_favorite_app->name_or_path)) { + pre_select_item = i + 2; } } if(pre_select_item == PRESELECTED_SPECIAL) { if(curr_favorite_app->name_or_path[0] == '\0') { pre_select_item = DEFAULT_INDEX; + submenu_change_item_label( + submenu, + DEFAULT_INDEX, + default_passport ? (PASSPORT_NAME_SELECTED) : (EXTERNAL_BROWSER_NAME_SELECTED)); } else { pre_select_item = EXTERNAL_APPLICATION_INDEX; + submenu_change_item_label( + submenu, EXTERNAL_APPLICATION_INDEX, EXTERNAL_APPLICATION_NAME_SELECTED); } } - submenu_set_header(submenu, is_dummy_app ? ("Dummy Mode App") : ("Favorite App")); + switch(favorite_id) { + case SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftShort: + submenu_set_header(submenu, "Left - Short"); + break; + case SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftLong: + submenu_set_header(submenu, "Left - Long"); + break; + case SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightShort: + submenu_set_header(submenu, "Right - Short"); + break; + case SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightLong: + submenu_set_header(submenu, "Right - Long"); + break; + case SCENE_STATE_SET_DUMMY_APP | DummyAppLeft: + submenu_set_header(submenu, "Left"); + break; + case SCENE_STATE_SET_DUMMY_APP | DummyAppRight: + submenu_set_header(submenu, "Right"); + break; + case SCENE_STATE_SET_DUMMY_APP | DummyAppDown: + submenu_set_header(submenu, "Down"); + break; + case SCENE_STATE_SET_DUMMY_APP | DummyAppOk: + submenu_set_header(submenu, "Middle"); + break; + default: + break; + } + submenu_set_selected_item(submenu, pre_select_item); // If set during loop, visual glitch. view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); @@ -177,6 +228,8 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e scene_manager_previous_scene(app->scene_manager); }; consumed = true; + + DESKTOP_SETTINGS_SAVE(&app->settings); } furi_string_free(temp_path); @@ -185,6 +238,5 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e void desktop_settings_scene_favorite_on_exit(void* context) { DesktopSettingsApp* app = context; - DESKTOP_SETTINGS_SAVE(&app->settings); submenu_reset(app->submenu); } 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/scenes/desktop_settings_scene_quick_apps_direction_menu.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_quick_apps_direction_menu.c new file mode 100644 index 000000000..dbc877ede --- /dev/null +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_quick_apps_direction_menu.c @@ -0,0 +1,187 @@ +#include +#include + +#include "../desktop_settings_app.h" +#include "desktop_settings_scene.h" +#include "desktop_settings_scene_i.h" + +enum QuickAppsSubmenuIndex { + QuickAppsSubmenuIndexFavoriteLeftClick, + QuickAppsSubmenuIndexFavoriteRightClick, + QuickAppsSubmenuIndexFavoriteLeftHold, + QuickAppsSubmenuIndexFavoriteRightHold, + QuickAppsSubmenuIndexDummyLeftClick, + QuickAppsSubmenuIndexDummyRightClick, + QuickAppsSubmenuIndexDummyDownClick, + QuickAppsSubmenuIndexDummyMiddleClick, +}; + +static void desktop_settings_scene_quick_apps_direction_menu_submenu_callback( + void* context, + uint32_t index) { + DesktopSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void desktop_settings_scene_quick_apps_direction_menu_on_enter(void* context) { + DesktopSettingsApp* app = context; + Submenu* submenu = app->submenu; + submenu_reset(submenu); + + uint32_t favorite_id = scene_manager_get_scene_state( + app->scene_manager, DesktopSettingsAppSceneQuickAppsDirectionMenu); + + if(favorite_id == SCENE_STATE_SET_FAVORITE_APP) { + submenu_add_item( + submenu, + "Left - Click", + QuickAppsSubmenuIndexFavoriteLeftClick, + desktop_settings_scene_quick_apps_direction_menu_submenu_callback, + app); + + submenu_add_item( + submenu, + "Right - Click", + QuickAppsSubmenuIndexFavoriteRightClick, + desktop_settings_scene_quick_apps_direction_menu_submenu_callback, + app); + + submenu_add_item( + submenu, + "Left - Hold", + QuickAppsSubmenuIndexFavoriteLeftHold, + desktop_settings_scene_quick_apps_direction_menu_submenu_callback, + app); + + submenu_add_item( + submenu, + "Right - Hold", + QuickAppsSubmenuIndexFavoriteRightHold, + desktop_settings_scene_quick_apps_direction_menu_submenu_callback, + app); + + submenu_set_header(app->submenu, "Default Mode"); + } else { + submenu_add_item( + submenu, + "Left - Click", + QuickAppsSubmenuIndexDummyLeftClick, + desktop_settings_scene_quick_apps_direction_menu_submenu_callback, + app); + + submenu_add_item( + submenu, + "Right - Click", + QuickAppsSubmenuIndexDummyRightClick, + desktop_settings_scene_quick_apps_direction_menu_submenu_callback, + app); + + submenu_add_item( + submenu, + "Down - Click", + QuickAppsSubmenuIndexDummyDownClick, + desktop_settings_scene_quick_apps_direction_menu_submenu_callback, + app); + + submenu_add_item( + submenu, + "Middle - Click", + QuickAppsSubmenuIndexDummyMiddleClick, + desktop_settings_scene_quick_apps_direction_menu_submenu_callback, + app); + + submenu_set_header(app->submenu, "Dummy Mode"); + } + + submenu_set_selected_item(app->submenu, app->quick_apps_direction_menu_idx); + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); +} + +bool desktop_settings_scene_quick_apps_direction_menu_on_event( + void* context, + SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case QuickAppsSubmenuIndexFavoriteLeftClick: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftShort); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + consumed = true; + break; + case QuickAppsSubmenuIndexFavoriteRightClick: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightShort); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + consumed = true; + break; + case QuickAppsSubmenuIndexFavoriteLeftHold: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftLong); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + consumed = true; + break; + case QuickAppsSubmenuIndexFavoriteRightHold: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightLong); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + consumed = true; + break; + case QuickAppsSubmenuIndexDummyLeftClick: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_DUMMY_APP | DummyAppLeft); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + consumed = true; + break; + case QuickAppsSubmenuIndexDummyRightClick: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_DUMMY_APP | DummyAppRight); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + consumed = true; + break; + case QuickAppsSubmenuIndexDummyDownClick: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_DUMMY_APP | DummyAppDown); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + consumed = true; + break; + case QuickAppsSubmenuIndexDummyMiddleClick: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_DUMMY_APP | DummyAppOk); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + consumed = true; + break; + default: + consumed = true; + break; + } + } else if(event.type == SceneManagerEventTypeBack) { + submenu_set_selected_item(app->submenu, 0); + } + + return consumed; +} + +void desktop_settings_scene_quick_apps_direction_menu_on_exit(void* context) { + DesktopSettingsApp* app = context; + app->quick_apps_direction_menu_idx = submenu_get_selected_item(app->submenu); + submenu_reset(app->submenu); +} diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_quick_apps_menu.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_quick_apps_menu.c new file mode 100644 index 000000000..acba4fa25 --- /dev/null +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_quick_apps_menu.c @@ -0,0 +1,80 @@ +#include +#include + +#include "../desktop_settings_app.h" +#include "desktop_settings_scene.h" +#include "desktop_settings_scene_i.h" + +#define SCENE_EVENT_SET_DEFAULT (0U) +#define SCENE_EVENT_SET_DUMMY (1U) + +static void + desktop_settings_scene_quick_apps_menu_submenu_callback(void* context, uint32_t index) { + DesktopSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void desktop_settings_scene_quick_apps_menu_on_enter(void* context) { + DesktopSettingsApp* app = context; + Submenu* submenu = app->submenu; + submenu_reset(submenu); + + submenu_add_item( + submenu, + "Default Mode", + SCENE_EVENT_SET_DEFAULT, + desktop_settings_scene_quick_apps_menu_submenu_callback, + app); + + submenu_add_item( + submenu, + "Dummy Mode", + SCENE_EVENT_SET_DUMMY, + desktop_settings_scene_quick_apps_menu_submenu_callback, + app); + + submenu_set_header(app->submenu, "Set Quick Access Apps"); + submenu_set_selected_item(app->submenu, app->quick_apps_menu_idx); + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); +} + +bool desktop_settings_scene_quick_apps_menu_on_event(void* context, SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case SCENE_EVENT_SET_DEFAULT: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneQuickAppsDirectionMenu, + SCENE_STATE_SET_FAVORITE_APP); + scene_manager_next_scene( + app->scene_manager, DesktopSettingsAppSceneQuickAppsDirectionMenu); + consumed = true; + break; + case SCENE_EVENT_SET_DUMMY: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneQuickAppsDirectionMenu, + SCENE_STATE_SET_DUMMY_APP); + scene_manager_next_scene( + app->scene_manager, DesktopSettingsAppSceneQuickAppsDirectionMenu); + consumed = true; + break; + default: + consumed = true; + break; + } + } else if(event.type == SceneManagerEventTypeBack) { + submenu_set_selected_item(app->submenu, 0); + } + + return consumed; +} + +void desktop_settings_scene_quick_apps_menu_on_exit(void* context) { + DesktopSettingsApp* app = context; + app->quick_apps_menu_idx = submenu_get_selected_item(app->submenu); + submenu_reset(app->submenu); +} diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c index 3e77bd8a1..8dec26016 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c @@ -9,14 +9,7 @@ typedef enum { DesktopSettingsPinSetup = 0, DesktopSettingsAutoLockDelay, DesktopSettingsClockDisplay, - DesktopSettingsFavoriteLeftShort, - DesktopSettingsFavoriteLeftLong, - DesktopSettingsFavoriteRightShort, - DesktopSettingsFavoriteRightLong, - DesktopSettingsDummyLeft, - DesktopSettingsDummyRight, - DesktopSettingsDummyDown, - DesktopSettingsDummyOk, + DesktopSettingsFavoriteApps, } DesktopSettingsEntry; #define AUTO_LOCK_DELAY_COUNT 6 @@ -93,15 +86,7 @@ void desktop_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, clock_enable_text[value_index]); - variable_item_list_add(variable_item_list, "Favorite App - Left Short", 1, NULL, NULL); - variable_item_list_add(variable_item_list, "Favorite App - Left Long", 1, NULL, NULL); - variable_item_list_add(variable_item_list, "Favorite App - Right Short", 1, NULL, NULL); - variable_item_list_add(variable_item_list, "Favorite App - Right Long", 1, NULL, NULL); - - variable_item_list_add(variable_item_list, "Dummy Mode App - Left", 1, NULL, NULL); - variable_item_list_add(variable_item_list, "Dummy Mode App - Right", 1, NULL, NULL); - variable_item_list_add(variable_item_list, "Dummy Mode App - Down", 1, NULL, NULL); - variable_item_list_add(variable_item_list, "Dummy Mode App - Ok", 1, NULL, NULL); + variable_item_list_add(variable_item_list, "Set Quick Access Apps", 1, NULL, NULL); variable_item_list_set_enter_callback( variable_item_list, desktop_settings_scene_start_var_list_enter_callback, app); @@ -119,62 +104,8 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinMenu); break; - case DesktopSettingsFavoriteLeftShort: - scene_manager_set_scene_state( - app->scene_manager, - DesktopSettingsAppSceneFavorite, - SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftShort); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); - break; - case DesktopSettingsFavoriteLeftLong: - scene_manager_set_scene_state( - app->scene_manager, - DesktopSettingsAppSceneFavorite, - SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftLong); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); - break; - case DesktopSettingsFavoriteRightShort: - scene_manager_set_scene_state( - app->scene_manager, - DesktopSettingsAppSceneFavorite, - SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightShort); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); - break; - case DesktopSettingsFavoriteRightLong: - scene_manager_set_scene_state( - app->scene_manager, - DesktopSettingsAppSceneFavorite, - SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightLong); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); - break; - - case DesktopSettingsDummyLeft: - scene_manager_set_scene_state( - app->scene_manager, - DesktopSettingsAppSceneFavorite, - SCENE_STATE_SET_DUMMY_APP | DummyAppLeft); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); - break; - case DesktopSettingsDummyRight: - scene_manager_set_scene_state( - app->scene_manager, - DesktopSettingsAppSceneFavorite, - SCENE_STATE_SET_DUMMY_APP | DummyAppRight); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); - break; - case DesktopSettingsDummyDown: - scene_manager_set_scene_state( - app->scene_manager, - DesktopSettingsAppSceneFavorite, - SCENE_STATE_SET_DUMMY_APP | DummyAppDown); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); - break; - case DesktopSettingsDummyOk: - scene_manager_set_scene_state( - app->scene_manager, - DesktopSettingsAppSceneFavorite, - SCENE_STATE_SET_DUMMY_APP | DummyAppOk); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + case DesktopSettingsFavoriteApps: + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneQuickAppsMenu); break; default: @@ -188,5 +119,4 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even void desktop_settings_scene_start_on_exit(void* context) { DesktopSettingsApp* app = context; variable_item_list_reset(app->variable_item_list); - DESKTOP_SETTINGS_SAVE(&app->settings); } 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 cd05846c0..09cc5af7a 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/icons/About/CertificationChina1_122x47.png b/assets/icons/About/CertificationChina1_122x47.png deleted file mode 100644 index c5eebec6776af61659c796fa55741f9a549bedae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5215 zcmb7IXH-*Lw++2_sUn0RO@vUScL+@hy%@YfiS?M;TCvfOBcMCi;^oyL!DL?r%WX9fn%M3I3I5$Mj59D zI^k6&{yyFYgMcR?ST8luWvD4o2Ze?Mo3c4X?^iN$r3UiAVttjt z;K0B@$v|026xtmut)!#`mXZO>$Vd<)BrriptP@TGi4i=e_{jl*V_eV(Un~NJ1Rirb zIivisY9J612mT_5;}CzcBQd|)Ckg>RM!?dNQsBRa`+A_TD2xZn_m7JHmh%Vvk3=lO z?Z2FJjQk^X}L{)9{sIJoy^2*L-B#1Ms714+xs{09pA z3wlA}1pGs}pVUy4E0J0DKOjRL9aA*Q4dG41AP9`Fw^Ptb#Ic0RhCWX2a3eU<9qXY6 z`o;PuV1sZy-v7Op6X3WxZ~tR=SRoNaIewOT0$u(SQv)fqqK!r9pew)vc zom9enX>1GxIMy!GY|xoX05*hTs&^}aLrd>3aC{QGO9IsdY_~*$MQ@^=U5WtSWV4e5 z!ZN_@hE47%+aI|<3ZPd+&oKkSJGAXY&oj^IjFAFf8Ut^!lWH2pHO_J{FH6 z(Y7mVft_o*6d`N58~vNGbUxizE5NG@>JGOl!resXV7ig#OWRF%{ZpwsS>vw)w+99a zM58a$R-LJh5Y)Lh@MhOoWEqvs9hDs%Is~*{)g67+Izx)!3;W!n{u;;?uSIe*QniID zYV*hB#iQ1<#uV+ z-EcY#^`<}o%SM!-fPAc?Wg6$1;ssfm&U>oyPMvoxI%1k2@eDwF;5lkTQ-XII^oGs8 z_OdM8E;(DHJ5%iNT=012qOjll?@wxURqNCrz;g5MkV>;VKLYuoFLY!xt8mTr?C@P}d2A!gaE8R|3V>3)+eXqP$9HaI1VV$B?Hs_=IZV5<`6YT`4 z^G;+T`IfC}BQr|L{tT^%zUgBEXp(u!tskeq4DTbOVQu&eYSHA zE}$Eu^u$kTJ>|EI>v^hAW+05lRJ3(PG!FXdXy8H*}*l7(?YAx;bztxv#CLcjR$hqJ`1Xy()bqXoY$RaCyY< zF^gAc^0{?Ke>H3C;qV3=T(!|u|9~lsEYLdjHEE`8CNgv2VWak7hIVIEcb;h8IW~7V zXSJd^B0s(}ojNzJ%I>P&IKZ?CWf|sxn#ovWfi!QOR#*!J93=1T?C>9T>GD!J7H|e1 zFon#njI3J~lC(7HkTyOx=$uZVBRRSxHI?p0Z4@C?8MW_6k>A2e9?8s1;;acWpqO>q z9IboM@w)Jg5}MQG1g8TWW`F`UbilK{uaM^7SHk` zHlPsvR-N9F=Z?BtvXYbcjl@nz#ARXOb~k7EWeV%04^Gf^b%$2vD!vsmFtTQzk$O>s1Bn|zyA8)Emo zKg$H`bx25|#2}q|Vp#|8%!L`y41R`hhQ^udD@#=h1jL=jYZ!W;i?zVg;G9soz$)mB z$W>wg)QNPtS8lF0Xi=r4^8y1xHa7#)Qc}U`1!)>)wW33(_%G7y8f0CBX^&n+6xtPH z&D726%IYmOJVA1C%5k=aDUzI|Kyc6})R9kW;8Q0e=TM}Ee$5nWW-jQtT^ z#Yx4l5s3%_Ld2uN1GR*G#-MgHUk`jA{9yR4$>7?%NiP|>Jh`B3?S5FcRrXePrksO} zD7_bDoOHffU#TOnqn-1nhgq$6?Ni&}@+?~`+qhb!#}-1iPT!;3W6=xz+eBH2{eBjs zVYi+LM8DLcuts+=Q*@=``IQD}gN=#)z*R5| zm0*8s!t8}{h|#28K}kVLGbF6}$p0`!$0*h}Ha_+|uerk9#?3%V9R^Yc2qm2I8hr(K z@C8@ySgr*gNm*~HCim#mN?e`XQ2CFJ*pH>|rC&@Z??v8&>+CSS@VUXG!qt7+M0Ub? z%yZ1$*^|``BnNXr_uTKcoV@V%LN~Vflk_{J?1QZ4tP$_xQNNnS^J*M%x4Iv?)>Y#B zllt5H=NEGN34I!{5!M)@}Z9kC+_1qynTT2 z$LE>pw8iQV^KYp0SXcxXl0A67e*DmCHDx=G|B&>BIGUEKFm-&c0_-VMK2 z7!E91t4XaF)D>(<4xV$L?%pA+41XHfFxi=$o8)%nYhfy7#Bq6Wi_(vA>}I$Oyq;c} zX`)Bdq*0uq$9Hc{znRLX?uu-W?2RO7W3|(?hhoQ=>Zto^%NbS~zES6J1b}jx`M6)6 zKcF0l7Mc36ju&hKxj||nzk%35!+PpB(D7-ya!K0yx;Jd4KO<}XYtKFtpzK(8w(b~9 ztV_x}bxkpui}SP%`_9==$ie1r4NYG(4UG!hgfOL$q+wIKYvx%KPh+>UDbNbT&`j3c zYSm)Q=K7$lo`aFxMfMW;2dTfMy{j+YN}}Pe@iZE~oKjWn@+76^i~@XY@2lSIw=Vv? zmOCv4zQDYj`Drg@4`nrC9s1XLr{Ir!m)LzN9#Md!Bie4YAyW%H8kZ0c92PeE++m?G z@#<$aWYyx;*E;OJ+J1eMyPlh*w1##;H!N;17SFA|7FjG=Mu{Uhk2+MUJm}fprqVn9zNx+uRko}w>6)P1^N-VDS8Lkc*6!%!rK>D9G)iJJ z2M)dLdzv1wrl*YxrG(P%6DLWHr8VB=<=R0K-8#h#g%900M+I!R&ggQ$y|Ln^!h>(1 z)FxoXDR!ZI840P`l!yc?cq1} zFYKpnENpsh01TnouygRSxLOtSwAiCJz5Ml+fupvgVz->Z`VAk4`b+hJU*abR&1bDFYTn@%(`;+4 z?FlcuRw}l~Tc4j^&iyREHaKbVl5^$OTF6Uq$mG^x>V6JWCDZ#u&%@MV&8PV?@HETiObFj&SB&WP;WYn-%EX1gxAmHE#ebb8VYOL#ZA(Dx}Kj76XBOL z@py^x+$?(};V0o!B4|>e!iHaX4|_8@yLj;CjM5tB=q<%|Ol)F4=kgUbTc)FhuY^|l zf%@;4>@#@?F9`dg*RLHB4jKw3ZoN2+IYfBdMvIwFJE zpe7NRRcuV;`%K>IPR;eI+wXn``pqXM{QP;&rA~7<3fgkU{*goef!p!S=HG3(&A+tt z$hq{Y@ppAKjl3JbGo!TF{iyl%C8=;#dusmhu~X*`J-bm|1yq2!+sV8YYKc%vQ#U`J z$LlKcKEan(Boo>@Cqb%&XH0tpzTFoW9`ra$3N(oSyugjqAR|~7jKk-uSSHHRL~MS{ zzQFW!F}-+OPOwPhZAG=>h@Q{ahb+VRTbmE$E}6(Se)2~+gj(^?jfX|O-ZK$1aPmmL+?^DQM`oti{VrBbfo(e9UvGgc)A@+W8;%_fED}|FnOnPK?GXmez zW9t&0Pn3&eSM}__;_~zzfie)P~nyw>V28Q1yeB61}Te|h>4g?otq5}8d8k~gkzumg2!gZFl zvLbryU#=#LFB7VnwGMuGM=?6KN|&VstPd;9m`M@C*(i}iCKHr)RPy}}iBZL^W|Y~O z6;h37DM7!#(aewmJKZ$H#RrX${wG2(jr3!~vR zq@K!$o8e3fw89tJOLxcKk*PoJ8CMnaZCfx2`ig0ESwY6mP9$`FW*js7GX11~xrE9Z rqu(eo!N7TLbCW}jx6tHO{sF*qKQAKgqL%dWdqG28V@QRzW90t;o{#2fgCGpt{r{gnt;J-61p8ogdWO`YAeYSz*bx9<@-Fd~ zR^6UY2lfUE#NfC!4BYfFWoocr~N1$?W` zz-`wpS@O%m@F$g4XC<_Qv$fkXZ&C;TS)RL*0#|Id*=r76cgrDw#~cVF)3frB zOw(6pd7MjmbS9^n%de7q8gmH*eN7^$NxuPF6@EXoi8>^*7SDDC7pUIr0?rh?%La<(z0qUK*$+(Ip2u9X-3pC)3^K@Q zP(0Wili7~plh?swHA|K(`DYv#LuSDVdAX8cGcc_&^DP1Iueb^Gy_#h1Ky^%CZLm*qp|d(S6?g!z6ae*z2u Xf^!1xOY<4R00000NkvXXu0mjf#QVd4 literal 0 HcmV?d00001 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@