diff --git a/applications/debug/accessor/helpers/wiegand.cpp b/applications/debug/accessor/helpers/wiegand.cpp index 79c9f723b..bb2885549 100644 --- a/applications/debug/accessor/helpers/wiegand.cpp +++ b/applications/debug/accessor/helpers/wiegand.cpp @@ -71,7 +71,7 @@ void WIEGAND::end() { } void WIEGAND::ReadD0() { - _bitCount++; // Increament bit count for Interrupt connected to D0 + _bitCount++; // Increment bit count for Interrupt connected to D0 if(_bitCount > 31) // If bit count more than 31, process high bits { _cardTempHigh |= ((0x80000000 & _cardTemp) >> 31); // shift value to high bits diff --git a/applications/debug/bt_debug_app/bt_debug_app.c b/applications/debug/bt_debug_app/bt_debug_app.c index ac442de0a..405051a4a 100644 --- a/applications/debug/bt_debug_app/bt_debug_app.c +++ b/applications/debug/bt_debug_app/bt_debug_app.c @@ -98,7 +98,7 @@ void bt_debug_app_free(BtDebugApp* app) { int32_t bt_debug_app(void* p) { UNUSED(p); if(!furi_hal_bt_is_testing_supported()) { - FURI_LOG_E(TAG, "Incorrect radio stack: radio testing fetures are absent."); + FURI_LOG_E(TAG, "Incorrect radio stack: radio testing features are absent."); DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); dialog_message_show_storage_error(dialogs, "Incorrect\nRadioStack"); return 255; diff --git a/applications/debug/display_test/display_test.c b/applications/debug/display_test/display_test.c index e7f366cbb..5b46d2b41 100644 --- a/applications/debug/display_test/display_test.c +++ b/applications/debug/display_test/display_test.c @@ -145,7 +145,7 @@ DisplayTest* display_test_alloc() { view_set_previous_callback(view, display_test_previous_callback); view_dispatcher_add_view(instance->view_dispatcher, DisplayTestViewConfigure, view); - // Configurtion items + // Configuration items VariableItem* item; instance->config_bias = false; instance->config_contrast = 32; diff --git a/applications/debug/unit_tests/furi/furi_memmgr_test.c b/applications/debug/unit_tests/furi/furi_memmgr_test.c index b0fd060cf..cf3848747 100644 --- a/applications/debug/unit_tests/furi/furi_memmgr_test.c +++ b/applications/debug/unit_tests/furi/furi_memmgr_test.c @@ -11,7 +11,7 @@ extern size_t memmgr_get_free_heap(void); extern size_t memmgr_get_minimum_free_heap(void); -// current heap managment realization consume: +// current heap management realization consume: // X bytes after allocate and 0 bytes after allocate and free, // where X = sizeof(void*) + sizeof(size_t), look to BlockLink_t const size_t heap_overhead_max_size = sizeof(void*) + sizeof(size_t); @@ -21,7 +21,7 @@ bool heap_equal(size_t heap_size, size_t heap_size_old) { const size_t heap_low = heap_size_old - heap_overhead_max_size; const size_t heap_high = heap_size_old + heap_overhead_max_size; - // not extact, so we must test it against bigger numbers than "overhead size" + // not exact, so we must test it against bigger numbers than "overhead size" const bool result = ((heap_size >= heap_low) && (heap_size <= heap_high)); // debug allocation info diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index 454c11c0f..07ec73a03 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -136,7 +136,7 @@ static bool nfc_test_digital_signal_test_encode( ref_timings_sum += ref[i]; if(timings_diff > timing_tolerance) { FURI_LOG_E( - TAG, "Too big differece in %d timings. Ref: %ld, DUT: %ld", i, ref[i], dut[i]); + TAG, "Too big difference in %d timings. Ref: %ld, DUT: %ld", i, ref[i], dut[i]); timing_check_success = false; break; } diff --git a/applications/debug/unit_tests/stream/stream_test.c b/applications/debug/unit_tests/stream/stream_test.c index c04e119c6..802e34025 100644 --- a/applications/debug/unit_tests/stream/stream_test.c +++ b/applications/debug/unit_tests/stream/stream_test.c @@ -219,21 +219,21 @@ MU_TEST_1(stream_composite_subtest, Stream* stream) { mu_check(stream_eof(stream)); mu_assert_int_eq(0, stream_tell(stream)); - // insert formated string at the end of stream + // insert formatted string at the end of stream // "" -> "dio666" mu_check(stream_insert_format(stream, "%s%d", "dio", 666)); mu_assert_int_eq(6, stream_size(stream)); mu_check(stream_eof(stream)); mu_assert_int_eq(6, stream_tell(stream)); - // insert formated string at the end of stream + // insert formatted string at the end of stream // "dio666" -> "dio666zlo555" mu_check(stream_insert_format(stream, "%s%d", "zlo", 555)); mu_assert_int_eq(12, stream_size(stream)); mu_check(stream_eof(stream)); mu_assert_int_eq(12, stream_tell(stream)); - // insert formated string at the 6 pos + // insert formatted string at the 6 pos // "dio666" -> "dio666baba13zlo555" mu_check(stream_seek(stream, 6, StreamOffsetFromStart)); mu_check(stream_insert_format(stream, "%s%d", "baba", 13)); diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 01ba4a994..794b801a5 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -265,8 +265,7 @@ void archive_file_array_load(ArchiveBrowserView* browser, int8_t dir) { offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 1; } if(offset_new > 0) { - offset_new = - CLAMP(offset_new, (int32_t)model->item_cnt - FILE_LIST_BUF_LEN, 0); + offset_new = CLAMP(offset_new, (int32_t)model->item_cnt, 0); } else { offset_new = 0; } diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h index 519a34a2d..09ffea1f9 100644 --- a/applications/main/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -3,9 +3,9 @@ #include "../archive_i.h" #include -#define TAB_RIGHT InputKeyRight // Default tab swith direction +#define TAB_RIGHT InputKeyRight // Default tab switch direction #define TAB_DEFAULT ArchiveTabFavorites // Start tab -#define FILE_LIST_BUF_LEN 100 +#define FILE_LIST_BUF_LEN 50 static const char* tab_default_paths[] = { [ArchiveTabFavorites] = "/app:favorites", diff --git a/applications/main/fap_loader/elf_cpp/elf_hashtable.cpp b/applications/main/fap_loader/elf_cpp/elf_hashtable.cpp index f8adbf9d8..e845ed008 100644 --- a/applications/main/fap_loader/elf_cpp/elf_hashtable.cpp +++ b/applications/main/fap_loader/elf_cpp/elf_hashtable.cpp @@ -31,7 +31,7 @@ bool elf_resolve_from_hashtable(const char* name, Elf32_Addr* address) { auto find_res = std::lower_bound(elf_api_table.cbegin(), elf_api_table.cend(), key); if((find_res == elf_api_table.cend() || (find_res->hash != gnu_sym_hash))) { - FURI_LOG_W(TAG, "Cant find symbol '%s' (hash %lx)!", name, gnu_sym_hash); + FURI_LOG_W(TAG, "Can't find symbol '%s' (hash %lx)!", name, gnu_sym_hash); result = false; } else { result = true; diff --git a/applications/main/infrared/scenes/infrared_scene_universal_tv.c b/applications/main/infrared/scenes/infrared_scene_universal_tv.c index 583f21fa3..e21bf8f90 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal_tv.c +++ b/applications/main/infrared/scenes/infrared_scene_universal_tv.c @@ -24,7 +24,7 @@ void infrared_scene_universal_tv_on_enter(void* context) { &I_Power_hvr_25x27, infrared_scene_universal_common_item_callback, context); - infrared_brute_force_add_record(brute_force, i++, "POWER"); + infrared_brute_force_add_record(brute_force, i++, "Power"); button_panel_add_item( button_panel, i, @@ -36,7 +36,7 @@ void infrared_scene_universal_tv_on_enter(void* context) { &I_Mute_hvr_25x27, infrared_scene_universal_common_item_callback, context); - infrared_brute_force_add_record(brute_force, i++, "MUTE"); + infrared_brute_force_add_record(brute_force, i++, "Mute"); button_panel_add_item( button_panel, i, @@ -48,7 +48,7 @@ void infrared_scene_universal_tv_on_enter(void* context) { &I_Vol_up_hvr_25x27, infrared_scene_universal_common_item_callback, context); - infrared_brute_force_add_record(brute_force, i++, "VOL+"); + infrared_brute_force_add_record(brute_force, i++, "Vol_up"); button_panel_add_item( button_panel, i, @@ -60,7 +60,7 @@ void infrared_scene_universal_tv_on_enter(void* context) { &I_Up_hvr_25x27, infrared_scene_universal_common_item_callback, context); - infrared_brute_force_add_record(brute_force, i++, "CH+"); + infrared_brute_force_add_record(brute_force, i++, "Ch_next"); button_panel_add_item( button_panel, i, @@ -72,7 +72,7 @@ void infrared_scene_universal_tv_on_enter(void* context) { &I_Vol_down_hvr_25x27, infrared_scene_universal_common_item_callback, context); - infrared_brute_force_add_record(brute_force, i++, "VOL-"); + infrared_brute_force_add_record(brute_force, i++, "Vol_dn"); button_panel_add_item( button_panel, i, @@ -84,7 +84,7 @@ void infrared_scene_universal_tv_on_enter(void* context) { &I_Down_hvr_25x27, infrared_scene_universal_common_item_callback, context); - infrared_brute_force_add_record(brute_force, i++, "CH-"); + infrared_brute_force_add_record(brute_force, i++, "Ch_prev"); button_panel_add_label(button_panel, 6, 11, FontPrimary, "TV remote"); button_panel_add_label(button_panel, 9, 64, FontSecondary, "Vol"); diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 49c8412c5..ce51d000d 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -21,6 +21,7 @@ ADD_SCENE(nfc, mf_ultralight_emulate, MfUltralightEmulate) ADD_SCENE(nfc, mf_ultralight_read_auth, MfUltralightReadAuth) ADD_SCENE(nfc, mf_ultralight_read_auth_result, MfUltralightReadAuthResult) ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) +ADD_SCENE(nfc, mf_ultralight_unlock_auto, MfUltralightUnlockAuto) ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu) ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn) ADD_SCENE(nfc, mf_desfire_read_success, MfDesfireReadSuccess) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c index ab4d37b09..c511e9dcb 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c @@ -19,10 +19,10 @@ void nfc_scene_mf_ultralight_menu_on_enter(void* context) { Submenu* submenu = nfc->submenu; MfUltralightData* data = &nfc->dev->dev_data.mf_ul_data; - if(data->data_read != data->data_size) { + if(!mf_ul_is_full_capture(data)) { submenu_add_item( submenu, - "Unlock With Password", + "Unlock", SubmenuIndexUnlock, nfc_scene_mf_ultralight_menu_submenu_callback, nfc); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c index 5dbb0c18a..2ab5e3f3f 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c @@ -24,25 +24,29 @@ void nfc_scene_mf_ultralight_read_auth_set_state(Nfc* nfc, NfcSceneMfUlReadState if(curr_state != state) { if(state == NfcSceneMfUlReadStateDetecting) { popup_reset(nfc->popup); - popup_set_text( - nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop); + popup_set_text(nfc->popup, "Apply the\ntarget card", 97, 24, AlignCenter, AlignTop); popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50); + nfc_blink_read_start(nfc); } else if(state == NfcSceneMfUlReadStateReading) { popup_reset(nfc->popup); popup_set_header( nfc->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop); popup_set_icon(nfc->popup, 12, 23, &A_Loading_24); + nfc_blink_detect_start(nfc); } else if(state == NfcSceneMfUlReadStateNotSupportedCard) { popup_reset(nfc->popup); popup_set_header(nfc->popup, "Wrong type of card!", 64, 3, AlignCenter, AlignTop); popup_set_text( nfc->popup, - "Only MIFARE\nUltralight & NTAG\n are supported", + "Only MIFARE\nUltralight & NTAG\nare supported", 4, 22, AlignLeft, AlignTop); popup_set_icon(nfc->popup, 73, 20, &I_DolphinCommon_56x48); + nfc_blink_stop(nfc); + notification_message(nfc->notifications, &sequence_error); + notification_message(nfc->notifications, &sequence_set_red_255); } scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightReadAuth, state); } @@ -62,8 +66,6 @@ void nfc_scene_mf_ultralight_read_auth_on_enter(void* context) { &nfc->dev->dev_data, nfc_scene_mf_ultralight_read_auth_worker_callback, nfc); - - nfc_blink_read_start(nfc); } bool nfc_scene_mf_ultralight_read_auth_on_event(void* context, SceneManagerEvent event) { @@ -86,8 +88,17 @@ bool nfc_scene_mf_ultralight_read_auth_on_event(void* context, SceneManagerEvent nfc, NfcSceneMfUlReadStateNotSupportedCard); } } else if(event.type == SceneManagerEventTypeBack) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; + NfcScene next_scene; + if(mf_ul_data->auth_method == MfUltralightAuthMethodManual) { + next_scene = NfcSceneMfUltralightKeyInput; + } else if(mf_ul_data->auth_method == MfUltralightAuthMethodAuto) { + next_scene = NfcSceneMfUltralightUnlockAuto; + } else { + next_scene = NfcSceneMfUltralightUnlockMenu; + } + consumed = + scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, next_scene); } return consumed; } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c index 178d03351..b125e9991 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c @@ -19,16 +19,20 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) { MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(mf_ul_data); Widget* widget = nfc->widget; + const char* title; FuriString* temp_str; temp_str = furi_string_alloc(); if((mf_ul_data->data_read == mf_ul_data->data_size) && (mf_ul_data->data_read > 0)) { - widget_add_string_element( - widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "All pages are unlocked!"); + if(mf_ul_data->auth_success) { + title = "All pages are unlocked!"; + } else { + title = "All unlocked but failed auth!"; + } } else { - widget_add_string_element( - widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Not all pages unlocked!"); + title = "Not all pages unlocked!"; } + widget_add_string_element(widget, 64, 0, AlignCenter, AlignTop, FontPrimary, title); furi_string_set(temp_str, "UID:"); for(size_t i = 0; i < nfc_data->uid_len; i++) { furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); @@ -65,6 +69,7 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) { nfc); furi_string_free(temp_str); + notification_message(nfc->notifications, &sequence_set_green_255); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); } @@ -81,8 +86,21 @@ bool nfc_scene_mf_ultralight_read_auth_result_on_event(void* context, SceneManag consumed = true; } } else if(event.type == SceneManagerEventTypeBack) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; + if(mf_ul_data->auth_method == MfUltralightAuthMethodManual || + mf_ul_data->auth_method == MfUltralightAuthMethodAuto) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } else { + NfcScene next_scene; + if((mf_ul_data->data_read == mf_ul_data->data_size) && (mf_ul_data->data_read > 0)) { + next_scene = NfcSceneMfUltralightMenu; + } else { + next_scene = NfcSceneMfUltralightUnlockMenu; + } + + consumed = + scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, next_scene); + } } return consumed; @@ -93,4 +111,6 @@ void nfc_scene_mf_ultralight_read_auth_result_on_exit(void* context) { // Clean views widget_reset(nfc->widget); + + notification_message_block(nfc->notifications, &sequence_reset_green); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_auto.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_auto.c new file mode 100644 index 000000000..c59fe3a7d --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_auto.c @@ -0,0 +1,64 @@ +#include "../nfc_i.h" + +bool nfc_scene_mf_ultralight_unlock_auto_worker_callback(NfcWorkerEvent event, void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + return true; +} + +void nfc_scene_mf_ultralight_unlock_auto_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + widget_add_string_multiline_element( + nfc->widget, + 54, + 30, + AlignLeft, + AlignCenter, + FontPrimary, + "Touch the\nreader to get\npassword..."); + widget_add_icon_element(nfc->widget, 0, 15, &I_Modern_reader_18x34); + widget_add_icon_element(nfc->widget, 20, 12, &I_Move_flipper_26x39); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + + // Start worker + nfc_worker_start( + nfc->worker, + NfcWorkerStateMfUltralightEmulate, + &nfc->dev->dev_data, + nfc_scene_mf_ultralight_unlock_auto_worker_callback, + nfc); + + nfc_blink_read_start(nfc); +} + +bool nfc_scene_mf_ultralight_unlock_auto_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if((event.event == NfcWorkerEventMfUltralightPwdAuth)) { + MfUltralightAuth* auth = &nfc->dev->dev_data.mf_ul_auth; + memcpy(nfc->byte_input_store, auth->pwd.raw, sizeof(auth->pwd.raw)); + nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodAuto; + nfc_worker_stop(nfc->worker); + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_mf_ultralight_unlock_auto_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + // Clear view + widget_reset(nfc->widget); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c index 3d5ecca24..484629b0b 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c @@ -1,9 +1,10 @@ #include "../nfc_i.h" enum SubmenuIndex { - SubmenuIndexMfUlUnlockMenuManual, + SubmenuIndexMfUlUnlockMenuAuto, SubmenuIndexMfUlUnlockMenuAmeebo, SubmenuIndexMfUlUnlockMenuXiaomi, + SubmenuIndexMfUlUnlockMenuManual, }; void nfc_scene_mf_ultralight_unlock_menu_submenu_callback(void* context, uint32_t index) { @@ -18,24 +19,32 @@ void nfc_scene_mf_ultralight_unlock_menu_on_enter(void* context) { uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareUl) { + submenu_add_item( + submenu, + "Unlock With Reader", + SubmenuIndexMfUlUnlockMenuAuto, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); + } submenu_add_item( submenu, - "Enter PWD Manually", - SubmenuIndexMfUlUnlockMenuManual, - nfc_scene_mf_ultralight_unlock_menu_submenu_callback, - nfc); - submenu_add_item( - submenu, - "Auth As Am11bo", + "Auth As Ameebo", SubmenuIndexMfUlUnlockMenuAmeebo, nfc_scene_mf_ultralight_unlock_menu_submenu_callback, nfc); submenu_add_item( submenu, - "Auth As Xiaomi", + "Auth As Xiaomi Air Purifier", SubmenuIndexMfUlUnlockMenuXiaomi, nfc_scene_mf_ultralight_unlock_menu_submenu_callback, nfc); + submenu_add_item( + submenu, + "Enter Password Manually", + SubmenuIndexMfUlUnlockMenuManual, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); submenu_set_selected_item(submenu, state); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } @@ -57,8 +66,12 @@ bool nfc_scene_mf_ultralight_unlock_menu_on_event(void* context, SceneManagerEve nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodXiaomi; scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockMenuAuto) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockAuto); + consumed = true; } - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfUltralightUnlockMenu, event.event); } return consumed; } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c index 514cd4e98..16efae9de 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c @@ -10,15 +10,43 @@ void nfc_scene_mf_ultralight_unlock_warn_dialog_callback(DialogExResult result, void nfc_scene_mf_ultralight_unlock_warn_on_enter(void* context) { Nfc* nfc = context; DialogEx* dialog_ex = nfc->dialog_ex; + MfUltralightAuthMethod auth_method = nfc->dev->dev_data.mf_ul_data.auth_method; dialog_ex_set_context(dialog_ex, nfc); dialog_ex_set_result_callback(dialog_ex, nfc_scene_mf_ultralight_unlock_warn_dialog_callback); - dialog_ex_set_header(dialog_ex, "Risky function!", 64, 4, AlignCenter, AlignTop); - dialog_ex_set_text( - dialog_ex, "Wrong password\ncan block your\ncard.", 4, 18, AlignLeft, AlignTop); - dialog_ex_set_icon(dialog_ex, 73, 20, &I_DolphinCommon_56x48); - dialog_ex_set_center_button_text(dialog_ex, "OK"); + if(auth_method == MfUltralightAuthMethodManual || auth_method == MfUltralightAuthMethodAuto) { + // Build dialog text + MfUltralightAuth* auth = &nfc->dev->dev_data.mf_ul_auth; + FuriString* password_str = + furi_string_alloc_set_str("Try to unlock the card with\npassword: "); + for(size_t i = 0; i < sizeof(auth->pwd.raw); ++i) { + furi_string_cat_printf(password_str, "%02X ", nfc->byte_input_store[i]); + } + furi_string_cat_str(password_str, "?\nCaution, a wrong password\ncan block the card!"); + nfc_text_store_set(nfc, furi_string_get_cstr(password_str)); + furi_string_free(password_str); + + dialog_ex_set_header( + dialog_ex, + auth_method == MfUltralightAuthMethodAuto ? "Password captured!" : "Risky function!", + 64, + 0, + AlignCenter, + AlignTop); + dialog_ex_set_text(dialog_ex, nfc->text_store, 64, 12, AlignCenter, AlignTop); + dialog_ex_set_left_button_text(dialog_ex, "Cancel"); + dialog_ex_set_right_button_text(dialog_ex, "Continue"); + + if(auth_method == MfUltralightAuthMethodAuto) + notification_message(nfc->notifications, &sequence_set_green_255); + } else { + dialog_ex_set_header(dialog_ex, "Risky function!", 64, 4, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog_ex, "Wrong password\ncan block your\ncard.", 4, 18, AlignLeft, AlignTop); + dialog_ex_set_icon(dialog_ex, 73, 20, &I_DolphinCommon_56x48); + dialog_ex_set_center_button_text(dialog_ex, "OK"); + } view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); } @@ -28,12 +56,33 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve bool consumed = false; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == DialogExResultCenter) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); - DOLPHIN_DEED(DolphinDeedNfcRead); + MfUltralightAuthMethod auth_method = nfc->dev->dev_data.mf_ul_data.auth_method; + if(auth_method == MfUltralightAuthMethodManual || auth_method == MfUltralightAuthMethodAuto) { + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultRight) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); + DOLPHIN_DEED(DolphinDeedNfcRead); + consumed = true; + } else if(event.event == DialogExResultLeft) { + if(auth_method == MfUltralightAuthMethodAuto) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + } else { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + } + } else if(event.type == SceneManagerEventTypeBack) { + // Cannot press back consumed = true; } + } else { + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultCenter) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); + DOLPHIN_DEED(DolphinDeedNfcRead); + consumed = true; + } + } } return consumed; @@ -43,5 +92,7 @@ void nfc_scene_mf_ultralight_unlock_warn_on_exit(void* context) { Nfc* nfc = context; dialog_ex_reset(nfc->dialog_ex); - submenu_reset(nfc->submenu); + nfc_text_store_clear(nfc); + + notification_message_block(nfc->notifications, &sequence_reset_green); } diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index 8f33972e0..7318fd009 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -87,6 +87,20 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { temp_str, "\nPages Read %d/%d", data->data_read / 4, data->data_size / 4); if(data->data_size > data->data_read) { furi_string_cat_printf(temp_str, "\nPassword-protected"); + } else if(data->auth_success) { + MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(data); + furi_string_cat_printf( + temp_str, + "\nPassword: %02X %02X %02X %02X", + config_pages->auth_data.pwd.raw[0], + config_pages->auth_data.pwd.raw[1], + config_pages->auth_data.pwd.raw[2], + config_pages->auth_data.pwd.raw[3]); + furi_string_cat_printf( + temp_str, + "\nPACK: %02X %02X", + config_pages->auth_data.pack.raw[0], + config_pages->auth_data.pack.raw[1]); } } else if(protocol == NfcDeviceProtocolMifareClassic) { MfClassicData* data = &dev_data->mf_classic_data; @@ -115,7 +129,7 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == GuiButtonTypeRight) { if(protocol == NfcDeviceProtocolMifareDesfire) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireApp); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireData); consumed = true; } else if(protocol == NfcDeviceProtocolMifareUl) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightData); diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index 1f82aef08..a64d4d00d 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -70,6 +70,8 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { consumed = true; } else if(event.event == NfcWorkerEventReadMfUltralight) { notification_message(nfc->notifications, &sequence_success); + // Set unlock password input to 0xFFFFFFFF only on fresh read + memset(nfc->byte_input_store, 0xFF, 4); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index f5b406ba4..a8a850ece 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -11,6 +11,8 @@ enum SubmenuIndex { SubmenuIndexDelete, SubmenuIndexInfo, SubmenuIndexRestoreOriginal, + SubmenuIndexMfUlUnlockByReader, + SubmenuIndexMfUlUnlockByPassword, }; void nfc_scene_saved_menu_submenu_callback(void* context, uint32_t index) { @@ -70,6 +72,21 @@ void nfc_scene_saved_menu_on_enter(void* context) { } submenu_add_item( submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc); + if(nfc->dev->format == NfcDeviceSaveFormatMifareUl && + !mf_ul_is_full_capture(&nfc->dev->dev_data.mf_ul_data)) { + submenu_add_item( + submenu, + "Unlock With Reader", + SubmenuIndexMfUlUnlockByReader, + nfc_scene_saved_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Unlock With Password", + SubmenuIndexMfUlUnlockByPassword, + nfc_scene_saved_menu_submenu_callback, + nfc); + } if(nfc->dev->shadow_file_exist) { submenu_add_item( submenu, @@ -142,6 +159,12 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexRestoreOriginal) { scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginalConfirm); consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockByReader) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockAuto); + consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockByPassword) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + consumed = true; } } diff --git a/applications/plugins/bt_hid_app/application.fam b/applications/plugins/bt_hid_app/application.fam deleted file mode 100644 index ae5339c31..000000000 --- a/applications/plugins/bt_hid_app/application.fam +++ /dev/null @@ -1,10 +0,0 @@ -App( - appid="Bluetooth_Remote", - name="Bluetooth Remote", - apptype=FlipperAppType.EXTERNAL, - entry_point="bt_hid_app", - stack_size=1 * 1024, - fap_category="Tools", - fap_icon="bt_remote_10px.png", - fap_icon_assets="assets", -) diff --git a/applications/plugins/bt_hid_app/bt_hid.c b/applications/plugins/bt_hid_app/bt_hid.c deleted file mode 100644 index 4a77a2490..000000000 --- a/applications/plugins/bt_hid_app/bt_hid.c +++ /dev/null @@ -1,216 +0,0 @@ -#include "bt_hid.h" -#include -#include -#include - -#define TAG "BtHidApp" - -enum BtDebugSubmenuIndex { - BtHidSubmenuIndexKeynote, - BtHidSubmenuIndexKeyboard, - BtHidSubmenuIndexMedia, - BtHidSubmenuIndexTikTok, - BtHidSubmenuIndexMouse, -}; - -void bt_hid_submenu_callback(void* context, uint32_t index) { - furi_assert(context); - BtHid* app = context; - if(index == BtHidSubmenuIndexKeynote) { - app->view_id = BtHidViewKeynote; - view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewKeynote); - } else if(index == BtHidSubmenuIndexKeyboard) { - app->view_id = BtHidViewKeyboard; - view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewKeyboard); - } else if(index == BtHidSubmenuIndexMedia) { - app->view_id = BtHidViewMedia; - view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewMedia); - } else if(index == BtHidSubmenuIndexMouse) { - app->view_id = BtHidViewMouse; - view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewMouse); - } else if(index == BtHidSubmenuIndexTikTok) { - app->view_id = BtHidViewTikTok; - view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok); - } -} - -void bt_hid_dialog_callback(DialogExResult result, void* context) { - furi_assert(context); - BtHid* app = context; - if(result == DialogExResultLeft) { - view_dispatcher_stop(app->view_dispatcher); - } else if(result == DialogExResultRight) { - view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view - } else if(result == DialogExResultCenter) { - view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewSubmenu); - } -} - -uint32_t bt_hid_exit_confirm_view(void* context) { - UNUSED(context); - return BtHidViewExitConfirm; -} - -uint32_t bt_hid_exit(void* context) { - UNUSED(context); - return VIEW_NONE; -} - -void bt_hid_connection_status_changed_callback(BtStatus status, void* context) { - furi_assert(context); - BtHid* bt_hid = context; - bool connected = (status == BtStatusConnected); - if(connected) { - notification_internal_message(bt_hid->notifications, &sequence_set_blue_255); - } else { - notification_internal_message(bt_hid->notifications, &sequence_reset_blue); - } - bt_hid_keynote_set_connected_status(bt_hid->bt_hid_keynote, connected); - bt_hid_keyboard_set_connected_status(bt_hid->bt_hid_keyboard, connected); - bt_hid_media_set_connected_status(bt_hid->bt_hid_media, connected); - bt_hid_mouse_set_connected_status(bt_hid->bt_hid_mouse, connected); - bt_hid_tiktok_set_connected_status(bt_hid->bt_hid_tiktok, connected); -} - -BtHid* bt_hid_app_alloc() { - BtHid* app = malloc(sizeof(BtHid)); - - // Gui - app->gui = furi_record_open(RECORD_GUI); - - // Bt - app->bt = furi_record_open(RECORD_BT); - - // Notifications - app->notifications = furi_record_open(RECORD_NOTIFICATION); - - // View dispatcher - app->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_enable_queue(app->view_dispatcher); - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - - // Submenu view - app->submenu = submenu_alloc(); - submenu_add_item( - app->submenu, "Keynote", BtHidSubmenuIndexKeynote, bt_hid_submenu_callback, app); - submenu_add_item( - app->submenu, "Keyboard", BtHidSubmenuIndexKeyboard, bt_hid_submenu_callback, app); - submenu_add_item(app->submenu, "Media", BtHidSubmenuIndexMedia, bt_hid_submenu_callback, app); - submenu_add_item( - app->submenu, "TikTok Controller", BtHidSubmenuIndexTikTok, bt_hid_submenu_callback, app); - submenu_add_item(app->submenu, "Mouse", BtHidSubmenuIndexMouse, bt_hid_submenu_callback, app); - view_set_previous_callback(submenu_get_view(app->submenu), bt_hid_exit); - view_dispatcher_add_view( - app->view_dispatcher, BtHidViewSubmenu, submenu_get_view(app->submenu)); - - // Dialog view - app->dialog = dialog_ex_alloc(); - dialog_ex_set_result_callback(app->dialog, bt_hid_dialog_callback); - dialog_ex_set_context(app->dialog, app); - dialog_ex_set_left_button_text(app->dialog, "Exit"); - dialog_ex_set_right_button_text(app->dialog, "Stay"); - dialog_ex_set_center_button_text(app->dialog, "Menu"); - dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop); - view_dispatcher_add_view( - app->view_dispatcher, BtHidViewExitConfirm, dialog_ex_get_view(app->dialog)); - - // Keynote view - app->bt_hid_keynote = bt_hid_keynote_alloc(); - view_set_previous_callback( - bt_hid_keynote_get_view(app->bt_hid_keynote), bt_hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, BtHidViewKeynote, bt_hid_keynote_get_view(app->bt_hid_keynote)); - - // Keyboard view - app->bt_hid_keyboard = bt_hid_keyboard_alloc(); - view_set_previous_callback( - bt_hid_keyboard_get_view(app->bt_hid_keyboard), bt_hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, BtHidViewKeyboard, bt_hid_keyboard_get_view(app->bt_hid_keyboard)); - - // Media view - app->bt_hid_media = bt_hid_media_alloc(); - view_set_previous_callback(bt_hid_media_get_view(app->bt_hid_media), bt_hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, BtHidViewMedia, bt_hid_media_get_view(app->bt_hid_media)); - - // TikTok view - app->bt_hid_tiktok = bt_hid_tiktok_alloc(); - view_set_previous_callback( - bt_hid_tiktok_get_view(app->bt_hid_tiktok), bt_hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, BtHidViewTikTok, bt_hid_tiktok_get_view(app->bt_hid_tiktok)); - - // Mouse view - app->bt_hid_mouse = bt_hid_mouse_alloc(); - view_set_previous_callback(bt_hid_mouse_get_view(app->bt_hid_mouse), bt_hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, BtHidViewMouse, bt_hid_mouse_get_view(app->bt_hid_mouse)); - - // TODO switch to menu after Media is done - app->view_id = BtHidViewSubmenu; - view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); - - return app; -} - -void bt_hid_app_free(BtHid* app) { - furi_assert(app); - - // Reset notification - notification_internal_message(app->notifications, &sequence_reset_blue); - - // Free views - view_dispatcher_remove_view(app->view_dispatcher, BtHidViewSubmenu); - submenu_free(app->submenu); - view_dispatcher_remove_view(app->view_dispatcher, BtHidViewExitConfirm); - dialog_ex_free(app->dialog); - view_dispatcher_remove_view(app->view_dispatcher, BtHidViewKeynote); - bt_hid_keynote_free(app->bt_hid_keynote); - view_dispatcher_remove_view(app->view_dispatcher, BtHidViewKeyboard); - bt_hid_keyboard_free(app->bt_hid_keyboard); - view_dispatcher_remove_view(app->view_dispatcher, BtHidViewMedia); - bt_hid_media_free(app->bt_hid_media); - view_dispatcher_remove_view(app->view_dispatcher, BtHidViewMouse); - bt_hid_mouse_free(app->bt_hid_mouse); - view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok); - bt_hid_tiktok_free(app->bt_hid_tiktok); - view_dispatcher_free(app->view_dispatcher); - - // Close records - furi_record_close(RECORD_GUI); - app->gui = NULL; - furi_record_close(RECORD_NOTIFICATION); - app->notifications = NULL; - furi_record_close(RECORD_BT); - app->bt = NULL; - - // Free rest - free(app); -} - -int32_t bt_hid_app(void* p) { - UNUSED(p); - // Switch profile to Hid - BtHid* app = bt_hid_app_alloc(); - bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app); - // Change profile - if(!bt_set_profile(app->bt, BtProfileHidKeyboard)) { - FURI_LOG_E(TAG, "Failed to switch profile"); - bt_hid_app_free(app); - return -1; - } - furi_hal_bt_start_advertising(); - - DOLPHIN_DEED(DolphinDeedPluginStart); - - view_dispatcher_run(app->view_dispatcher); - - bt_set_status_changed_callback(app->bt, NULL, NULL); - // Change back profile to Serial - bt_set_profile(app->bt, BtProfileSerial); - - bt_hid_app_free(app); - - return 0; -} diff --git a/applications/plugins/bt_hid_app/bt_hid.h b/applications/plugins/bt_hid_app/bt_hid.h deleted file mode 100644 index 89e8807fe..000000000 --- a/applications/plugins/bt_hid_app/bt_hid.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include -#include -#include "views/bt_hid_keynote.h" -#include "views/bt_hid_keyboard.h" -#include "views/bt_hid_media.h" -#include "views/bt_hid_mouse.h" -#include "views/bt_hid_tiktok.h" - -typedef struct { - Bt* bt; - Gui* gui; - NotificationApp* notifications; - ViewDispatcher* view_dispatcher; - Submenu* submenu; - DialogEx* dialog; - BtHidKeynote* bt_hid_keynote; - BtHidKeyboard* bt_hid_keyboard; - BtHidMedia* bt_hid_media; - BtHidMouse* bt_hid_mouse; - BtHidTikTok* bt_hid_tiktok; - uint32_t view_id; -} BtHid; - -typedef enum { - BtHidViewSubmenu, - BtHidViewKeynote, - BtHidViewKeyboard, - BtHidViewMedia, - BtHidViewMouse, - BtHidViewTikTok, - BtHidViewExitConfirm, -} BtHidView; diff --git a/applications/plugins/bt_hid_app/views/bt_hid_keyboard.h b/applications/plugins/bt_hid_app/views/bt_hid_keyboard.h deleted file mode 100644 index b2cc928e2..000000000 --- a/applications/plugins/bt_hid_app/views/bt_hid_keyboard.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -typedef struct BtHidKeyboard BtHidKeyboard; - -BtHidKeyboard* bt_hid_keyboard_alloc(); - -void bt_hid_keyboard_free(BtHidKeyboard* bt_hid_keyboard); - -View* bt_hid_keyboard_get_view(BtHidKeyboard* bt_hid_keyboard); - -void bt_hid_keyboard_set_connected_status(BtHidKeyboard* bt_hid_keyboard, bool connected); diff --git a/applications/plugins/bt_hid_app/views/bt_hid_keynote.h b/applications/plugins/bt_hid_app/views/bt_hid_keynote.h deleted file mode 100644 index 05b121457..000000000 --- a/applications/plugins/bt_hid_app/views/bt_hid_keynote.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -typedef struct BtHidKeynote BtHidKeynote; - -BtHidKeynote* bt_hid_keynote_alloc(); - -void bt_hid_keynote_free(BtHidKeynote* bt_hid_keynote); - -View* bt_hid_keynote_get_view(BtHidKeynote* bt_hid_keynote); - -void bt_hid_keynote_set_connected_status(BtHidKeynote* bt_hid_keynote, bool connected); diff --git a/applications/plugins/bt_hid_app/views/bt_hid_media.h b/applications/plugins/bt_hid_app/views/bt_hid_media.h deleted file mode 100644 index 804239dce..000000000 --- a/applications/plugins/bt_hid_app/views/bt_hid_media.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -typedef struct BtHidMedia BtHidMedia; - -BtHidMedia* bt_hid_media_alloc(); - -void bt_hid_media_free(BtHidMedia* bt_hid_media); - -View* bt_hid_media_get_view(BtHidMedia* bt_hid_media); - -void bt_hid_media_set_connected_status(BtHidMedia* bt_hid_media, bool connected); diff --git a/applications/plugins/bt_hid_app/views/bt_hid_mouse.h b/applications/plugins/bt_hid_app/views/bt_hid_mouse.h deleted file mode 100644 index a82971d76..000000000 --- a/applications/plugins/bt_hid_app/views/bt_hid_mouse.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -typedef struct BtHidMouse BtHidMouse; - -BtHidMouse* bt_hid_mouse_alloc(); - -void bt_hid_mouse_free(BtHidMouse* bt_hid_mouse); - -View* bt_hid_mouse_get_view(BtHidMouse* bt_hid_mouse); - -void bt_hid_mouse_set_connected_status(BtHidMouse* bt_hid_mouse, bool connected); diff --git a/applications/plugins/bt_hid_app/views/bt_hid_tiktok.h b/applications/plugins/bt_hid_app/views/bt_hid_tiktok.h deleted file mode 100644 index 03c9afeca..000000000 --- a/applications/plugins/bt_hid_app/views/bt_hid_tiktok.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -typedef struct BtHidTikTok BtHidTikTok; - -BtHidTikTok* bt_hid_tiktok_alloc(); - -void bt_hid_tiktok_free(BtHidTikTok* bt_hid_tiktok); - -View* bt_hid_tiktok_get_view(BtHidTikTok* bt_hid_tiktok); - -void bt_hid_tiktok_set_connected_status(BtHidTikTok* bt_hid_tiktok, bool connected); diff --git a/applications/plugins/hid_app/application.fam b/applications/plugins/hid_app/application.fam new file mode 100644 index 000000000..b8c13e353 --- /dev/null +++ b/applications/plugins/hid_app/application.fam @@ -0,0 +1,24 @@ +App( + appid="hid_usb", + name="USB Remote", + apptype=FlipperAppType.PLUGIN, + entry_point="hid_usb_app", + stack_size=1 * 1024, + fap_category="Tools", + fap_icon="hid_usb_10px.png", + fap_icon_assets="assets", + fap_icon_assets_symbol="hid", +) + + +App( + appid="hid_ble", + name="Bluetooth Remote", + apptype=FlipperAppType.PLUGIN, + entry_point="hid_ble_app", + stack_size=1 * 1024, + fap_category="Tools", + fap_icon="hid_ble_10px.png", + fap_icon_assets="assets", + fap_icon_assets_symbol="hid", +) diff --git a/applications/plugins/bt_hid_app/assets/Arr_dwn_7x9.png b/applications/plugins/hid_app/assets/Arr_dwn_7x9.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Arr_dwn_7x9.png rename to applications/plugins/hid_app/assets/Arr_dwn_7x9.png diff --git a/applications/plugins/bt_hid_app/assets/Arr_up_7x9.png b/applications/plugins/hid_app/assets/Arr_up_7x9.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Arr_up_7x9.png rename to applications/plugins/hid_app/assets/Arr_up_7x9.png diff --git a/applications/plugins/bt_hid_app/assets/Ble_connected_15x15.png b/applications/plugins/hid_app/assets/Ble_connected_15x15.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Ble_connected_15x15.png rename to applications/plugins/hid_app/assets/Ble_connected_15x15.png diff --git a/applications/plugins/bt_hid_app/assets/Ble_disconnected_15x15.png b/applications/plugins/hid_app/assets/Ble_disconnected_15x15.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Ble_disconnected_15x15.png rename to applications/plugins/hid_app/assets/Ble_disconnected_15x15.png diff --git a/applications/plugins/bt_hid_app/assets/ButtonDown_7x4.png b/applications/plugins/hid_app/assets/ButtonDown_7x4.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/ButtonDown_7x4.png rename to applications/plugins/hid_app/assets/ButtonDown_7x4.png diff --git a/applications/plugins/bt_hid_app/assets/ButtonLeft_4x7.png b/applications/plugins/hid_app/assets/ButtonLeft_4x7.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/ButtonLeft_4x7.png rename to applications/plugins/hid_app/assets/ButtonLeft_4x7.png diff --git a/applications/plugins/bt_hid_app/assets/ButtonRight_4x7.png b/applications/plugins/hid_app/assets/ButtonRight_4x7.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/ButtonRight_4x7.png rename to applications/plugins/hid_app/assets/ButtonRight_4x7.png diff --git a/applications/plugins/bt_hid_app/assets/ButtonUp_7x4.png b/applications/plugins/hid_app/assets/ButtonUp_7x4.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/ButtonUp_7x4.png rename to applications/plugins/hid_app/assets/ButtonUp_7x4.png diff --git a/applications/plugins/bt_hid_app/assets/Button_18x18.png b/applications/plugins/hid_app/assets/Button_18x18.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Button_18x18.png rename to applications/plugins/hid_app/assets/Button_18x18.png diff --git a/applications/plugins/bt_hid_app/assets/Circles_47x47.png b/applications/plugins/hid_app/assets/Circles_47x47.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Circles_47x47.png rename to applications/plugins/hid_app/assets/Circles_47x47.png diff --git a/applications/plugins/bt_hid_app/assets/Left_mouse_icon_9x9.png b/applications/plugins/hid_app/assets/Left_mouse_icon_9x9.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Left_mouse_icon_9x9.png rename to applications/plugins/hid_app/assets/Left_mouse_icon_9x9.png diff --git a/applications/plugins/bt_hid_app/assets/Like_def_11x9.png b/applications/plugins/hid_app/assets/Like_def_11x9.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Like_def_11x9.png rename to applications/plugins/hid_app/assets/Like_def_11x9.png diff --git a/applications/plugins/bt_hid_app/assets/Like_pressed_17x17.png b/applications/plugins/hid_app/assets/Like_pressed_17x17.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Like_pressed_17x17.png rename to applications/plugins/hid_app/assets/Like_pressed_17x17.png diff --git a/applications/plugins/bt_hid_app/assets/Ok_btn_9x9.png b/applications/plugins/hid_app/assets/Ok_btn_9x9.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Ok_btn_9x9.png rename to applications/plugins/hid_app/assets/Ok_btn_9x9.png diff --git a/applications/plugins/bt_hid_app/assets/Ok_btn_pressed_13x13.png b/applications/plugins/hid_app/assets/Ok_btn_pressed_13x13.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Ok_btn_pressed_13x13.png rename to applications/plugins/hid_app/assets/Ok_btn_pressed_13x13.png diff --git a/applications/plugins/bt_hid_app/assets/Pin_arrow_down_7x9.png b/applications/plugins/hid_app/assets/Pin_arrow_down_7x9.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Pin_arrow_down_7x9.png rename to applications/plugins/hid_app/assets/Pin_arrow_down_7x9.png diff --git a/applications/plugins/bt_hid_app/assets/Pin_arrow_left_9x7.png b/applications/plugins/hid_app/assets/Pin_arrow_left_9x7.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Pin_arrow_left_9x7.png rename to applications/plugins/hid_app/assets/Pin_arrow_left_9x7.png diff --git a/applications/plugins/bt_hid_app/assets/Pin_arrow_right_9x7.png b/applications/plugins/hid_app/assets/Pin_arrow_right_9x7.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Pin_arrow_right_9x7.png rename to applications/plugins/hid_app/assets/Pin_arrow_right_9x7.png diff --git a/applications/plugins/bt_hid_app/assets/Pin_arrow_up_7x9.png b/applications/plugins/hid_app/assets/Pin_arrow_up_7x9.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Pin_arrow_up_7x9.png rename to applications/plugins/hid_app/assets/Pin_arrow_up_7x9.png diff --git a/applications/plugins/bt_hid_app/assets/Pin_back_arrow_10x8.png b/applications/plugins/hid_app/assets/Pin_back_arrow_10x8.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Pin_back_arrow_10x8.png rename to applications/plugins/hid_app/assets/Pin_back_arrow_10x8.png diff --git a/applications/plugins/bt_hid_app/assets/Pressed_Button_13x13.png b/applications/plugins/hid_app/assets/Pressed_Button_13x13.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Pressed_Button_13x13.png rename to applications/plugins/hid_app/assets/Pressed_Button_13x13.png diff --git a/applications/plugins/bt_hid_app/assets/Right_mouse_icon_9x9.png b/applications/plugins/hid_app/assets/Right_mouse_icon_9x9.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Right_mouse_icon_9x9.png rename to applications/plugins/hid_app/assets/Right_mouse_icon_9x9.png diff --git a/applications/plugins/bt_hid_app/assets/Space_65x18.png b/applications/plugins/hid_app/assets/Space_65x18.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Space_65x18.png rename to applications/plugins/hid_app/assets/Space_65x18.png diff --git a/applications/plugins/bt_hid_app/assets/Voldwn_6x6.png b/applications/plugins/hid_app/assets/Voldwn_6x6.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Voldwn_6x6.png rename to applications/plugins/hid_app/assets/Voldwn_6x6.png diff --git a/applications/plugins/bt_hid_app/assets/Volup_8x6.png b/applications/plugins/hid_app/assets/Volup_8x6.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Volup_8x6.png rename to applications/plugins/hid_app/assets/Volup_8x6.png diff --git a/applications/plugins/hid_app/hid.c b/applications/plugins/hid_app/hid.c new file mode 100644 index 000000000..2a617fdea --- /dev/null +++ b/applications/plugins/hid_app/hid.c @@ -0,0 +1,365 @@ +#include "hid.h" +#include "views.h" +#include +#include + +#define TAG "HidApp" + +enum HidDebugSubmenuIndex { + HidSubmenuIndexKeynote, + HidSubmenuIndexKeyboard, + HidSubmenuIndexMedia, + BtHidSubmenuIndexTikTok, + HidSubmenuIndexMouse, +}; +typedef enum { ConnTypeSubmenuIndexBluetooth, ConnTypeSubmenuIndexUsb } ConnTypeDebugSubmenuIndex; + +static void hid_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + Hid* app = context; + if(index == HidSubmenuIndexKeynote) { + app->view_id = HidViewKeynote; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote); + } else if(index == HidSubmenuIndexKeyboard) { + app->view_id = HidViewKeyboard; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeyboard); + } else if(index == HidSubmenuIndexMedia) { + app->view_id = HidViewMedia; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMedia); + } else if(index == HidSubmenuIndexMouse) { + app->view_id = HidViewMouse; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouse); + } else if(index == BtHidSubmenuIndexTikTok) { + app->view_id = BtHidViewTikTok; + view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok); + } +} + +static void bt_hid_connection_status_changed_callback(BtStatus status, void* context) { + furi_assert(context); + Hid* hid = context; + bool connected = (status == BtStatusConnected); + if(connected) { + notification_internal_message(hid->notifications, &sequence_set_blue_255); + } else { + notification_internal_message(hid->notifications, &sequence_reset_blue); + } + hid_keynote_set_connected_status(hid->hid_keynote, connected); + hid_keyboard_set_connected_status(hid->hid_keyboard, connected); + hid_media_set_connected_status(hid->hid_media, connected); + hid_mouse_set_connected_status(hid->hid_mouse, connected); + hid_tiktok_set_connected_status(hid->hid_tiktok, connected); +} + +static void hid_dialog_callback(DialogExResult result, void* context) { + furi_assert(context); + Hid* app = context; + if(result == DialogExResultLeft) { + view_dispatcher_stop(app->view_dispatcher); + } else if(result == DialogExResultRight) { + view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view + } else if(result == DialogExResultCenter) { + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewSubmenu); + } +} + +static uint32_t hid_exit_confirm_view(void* context) { + UNUSED(context); + return HidViewExitConfirm; +} + +static uint32_t hid_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +Hid* hid_alloc(HidTransport transport) { + Hid* app = malloc(sizeof(Hid)); + app->transport = transport; + + // Gui + app->gui = furi_record_open(RECORD_GUI); + + // Bt + app->bt = furi_record_open(RECORD_BT); + + // Notifications + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + // View dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + // Device Type Submenu view + app->device_type_submenu = submenu_alloc(); + submenu_add_item( + app->device_type_submenu, "Keynote", HidSubmenuIndexKeynote, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, "Keyboard", HidSubmenuIndexKeyboard, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, "Media", HidSubmenuIndexMedia, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, "Mouse", HidSubmenuIndexMouse, hid_submenu_callback, app); + if(app->transport == HidTransportBle) { + submenu_add_item( + app->device_type_submenu, + "TikTok Controller", + BtHidSubmenuIndexTikTok, + hid_submenu_callback, + app); + } + view_set_previous_callback(submenu_get_view(app->device_type_submenu), hid_exit); + view_dispatcher_add_view( + app->view_dispatcher, HidViewSubmenu, submenu_get_view(app->device_type_submenu)); + app->view_id = HidViewSubmenu; + view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); + return app; +} + +Hid* hid_app_alloc_view(void* context) { + furi_assert(context); + Hid* app = context; + // Dialog view + app->dialog = dialog_ex_alloc(); + dialog_ex_set_result_callback(app->dialog, hid_dialog_callback); + dialog_ex_set_context(app->dialog, app); + dialog_ex_set_left_button_text(app->dialog, "Exit"); + dialog_ex_set_right_button_text(app->dialog, "Stay"); + dialog_ex_set_center_button_text(app->dialog, "Menu"); + dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop); + view_dispatcher_add_view( + app->view_dispatcher, HidViewExitConfirm, dialog_ex_get_view(app->dialog)); + + // Keynote view + app->hid_keynote = hid_keynote_alloc(app); + view_set_previous_callback(hid_keynote_get_view(app->hid_keynote), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewKeynote, hid_keynote_get_view(app->hid_keynote)); + + // Keyboard view + app->hid_keyboard = hid_keyboard_alloc(app); + view_set_previous_callback(hid_keyboard_get_view(app->hid_keyboard), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewKeyboard, hid_keyboard_get_view(app->hid_keyboard)); + + // Media view + app->hid_media = hid_media_alloc(app); + view_set_previous_callback(hid_media_get_view(app->hid_media), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewMedia, hid_media_get_view(app->hid_media)); + + // TikTok view + app->hid_tiktok = hid_tiktok_alloc(app); + view_set_previous_callback(hid_tiktok_get_view(app->hid_tiktok), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, BtHidViewTikTok, hid_tiktok_get_view(app->hid_tiktok)); + + // Mouse view + app->hid_mouse = hid_mouse_alloc(app); + view_set_previous_callback(hid_mouse_get_view(app->hid_mouse), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse)); + + return app; +} + +void hid_free(Hid* app) { + furi_assert(app); + + // Reset notification + notification_internal_message(app->notifications, &sequence_reset_blue); + + // Free views + view_dispatcher_remove_view(app->view_dispatcher, HidViewSubmenu); + submenu_free(app->device_type_submenu); + view_dispatcher_remove_view(app->view_dispatcher, HidViewExitConfirm); + dialog_ex_free(app->dialog); + view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynote); + hid_keynote_free(app->hid_keynote); + view_dispatcher_remove_view(app->view_dispatcher, HidViewKeyboard); + hid_keyboard_free(app->hid_keyboard); + view_dispatcher_remove_view(app->view_dispatcher, HidViewMedia); + hid_media_free(app->hid_media); + view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse); + hid_mouse_free(app->hid_mouse); + view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok); + hid_tiktok_free(app->hid_tiktok); + view_dispatcher_free(app->view_dispatcher); + + // Close records + furi_record_close(RECORD_GUI); + app->gui = NULL; + furi_record_close(RECORD_NOTIFICATION); + app->notifications = NULL; + furi_record_close(RECORD_BT); + app->bt = NULL; + + // Free rest + free(app); +} + +void hid_hal_keyboard_press(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_kb_press(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_kb_press(event); + } else { + furi_crash(NULL); + } +} + +void hid_hal_keyboard_release(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_kb_release(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_kb_release(event); + } else { + furi_crash(NULL); + } +} + +void hid_hal_keyboard_release_all(Hid* instance) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_kb_release_all(); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_kb_release_all(); + } else { + furi_crash(NULL); + } +} + +void hid_hal_consumer_key_press(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_consumer_key_press(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_consumer_key_press(event); + } else { + furi_crash(NULL); + } +} + +void hid_hal_consumer_key_release(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_consumer_key_release(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_consumer_key_release(event); + } else { + furi_crash(NULL); + } +} + +void hid_hal_consumer_key_release_all(Hid* instance) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_consumer_key_release_all(); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_kb_release_all(); + } else { + furi_crash(NULL); + } +} + +void hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_mouse_move(dx, dy); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_mouse_move(dx, dy); + } else { + furi_crash(NULL); + } +} + +void hid_hal_mouse_scroll(Hid* instance, int8_t delta) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_mouse_scroll(delta); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_mouse_scroll(delta); + } else { + furi_crash(NULL); + } +} + +void hid_hal_mouse_press(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_mouse_press(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_mouse_press(event); + } else { + furi_crash(NULL); + } +} + +void hid_hal_mouse_release(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_mouse_release(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_mouse_release(event); + } else { + furi_crash(NULL); + } +} + +void hid_hal_mouse_release_all(Hid* instance) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_mouse_release_all(); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT); + furi_hal_hid_mouse_release(HID_MOUSE_BTN_RIGHT); + } else { + furi_crash(NULL); + } +} + +int32_t hid_usb_app(void* p) { + UNUSED(p); + Hid* app = hid_alloc(HidTransportUsb); + app = hid_app_alloc_view(app); + FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); + furi_hal_usb_unlock(); + furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true); + + bt_hid_connection_status_changed_callback(BtStatusConnected, app); + + DOLPHIN_DEED(DolphinDeedPluginStart); + + view_dispatcher_run(app->view_dispatcher); + + furi_hal_usb_set_config(usb_mode_prev, NULL); + + hid_free(app); + + return 0; +} + +int32_t hid_ble_app(void* p) { + UNUSED(p); + Hid* app = hid_alloc(HidTransportBle); + app = hid_app_alloc_view(app); + + if(!bt_set_profile(app->bt, BtProfileHidKeyboard)) { + FURI_LOG_E(TAG, "Failed to switch profile"); + } + furi_hal_bt_start_advertising(); + bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app); + + DOLPHIN_DEED(DolphinDeedPluginStart); + + view_dispatcher_run(app->view_dispatcher); + + bt_set_status_changed_callback(app->bt, NULL, NULL); + bt_set_profile(app->bt, BtProfileSerial); + + hid_free(app); + + return 0; +} diff --git a/applications/plugins/hid_app/hid.h b/applications/plugins/hid_app/hid.h new file mode 100644 index 000000000..81ebcf566 --- /dev/null +++ b/applications/plugins/hid_app/hid.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include "views/hid_keynote.h" +#include "views/hid_keyboard.h" +#include "views/hid_media.h" +#include "views/hid_mouse.h" +#include "views/hid_tiktok.h" + +typedef enum { + HidTransportUsb, + HidTransportBle, +} HidTransport; + +typedef struct Hid Hid; + +struct Hid { + Bt* bt; + Gui* gui; + NotificationApp* notifications; + ViewDispatcher* view_dispatcher; + Submenu* device_type_submenu; + DialogEx* dialog; + HidKeynote* hid_keynote; + HidKeyboard* hid_keyboard; + HidMedia* hid_media; + HidMouse* hid_mouse; + HidTikTok* hid_tiktok; + + HidTransport transport; + uint32_t view_id; +}; + +void hid_hal_keyboard_press(Hid* instance, uint16_t event); +void hid_hal_keyboard_release(Hid* instance, uint16_t event); +void hid_hal_keyboard_release_all(Hid* instance); + +void hid_hal_consumer_key_press(Hid* instance, uint16_t event); +void hid_hal_consumer_key_release(Hid* instance, uint16_t event); +void hid_hal_consumer_key_release_all(Hid* instance); + +void hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy); +void hid_hal_mouse_scroll(Hid* instance, int8_t delta); +void hid_hal_mouse_press(Hid* instance, uint16_t event); +void hid_hal_mouse_release(Hid* instance, uint16_t event); +void hid_hal_mouse_release_all(Hid* instance); \ No newline at end of file diff --git a/applications/plugins/bt_hid_app/bt_remote_10px.png b/applications/plugins/hid_app/hid_ble_10px.png similarity index 100% rename from applications/plugins/bt_hid_app/bt_remote_10px.png rename to applications/plugins/hid_app/hid_ble_10px.png diff --git a/applications/plugins/hid_app/hid_usb_10px.png b/applications/plugins/hid_app/hid_usb_10px.png new file mode 100644 index 000000000..415de7d23 Binary files /dev/null and b/applications/plugins/hid_app/hid_usb_10px.png differ diff --git a/applications/plugins/hid_app/views.h b/applications/plugins/hid_app/views.h new file mode 100644 index 000000000..68a827ad6 --- /dev/null +++ b/applications/plugins/hid_app/views.h @@ -0,0 +1,9 @@ +typedef enum { + HidViewSubmenu, + HidViewKeynote, + HidViewKeyboard, + HidViewMedia, + HidViewMouse, + BtHidViewTikTok, + HidViewExitConfirm, +} HidView; \ No newline at end of file diff --git a/applications/plugins/bt_hid_app/views/bt_hid_keyboard.c b/applications/plugins/hid_app/views/hid_keyboard.c similarity index 80% rename from applications/plugins/bt_hid_app/views/bt_hid_keyboard.c rename to applications/plugins/hid_app/views/hid_keyboard.c index b88a38325..dff4a7df7 100644 --- a/applications/plugins/bt_hid_app/views/bt_hid_keyboard.c +++ b/applications/plugins/hid_app/views/hid_keyboard.c @@ -1,14 +1,15 @@ -#include "bt_hid_keyboard.h" +#include "hid_keyboard.h" #include -#include -#include #include #include +#include "../hid.h" +#include "hid_icons.h" -#include "Bluetooth_Remote_icons.h" +#define TAG "HidKeyboard" -struct BtHidKeyboard { +struct HidKeyboard { View* view; + Hid* hid; }; typedef struct { @@ -24,7 +25,7 @@ typedef struct { bool back_pressed; bool connected; char key_string[5]; -} BtHidKeyboardModel; +} HidKeyboardModel; typedef struct { uint8_t width; @@ -32,13 +33,12 @@ typedef struct { const Icon* icon; char* shift_key; uint8_t value; -} BtHidKeyboardKey; +} HidKeyboardKey; typedef struct { int8_t x; int8_t y; -} BtHidKeyboardPoint; - +} HidKeyboardPoint; // 4 BY 12 #define MARGIN_TOP 0 #define MARGIN_LEFT 4 @@ -49,7 +49,7 @@ typedef struct { #define COLUMN_COUNT 12 // 0 width items are not drawn, but there value is used -const BtHidKeyboardKey bt_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { +const HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { { {.width = 1, .icon = NULL, .key = "1", .shift_key = "!", .value = HID_KEYBOARD_1}, {.width = 1, .icon = NULL, .key = "2", .shift_key = "@", .value = HID_KEYBOARD_2}, @@ -112,7 +112,7 @@ const BtHidKeyboardKey bt_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { }, { {.width = 1, .icon = &I_Pin_arrow_up_7x9, .value = HID_KEYBOARD_L_SHIFT}, - {.width = 1, .icon = NULL, .key = ",", .shift_key = "<", .value = HID_KEYPAD_COMMA}, + {.width = 1, .icon = NULL, .key = ",", .shift_key = "<", .value = HID_KEYBOARD_COMMA}, {.width = 1, .icon = NULL, .key = ".", .shift_key = ">", .value = HID_KEYBOARD_DOT}, {.width = 4, .icon = NULL, .key = " ", .value = HID_KEYBOARD_SPACEBAR}, {.width = 0, .value = HID_KEYBOARD_SPACEBAR}, @@ -140,19 +140,19 @@ const BtHidKeyboardKey bt_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { }, }; -static void bt_hid_keyboard_to_upper(char* str) { +static void hid_keyboard_to_upper(char* str) { while(*str) { *str = toupper((unsigned char)*str); str++; } } -static void bt_hid_keyboard_draw_key( +static void hid_keyboard_draw_key( Canvas* canvas, - BtHidKeyboardModel* model, + HidKeyboardModel* model, uint8_t x, uint8_t y, - BtHidKeyboardKey key, + HidKeyboardKey key, bool selected) { if(!key.width) return; @@ -190,7 +190,7 @@ static void bt_hid_keyboard_draw_key( if((model->ctrl && key.value == HID_KEYBOARD_L_CTRL) || (model->alt && key.value == HID_KEYBOARD_L_ALT) || (model->gui && key.value == HID_KEYBOARD_L_GUI)) { - bt_hid_keyboard_to_upper(model->key_string); + hid_keyboard_to_upper(model->key_string); } canvas_draw_str_aligned( canvas, @@ -202,9 +202,9 @@ static void bt_hid_keyboard_draw_key( } } -static void bt_hid_keyboard_draw_callback(Canvas* canvas, void* context) { +static void hid_keyboard_draw_callback(Canvas* canvas, void* context) { furi_assert(context); - BtHidKeyboardModel* model = context; + HidKeyboardModel* model = context; // Header if(!model->connected) { @@ -225,17 +225,17 @@ static void bt_hid_keyboard_draw_callback(Canvas* canvas, void* context) { // Start shifting the all keys up if on the next row (Scrolling) uint8_t initY = model->y - 4 > 0 ? model->y - 4 : 0; for(uint8_t y = initY; y < ROW_COUNT; y++) { - const BtHidKeyboardKey* keyboardKeyRow = bt_hid_keyboard_keyset[y]; + const HidKeyboardKey* keyboardKeyRow = hid_keyboard_keyset[y]; uint8_t x = 0; for(uint8_t i = 0; i < COLUMN_COUNT; i++) { - BtHidKeyboardKey key = keyboardKeyRow[i]; + HidKeyboardKey key = keyboardKeyRow[i]; // Select when the button is hovered // Select if the button is hovered within its width // Select if back is clicked and its the backspace key // Deselect when the button clicked or not hovered bool keySelected = (x <= model->x && model->x < (x + key.width)) && y == model->y; bool backSelected = model->back_pressed && key.value == HID_KEYBOARD_DELETE; - bt_hid_keyboard_draw_key( + hid_keyboard_draw_key( canvas, model, x, @@ -247,8 +247,8 @@ static void bt_hid_keyboard_draw_callback(Canvas* canvas, void* context) { } } -static uint8_t bt_hid_keyboard_get_selected_key(BtHidKeyboardModel* model) { - BtHidKeyboardKey key = bt_hid_keyboard_keyset[model->y][model->x]; +static uint8_t hid_keyboard_get_selected_key(HidKeyboardModel* model) { + HidKeyboardKey key = hid_keyboard_keyset[model->y][model->x]; // Use upper case if shift is toggled bool useUppercase = model->shift; // Check if the key has an upper case version @@ -259,34 +259,34 @@ static uint8_t bt_hid_keyboard_get_selected_key(BtHidKeyboardModel* model) { return key.value; } -static void bt_hid_keyboard_get_select_key(BtHidKeyboardModel* model, BtHidKeyboardPoint delta) { +static void hid_keyboard_get_select_key(HidKeyboardModel* model, HidKeyboardPoint delta) { // Keep going until a valid spot is found, this allows for nulls and zero width keys in the map do { if(((int8_t)model->y) + delta.y < 0) model->y = ROW_COUNT - 1; else model->y = (model->y + delta.y) % ROW_COUNT; - } while(delta.y != 0 && bt_hid_keyboard_keyset[model->y][model->x].value == 0); + } while(delta.y != 0 && hid_keyboard_keyset[model->y][model->x].value == 0); do { if(((int8_t)model->x) + delta.x < 0) model->x = COLUMN_COUNT - 1; else model->x = (model->x + delta.x) % COLUMN_COUNT; - } while(delta.x != 0 && bt_hid_keyboard_keyset[model->y][model->x].width == + } while(delta.x != 0 && hid_keyboard_keyset[model->y][model->x].width == 0); // Skip zero width keys, pretend they are one key } -static void bt_hid_keyboard_process(BtHidKeyboard* bt_hid_keyboard, InputEvent* event) { +static void hid_keyboard_process(HidKeyboard* hid_keyboard, InputEvent* event) { with_view_model( - bt_hid_keyboard->view, - BtHidKeyboardModel * model, + hid_keyboard->view, + HidKeyboardModel * model, { if(event->key == InputKeyOk) { if(event->type == InputTypePress) { model->ok_pressed = true; } else if(event->type == InputTypeLong || event->type == InputTypeShort) { - model->last_key_code = bt_hid_keyboard_get_selected_key(model); + model->last_key_code = hid_keyboard_get_selected_key(model); // Toggle the modifier key when clicked, and click the key if(model->last_key_code == HID_KEYBOARD_L_SHIFT) { @@ -314,10 +314,12 @@ static void bt_hid_keyboard_process(BtHidKeyboard* bt_hid_keyboard, InputEvent* else model->modifier_code &= ~KEY_MOD_LEFT_GUI; } - furi_hal_bt_hid_kb_press(model->modifier_code | model->last_key_code); + hid_hal_keyboard_press( + hid_keyboard->hid, model->modifier_code | model->last_key_code); } else if(event->type == InputTypeRelease) { // Release happens after short and long presses - furi_hal_bt_hid_kb_release(model->modifier_code | model->last_key_code); + hid_hal_keyboard_release( + hid_keyboard->hid, model->modifier_code | model->last_key_code); model->ok_pressed = false; } } else if(event->key == InputKeyBack) { @@ -325,66 +327,67 @@ static void bt_hid_keyboard_process(BtHidKeyboard* bt_hid_keyboard, InputEvent* if(event->type == InputTypePress) { model->back_pressed = true; } else if(event->type == InputTypeShort) { - furi_hal_bt_hid_kb_press(HID_KEYBOARD_DELETE); - furi_hal_bt_hid_kb_release(HID_KEYBOARD_DELETE); + hid_hal_keyboard_press(hid_keyboard->hid, HID_KEYBOARD_DELETE); + hid_hal_keyboard_release(hid_keyboard->hid, HID_KEYBOARD_DELETE); } else if(event->type == InputTypeRelease) { model->back_pressed = false; } } else if(event->type == InputTypePress || event->type == InputTypeRepeat) { // Cycle the selected keys if(event->key == InputKeyUp) { - bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 0, .y = -1}); + hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 0, .y = -1}); } else if(event->key == InputKeyDown) { - bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 0, .y = 1}); + hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 0, .y = 1}); } else if(event->key == InputKeyLeft) { - bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = -1, .y = 0}); + hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = -1, .y = 0}); } else if(event->key == InputKeyRight) { - bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 1, .y = 0}); + hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 1, .y = 0}); } } }, true); } -static bool bt_hid_keyboard_input_callback(InputEvent* event, void* context) { +static bool hid_keyboard_input_callback(InputEvent* event, void* context) { furi_assert(context); - BtHidKeyboard* bt_hid_keyboard = context; + HidKeyboard* hid_keyboard = context; bool consumed = false; if(event->type == InputTypeLong && event->key == InputKeyBack) { - furi_hal_bt_hid_kb_release_all(); + hid_hal_keyboard_release_all(hid_keyboard->hid); } else { - bt_hid_keyboard_process(bt_hid_keyboard, event); + hid_keyboard_process(hid_keyboard, event); consumed = true; } return consumed; } -BtHidKeyboard* bt_hid_keyboard_alloc() { - BtHidKeyboard* bt_hid_keyboard = malloc(sizeof(BtHidKeyboard)); - bt_hid_keyboard->view = view_alloc(); - view_set_context(bt_hid_keyboard->view, bt_hid_keyboard); - view_allocate_model(bt_hid_keyboard->view, ViewModelTypeLocking, sizeof(BtHidKeyboardModel)); - view_set_draw_callback(bt_hid_keyboard->view, bt_hid_keyboard_draw_callback); - view_set_input_callback(bt_hid_keyboard->view, bt_hid_keyboard_input_callback); +HidKeyboard* hid_keyboard_alloc(Hid* bt_hid) { + HidKeyboard* hid_keyboard = malloc(sizeof(HidKeyboard)); + hid_keyboard->view = view_alloc(); + hid_keyboard->hid = bt_hid; + view_set_context(hid_keyboard->view, hid_keyboard); + view_allocate_model(hid_keyboard->view, ViewModelTypeLocking, sizeof(HidKeyboardModel)); + view_set_draw_callback(hid_keyboard->view, hid_keyboard_draw_callback); + view_set_input_callback(hid_keyboard->view, hid_keyboard_input_callback); - return bt_hid_keyboard; + return hid_keyboard; } -void bt_hid_keyboard_free(BtHidKeyboard* bt_hid_keyboard) { - furi_assert(bt_hid_keyboard); - view_free(bt_hid_keyboard->view); - free(bt_hid_keyboard); +void hid_keyboard_free(HidKeyboard* hid_keyboard) { + furi_assert(hid_keyboard); + view_free(hid_keyboard->view); + free(hid_keyboard); } -View* bt_hid_keyboard_get_view(BtHidKeyboard* bt_hid_keyboard) { - furi_assert(bt_hid_keyboard); - return bt_hid_keyboard->view; +View* hid_keyboard_get_view(HidKeyboard* hid_keyboard) { + furi_assert(hid_keyboard); + return hid_keyboard->view; } -void bt_hid_keyboard_set_connected_status(BtHidKeyboard* bt_hid_keyboard, bool connected) { - furi_assert(bt_hid_keyboard); +void hid_keyboard_set_connected_status(HidKeyboard* hid_keyboard, bool connected) { + furi_assert(hid_keyboard); with_view_model( - bt_hid_keyboard->view, BtHidKeyboardModel * model, { model->connected = connected; }, true); + hid_keyboard->view, HidKeyboardModel * model, { model->connected = connected; }, true); } diff --git a/applications/plugins/hid_app/views/hid_keyboard.h b/applications/plugins/hid_app/views/hid_keyboard.h new file mode 100644 index 000000000..712771364 --- /dev/null +++ b/applications/plugins/hid_app/views/hid_keyboard.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidKeyboard HidKeyboard; + +HidKeyboard* hid_keyboard_alloc(Hid* bt_hid); + +void hid_keyboard_free(HidKeyboard* hid_keyboard); + +View* hid_keyboard_get_view(HidKeyboard* hid_keyboard); + +void hid_keyboard_set_connected_status(HidKeyboard* hid_keyboard, bool connected); diff --git a/applications/plugins/bt_hid_app/views/bt_hid_keynote.c b/applications/plugins/hid_app/views/hid_keynote.c similarity index 60% rename from applications/plugins/bt_hid_app/views/bt_hid_keynote.c rename to applications/plugins/hid_app/views/hid_keynote.c index 090422521..c95f42780 100644 --- a/applications/plugins/bt_hid_app/views/bt_hid_keynote.c +++ b/applications/plugins/hid_app/views/hid_keynote.c @@ -1,13 +1,14 @@ -#include "bt_hid_keynote.h" -#include -#include -#include +#include "hid_keynote.h" #include +#include "../hid.h" -#include "Bluetooth_Remote_icons.h" +#include "hid_icons.h" -struct BtHidKeynote { +#define TAG "HidKeynote" + +struct HidKeynote { View* view; + Hid* hid; }; typedef struct { @@ -18,9 +19,9 @@ typedef struct { bool ok_pressed; bool back_pressed; bool connected; -} BtHidKeynoteModel; +} HidKeynoteModel; -static void bt_hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { +static void hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { canvas_draw_triangle(canvas, x, y, 5, 3, dir); if(dir == CanvasDirectionBottomToTop) { canvas_draw_line(canvas, x, y + 6, x, y - 1); @@ -33,9 +34,9 @@ static void bt_hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, Canv } } -static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { +static void hid_keynote_draw_callback(Canvas* canvas, void* context) { furi_assert(context); - BtHidKeynoteModel* model = context; + HidKeynoteModel* model = context; // Header if(model->connected) { @@ -56,7 +57,7 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { elements_slightly_rounded_box(canvas, 24, 26, 13, 13); canvas_set_color(canvas, ColorWhite); } - bt_hid_keynote_draw_arrow(canvas, 30, 30, CanvasDirectionBottomToTop); + hid_keynote_draw_arrow(canvas, 30, 30, CanvasDirectionBottomToTop); canvas_set_color(canvas, ColorBlack); // Down @@ -65,7 +66,7 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { elements_slightly_rounded_box(canvas, 24, 47, 13, 13); canvas_set_color(canvas, ColorWhite); } - bt_hid_keynote_draw_arrow(canvas, 30, 55, CanvasDirectionTopToBottom); + hid_keynote_draw_arrow(canvas, 30, 55, CanvasDirectionTopToBottom); canvas_set_color(canvas, ColorBlack); // Left @@ -74,7 +75,7 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { elements_slightly_rounded_box(canvas, 3, 47, 13, 13); canvas_set_color(canvas, ColorWhite); } - bt_hid_keynote_draw_arrow(canvas, 7, 53, CanvasDirectionRightToLeft); + hid_keynote_draw_arrow(canvas, 7, 53, CanvasDirectionRightToLeft); canvas_set_color(canvas, ColorBlack); // Right @@ -83,7 +84,7 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { elements_slightly_rounded_box(canvas, 45, 47, 13, 13); canvas_set_color(canvas, ColorWhite); } - bt_hid_keynote_draw_arrow(canvas, 53, 53, CanvasDirectionLeftToRight); + hid_keynote_draw_arrow(canvas, 53, 53, CanvasDirectionLeftToRight); canvas_set_color(canvas, ColorBlack); // Ok @@ -106,100 +107,101 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back"); } -static void bt_hid_keynote_process(BtHidKeynote* bt_hid_keynote, InputEvent* event) { +static void hid_keynote_process(HidKeynote* hid_keynote, InputEvent* event) { with_view_model( - bt_hid_keynote->view, - BtHidKeynoteModel * model, + hid_keynote->view, + HidKeynoteModel * model, { if(event->type == InputTypePress) { if(event->key == InputKeyUp) { model->up_pressed = true; - furi_hal_bt_hid_kb_press(HID_KEYBOARD_UP_ARROW); + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_UP_ARROW); } else if(event->key == InputKeyDown) { model->down_pressed = true; - furi_hal_bt_hid_kb_press(HID_KEYBOARD_DOWN_ARROW); + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_DOWN_ARROW); } else if(event->key == InputKeyLeft) { model->left_pressed = true; - furi_hal_bt_hid_kb_press(HID_KEYBOARD_LEFT_ARROW); + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_LEFT_ARROW); } else if(event->key == InputKeyRight) { model->right_pressed = true; - furi_hal_bt_hid_kb_press(HID_KEYBOARD_RIGHT_ARROW); + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_RIGHT_ARROW); } else if(event->key == InputKeyOk) { model->ok_pressed = true; - furi_hal_bt_hid_kb_press(HID_KEYBOARD_SPACEBAR); + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_SPACEBAR); } else if(event->key == InputKeyBack) { model->back_pressed = true; } } else if(event->type == InputTypeRelease) { if(event->key == InputKeyUp) { model->up_pressed = false; - furi_hal_bt_hid_kb_release(HID_KEYBOARD_UP_ARROW); + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_UP_ARROW); } else if(event->key == InputKeyDown) { model->down_pressed = false; - furi_hal_bt_hid_kb_release(HID_KEYBOARD_DOWN_ARROW); + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_DOWN_ARROW); } else if(event->key == InputKeyLeft) { model->left_pressed = false; - furi_hal_bt_hid_kb_release(HID_KEYBOARD_LEFT_ARROW); + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_LEFT_ARROW); } else if(event->key == InputKeyRight) { model->right_pressed = false; - furi_hal_bt_hid_kb_release(HID_KEYBOARD_RIGHT_ARROW); + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_RIGHT_ARROW); } else if(event->key == InputKeyOk) { model->ok_pressed = false; - furi_hal_bt_hid_kb_release(HID_KEYBOARD_SPACEBAR); + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_SPACEBAR); } else if(event->key == InputKeyBack) { model->back_pressed = false; } } else if(event->type == InputTypeShort) { if(event->key == InputKeyBack) { - furi_hal_bt_hid_kb_press(HID_KEYBOARD_DELETE); - furi_hal_bt_hid_kb_release(HID_KEYBOARD_DELETE); - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_AC_BACK); - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_AC_BACK); + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_DELETE); + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_DELETE); + hid_hal_consumer_key_press(hid_keynote->hid, HID_CONSUMER_AC_BACK); + hid_hal_consumer_key_release(hid_keynote->hid, HID_CONSUMER_AC_BACK); } } }, true); } -static bool bt_hid_keynote_input_callback(InputEvent* event, void* context) { +static bool hid_keynote_input_callback(InputEvent* event, void* context) { furi_assert(context); - BtHidKeynote* bt_hid_keynote = context; + HidKeynote* hid_keynote = context; bool consumed = false; if(event->type == InputTypeLong && event->key == InputKeyBack) { - furi_hal_bt_hid_kb_release_all(); + hid_hal_keyboard_release_all(hid_keynote->hid); } else { - bt_hid_keynote_process(bt_hid_keynote, event); + hid_keynote_process(hid_keynote, event); consumed = true; } return consumed; } -BtHidKeynote* bt_hid_keynote_alloc() { - BtHidKeynote* bt_hid_keynote = malloc(sizeof(BtHidKeynote)); - bt_hid_keynote->view = view_alloc(); - view_set_context(bt_hid_keynote->view, bt_hid_keynote); - view_allocate_model(bt_hid_keynote->view, ViewModelTypeLocking, sizeof(BtHidKeynoteModel)); - view_set_draw_callback(bt_hid_keynote->view, bt_hid_keynote_draw_callback); - view_set_input_callback(bt_hid_keynote->view, bt_hid_keynote_input_callback); +HidKeynote* hid_keynote_alloc(Hid* hid) { + HidKeynote* hid_keynote = malloc(sizeof(HidKeynote)); + hid_keynote->view = view_alloc(); + hid_keynote->hid = hid; + view_set_context(hid_keynote->view, hid_keynote); + view_allocate_model(hid_keynote->view, ViewModelTypeLocking, sizeof(HidKeynoteModel)); + view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback); + view_set_input_callback(hid_keynote->view, hid_keynote_input_callback); - return bt_hid_keynote; + return hid_keynote; } -void bt_hid_keynote_free(BtHidKeynote* bt_hid_keynote) { - furi_assert(bt_hid_keynote); - view_free(bt_hid_keynote->view); - free(bt_hid_keynote); +void hid_keynote_free(HidKeynote* hid_keynote) { + furi_assert(hid_keynote); + view_free(hid_keynote->view); + free(hid_keynote); } -View* bt_hid_keynote_get_view(BtHidKeynote* bt_hid_keynote) { - furi_assert(bt_hid_keynote); - return bt_hid_keynote->view; +View* hid_keynote_get_view(HidKeynote* hid_keynote) { + furi_assert(hid_keynote); + return hid_keynote->view; } -void bt_hid_keynote_set_connected_status(BtHidKeynote* bt_hid_keynote, bool connected) { - furi_assert(bt_hid_keynote); +void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected) { + furi_assert(hid_keynote); with_view_model( - bt_hid_keynote->view, BtHidKeynoteModel * model, { model->connected = connected; }, true); + hid_keynote->view, HidKeynoteModel * model, { model->connected = connected; }, true); } diff --git a/applications/plugins/hid_app/views/hid_keynote.h b/applications/plugins/hid_app/views/hid_keynote.h new file mode 100644 index 000000000..4d4a0a9b1 --- /dev/null +++ b/applications/plugins/hid_app/views/hid_keynote.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidKeynote HidKeynote; + +HidKeynote* hid_keynote_alloc(Hid* bt_hid); + +void hid_keynote_free(HidKeynote* hid_keynote); + +View* hid_keynote_get_view(HidKeynote* hid_keynote); + +void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected); diff --git a/applications/plugins/bt_hid_app/views/bt_hid_media.c b/applications/plugins/hid_app/views/hid_media.c similarity index 59% rename from applications/plugins/bt_hid_app/views/bt_hid_media.c rename to applications/plugins/hid_app/views/hid_media.c index c0480852f..5d764f73a 100644 --- a/applications/plugins/bt_hid_app/views/bt_hid_media.c +++ b/applications/plugins/hid_app/views/hid_media.c @@ -1,13 +1,17 @@ -#include "bt_hid_media.h" +#include "hid_media.h" #include #include #include #include +#include "../hid.h" -#include "Bluetooth_Remote_icons.h" +#include "hid_icons.h" -struct BtHidMedia { +#define TAG "HidMedia" + +struct HidMedia { View* view; + Hid* hid; }; typedef struct { @@ -17,9 +21,9 @@ typedef struct { bool down_pressed; bool ok_pressed; bool connected; -} BtHidMediaModel; +} HidMediaModel; -static void bt_hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { +static void hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { canvas_draw_triangle(canvas, x, y, 5, 3, dir); if(dir == CanvasDirectionBottomToTop) { canvas_draw_dot(canvas, x, y - 1); @@ -32,9 +36,9 @@ static void bt_hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, Canvas } } -static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { +static void hid_media_draw_callback(Canvas* canvas, void* context) { furi_assert(context); - BtHidMediaModel* model = context; + HidMediaModel* model = context; // Header if(model->connected) { @@ -76,8 +80,8 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - bt_hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft); - bt_hid_media_draw_arrow(canvas, 86, 31, CanvasDirectionRightToLeft); + hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft); + hid_media_draw_arrow(canvas, 86, 31, CanvasDirectionRightToLeft); canvas_set_color(canvas, ColorBlack); // Right @@ -87,8 +91,8 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - bt_hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight); - bt_hid_media_draw_arrow(canvas, 116, 31, CanvasDirectionLeftToRight); + hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight); + hid_media_draw_arrow(canvas, 116, 31, CanvasDirectionLeftToRight); canvas_set_color(canvas, ColorBlack); // Ok @@ -96,7 +100,7 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { canvas_draw_icon(canvas, 93, 25, &I_Pressed_Button_13x13); canvas_set_color(canvas, ColorWhite); } - bt_hid_media_draw_arrow(canvas, 96, 31, CanvasDirectionLeftToRight); + hid_media_draw_arrow(canvas, 96, 31, CanvasDirectionLeftToRight); canvas_draw_line(canvas, 100, 29, 100, 33); canvas_draw_line(canvas, 102, 29, 102, 33); canvas_set_color(canvas, ColorBlack); @@ -107,100 +111,101 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); } -static void bt_hid_media_process_press(BtHidMedia* bt_hid_media, InputEvent* event) { +static void hid_media_process_press(HidMedia* hid_media, InputEvent* event) { with_view_model( - bt_hid_media->view, - BtHidMediaModel * model, + hid_media->view, + HidMediaModel * model, { if(event->key == InputKeyUp) { model->up_pressed = true; - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_INCREMENT); + hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_VOLUME_INCREMENT); } else if(event->key == InputKeyDown) { model->down_pressed = true; - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_DECREMENT); + hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_VOLUME_DECREMENT); } else if(event->key == InputKeyLeft) { model->left_pressed = true; - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_SCAN_PREVIOUS_TRACK); + hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_SCAN_PREVIOUS_TRACK); } else if(event->key == InputKeyRight) { model->right_pressed = true; - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_SCAN_NEXT_TRACK); + hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_SCAN_NEXT_TRACK); } else if(event->key == InputKeyOk) { model->ok_pressed = true; - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_PLAY_PAUSE); + hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_PLAY_PAUSE); } }, true); } -static void bt_hid_media_process_release(BtHidMedia* bt_hid_media, InputEvent* event) { +static void hid_media_process_release(HidMedia* hid_media, InputEvent* event) { with_view_model( - bt_hid_media->view, - BtHidMediaModel * model, + hid_media->view, + HidMediaModel * model, { if(event->key == InputKeyUp) { model->up_pressed = false; - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_INCREMENT); + hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_VOLUME_INCREMENT); } else if(event->key == InputKeyDown) { model->down_pressed = false; - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_DECREMENT); + hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_VOLUME_DECREMENT); } else if(event->key == InputKeyLeft) { model->left_pressed = false; - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_SCAN_PREVIOUS_TRACK); + hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_SCAN_PREVIOUS_TRACK); } else if(event->key == InputKeyRight) { model->right_pressed = false; - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_SCAN_NEXT_TRACK); + hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_SCAN_NEXT_TRACK); } else if(event->key == InputKeyOk) { model->ok_pressed = false; - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_PLAY_PAUSE); + hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_PLAY_PAUSE); } }, true); } -static bool bt_hid_media_input_callback(InputEvent* event, void* context) { +static bool hid_media_input_callback(InputEvent* event, void* context) { furi_assert(context); - BtHidMedia* bt_hid_media = context; + HidMedia* hid_media = context; bool consumed = false; if(event->type == InputTypePress) { - bt_hid_media_process_press(bt_hid_media, event); + hid_media_process_press(hid_media, event); consumed = true; } else if(event->type == InputTypeRelease) { - bt_hid_media_process_release(bt_hid_media, event); + hid_media_process_release(hid_media, event); consumed = true; } else if(event->type == InputTypeShort) { if(event->key == InputKeyBack) { - furi_hal_bt_hid_consumer_key_release_all(); + hid_hal_consumer_key_release_all(hid_media->hid); } } return consumed; } -BtHidMedia* bt_hid_media_alloc() { - BtHidMedia* bt_hid_media = malloc(sizeof(BtHidMedia)); - bt_hid_media->view = view_alloc(); - view_set_context(bt_hid_media->view, bt_hid_media); - view_allocate_model(bt_hid_media->view, ViewModelTypeLocking, sizeof(BtHidMediaModel)); - view_set_draw_callback(bt_hid_media->view, bt_hid_media_draw_callback); - view_set_input_callback(bt_hid_media->view, bt_hid_media_input_callback); +HidMedia* hid_media_alloc(Hid* hid) { + HidMedia* hid_media = malloc(sizeof(HidMedia)); + hid_media->view = view_alloc(); + hid_media->hid = hid; + view_set_context(hid_media->view, hid_media); + view_allocate_model(hid_media->view, ViewModelTypeLocking, sizeof(HidMediaModel)); + view_set_draw_callback(hid_media->view, hid_media_draw_callback); + view_set_input_callback(hid_media->view, hid_media_input_callback); - return bt_hid_media; + return hid_media; } -void bt_hid_media_free(BtHidMedia* bt_hid_media) { - furi_assert(bt_hid_media); - view_free(bt_hid_media->view); - free(bt_hid_media); +void hid_media_free(HidMedia* hid_media) { + furi_assert(hid_media); + view_free(hid_media->view); + free(hid_media); } -View* bt_hid_media_get_view(BtHidMedia* bt_hid_media) { - furi_assert(bt_hid_media); - return bt_hid_media->view; +View* hid_media_get_view(HidMedia* hid_media) { + furi_assert(hid_media); + return hid_media->view; } -void bt_hid_media_set_connected_status(BtHidMedia* bt_hid_media, bool connected) { - furi_assert(bt_hid_media); +void hid_media_set_connected_status(HidMedia* hid_media, bool connected) { + furi_assert(hid_media); with_view_model( - bt_hid_media->view, BtHidMediaModel * model, { model->connected = connected; }, true); + hid_media->view, HidMediaModel * model, { model->connected = connected; }, true); } diff --git a/applications/plugins/hid_app/views/hid_media.h b/applications/plugins/hid_app/views/hid_media.h new file mode 100644 index 000000000..4aa51dc17 --- /dev/null +++ b/applications/plugins/hid_app/views/hid_media.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +typedef struct HidMedia HidMedia; + +HidMedia* hid_media_alloc(); + +void hid_media_free(HidMedia* hid_media); + +View* hid_media_get_view(HidMedia* hid_media); + +void hid_media_set_connected_status(HidMedia* hid_media, bool connected); diff --git a/applications/plugins/bt_hid_app/views/bt_hid_mouse.c b/applications/plugins/hid_app/views/hid_mouse.c similarity index 68% rename from applications/plugins/bt_hid_app/views/bt_hid_mouse.c rename to applications/plugins/hid_app/views/hid_mouse.c index 5fb0cf358..d1d76c15a 100644 --- a/applications/plugins/bt_hid_app/views/bt_hid_mouse.c +++ b/applications/plugins/hid_app/views/hid_mouse.c @@ -1,16 +1,15 @@ -#include "bt_hid_mouse.h" -#include -#include -#include +#include "hid_mouse.h" #include +#include "../hid.h" -#include "Bluetooth_Remote_icons.h" +#include "hid_icons.h" -struct BtHidMouse { +#define TAG "HidMouse" + +struct HidMouse { View* view; + Hid* hid; }; -#define MOUSE_MOVE_SHORT 5 -#define MOUSE_MOVE_LONG 20 typedef struct { bool left_pressed; @@ -21,11 +20,11 @@ typedef struct { bool left_mouse_held; bool right_mouse_pressed; bool connected; -} BtHidMouseModel; +} HidMouseModel; -static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) { +static void hid_mouse_draw_callback(Canvas* canvas, void* context) { furi_assert(context); - BtHidMouseModel* model = context; + HidMouseModel* model = context; // Header if(model->connected) { @@ -103,15 +102,15 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) { } } -static void bt_hid_mouse_process(BtHidMouse* bt_hid_mouse, InputEvent* event) { +static void hid_mouse_process(HidMouse* hid_mouse, InputEvent* event) { with_view_model( - bt_hid_mouse->view, - BtHidMouseModel * model, + hid_mouse->view, + HidMouseModel * model, { if(event->key == InputKeyBack) { if(event->type == InputTypeShort) { - furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_RIGHT); - furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_RIGHT); + hid_hal_mouse_press(hid_mouse->hid, HID_MOUSE_BTN_RIGHT); + hid_hal_mouse_release(hid_mouse->hid, HID_MOUSE_BTN_RIGHT); } else if(event->type == InputTypePress) { model->right_mouse_pressed = true; } else if(event->type == InputTypeRelease) { @@ -120,11 +119,12 @@ static void bt_hid_mouse_process(BtHidMouse* bt_hid_mouse, InputEvent* event) { } else if(event->key == InputKeyOk) { if(event->type == InputTypeShort) { // Just release if it was being held before - if(!model->left_mouse_held) furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); - furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT); + if(!model->left_mouse_held) + hid_hal_mouse_press(hid_mouse->hid, HID_MOUSE_BTN_LEFT); + hid_hal_mouse_release(hid_mouse->hid, HID_MOUSE_BTN_LEFT); model->left_mouse_held = false; } else if(event->type == InputTypeLong) { - furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); + hid_hal_mouse_press(hid_mouse->hid, HID_MOUSE_BTN_LEFT); model->left_mouse_held = true; model->left_mouse_pressed = true; } else if(event->type == InputTypePress) { @@ -133,40 +133,39 @@ static void bt_hid_mouse_process(BtHidMouse* bt_hid_mouse, InputEvent* event) { // Only release if it wasn't a long press if(!model->left_mouse_held) model->left_mouse_pressed = false; } - } else if(event->key == InputKeyRight) { if(event->type == InputTypePress) { model->right_pressed = true; - furi_hal_bt_hid_mouse_move(MOUSE_MOVE_SHORT, 0); + hid_hal_mouse_move(hid_mouse->hid, MOUSE_MOVE_SHORT, 0); } else if(event->type == InputTypeRepeat) { - furi_hal_bt_hid_mouse_move(MOUSE_MOVE_LONG, 0); + hid_hal_mouse_move(hid_mouse->hid, MOUSE_MOVE_LONG, 0); } else if(event->type == InputTypeRelease) { model->right_pressed = false; } } else if(event->key == InputKeyLeft) { if(event->type == InputTypePress) { model->left_pressed = true; - furi_hal_bt_hid_mouse_move(-MOUSE_MOVE_SHORT, 0); + hid_hal_mouse_move(hid_mouse->hid, -MOUSE_MOVE_SHORT, 0); } else if(event->type == InputTypeRepeat) { - furi_hal_bt_hid_mouse_move(-MOUSE_MOVE_LONG, 0); + hid_hal_mouse_move(hid_mouse->hid, -MOUSE_MOVE_LONG, 0); } else if(event->type == InputTypeRelease) { model->left_pressed = false; } } else if(event->key == InputKeyDown) { if(event->type == InputTypePress) { model->down_pressed = true; - furi_hal_bt_hid_mouse_move(0, MOUSE_MOVE_SHORT); + hid_hal_mouse_move(hid_mouse->hid, 0, MOUSE_MOVE_SHORT); } else if(event->type == InputTypeRepeat) { - furi_hal_bt_hid_mouse_move(0, MOUSE_MOVE_LONG); + hid_hal_mouse_move(hid_mouse->hid, 0, MOUSE_MOVE_LONG); } else if(event->type == InputTypeRelease) { model->down_pressed = false; } } else if(event->key == InputKeyUp) { if(event->type == InputTypePress) { model->up_pressed = true; - furi_hal_bt_hid_mouse_move(0, -MOUSE_MOVE_SHORT); + hid_hal_mouse_move(hid_mouse->hid, 0, -MOUSE_MOVE_SHORT); } else if(event->type == InputTypeRepeat) { - furi_hal_bt_hid_mouse_move(0, -MOUSE_MOVE_LONG); + hid_hal_mouse_move(hid_mouse->hid, 0, -MOUSE_MOVE_LONG); } else if(event->type == InputTypeRelease) { model->up_pressed = false; } @@ -175,45 +174,46 @@ static void bt_hid_mouse_process(BtHidMouse* bt_hid_mouse, InputEvent* event) { true); } -static bool bt_hid_mouse_input_callback(InputEvent* event, void* context) { +static bool hid_mouse_input_callback(InputEvent* event, void* context) { furi_assert(context); - BtHidMouse* bt_hid_mouse = context; + HidMouse* hid_mouse = context; bool consumed = false; if(event->type == InputTypeLong && event->key == InputKeyBack) { - furi_hal_bt_hid_mouse_release_all(); + hid_hal_mouse_release_all(hid_mouse->hid); } else { - bt_hid_mouse_process(bt_hid_mouse, event); + hid_mouse_process(hid_mouse, event); consumed = true; } return consumed; } -BtHidMouse* bt_hid_mouse_alloc() { - BtHidMouse* bt_hid_mouse = malloc(sizeof(BtHidMouse)); - bt_hid_mouse->view = view_alloc(); - view_set_context(bt_hid_mouse->view, bt_hid_mouse); - view_allocate_model(bt_hid_mouse->view, ViewModelTypeLocking, sizeof(BtHidMouseModel)); - view_set_draw_callback(bt_hid_mouse->view, bt_hid_mouse_draw_callback); - view_set_input_callback(bt_hid_mouse->view, bt_hid_mouse_input_callback); +HidMouse* hid_mouse_alloc(Hid* hid) { + HidMouse* hid_mouse = malloc(sizeof(HidMouse)); + hid_mouse->view = view_alloc(); + hid_mouse->hid = hid; + view_set_context(hid_mouse->view, hid_mouse); + view_allocate_model(hid_mouse->view, ViewModelTypeLocking, sizeof(HidMouseModel)); + view_set_draw_callback(hid_mouse->view, hid_mouse_draw_callback); + view_set_input_callback(hid_mouse->view, hid_mouse_input_callback); - return bt_hid_mouse; + return hid_mouse; } -void bt_hid_mouse_free(BtHidMouse* bt_hid_mouse) { - furi_assert(bt_hid_mouse); - view_free(bt_hid_mouse->view); - free(bt_hid_mouse); +void hid_mouse_free(HidMouse* hid_mouse) { + furi_assert(hid_mouse); + view_free(hid_mouse->view); + free(hid_mouse); } -View* bt_hid_mouse_get_view(BtHidMouse* bt_hid_mouse) { - furi_assert(bt_hid_mouse); - return bt_hid_mouse->view; +View* hid_mouse_get_view(HidMouse* hid_mouse) { + furi_assert(hid_mouse); + return hid_mouse->view; } -void bt_hid_mouse_set_connected_status(BtHidMouse* bt_hid_mouse, bool connected) { - furi_assert(bt_hid_mouse); +void hid_mouse_set_connected_status(HidMouse* hid_mouse, bool connected) { + furi_assert(hid_mouse); with_view_model( - bt_hid_mouse->view, BtHidMouseModel * model, { model->connected = connected; }, true); + hid_mouse->view, HidMouseModel * model, { model->connected = connected; }, true); } diff --git a/applications/plugins/hid_app/views/hid_mouse.h b/applications/plugins/hid_app/views/hid_mouse.h new file mode 100644 index 000000000..d9fb2fd88 --- /dev/null +++ b/applications/plugins/hid_app/views/hid_mouse.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#define MOUSE_MOVE_SHORT 5 +#define MOUSE_MOVE_LONG 20 + +typedef struct Hid Hid; +typedef struct HidMouse HidMouse; + +HidMouse* hid_mouse_alloc(Hid* bt_hid); + +void hid_mouse_free(HidMouse* hid_mouse); + +View* hid_mouse_get_view(HidMouse* hid_mouse); + +void hid_mouse_set_connected_status(HidMouse* hid_mouse, bool connected); diff --git a/applications/plugins/bt_hid_app/views/bt_hid_tiktok.c b/applications/plugins/hid_app/views/hid_tiktok.c similarity index 59% rename from applications/plugins/bt_hid_app/views/bt_hid_tiktok.c rename to applications/plugins/hid_app/views/hid_tiktok.c index f751c53d2..42abf4148 100644 --- a/applications/plugins/bt_hid_app/views/bt_hid_tiktok.c +++ b/applications/plugins/hid_app/views/hid_tiktok.c @@ -1,13 +1,14 @@ -#include "bt_hid_tiktok.h" -#include -#include -#include +#include "hid_tiktok.h" +#include "../hid.h" #include -#include "Bluetooth_Remote_icons.h" +#include "hid_icons.h" -struct BtHidTikTok { +#define TAG "HidTikTok" + +struct HidTikTok { View* view; + Hid* hid; }; typedef struct { @@ -18,11 +19,11 @@ typedef struct { bool ok_pressed; bool connected; bool is_cursor_set; -} BtHidTikTokModel; +} HidTikTokModel; -static void bt_hid_tiktok_draw_callback(Canvas* canvas, void* context) { +static void hid_tiktok_draw_callback(Canvas* canvas, void* context) { furi_assert(context); - BtHidTikTokModel* model = context; + HidTikTokModel* model = context; // Header if(model->connected) { @@ -89,102 +90,104 @@ static void bt_hid_tiktok_draw_callback(Canvas* canvas, void* context) { elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); } -static void bt_hid_tiktok_reset_cursor() { +static void hid_tiktok_reset_cursor(HidTikTok* hid_tiktok) { // Set cursor to the phone's left up corner // Delays to guarantee one packet per connection interval for(size_t i = 0; i < 8; i++) { - furi_hal_bt_hid_mouse_move(-127, -127); + hid_hal_mouse_move(hid_tiktok->hid, -127, -127); furi_delay_ms(50); } // Move cursor from the corner - furi_hal_bt_hid_mouse_move(20, 120); + hid_hal_mouse_move(hid_tiktok->hid, 20, 120); furi_delay_ms(50); } -static void bt_hid_tiktok_process_press(BtHidTikTokModel* model, InputEvent* event) { +static void + hid_tiktok_process_press(HidTikTok* hid_tiktok, HidTikTokModel* model, InputEvent* event) { if(event->key == InputKeyUp) { model->up_pressed = true; } else if(event->key == InputKeyDown) { model->down_pressed = true; } else if(event->key == InputKeyLeft) { model->left_pressed = true; - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_DECREMENT); + hid_hal_consumer_key_press(hid_tiktok->hid, HID_CONSUMER_VOLUME_DECREMENT); } else if(event->key == InputKeyRight) { model->right_pressed = true; - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_INCREMENT); + hid_hal_consumer_key_press(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT); } else if(event->key == InputKeyOk) { model->ok_pressed = true; } } -static void bt_hid_tiktok_process_release(BtHidTikTokModel* model, InputEvent* event) { +static void + hid_tiktok_process_release(HidTikTok* hid_tiktok, HidTikTokModel* model, InputEvent* event) { if(event->key == InputKeyUp) { model->up_pressed = false; } else if(event->key == InputKeyDown) { model->down_pressed = false; } else if(event->key == InputKeyLeft) { model->left_pressed = false; - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_DECREMENT); + hid_hal_consumer_key_release(hid_tiktok->hid, HID_CONSUMER_VOLUME_DECREMENT); } else if(event->key == InputKeyRight) { model->right_pressed = false; - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_INCREMENT); + hid_hal_consumer_key_release(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT); } else if(event->key == InputKeyOk) { model->ok_pressed = false; } } -static bool bt_hid_tiktok_input_callback(InputEvent* event, void* context) { +static bool hid_tiktok_input_callback(InputEvent* event, void* context) { furi_assert(context); - BtHidTikTok* bt_hid_tiktok = context; + HidTikTok* hid_tiktok = context; bool consumed = false; with_view_model( - bt_hid_tiktok->view, - BtHidTikTokModel * model, + hid_tiktok->view, + HidTikTokModel * model, { if(event->type == InputTypePress) { - bt_hid_tiktok_process_press(model, event); + hid_tiktok_process_press(hid_tiktok, model, event); if(model->connected && !model->is_cursor_set) { - bt_hid_tiktok_reset_cursor(); + hid_tiktok_reset_cursor(hid_tiktok); model->is_cursor_set = true; } consumed = true; } else if(event->type == InputTypeRelease) { - bt_hid_tiktok_process_release(model, event); + hid_tiktok_process_release(hid_tiktok, model, event); consumed = true; } else if(event->type == InputTypeShort) { if(event->key == InputKeyOk) { - furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); + hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); furi_delay_ms(50); - furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT); + hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); furi_delay_ms(50); - furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); + hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); furi_delay_ms(50); - furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT); + hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); consumed = true; } else if(event->key == InputKeyUp) { // Emulate up swipe - furi_hal_bt_hid_mouse_scroll(-6); - furi_hal_bt_hid_mouse_scroll(-12); - furi_hal_bt_hid_mouse_scroll(-19); - furi_hal_bt_hid_mouse_scroll(-12); - furi_hal_bt_hid_mouse_scroll(-6); + hid_hal_mouse_scroll(hid_tiktok->hid, -6); + hid_hal_mouse_scroll(hid_tiktok->hid, -12); + hid_hal_mouse_scroll(hid_tiktok->hid, -19); + hid_hal_mouse_scroll(hid_tiktok->hid, -12); + hid_hal_mouse_scroll(hid_tiktok->hid, -6); consumed = true; } else if(event->key == InputKeyDown) { // Emulate down swipe - furi_hal_bt_hid_mouse_scroll(6); - furi_hal_bt_hid_mouse_scroll(12); - furi_hal_bt_hid_mouse_scroll(19); - furi_hal_bt_hid_mouse_scroll(12); - furi_hal_bt_hid_mouse_scroll(6); + hid_hal_mouse_scroll(hid_tiktok->hid, 6); + hid_hal_mouse_scroll(hid_tiktok->hid, 12); + hid_hal_mouse_scroll(hid_tiktok->hid, 19); + hid_hal_mouse_scroll(hid_tiktok->hid, 12); + hid_hal_mouse_scroll(hid_tiktok->hid, 6); consumed = true; } else if(event->key == InputKeyBack) { - furi_hal_bt_hid_consumer_key_release_all(); + hid_hal_consumer_key_release_all(hid_tiktok->hid); consumed = true; } } else if(event->type == InputTypeLong) { if(event->key == InputKeyBack) { - furi_hal_bt_hid_consumer_key_release_all(); + hid_hal_consumer_key_release_all(hid_tiktok->hid); model->is_cursor_set = false; consumed = false; } @@ -195,33 +198,34 @@ static bool bt_hid_tiktok_input_callback(InputEvent* event, void* context) { return consumed; } -BtHidTikTok* bt_hid_tiktok_alloc() { - BtHidTikTok* bt_hid_tiktok = malloc(sizeof(BtHidTikTok)); - bt_hid_tiktok->view = view_alloc(); - view_set_context(bt_hid_tiktok->view, bt_hid_tiktok); - view_allocate_model(bt_hid_tiktok->view, ViewModelTypeLocking, sizeof(BtHidTikTokModel)); - view_set_draw_callback(bt_hid_tiktok->view, bt_hid_tiktok_draw_callback); - view_set_input_callback(bt_hid_tiktok->view, bt_hid_tiktok_input_callback); +HidTikTok* hid_tiktok_alloc(Hid* bt_hid) { + HidTikTok* hid_tiktok = malloc(sizeof(HidTikTok)); + hid_tiktok->hid = bt_hid; + hid_tiktok->view = view_alloc(); + view_set_context(hid_tiktok->view, hid_tiktok); + view_allocate_model(hid_tiktok->view, ViewModelTypeLocking, sizeof(HidTikTokModel)); + view_set_draw_callback(hid_tiktok->view, hid_tiktok_draw_callback); + view_set_input_callback(hid_tiktok->view, hid_tiktok_input_callback); - return bt_hid_tiktok; + return hid_tiktok; } -void bt_hid_tiktok_free(BtHidTikTok* bt_hid_tiktok) { - furi_assert(bt_hid_tiktok); - view_free(bt_hid_tiktok->view); - free(bt_hid_tiktok); +void hid_tiktok_free(HidTikTok* hid_tiktok) { + furi_assert(hid_tiktok); + view_free(hid_tiktok->view); + free(hid_tiktok); } -View* bt_hid_tiktok_get_view(BtHidTikTok* bt_hid_tiktok) { - furi_assert(bt_hid_tiktok); - return bt_hid_tiktok->view; +View* hid_tiktok_get_view(HidTikTok* hid_tiktok) { + furi_assert(hid_tiktok); + return hid_tiktok->view; } -void bt_hid_tiktok_set_connected_status(BtHidTikTok* bt_hid_tiktok, bool connected) { - furi_assert(bt_hid_tiktok); +void hid_tiktok_set_connected_status(HidTikTok* hid_tiktok, bool connected) { + furi_assert(hid_tiktok); with_view_model( - bt_hid_tiktok->view, - BtHidTikTokModel * model, + hid_tiktok->view, + HidTikTokModel * model, { model->connected = connected; model->is_cursor_set = false; diff --git a/applications/plugins/hid_app/views/hid_tiktok.h b/applications/plugins/hid_app/views/hid_tiktok.h new file mode 100644 index 000000000..b2efc3692 --- /dev/null +++ b/applications/plugins/hid_app/views/hid_tiktok.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidTikTok HidTikTok; + +HidTikTok* hid_tiktok_alloc(Hid* bt_hid); + +void hid_tiktok_free(HidTikTok* hid_tiktok); + +View* hid_tiktok_get_view(HidTikTok* hid_tiktok); + +void hid_tiktok_set_connected_status(HidTikTok* hid_tiktok, bool connected); diff --git a/applications/services/applications.h b/applications/services/applications.h index 012e80ddb..acbfea312 100644 --- a/applications/services/applications.h +++ b/applications/services/applications.h @@ -56,7 +56,7 @@ extern const size_t FLIPPER_DEBUG_APPS_COUNT; extern const FlipperApplication FLIPPER_SYSTEM_APPS[]; extern const size_t FLIPPER_SYSTEM_APPS_COUNT; -/* Seperate scene app holder +/* Separate scene app holder * Spawned by loader */ extern const FlipperApplication FLIPPER_SCENE; diff --git a/applications/services/desktop/animations/animation_manager.h b/applications/services/desktop/animations/animation_manager.h index 9802c4f1f..234d20de0 100644 --- a/applications/services/desktop/animations/animation_manager.h +++ b/applications/services/desktop/animations/animation_manager.h @@ -69,7 +69,7 @@ View* animation_manager_get_animation_view(AnimationManager* animation_manager); void animation_manager_set_context(AnimationManager* animation_manager, void* context); /** - * Set callback for Animation Manager for defered calls + * Set callback for Animation Manager for deferred calls * for animation_manager_new_idle_process(). * Animation Manager doesn't have it's own thread, so main thread gives * callbacks to A.M. to call when it should perform some inner manipulations. @@ -96,7 +96,7 @@ void animation_manager_set_new_idle_callback( void animation_manager_new_idle_process(AnimationManager* animation_manager); /** - * Set callback for Animation Manager for defered calls + * Set callback for Animation Manager for deferred calls * for animation_manager_check_blocking_process(). * * @animation_manager instance @@ -115,7 +115,7 @@ void animation_manager_set_check_callback( void animation_manager_check_blocking_process(AnimationManager* animation_manager); /** - * Set callback for Animation Manager for defered calls + * Set callback for Animation Manager for deferred calls * for animation_manager_interact_process(). * * @animation_manager instance diff --git a/applications/services/gui/elements.h b/applications/services/gui/elements.h index b2d204de7..9f155402b 100644 --- a/applications/services/gui/elements.h +++ b/applications/services/gui/elements.h @@ -90,7 +90,7 @@ void elements_button_center(Canvas* canvas, const char* str); * * @param canvas Canvas instance * @param x, y coordinates based on align param - * @param horizontal, vertical aligment of multiline text + * @param horizontal, vertical alignment of multiline text * @param text string (possible multiline) */ void elements_multiline_text_aligned( diff --git a/applications/services/gui/modules/dialog_ex.h b/applications/services/gui/modules/dialog_ex.h index 4c6094239..26a465354 100644 --- a/applications/services/gui/modules/dialog_ex.h +++ b/applications/services/gui/modules/dialog_ex.h @@ -76,8 +76,8 @@ void dialog_ex_set_context(DialogEx* dialog_ex, void* context); * @param text text to be shown, can be multiline * @param x x position * @param y y position - * @param horizontal horizontal text aligment - * @param vertical vertical text aligment + * @param horizontal horizontal text alignment + * @param vertical vertical text alignment */ void dialog_ex_set_header( DialogEx* dialog_ex, @@ -95,8 +95,8 @@ void dialog_ex_set_header( * @param text text to be shown, can be multiline * @param x x position * @param y y position - * @param horizontal horizontal text aligment - * @param vertical vertical text aligment + * @param horizontal horizontal text alignment + * @param vertical vertical text alignment */ void dialog_ex_set_text( DialogEx* dialog_ex, diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index 60e78b01c..203be23ed 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -232,7 +232,10 @@ static bool browser_is_item_in_array(FileBrowserModel* model, uint32_t idx) { static bool browser_is_list_load_required(FileBrowserModel* model) { size_t array_size = items_array_size(model->items); - uint32_t item_cnt = (model->is_root) ? model->item_cnt : model->item_cnt - 1; + if((array_size > 0) && (!model->is_root) && (model->array_offset == 0)) { + array_size--; + } + uint32_t item_cnt = (model->is_root) ? (model->item_cnt) : (model->item_cnt - 1); if((model->list_loading) || (array_size >= item_cnt)) { return false; @@ -524,7 +527,7 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { model->list_loading = true; int32_t load_offset = CLAMP( model->item_idx - ITEM_LIST_LEN_MAX / 4 * 3, - (int32_t)model->item_cnt - ITEM_LIST_LEN_MAX, + (int32_t)model->item_cnt, 0); file_browser_worker_load( browser->worker, load_offset, ITEM_LIST_LEN_MAX); @@ -535,7 +538,7 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { model->list_loading = true; int32_t load_offset = CLAMP( model->item_idx - ITEM_LIST_LEN_MAX / 4 * 1, - (int32_t)model->item_cnt - ITEM_LIST_LEN_MAX, + (int32_t)model->item_cnt, 0); file_browser_worker_load( browser->worker, load_offset, ITEM_LIST_LEN_MAX); @@ -590,6 +593,19 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { } consumed = true; } + } else if(event->key == InputKeyBack) { + if(event->type == InputTypeShort) { + bool is_root = false; + with_view_model( + browser->view, FileBrowserModel * model, { is_root = model->is_root; }, false); + + if(!is_root && !file_browser_worker_is_in_start_folder(browser->worker)) { + consumed = true; + if(!is_root) { + file_browser_worker_folder_exit(browser->worker); + } + } + } } return consumed; diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index e3c38b053..8efff08a0 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -37,6 +37,8 @@ struct BrowserWorker { FuriThread* thread; FuriString* filter_extension; + FuriString* path_start; + FuriString* path_current; FuriString* path_next; int32_t item_sel_idx; uint32_t load_offset; @@ -293,6 +295,7 @@ static int32_t browser_worker(void* context) { int32_t file_idx = 0; browser_folder_init(browser, path, filename, &items_cnt, &file_idx); + furi_string_set(browser->path_current, path); FURI_LOG_D( TAG, "Enter folder: %s items: %lu idx: %ld", @@ -315,6 +318,7 @@ static int32_t browser_worker(void* context) { // Pop previous selected item index from history array idx_last_array_pop_back(&file_idx, browser->idx_last); } + furi_string_set(browser->path_current, path); FURI_LOG_D( TAG, "Exit to: %s items: %lu idx: %ld", @@ -369,8 +373,14 @@ BrowserWorker* browser->filter_extension = furi_string_alloc_set(filter_ext); browser->skip_assets = skip_assets; + browser->path_start = furi_string_alloc_set(path); + browser->path_current = furi_string_alloc_set(path); browser->path_next = furi_string_alloc_set(path); + if(browser_path_is_file(browser->path_start)) { + browser_path_trim(browser->path_start); + } + browser->thread = furi_thread_alloc_ex("BrowserWorker", 2048, browser_worker, browser); furi_thread_start(browser->thread); @@ -386,6 +396,8 @@ void file_browser_worker_free(BrowserWorker* browser) { furi_string_free(browser->filter_extension); furi_string_free(browser->path_next); + furi_string_free(browser->path_current); + furi_string_free(browser->path_start); idx_last_array_clear(browser->idx_last); @@ -444,6 +456,11 @@ void file_browser_worker_folder_enter(BrowserWorker* browser, FuriString* path, furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderEnter); } +bool file_browser_worker_is_in_start_folder(BrowserWorker* browser) { + furi_assert(browser); + return (furi_string_cmp(browser->path_start, browser->path_current) == 0); +} + void file_browser_worker_folder_exit(BrowserWorker* browser) { furi_assert(browser); furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderExit); diff --git a/applications/services/gui/modules/file_browser_worker.h b/applications/services/gui/modules/file_browser_worker.h index 230bb5b45..2f8155401 100644 --- a/applications/services/gui/modules/file_browser_worker.h +++ b/applications/services/gui/modules/file_browser_worker.h @@ -52,6 +52,8 @@ void file_browser_worker_set_config( void file_browser_worker_folder_enter(BrowserWorker* browser, FuriString* path, int32_t item_idx); +bool file_browser_worker_is_in_start_folder(BrowserWorker* browser); + void file_browser_worker_folder_exit(BrowserWorker* browser); void file_browser_worker_folder_refresh(BrowserWorker* browser, int32_t item_idx); diff --git a/applications/services/gui/modules/popup.h b/applications/services/gui/modules/popup.h index 94f49a2ba..13371a05d 100644 --- a/applications/services/gui/modules/popup.h +++ b/applications/services/gui/modules/popup.h @@ -64,7 +64,7 @@ void popup_set_context(Popup* popup, void* context); * @param x x position * @param y y position * @param horizontal horizontal alignment - * @param vertical vertical aligment + * @param vertical vertical alignment */ void popup_set_header( Popup* popup, @@ -83,7 +83,7 @@ void popup_set_header( * @param x x position * @param y y position * @param horizontal horizontal alignment - * @param vertical vertical aligment + * @param vertical vertical alignment */ void popup_set_text( Popup* popup, diff --git a/applications/services/gui/modules/text_box.c b/applications/services/gui/modules/text_box.c index 99d7d04f1..079a1294d 100644 --- a/applications/services/gui/modules/text_box.c +++ b/applications/services/gui/modules/text_box.c @@ -43,7 +43,7 @@ static void text_box_process_up(TextBox* text_box) { model->scroll_pos--; // Reach last symbol of previous line model->text_pos--; - // Search prevous line start + // Search previous line start while((model->text_pos != model->text) && (*(--model->text_pos) != '\n')) ; if(*model->text_pos == '\n') { diff --git a/applications/services/gui/modules/text_input.h b/applications/services/gui/modules/text_input.h index 893fbd533..218df3141 100644 --- a/applications/services/gui/modules/text_input.h +++ b/applications/services/gui/modules/text_input.h @@ -1,6 +1,6 @@ /** * @file text_input.h - * GUI: TextInput keybord view module API + * GUI: TextInput keyboard view module API */ #pragma once diff --git a/applications/services/gui/view_dispatcher.c b/applications/services/gui/view_dispatcher.c index 4034cc0b4..046958749 100644 --- a/applications/services/gui/view_dispatcher.c +++ b/applications/services/gui/view_dispatcher.c @@ -152,7 +152,7 @@ void view_dispatcher_remove_view(ViewDispatcher* view_dispatcher, uint32_t view_ if(view_dispatcher->current_view == view) { view_dispatcher_set_current_view(view_dispatcher, NULL); } - // Check if view is recieving input + // Check if view is receiving input if(view_dispatcher->ongoing_input_view == view) { view_dispatcher->ongoing_input_view = NULL; } diff --git a/applications/services/input/input.h b/applications/services/input/input.h index 172b16361..ec3d09711 100644 --- a/applications/services/input/input.h +++ b/applications/services/input/input.h @@ -20,8 +20,8 @@ typedef enum { InputTypePress, /**< Press event, emitted after debounce */ InputTypeRelease, /**< Release event, emitted after debounce */ InputTypeShort, /**< Short event, emitted after InputTypeRelease done withing INPUT_LONG_PRESS interval */ - InputTypeLong, /**< Long event, emmited after INPUT_LONG_PRESS interval, asynchronouse to InputTypeRelease */ - InputTypeRepeat, /**< Repeat event, emmited with INPUT_REPEATE_PRESS period after InputTypeLong event */ + InputTypeLong, /**< Long event, emitted after INPUT_LONG_PRESS_COUNTS interval, asynchronous to InputTypeRelease */ + InputTypeRepeat, /**< Repeat event, emitted with INPUT_LONG_PRESS_COUNTS period after InputTypeLong event */ InputTypeMAX, /**< Special value for exceptional */ } InputType; diff --git a/applications/services/power/power_service/power.h b/applications/services/power/power_service/power.h index bdf5fa529..32433ebca 100644 --- a/applications/services/power/power_service/power.h +++ b/applications/services/power/power_service/power.h @@ -86,7 +86,7 @@ FuriPubSub* power_get_pubsub(Power* power); */ bool power_is_battery_healthy(Power* power); -/** Enable or disable battery low level notification mesage +/** Enable or disable battery low level notification message * * @param power Power instance * @param enable true - enable, false - disable diff --git a/applications/services/rpc/rpc.h b/applications/services/rpc/rpc.h index 40493949d..21836d9a4 100644 --- a/applications/services/rpc/rpc.h +++ b/applications/services/rpc/rpc.h @@ -25,7 +25,7 @@ typedef void (*RpcSendBytesCallback)(void* context, uint8_t* bytes, size_t bytes typedef void (*RpcBufferIsEmptyCallback)(void* context); /** Callback to notify transport layer that close_session command * is received. Any other actions lays on transport layer. - * No destruction or session close preformed. */ + * No destruction or session close performed. */ typedef void (*RpcSessionClosedCallback)(void* context); /** Callback to notify transport layer that session was closed * and all operations were finished */ diff --git a/applications/services/rpc/rpc_storage.c b/applications/services/rpc/rpc_storage.c index 16e343fce..3c6ff7f94 100644 --- a/applications/services/rpc/rpc_storage.c +++ b/applications/services/rpc/rpc_storage.c @@ -330,7 +330,7 @@ static void rpc_system_storage_read_process(const PB_Main* request, void* contex rpc_system_storage_reset_state(rpc_storage, session, true); - /* use same message memory to send reponse */ + /* use same message memory to send response */ PB_Main* response = malloc(sizeof(PB_Main)); const char* path = request->content.storage_read_request.path; Storage* fs_api = furi_record_open(RECORD_STORAGE); diff --git a/applications/services/storage/filesystem_api_defines.h b/applications/services/storage/filesystem_api_defines.h index b6f1d8f13..b73e6eb33 100644 --- a/applications/services/storage/filesystem_api_defines.h +++ b/applications/services/storage/filesystem_api_defines.h @@ -25,13 +25,13 @@ typedef enum { typedef enum { FSE_OK, /**< No error */ FSE_NOT_READY, /**< FS not ready */ - FSE_EXIST, /**< File/Dir alrady exist */ + FSE_EXIST, /**< File/Dir already exist */ FSE_NOT_EXIST, /**< File/Dir does not exist */ FSE_INVALID_PARAMETER, /**< Invalid API parameter */ FSE_DENIED, /**< Access denied */ FSE_INVALID_NAME, /**< Invalid name/path */ FSE_INTERNAL, /**< Internal error */ - FSE_NOT_IMPLEMENTED, /**< Functon not implemented */ + FSE_NOT_IMPLEMENTED, /**< Function not implemented */ FSE_ALREADY_OPEN, /**< File/Dir already opened */ } FS_Error; diff --git a/applications/services/storage/filesystem_api_internal.h b/applications/services/storage/filesystem_api_internal.h index bd4bcf823..967d3bb41 100644 --- a/applications/services/storage/filesystem_api_internal.h +++ b/applications/services/storage/filesystem_api_internal.h @@ -17,7 +17,7 @@ typedef enum { struct File { uint32_t file_id; /**< File ID for internal references */ FileType type; - FS_Error error_id; /**< Standart API error from FS_Error enum */ + FS_Error error_id; /**< Standard API error from FS_Error enum */ int32_t internal_error_id; /**< Internal API error value */ void* storage; }; diff --git a/applications/services/storage/storage.h b/applications/services/storage/storage.h index 9c133e9be..e093cbe0f 100644 --- a/applications/services/storage/storage.h +++ b/applications/services/storage/storage.h @@ -255,19 +255,19 @@ FS_Error storage_common_fs_info( const char* storage_error_get_desc(FS_Error error_id); /** Retrieves the error id from the file object - * @param file pointer to file object. Pointer must not point to NULL. YOU CANNOT RETREIVE THE ERROR ID IF THE FILE HAS BEEN CLOSED + * @param file pointer to file object. Pointer must not point to NULL. YOU CANNOT RETRIEVE THE ERROR ID IF THE FILE HAS BEEN CLOSED * @return FS_Error error id */ FS_Error storage_file_get_error(File* file); /** Retrieves the internal (storage-specific) error id from the file object - * @param file pointer to file object. Pointer must not point to NULL. YOU CANNOT RETREIVE THE INTERNAL ERROR ID IF THE FILE HAS BEEN CLOSED + * @param file pointer to file object. Pointer must not point to NULL. YOU CANNOT RETRIEVE THE INTERNAL ERROR ID IF THE FILE HAS BEEN CLOSED * @return FS_Error error id */ int32_t storage_file_get_internal_error(File* file); /** Retrieves the error text from the file object - * @param file pointer to file object. Pointer must not point to NULL. YOU CANNOT RETREIVE THE ERROR TEXT IF THE FILE HAS BEEN CLOSED + * @param file pointer to file object. Pointer must not point to NULL. YOU CANNOT RETRIEVE THE ERROR TEXT IF THE FILE HAS BEEN CLOSED * @return const char* error text */ const char* storage_file_get_error_desc(File* file); diff --git a/applications/settings/storage_settings/storage_settings.h b/applications/settings/storage_settings/storage_settings.h index 664e74c84..fd841623e 100644 --- a/applications/settings/storage_settings/storage_settings.h +++ b/applications/settings/storage_settings/storage_settings.h @@ -26,7 +26,7 @@ typedef struct { NotificationApp* notification; Storage* fs_api; - // view managment + // view management SceneManager* scene_manager; ViewDispatcher* view_dispatcher; diff --git a/applications/system/storage_move_to_sd/storage_move_to_sd.h b/applications/system/storage_move_to_sd/storage_move_to_sd.h index dc1d669b5..a62d87c1f 100644 --- a/applications/system/storage_move_to_sd/storage_move_to_sd.h +++ b/applications/system/storage_move_to_sd/storage_move_to_sd.h @@ -30,7 +30,7 @@ typedef struct { Widget* widget; NotificationApp* notifications; - // view managment + // view management SceneManager* scene_manager; ViewDispatcher* view_dispatcher; diff --git a/documentation/fbt.md b/documentation/fbt.md index 2bf9ea28e..4726268d0 100644 --- a/documentation/fbt.md +++ b/documentation/fbt.md @@ -44,7 +44,7 @@ To run cleanup (think of `make clean`) for specified targets, add `-c` option. - `fw_dist` - build & publish firmware to `dist` folder. This is a default target, when no other are specified - `fap_dist` - build external plugins & publish to `dist` folder -- `updater_package`, `updater_minpackage` - build self-update package. Minimal version only inclues firmware's DFU file; full version also includes radio stack & resources for SD card +- `updater_package`, `updater_minpackage` - build self-update package. Minimal version only includes firmware's DFU file; full version also includes radio stack & resources for SD card - `copro_dist` - bundle Core2 FUS+stack binaries for qFlipper - `flash` - flash attached device with OpenOCD over ST-Link - `flash_usb`, `flash_usb_full` - build, upload and install update package to device over USB. See details on `updater_package`, `updater_minpackage` diff --git a/fbt_options.py b/fbt_options.py index 60c0662ed..1aa2a25f9 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -32,7 +32,7 @@ COPRO_STACK_TYPE = "ble_light" # Leave 0 to let scripts automatically calculate it COPRO_STACK_ADDR = "0x0" -# If you override COPRO_CUBE_DIR on commandline, override this aswell +# If you override COPRO_CUBE_DIR on commandline, override this as well COPRO_STACK_BIN_DIR = posixpath.join( COPRO_CUBE_DIR, "Projects", diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 1b205b7c7..f2e55b37c 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,7.52,, +Version,+,7.6,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -836,6 +836,7 @@ Function,+,file_browser_worker_folder_enter,void,"BrowserWorker*, FuriString*, i Function,+,file_browser_worker_folder_exit,void,BrowserWorker* Function,+,file_browser_worker_folder_refresh,void,"BrowserWorker*, int32_t" Function,+,file_browser_worker_free,void,BrowserWorker* +Function,+,file_browser_worker_is_in_start_folder,_Bool,BrowserWorker* Function,+,file_browser_worker_load,void,"BrowserWorker*, uint32_t, uint32_t" Function,+,file_browser_worker_set_callback_context,void,"BrowserWorker*, void*" Function,+,file_browser_worker_set_config,void,"BrowserWorker*, FuriString*, const char*, _Bool" @@ -1921,6 +1922,7 @@ Function,-,mf_df_prepare_read_records,uint16_t,"uint8_t*, uint8_t, uint32_t, uin Function,-,mf_df_prepare_select_application,uint16_t,"uint8_t*, uint8_t[3]" Function,-,mf_df_read_card,_Bool,"FuriHalNfcTxRxContext*, MifareDesfireData*" Function,-,mf_ul_check_card_type,_Bool,"uint8_t, uint8_t, uint8_t" +Function,-,mf_ul_is_full_capture,_Bool,MfUltralightData* Function,-,mf_ul_prepare_emulation,void,"MfUltralightEmulator*, MfUltralightData*" Function,-,mf_ul_prepare_emulation_response,_Bool,"uint8_t*, uint16_t, uint8_t*, uint16_t*, uint32_t*, void*" Function,-,mf_ul_pwdgen_amiibo,uint32_t,FuriHalNfcDevData* diff --git a/furi/core/log.c b/furi/core/log.c index bb163c956..a3967ed92 100644 --- a/furi/core/log.c +++ b/furi/core/log.c @@ -8,7 +8,7 @@ typedef struct { FuriLogLevel log_level; FuriLogPuts puts; - FuriLogTimestamp timetamp; + FuriLogTimestamp timestamp; FuriMutex* mutex; } FuriLogParams; @@ -18,7 +18,7 @@ void furi_log_init() { // Set default logging parameters furi_log.log_level = FURI_LOG_LEVEL_DEFAULT; furi_log.puts = furi_hal_console_puts; - furi_log.timetamp = furi_get_tick; + furi_log.timestamp = furi_get_tick; furi_log.mutex = furi_mutex_alloc(FuriMutexTypeNormal); } @@ -59,7 +59,7 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form furi_string_printf( string, "%lu %s[%s][%s] " FURI_LOG_CLR_RESET, - furi_log.timetamp(), + furi_log.timestamp(), color, log_letter, tag); @@ -98,5 +98,5 @@ void furi_log_set_puts(FuriLogPuts puts) { void furi_log_set_timestamp(FuriLogTimestamp timestamp) { furi_assert(timestamp); - furi_log.timetamp = timestamp; + furi_log.timestamp = timestamp; } diff --git a/furi/core/memmgr.h b/furi/core/memmgr.h index d3ad0c87c..bc0c35faa 100644 --- a/furi/core/memmgr.h +++ b/furi/core/memmgr.h @@ -1,6 +1,6 @@ /** * @file memmgr.h - * Furi: memory managment API and glue + * Furi: memory management API and glue */ #pragma once diff --git a/furi/core/memmgr_heap.h b/furi/core/memmgr_heap.h index 1521fce42..9aacba1ca 100644 --- a/furi/core/memmgr_heap.h +++ b/furi/core/memmgr_heap.h @@ -1,6 +1,6 @@ /** * @file memmgr_heap.h - * Furi: heap memory managment API and allocator + * Furi: heap memory management API and allocator */ #pragma once diff --git a/furi/core/thread.h b/furi/core/thread.h index 6ec8ebfec..c2f5a9130 100644 --- a/furi/core/thread.h +++ b/furi/core/thread.h @@ -48,7 +48,7 @@ typedef int32_t (*FuriThreadCallback)(void* context); */ typedef void (*FuriThreadStdoutWriteCallback)(const char* data, size_t size); -/** FuriThread state change calback called upon thread state change +/** FuriThread state change callback called upon thread state change * @param state new thread state * @param context callback context */ @@ -194,7 +194,7 @@ size_t furi_thread_get_heap_size(FuriThread* thread); */ int32_t furi_thread_get_return_code(FuriThread* thread); -/** Thread releated methods that doesn't involve FuriThread directly */ +/** Thread related methods that doesn't involve FuriThread directly */ /** Get FreeRTOS FuriThreadId for current thread * diff --git a/furi/core/valuemutex.c b/furi/core/valuemutex.c index af2a0755c..bf4e6130b 100644 --- a/furi/core/valuemutex.c +++ b/furi/core/valuemutex.c @@ -4,7 +4,7 @@ bool init_mutex(ValueMutex* valuemutex, void* value, size_t size) { // mutex without name, - // no attributes (unfortunatly robust mutex is not supported by FreeRTOS), + // no attributes (unfortunately robust mutex is not supported by FreeRTOS), // with dynamic memory allocation valuemutex->mutex = furi_mutex_alloc(FuriMutexTypeNormal); if(valuemutex->mutex == NULL) return false; diff --git a/furi/core/valuemutex.h b/furi/core/valuemutex.h index 41762fdd3..0d867a1df 100644 --- a/furi/core/valuemutex.h +++ b/furi/core/valuemutex.h @@ -39,7 +39,7 @@ bool delete_mutex(ValueMutex* valuemutex); void* acquire_mutex(ValueMutex* valuemutex, uint32_t timeout); /** - * Helper: infinitly wait for mutex + * Helper: infinitely wait for mutex */ static inline void* acquire_mutex_block(ValueMutex* valuemutex) { return acquire_mutex(valuemutex, FuriWaitForever); @@ -135,7 +135,7 @@ void consumer_app(void* _p) { flapp_exit(NULL); } - // continously read value every 1s + // continuously read value every 1s uint32_t counter; while(1) { if(read_mutex(counter_mutex, &counter, sizeof(counter), OsWaitForever)) { diff --git a/furi/furi.c b/furi/furi.c index 76aed024f..a616bce63 100644 --- a/furi/furi.c +++ b/furi/furi.c @@ -15,9 +15,9 @@ void furi_run() { furi_assert(xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED); #if(__ARM_ARCH_7A__ == 0U) - /* Service Call interrupt might be configured before kernel start */ - /* and when its priority is lower or equal to BASEPRI, svc intruction */ - /* causes a Hard Fault. */ + /* Service Call interrupt might be configured before kernel start */ + /* and when its priority is lower or equal to BASEPRI, svc instruction */ + /* causes a Hard Fault. */ NVIC_SetPriority(SVCall_IRQn, 0U); #endif diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index c6a775e55..d80187b09 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -7,6 +7,7 @@ #include #include +#define TAG "NfcDevice" #define NFC_DEVICE_KEYS_FOLDER EXT_PATH("nfc/cache") #define NFC_DEVICE_KEYS_EXTENSION ".keys" @@ -213,6 +214,9 @@ bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { uint32_t auth_counter; if(!flipper_format_read_uint32(file, "Failed authentication attempts", &auth_counter, 1)) auth_counter = 0; + data->curr_authlim = auth_counter; + + data->auth_success = mf_ul_is_full_capture(data); parsed = true; } while(false); @@ -627,7 +631,10 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { *app_head = app; app_head = &app->next; } - if(!parsed_apps) break; + if(!parsed_apps) { + // accept non-parsed apps, just log a warning: + FURI_LOG_W(TAG, "Non-parsed apps found!"); + } } parsed = true; } while(false); diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index 3d302c18b..4be07f016 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -67,6 +67,7 @@ typedef struct { union { NfcReaderRequestData reader_data; NfcMfClassicDictAttackData mf_classic_dict_attack_data; + MfUltralightAuth mf_ul_auth; }; union { EmvData emv_data; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index c701132c3..450428a18 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -527,10 +527,25 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { } } +void nfc_worker_mf_ultralight_auth_received_callback(MfUltralightAuth auth, void* context) { + furi_assert(context); + + NfcWorker* nfc_worker = context; + nfc_worker->dev_data->mf_ul_auth = auth; + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventMfUltralightPwdAuth, nfc_worker->context); + } +} + void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker) { FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; MfUltralightEmulator emulator = {}; mf_ul_prepare_emulation(&emulator, &nfc_worker->dev_data->mf_ul_data); + + // TODO rework with reader analyzer + emulator.auth_received_callback = nfc_worker_mf_ultralight_auth_received_callback; + emulator.context = nfc_worker; + while(nfc_worker->state == NfcWorkerStateMfUltralightEmulate) { mf_ul_reset_emulation(&emulator, true); furi_hal_nfc_emulate_nfca( @@ -905,7 +920,8 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) { if(furi_hal_nfc_detect(nfc_data, 300) && nfc_data->type == FuriHalNfcTypeA) { if(mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); - if(data->auth_method == MfUltralightAuthMethodManual) { + if(data->auth_method == MfUltralightAuthMethodManual || + data->auth_method == MfUltralightAuthMethodAuto) { nfc_worker->callback(NfcWorkerEventMfUltralightPassKey, nfc_worker->context); key = nfc_util_bytes2num(data->auth_key, 4); } else if(data->auth_method == MfUltralightAuthMethodAmeebo) { diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index ddee34c95..fdcaa72fd 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -65,8 +65,8 @@ typedef enum { NfcWorkerEventDetectReaderMfkeyCollected, // Mifare Ultralight events - NfcWorkerEventMfUltralightPassKey, - + NfcWorkerEventMfUltralightPassKey, // NFC worker requesting manual key + NfcWorkerEventMfUltralightPwdAuth, // Reader sent auth command } NfcWorkerEvent; typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); diff --git a/lib/nfc/protocols/mifare_ultralight.c b/lib/nfc/protocols/mifare_ultralight.c index a8d1f5548..85e234bd9 100644 --- a/lib/nfc/protocols/mifare_ultralight.c +++ b/lib/nfc/protocols/mifare_ultralight.c @@ -51,7 +51,7 @@ void mf_ul_reset(MfUltralightData* data) { data->data_size = 0; data->data_read = 0; data->curr_authlim = 0; - data->has_auth = false; + data->auth_success = false; } static MfUltralightFeatures mf_ul_get_features(MfUltralightType type) { @@ -756,6 +756,34 @@ bool mf_ul_read_card( mf_ultralight_read_tearing_flags(tx_rx, data); } data->curr_authlim = 0; + + if(reader->pages_read == reader->pages_to_read && + reader->supported_features & MfUltralightSupportAuth && !data->auth_success) { + MfUltralightConfigPages* config = mf_ultralight_get_config_pages(data); + if(config->access.authlim == 0) { + // Attempt to auth with default PWD + uint16_t pack; + data->auth_success = mf_ultralight_authenticate(tx_rx, MF_UL_DEFAULT_PWD, &pack); + if(data->auth_success) { + config->auth_data.pwd.value = MF_UL_DEFAULT_PWD; + config->auth_data.pack.value = pack; + } else { + furi_hal_nfc_sleep(); + furi_hal_nfc_activate_nfca(300, NULL); + } + } + } + } + + if(reader->pages_read != reader->pages_to_read) { + if(reader->supported_features & MfUltralightSupportAuth) { + // Probably password protected, fix AUTH0 and PROT so before AUTH0 + // can be written and since AUTH0 won't be readable, like on the + // original card + MfUltralightConfigPages* config = mf_ultralight_get_config_pages(data); + config->auth0 = reader->pages_read; + config->access.prot = true; + } } return card_read; @@ -1201,6 +1229,8 @@ static void mf_ul_emulate_write( } void mf_ul_reset_emulation(MfUltralightEmulator* emulator, bool is_power_cycle) { + emulator->comp_write_cmd_started = false; + emulator->sector_select_cmd_started = false; emulator->curr_sector = 0; emulator->ntag_i2c_plus_sector3_lockout = false; emulator->auth_success = false; @@ -1244,8 +1274,7 @@ void mf_ul_prepare_emulation(MfUltralightEmulator* emulator, MfUltralightData* d emulator->config = mf_ultralight_get_config_pages(&emulator->data); emulator->page_num = emulator->data.data_size / 4; emulator->data_changed = false; - emulator->comp_write_cmd_started = false; - emulator->sector_select_cmd_started = false; + memset(&emulator->auth_attempt, 0, sizeof(MfUltralightAuth)); mf_ul_reset_emulation(emulator, true); } @@ -1706,6 +1735,17 @@ bool mf_ul_prepare_emulation_response( } else if(cmd == MF_UL_AUTH) { if(emulator->supported_features & MfUltralightSupportAuth) { if(buff_rx_len == (1 + 4) * 8) { + // Record password sent by PCD + memcpy( + emulator->auth_attempt.pwd.raw, + &buff_rx[1], + sizeof(emulator->auth_attempt.pwd.raw)); + emulator->auth_attempted = true; + if(emulator->auth_received_callback) { + emulator->auth_received_callback( + emulator->auth_attempt, emulator->context); + } + uint16_t scaled_authlim = mf_ultralight_calc_auth_count(&emulator->data); if(scaled_authlim != 0 && emulator->data.curr_authlim >= scaled_authlim) { if(emulator->data.curr_authlim != UINT16_MAX) { @@ -1863,3 +1903,14 @@ bool mf_ul_prepare_emulation_response( return tx_bits > 0; } + +bool mf_ul_is_full_capture(MfUltralightData* data) { + if(data->data_read != data->data_size) return false; + + // Having read all the pages doesn't mean that we've got everything. + // By default PWD is 0xFFFFFFFF, but if read back it is always 0x00000000, + // so a default read on an auth-supported NTAG is never complete. + if(!(mf_ul_get_features(data->type) & MfUltralightSupportAuth)) return true; + MfUltralightConfigPages* config = mf_ultralight_get_config_pages(data); + return config->auth_data.pwd.value != 0 || config->auth_data.pack.value != 0; +} diff --git a/lib/nfc/protocols/mifare_ultralight.h b/lib/nfc/protocols/mifare_ultralight.h index 9642824f7..4ab22e89c 100644 --- a/lib/nfc/protocols/mifare_ultralight.h +++ b/lib/nfc/protocols/mifare_ultralight.h @@ -28,10 +28,13 @@ #define MF_UL_NTAG203_COUNTER_PAGE (41) +#define MF_UL_DEFAULT_PWD (0xFFFFFFFF) + typedef enum { MfUltralightAuthMethodManual, MfUltralightAuthMethodAmeebo, MfUltralightAuthMethodXiaomi, + MfUltralightAuthMethodAuto, } MfUltralightAuthMethod; // Important: order matters; some features are based on positioning in this enum @@ -110,7 +113,6 @@ typedef struct { uint8_t signature[32]; uint32_t counter[3]; uint8_t tearing[3]; - bool has_auth; MfUltralightAuthMethod auth_method; uint8_t auth_key[4]; bool auth_success; @@ -169,6 +171,9 @@ typedef struct { MfUltralightFeatures supported_features; } MfUltralightReader; +// TODO rework with reader analyzer +typedef void (*MfUltralightAuthReceivedCallback)(MfUltralightAuth auth, void* context); + typedef struct { MfUltralightData data; MfUltralightConfigPages* config; @@ -185,6 +190,12 @@ typedef struct { bool sector_select_cmd_started; bool ntag_i2c_plus_sector3_lockout; bool read_counter_incremented; + bool auth_attempted; + MfUltralightAuth auth_attempt; + + // TODO rework with reader analyzer + MfUltralightAuthReceivedCallback auth_received_callback; + void* context; } MfUltralightEmulator; void mf_ul_reset(MfUltralightData* data); @@ -241,3 +252,5 @@ bool mf_ul_prepare_emulation_response( uint32_t mf_ul_pwdgen_amiibo(FuriHalNfcDevData* data); uint32_t mf_ul_pwdgen_xiaomi(FuriHalNfcDevData* data); + +bool mf_ul_is_full_capture(MfUltralightData* data); diff --git a/lib/subghz/protocols/princeton.c b/lib/subghz/protocols/princeton.c index a4a0921ac..ab1c58765 100644 --- a/lib/subghz/protocols/princeton.c +++ b/lib/subghz/protocols/princeton.c @@ -15,8 +15,8 @@ #define TAG "SubGhzProtocolPrinceton" static const SubGhzBlockConst subghz_protocol_princeton_const = { - .te_short = 400, - .te_long = 1200, + .te_short = 390, + .te_long = 1170, .te_delta = 300, .min_count_bit_for_found = 24, }; @@ -245,8 +245,7 @@ void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t break; case PrincetonDecoderStepCheckDuration: if(!level) { - if(duration >= ((uint32_t)subghz_protocol_princeton_const.te_short * 10 + - subghz_protocol_princeton_const.te_delta)) { + if(duration >= ((uint32_t)subghz_protocol_princeton_const.te_long * 2)) { instance->decoder.parser_step = PrincetonDecoderStepSaveDuration; if(instance->decoder.decode_count_bit == subghz_protocol_princeton_const.min_count_bit_for_found) { diff --git a/lib/toolbox/path.c b/lib/toolbox/path.c index ce65aca4f..3d161a196 100644 --- a/lib/toolbox/path.c +++ b/lib/toolbox/path.c @@ -95,22 +95,17 @@ bool path_contains_only_ascii(const char* path) { name_pos++; } - while(*name_pos != '\0') { - if((*name_pos >= '0') && (*name_pos <= '9')) { - name_pos++; - continue; - } else if((*name_pos >= 'A') && (*name_pos <= 'Z')) { - name_pos++; - continue; - } else if((*name_pos >= 'a') && (*name_pos <= 'z')) { - name_pos++; - continue; - } else if(strchr(" .!#\\$%&'()-@^_`{}~", *name_pos) != NULL) { - name_pos++; - continue; - } + for(; *name_pos; ++name_pos) { + const char c = *name_pos; - return false; + // Regular ASCII characters from 0x20 to 0x7e + const bool is_out_of_range = (c < ' ') || (c > '~'); + // Cross-platform forbidden character set + const bool is_forbidden = strchr("\\<>*|\":?", c); + + if(is_out_of_range || is_forbidden) { + return false; + } } return true; diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index 908a64a6f..a7460d889 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -64,6 +64,7 @@ class FlipperApplication: fap_author: str = "" fap_weburl: str = "" fap_icon_assets: Optional[str] = None + fap_icon_assets_symbol: Optional[str] = None fap_extbuild: List[ExternallyBuiltFile] = field(default_factory=list) fap_private_libs: List[Library] = field(default_factory=list) # Internally used by fbt diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index a4116e513..f0015cf25 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -60,7 +60,7 @@ def BuildAppElf(env, app): fap_icons = app_env.CompileIcons( app_env.Dir(app_work_dir), app._appdir.Dir(app.fap_icon_assets), - icon_bundle_name=f"{app.appid}_icons", + icon_bundle_name=f"{app.fap_icon_assets_symbol if app.fap_icon_assets_symbol else app.appid }_icons", ) app_env.Alias("_fap_icons", fap_icons)