Просмотр исходного кода

Implemented alternative names functionality (#9)

* Started implementation of alternative names

* Implemented alternative names functionality

* Update README.md

* Update README.md
Sandro Kalatozishvili 1 год назад
Родитель
Сommit
6c0de2da96

BIN
.flipcorg/gallery/screen7.png


+ 43 - 1
README.md

@@ -58,6 +58,46 @@ Button name | Description
 `Stop`      | Stop
 `Stop`      | Stop
 
 
 
 
+## Alternative button names
+In addition to the predefined names, `XRemote` uses alternative button names to make it as easy as possible to interact with different types of IR dumps. This means that if a button is not found in the file with the appropriate name, the application will try to find the same button with alternative names. Ensure this feature is enabled in the application settings before you use it.
+
+Alternate names are case insensitive and defined in the file:
+```
+SD Card/apps_data/flipper_xremote/alt_names.cfg
+```
+
+If this file does not exist, it will be created automatically with default values when the application is launched. You are free to remove, edit or add any values you want to this file. Here is the alt_names.cfg file with default contents:
+
+```
+Filetype: XRemote Alt-Names
+Version: 1
+# 
+Power: shutdown,off,on,standby
+Setup: settings,config,cfg
+Input: source,select
+Menu: osd,gui
+List: guide
+Info: display
+Mode: aspect,format
+Back: return,exit
+Ok: enter,select
+Up: uparrow
+Down: downarrow
+Left: leftarrow
+Right: rightarrow
+Mute: silence,silent,unmute
+Vol_up: vol+,volume+,volup,+
+Vol_dn: vol-,volume-,voldown,-
+Ch_next: ch+,channel+,chup
+Ch_prev: ch-,channel-,chdown
+Next: next,skip,ffwd
+Prev: prev,back,rewind,rew
+Fast_fo: fastfwd,fastforward,ff
+Fast_ba: fastback,fastrewind,fb
+Play_pa: playpause,play,pause
+
+```
+
 ## Installation options
 ## Installation options
 
 
 1. Install the latest stable version directly from the official [application catalog](https://lab.flipper.net/apps/flipper_xremote).
 1. Install the latest stable version directly from the official [application catalog](https://lab.flipper.net/apps/flipper_xremote).
@@ -73,7 +113,7 @@ Button name | Description
    - Use deploy script from this repository to build and run the application on the device:
    - Use deploy script from this repository to build and run the application on the device:
 
 
     ```bash
     ```bash
-    ./deploy.sh --fw=/path/to/the/firmware
+    ./deploy.sh -b --fw=/path/to/the/firmware
     ```
     ```
 2. If you don't have the firmware or the Linux please refer to the [official documentation](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/AppsOnSDCard.md) for build instructions.
 2. If you don't have the firmware or the Linux please refer to the [official documentation](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/AppsOnSDCard.md) for build instructions.
 
 
@@ -89,6 +129,7 @@ Button name | Description
   - [x] Player buttons page
   - [x] Player buttons page
   - [x] Custom buttons page
   - [x] Custom buttons page
   - [x] Edit custom layout
   - [x] Edit custom layout
+  - [x] Alternative button names
   - [ ] Add or remove button
   - [ ] Add or remove button
   - [ ] All buttons page
   - [ ] All buttons page
 - [x] Application settings
 - [x] Application settings
@@ -98,6 +139,7 @@ Button name | Description
   - [x] Vertical/horizontal views
   - [x] Vertical/horizontal views
   - [x] IR command repeat count
   - [x] IR command repeat count
   - [x] Exit button behavior
   - [x] Exit button behavior
+  - [x] Enable/disable alt names
 
 
 ## Screens
 ## Screens
 
 

+ 46 - 14
deploy.sh

@@ -9,11 +9,39 @@ XCLR_DIM="\x1B[2m"
 XCLR_RED="\x1B[31m"
 XCLR_RED="\x1B[31m"
 XCLR_RESET="\x1B[0m\n"
 XCLR_RESET="\x1B[0m\n"
 
 
-# Parse firmware path from arguments if present
+FBT_CMD="./fbt"
+FBT_DBG="DEBUG=0"
+FBT_ARGS="COMPACT=1 launch"
+
+BUILD_PROJECT=0
+LINK_PROJECT=0
+RUN_QFLIPPER=0
+BUILD_DONE=0
+
 for arg in "$@"; do
 for arg in "$@"; do
     if [[ $arg == --firmware=* || $arg == --fw=* ]]; then
     if [[ $arg == --firmware=* || $arg == --fw=* ]]; then
         FLIPPER_FIRMWARE="${arg#*=}"
         FLIPPER_FIRMWARE="${arg#*=}"
     fi
     fi
+
+    if [[ $arg == "--build" || $arg == "-b" ]]; then
+        BUILD_PROJECT=1
+    fi
+
+    if [[ $arg == "--run" || $arg == "-r" ]]; then
+        RUN_PROJECT=1
+    fi
+
+    if [[ $arg == "--link" || $arg == "-l" ]]; then
+        LINK_PROJECT=1
+    fi
+
+    if [[ $arg == "--debug" || $arg == "-d" ]]; then
+        FBT_DBG="DEBUG=1"
+    fi
+
+    if [[ $arg == "--sudo" || $arg == "-s" ]]; then
+        FBT_CMD="sudo ./fbt"
+    fi
 done
 done
 
 
 # Check if FLIPPER_FIRMWARE variable is set
 # Check if FLIPPER_FIRMWARE variable is set
@@ -41,21 +69,25 @@ XREMOTE_PROJ_NAME=$(basename "$XREMOTE_PROJ_PATH")
 FLIPPER_APPSRC="applications_user/$XREMOTE_PROJ_NAME"
 FLIPPER_APPSRC="applications_user/$XREMOTE_PROJ_NAME"
 FLIPPER_USER_APP="$FLIPPER_FIRMWARE/$FLIPPER_APPSRC"
 FLIPPER_USER_APP="$FLIPPER_FIRMWARE/$FLIPPER_APPSRC"
 
 
-# Unlink existing user application first
-[ -s $FLIPPER_USER_APP ] && rm -f $FLIPPER_USER_APP
-ln -s $XREMOTE_PROJ_PATH $FLIPPER_FIRMWARE/applications_user
+link_project() {
+    [ -s $FLIPPER_USER_APP ] && rm -f $FLIPPER_USER_APP
+    ln -s $XREMOTE_PROJ_PATH $FLIPPER_FIRMWARE/applications_user
+}
 
 
-# Build and deploy the project
-cd $FLIPPER_FIRMWARE
-DEPLOY_DONE=0
-sudo ./fbt COMPACT=1 DEBUG=0 launch APPSRC=$FLIPPER_APPSRC && DEPLOY_DONE=1
+build_project() {
+    cd $FLIPPER_FIRMWARE
+    $FBT_CMD $FBT_ARGS $FBT_DBG APPSRC=$FLIPPER_APPSRC && BUILD_DONE=1
+}
 
 
-# Run qflipper command if asked
-for arg in "$@"; do
-    if [[ $arg == "--run" || $arg == "-r" ]]; then
-        [ $DEPLOY_DONE -eq 1 ] && sudo qflipper
+run_project() {
+    if [[ $BUILD_PROJECT -eq 0 || $BUILD_DONE -eq 1  ]]; then
+        qFlipper
     fi
     fi
-done
+}
+
+[ $LINK_PROJECT -eq 1 ] && link_project
+[ $BUILD_PROJECT -eq 1 ] && build_project
+[ $RUN_PROJECT -eq 1 ] && run_project
 
 
 # Return with success
 # Return with success
-exit 0
+exit 0

+ 7 - 4
infrared/infrared_remote.c

@@ -16,6 +16,8 @@
 #include <stddef.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <stdlib.h>
 #include <m-array.h>
 #include <m-array.h>
+#include <string.h>
+#include <ctype.h>
 #include <toolbox/path.h>
 #include <toolbox/path.h>
 #include <storage/storage.h>
 #include <storage/storage.h>
 #include <core/common_defines.h>
 #include <core/common_defines.h>
@@ -89,7 +91,9 @@ InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t
 bool infrared_remote_find_button_by_name(InfraredRemote* remote, const char* name, size_t* index) {
 bool infrared_remote_find_button_by_name(InfraredRemote* remote, const char* name, size_t* index) {
     for(size_t i = 0; i < InfraredButtonArray_size(remote->buttons); i++) {
     for(size_t i = 0; i < InfraredButtonArray_size(remote->buttons); i++) {
         InfraredRemoteButton* button = *InfraredButtonArray_get(remote->buttons, i);
         InfraredRemoteButton* button = *InfraredButtonArray_get(remote->buttons, i);
-        if(!strcmp(infrared_remote_button_get_name(button), name)) {
+        FuriString* firi_name = infrared_remote_button_get_furi_name(button);
+
+        if(button && !furi_string_cmpi_str(firi_name, name)) {
             *index = i;
             *index = i;
             return true;
             return true;
         }
         }
@@ -101,9 +105,8 @@ InfraredRemoteButton*
     infrared_remote_get_button_by_name(InfraredRemote* remote, const char* name) {
     infrared_remote_get_button_by_name(InfraredRemote* remote, const char* name) {
     for(size_t i = 0; i < InfraredButtonArray_size(remote->buttons); i++) {
     for(size_t i = 0; i < InfraredButtonArray_size(remote->buttons); i++) {
         InfraredRemoteButton* button = *InfraredButtonArray_get(remote->buttons, i);
         InfraredRemoteButton* button = *InfraredButtonArray_get(remote->buttons, i);
-        if(!strcmp(infrared_remote_button_get_name(button), name)) {
-            return button;
-        }
+        FuriString* firi_name = infrared_remote_button_get_furi_name(button);
+        if(button && !furi_string_cmpi_str(firi_name, name)) return button;
     }
     }
     return NULL;
     return NULL;
 }
 }

+ 7 - 1
infrared/infrared_remote_button.c

@@ -3,7 +3,9 @@
    https://github.com/DarkFlippers/unleashed-firmware
    https://github.com/DarkFlippers/unleashed-firmware
 
 
    The original project is licensed under the GNU GPLv3
    The original project is licensed under the GNU GPLv3
-   No modifications were made to this file.
+
+   Modifications made:
+   - Added function infrared_remote_button_get_furi_name()
 */
 */
 
 
 #include "infrared_remote_button.h"
 #include "infrared_remote_button.h"
@@ -36,6 +38,10 @@ const char* infrared_remote_button_get_name(InfraredRemoteButton* button) {
     return furi_string_get_cstr(button->name);
     return furi_string_get_cstr(button->name);
 }
 }
 
 
+FuriString* infrared_remote_button_get_furi_name(InfraredRemoteButton* button) {
+    return button->name;
+}
+
 void infrared_remote_button_set_signal(InfraredRemoteButton* button, InfraredSignal* signal) {
 void infrared_remote_button_set_signal(InfraredRemoteButton* button, InfraredSignal* signal) {
     infrared_signal_set_signal(button->signal, signal);
     infrared_signal_set_signal(button->signal, signal);
 }
 }

+ 4 - 1
infrared/infrared_remote_button.h

@@ -3,7 +3,9 @@
    https://github.com/DarkFlippers/unleashed-firmware
    https://github.com/DarkFlippers/unleashed-firmware
 
 
    The original project is licensed under the GNU GPLv3
    The original project is licensed under the GNU GPLv3
-   No modifications were made to this file.
+
+   Modifications made:
+   - Added function infrared_remote_button_get_furi_name()
 */
 */
 
 
 #pragma once
 #pragma once
@@ -17,6 +19,7 @@ void infrared_remote_button_free(InfraredRemoteButton* button);
 
 
 void infrared_remote_button_set_name(InfraredRemoteButton* button, const char* name);
 void infrared_remote_button_set_name(InfraredRemoteButton* button, const char* name);
 const char* infrared_remote_button_get_name(InfraredRemoteButton* button);
 const char* infrared_remote_button_get_name(InfraredRemoteButton* button);
+FuriString* infrared_remote_button_get_furi_name(InfraredRemoteButton* button);
 
 
 void infrared_remote_button_set_signal(InfraredRemoteButton* button, InfraredSignal* signal);
 void infrared_remote_button_set_signal(InfraredRemoteButton* button, InfraredSignal* signal);
 InfraredSignal* infrared_remote_button_get_signal(InfraredRemoteButton* button);
 InfraredSignal* infrared_remote_button_get_signal(InfraredRemoteButton* button);

BIN
screens/settings_menu.png


+ 70 - 4
views/xremote_common_view.c

@@ -90,9 +90,7 @@ XRemoteView*
 
 
 void xremote_view_clear_context(XRemoteView* rview) {
 void xremote_view_clear_context(XRemoteView* rview) {
     furi_assert(rview);
     furi_assert(rview);
-
     if(rview->context && rview->on_clear) rview->on_clear(rview->context);
     if(rview->context && rview->on_clear) rview->on_clear(rview->context);
-
     rview->context = NULL;
     rview->context = NULL;
 }
 }
 
 
@@ -134,16 +132,84 @@ View* xremote_view_get_view(XRemoteView* rview) {
     return rview->view;
     return rview->view;
 }
 }
 
 
+InfraredRemoteButton*
+    infrared_remote_get_button_by_alt_name(InfraredRemote* remote, const char* name) {
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);
+    FuriString* header = furi_string_alloc();
+
+    FURI_LOG_I(XREMOTE_APP_TAG, "loading alt_names file: \'%s\'", XREMOTE_ALT_NAMES);
+
+    InfraredRemoteButton* button = NULL;
+    uint32_t version = 0;
+
+    do {
+        /* Open file and read the header */
+        if(!flipper_format_buffered_file_open_existing(ff, XREMOTE_ALT_NAMES)) break;
+        if(!flipper_format_read_header(ff, header, &version)) break;
+        if(!furi_string_equal(header, "XRemote Alt-Names") || (version != 1)) break;
+
+        FuriString* value = furi_string_alloc();
+        if(!flipper_format_read_string(ff, name, value)) break;
+
+        size_t start = 0;
+        size_t posit = furi_string_search_str(value, ",", start);
+
+        if(posit == FURI_STRING_FAILURE) {
+            const char* alt_name_cstr = furi_string_get_cstr(value);
+            button = infrared_remote_get_button_by_name(remote, alt_name_cstr);
+        } else {
+            FuriString* alt_name = furi_string_alloc();
+
+            while(posit != FURI_STRING_FAILURE) {
+                furi_string_set_n(alt_name, value, start, posit - start);
+                const char* alt_name_cstr = furi_string_get_cstr(alt_name);
+                button = infrared_remote_get_button_by_name(remote, alt_name_cstr);
+
+                furi_string_reset(alt_name);
+                if(button != NULL) break;
+
+                start = posit + 1; // Move to the next position
+                posit = furi_string_search_str(value, ",", start);
+            }
+
+            if(posit == FURI_STRING_FAILURE && button == NULL) {
+                size_t str_len = furi_string_utf8_length(value);
+                furi_string_set_n(alt_name, value, start, str_len - start);
+                const char* alt_name_cstr = furi_string_get_cstr(alt_name);
+                button = infrared_remote_get_button_by_name(remote, alt_name_cstr);
+            }
+
+            furi_string_free(alt_name);
+        }
+
+    } while(false);
+
+    furi_record_close(RECORD_STORAGE);
+    furi_string_free(header);
+    flipper_format_free(ff);
+
+    return button;
+}
+
 InfraredRemoteButton* xremote_view_get_button_by_name(XRemoteView* rview, const char* name) {
 InfraredRemoteButton* xremote_view_get_button_by_name(XRemoteView* rview, const char* name) {
     xremote_app_assert(rview->context, NULL);
     xremote_app_assert(rview->context, NULL);
+    xremote_app_assert(rview->app_ctx, NULL);
+
+    XRemoteAppSettings* settings = rview->app_ctx->app_settings;
     XRemoteAppButtons* buttons = (XRemoteAppButtons*)rview->context;
     XRemoteAppButtons* buttons = (XRemoteAppButtons*)rview->context;
-    return infrared_remote_get_button_by_name(buttons->remote, name);
+    InfraredRemoteButton* button = infrared_remote_get_button_by_name(buttons->remote, name);
+
+    if(button == NULL && settings->alt_names)
+        button = infrared_remote_get_button_by_alt_name(buttons->remote, name);
+
+    return button;
 }
 }
 
 
 bool xremote_view_press_button(XRemoteView* rview, InfraredRemoteButton* button) {
 bool xremote_view_press_button(XRemoteView* rview, InfraredRemoteButton* button) {
     xremote_app_assert(button, false);
     xremote_app_assert(button, false);
-    XRemoteAppSettings* settings = rview->app_ctx->app_settings;
 
 
+    XRemoteAppSettings* settings = rview->app_ctx->app_settings;
     InfraredSignal* signal = infrared_remote_button_get_signal(button);
     InfraredSignal* signal = infrared_remote_button_get_signal(button);
     xremote_app_assert(signal, false);
     xremote_app_assert(signal, false);
 
 

+ 1 - 0
xremote.c

@@ -81,6 +81,7 @@ int32_t xremote_main(void* p) {
     /* Allocate context and main application */
     /* Allocate context and main application */
     XRemoteAppContext* context = xremote_app_context_alloc(p);
     XRemoteAppContext* context = xremote_app_context_alloc(p);
     XRemoteApp* app = xremote_app_alloc(context);
     XRemoteApp* app = xremote_app_alloc(context);
+    xremote_app_alt_names_check_and_store();
 
 
     /* Allocate and build the menu */
     /* Allocate and build the menu */
     xremote_app_submenu_alloc(app, XRemoteViewSubmenu, xremote_exit_callback);
     xremote_app_submenu_alloc(app, XRemoteViewSubmenu, xremote_exit_callback);

+ 3 - 3
xremote.h

@@ -8,9 +8,9 @@
 
 
 #include "xremote_app.h"
 #include "xremote_app.h"
 
 
-#define XREMOTE_VERSION_MAJ   1
-#define XREMOTE_VERSION_MIN   2
-#define XREMOTE_BUILD_NUMBER  0
+#define XREMOTE_VERSION_MAJ 1
+#define XREMOTE_VERSION_MIN 2
+#define XREMOTE_BUILD_NUMBER 2
 
 
 /* Returns FAP_VERSION + XREMOTE_BUILD_NUMBER */
 /* Returns FAP_VERSION + XREMOTE_BUILD_NUMBER */
 void xremote_get_version(char* version, size_t length);
 void xremote_get_version(char* version, size_t length);

+ 67 - 10
xremote_app.c

@@ -13,7 +13,6 @@
 //////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////
 
 
 #define XREMOTE_APP_SETTINGS APP_DATA_PATH("xremote.cfg")
 #define XREMOTE_APP_SETTINGS APP_DATA_PATH("xremote.cfg")
-#define TAG "XRemoteApp"
 
 
 #define XREMOTE_ORIENTATION_TEXT_HORIZONTAL "Horizontal"
 #define XREMOTE_ORIENTATION_TEXT_HORIZONTAL "Horizontal"
 #define XREMOTE_ORIENTATION_TEXT_VERTICAL "Vertical"
 #define XREMOTE_ORIENTATION_TEXT_VERTICAL "Vertical"
@@ -44,6 +43,10 @@ const char* xremote_app_get_exit_str(XRemoteAppExit exit_behavior) {
     return exit_behavior == XRemoteAppExitPress ? "Press" : "Hold";
     return exit_behavior == XRemoteAppExitPress ? "Press" : "Hold";
 }
 }
 
 
+const char* xremote_app_get_alt_names_str(uint8_t alt_names_index) {
+    return alt_names_index ? "On" : "Off";
+}
+
 const char* xremote_app_get_orientation_str(ViewOrientation view_orientation) {
 const char* xremote_app_get_orientation_str(ViewOrientation view_orientation) {
     return view_orientation == ViewOrientationHorizontal ? "Horizontal" : "Vertical";
     return view_orientation == ViewOrientationHorizontal ? "Horizontal" : "Vertical";
 }
 }
@@ -145,6 +148,49 @@ bool xremote_app_extension_store(XRemoteAppButtons* buttons, FuriString* path) {
     return success;
     return success;
 }
 }
 
 
+bool xremote_app_alt_names_check_and_store() {
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    FlipperFormat* ff = flipper_format_file_alloc(storage);
+    bool success = false;
+
+    do {
+        if(!flipper_format_file_open_new(ff, XREMOTE_ALT_NAMES)) break;
+        if(!flipper_format_write_header_cstr(ff, "XRemote Alt-Names", 1)) break;
+        if(!flipper_format_write_comment_cstr(ff, "")) break;
+
+        if(!flipper_format_write_string_cstr(ff, "Power", "shutdown,off,on,standby")) break;
+        if(!flipper_format_write_string_cstr(ff, "Setup", "settings,config,cfg")) break;
+        if(!flipper_format_write_string_cstr(ff, "Input", "source,select")) break;
+        if(!flipper_format_write_string_cstr(ff, "Menu", "osd,gui")) break;
+        if(!flipper_format_write_string_cstr(ff, "List", "guide")) break;
+        if(!flipper_format_write_string_cstr(ff, "Info", "display")) break;
+        if(!flipper_format_write_string_cstr(ff, "Mode", "aspect,format")) break;
+        if(!flipper_format_write_string_cstr(ff, "Back", "return,exit")) break;
+        if(!flipper_format_write_string_cstr(ff, "Ok", "enter,select")) break;
+        if(!flipper_format_write_string_cstr(ff, "Up", "uparrow")) break;
+        if(!flipper_format_write_string_cstr(ff, "Down", "downarrow")) break;
+        if(!flipper_format_write_string_cstr(ff, "Left", "leftarrow")) break;
+        if(!flipper_format_write_string_cstr(ff, "Right", "rightarrow")) break;
+        if(!flipper_format_write_string_cstr(ff, "Mute", "silence,silent,unmute")) break;
+        if(!flipper_format_write_string_cstr(ff, "Vol_up", "vol+,volume+,volup,+")) break;
+        if(!flipper_format_write_string_cstr(ff, "Vol_dn", "vol-,volume-,voldown,-")) break;
+        if(!flipper_format_write_string_cstr(ff, "Ch_next", "ch+,channel+,chup")) break;
+        if(!flipper_format_write_string_cstr(ff, "Ch_prev", "ch-,channel-,chdown")) break;
+        if(!flipper_format_write_string_cstr(ff, "Next", "next,skip,ffwd")) break;
+        if(!flipper_format_write_string_cstr(ff, "Prev", "prev,back,rewind,rew")) break;
+        if(!flipper_format_write_string_cstr(ff, "Fast_fo", "fastfwd,fastforward,ff")) break;
+        if(!flipper_format_write_string_cstr(ff, "Fast_ba", "fastback,fastrewind,fb")) break;
+        if(!flipper_format_write_string_cstr(ff, "Play_pa", "playpause,play,pause")) break;
+
+        success = true;
+    } while(false);
+
+    furi_record_close(RECORD_STORAGE);
+    flipper_format_free(ff);
+
+    return success;
+}
+
 void xremote_app_buttons_free(XRemoteAppButtons* buttons) {
 void xremote_app_buttons_free(XRemoteAppButtons* buttons) {
     xremote_app_assert_void(buttons);
     xremote_app_assert_void(buttons);
     infrared_remote_free(buttons->remote);
     infrared_remote_free(buttons->remote);
@@ -183,7 +229,7 @@ XRemoteAppButtons* xremote_app_buttons_alloc() {
 
 
 XRemoteAppButtons* xremote_app_buttons_load(XRemoteAppContext* app_ctx) {
 XRemoteAppButtons* xremote_app_buttons_load(XRemoteAppContext* app_ctx) {
     /* Show file selection dialog (returns selected file path with app_ctx->file_path) */
     /* Show file selection dialog (returns selected file path with app_ctx->file_path) */
-    if(!xremote_app_browser_select_file(app_ctx, XREMOTE_APP_EXTENSION)) return NULL;
+    if(!xremote_app_context_select_file(app_ctx, XREMOTE_APP_EXTENSION)) return NULL;
     XRemoteAppButtons* buttons = xremote_app_buttons_alloc();
     XRemoteAppButtons* buttons = xremote_app_buttons_alloc();
     buttons->app_ctx = app_ctx;
     buttons->app_ctx = app_ctx;
 
 
@@ -207,6 +253,7 @@ XRemoteAppSettings* xremote_app_settings_alloc() {
     settings->orientation = ViewOrientationHorizontal;
     settings->orientation = ViewOrientationHorizontal;
     settings->exit_behavior = XRemoteAppExitPress;
     settings->exit_behavior = XRemoteAppExitPress;
     settings->repeat_count = 2;
     settings->repeat_count = 2;
+    settings->alt_names = 0;
     return settings;
     return settings;
 }
 }
 
 
@@ -219,7 +266,7 @@ bool xremote_app_settings_store(XRemoteAppSettings* settings) {
     Storage* storage = furi_record_open(RECORD_STORAGE);
     Storage* storage = furi_record_open(RECORD_STORAGE);
     FlipperFormat* ff = flipper_format_file_alloc(storage);
     FlipperFormat* ff = flipper_format_file_alloc(storage);
 
 
-    FURI_LOG_I(TAG, "store config file: \'%s\'", XREMOTE_APP_SETTINGS);
+    FURI_LOG_I(XREMOTE_APP_TAG, "store config file: \'%s\'", XREMOTE_APP_SETTINGS);
     bool success = false;
     bool success = false;
 
 
     do {
     do {
@@ -238,6 +285,9 @@ bool xremote_app_settings_store(XRemoteAppSettings* settings) {
         value = settings->repeat_count;
         value = settings->repeat_count;
         if(!flipper_format_write_uint32(ff, "repeat", &value, 1)) break;
         if(!flipper_format_write_uint32(ff, "repeat", &value, 1)) break;
 
 
+        value = settings->alt_names;
+        if(!flipper_format_write_uint32(ff, "altNames", &value, 1)) break;
+
         success = true;
         success = true;
     } while(false);
     } while(false);
 
 
@@ -252,7 +302,7 @@ bool xremote_app_settings_load(XRemoteAppSettings* settings) {
     FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);
     FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);
     FuriString* header = furi_string_alloc();
     FuriString* header = furi_string_alloc();
 
 
-    FURI_LOG_I(TAG, "load config file: \'%s\'", XREMOTE_APP_SETTINGS);
+    FURI_LOG_I(XREMOTE_APP_TAG, "load config file: \'%s\'", XREMOTE_APP_SETTINGS);
     uint32_t version = 0;
     uint32_t version = 0;
     uint32_t value = 0;
     uint32_t value = 0;
     bool success = false;
     bool success = false;
@@ -273,6 +323,9 @@ bool xremote_app_settings_load(XRemoteAppSettings* settings) {
         if(!flipper_format_read_uint32(ff, "repeat", &value, 1)) break;
         if(!flipper_format_read_uint32(ff, "repeat", &value, 1)) break;
         settings->repeat_count = value;
         settings->repeat_count = value;
 
 
+        if(!flipper_format_read_uint32(ff, "altNames", &value, 1)) break;
+        settings->alt_names = value;
+
         success = true;
         success = true;
     } while(false);
     } while(false);
 
 
@@ -326,24 +379,23 @@ void xremote_app_context_free(XRemoteAppContext* ctx) {
     free(ctx);
     free(ctx);
 }
 }
 
 
-bool xremote_app_browser_select_file(XRemoteAppContext* app_ctx, const char* extension) {
+bool xremote_app_browser_select_file(FuriString** file_path, const char* extension) {
     DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
     DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
     Storage* storage = furi_record_open(RECORD_STORAGE);
     Storage* storage = furi_record_open(RECORD_STORAGE);
     storage_simply_mkdir(storage, XREMOTE_APP_FOLDER);
     storage_simply_mkdir(storage, XREMOTE_APP_FOLDER);
 
 
-    if(app_ctx->file_path == NULL) {
-        app_ctx->file_path = furi_string_alloc();
-        furi_string_set(app_ctx->file_path, XREMOTE_APP_FOLDER);
+    if(*file_path == NULL) {
+        *file_path = furi_string_alloc();
+        furi_string_set(*file_path, XREMOTE_APP_FOLDER);
     }
     }
 
 
     /* Open file browser (view and dialogs are managed by the browser itself) */
     /* Open file browser (view and dialogs are managed by the browser itself) */
     DialogsFileBrowserOptions browser;
     DialogsFileBrowserOptions browser;
     dialog_file_browser_set_basic_options(&browser, extension, &I_IR_Icon_10x10);
     dialog_file_browser_set_basic_options(&browser, extension, &I_IR_Icon_10x10);
     browser.base_path = XREMOTE_APP_FOLDER;
     browser.base_path = XREMOTE_APP_FOLDER;
-    FuriString* path = app_ctx->file_path;
 
 
     /* Show file selection dialog (returns selected file path with file_path) */
     /* Show file selection dialog (returns selected file path with file_path) */
-    bool status = dialog_file_browser_show(dialogs, path, path, &browser);
+    bool status = dialog_file_browser_show(dialogs, *file_path, *file_path, &browser);
 
 
     /* Cleanup file loading context */
     /* Cleanup file loading context */
     furi_record_close(RECORD_STORAGE);
     furi_record_close(RECORD_STORAGE);
@@ -352,6 +404,11 @@ bool xremote_app_browser_select_file(XRemoteAppContext* app_ctx, const char* ext
     return status;
     return status;
 }
 }
 
 
+bool xremote_app_context_select_file(XRemoteAppContext* app_ctx, const char* extension) {
+    if(app_ctx == NULL) return false;
+    return xremote_app_browser_select_file(&app_ctx->file_path, extension);
+}
+
 const char* xremote_app_context_get_exit_str(XRemoteAppContext* app_ctx) {
 const char* xremote_app_context_get_exit_str(XRemoteAppContext* app_ctx) {
     XRemoteAppExit exit_behavior = app_ctx->app_settings->exit_behavior;
     XRemoteAppExit exit_behavior = app_ctx->app_settings->exit_behavior;
     return exit_behavior == XRemoteAppExitHold ? "Hold to exit" : "Press to exit";
     return exit_behavior == XRemoteAppExitHold ? "Hold to exit" : "Press to exit";

+ 10 - 2
xremote_app.h

@@ -32,9 +32,13 @@
 // XRemote generic functions and definitions
 // XRemote generic functions and definitions
 //////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////
 
 
+#define XREMOTE_APP_TEXT_MAX 128
 #define XREMOTE_APP_EXTENSION ".ir"
 #define XREMOTE_APP_EXTENSION ".ir"
+#define XREMOTE_APP_TAG "XRemoteApp"
+
 #define XREMOTE_APP_FOLDER ANY_PATH("infrared")
 #define XREMOTE_APP_FOLDER ANY_PATH("infrared")
-#define XREMOTE_APP_TEXT_MAX 128
+#define XREMOTE_APP_SETTINGS APP_DATA_PATH("xremote.cfg")
+#define XREMOTE_ALT_NAMES APP_DATA_PATH("alt_names.cfg")
 
 
 #define xremote_app_assert_void(cond) \
 #define xremote_app_assert_void(cond) \
     if(!cond) return
     if(!cond) return
@@ -49,6 +53,7 @@ uint32_t xremote_app_get_exit_index(XRemoteAppExit exit_behavior);
 
 
 ViewOrientation xremote_app_get_orientation(uint8_t orientation_index);
 ViewOrientation xremote_app_get_orientation(uint8_t orientation_index);
 const char* xremote_app_get_orientation_str(ViewOrientation view_orientation);
 const char* xremote_app_get_orientation_str(ViewOrientation view_orientation);
+const char* xremote_app_get_alt_names_str(uint8_t alt_names_index);
 uint32_t xremote_app_get_orientation_index(ViewOrientation view_orientation);
 uint32_t xremote_app_get_orientation_index(ViewOrientation view_orientation);
 
 
 //////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////
@@ -59,6 +64,7 @@ typedef struct {
     ViewOrientation orientation;
     ViewOrientation orientation;
     XRemoteAppExit exit_behavior;
     XRemoteAppExit exit_behavior;
     uint32_t repeat_count;
     uint32_t repeat_count;
+    uint32_t alt_names;
 } XRemoteAppSettings;
 } XRemoteAppSettings;
 
 
 XRemoteAppSettings* xremote_app_settings_alloc();
 XRemoteAppSettings* xremote_app_settings_alloc();
@@ -87,7 +93,8 @@ const char* xremote_app_context_get_exit_str(XRemoteAppContext* app_ctx);
 void xremote_app_context_notify_led(XRemoteAppContext* app_ctx);
 void xremote_app_context_notify_led(XRemoteAppContext* app_ctx);
 void xremote_app_notification_blink(NotificationApp* notifications);
 void xremote_app_notification_blink(NotificationApp* notifications);
 bool xremote_app_send_signal(XRemoteAppContext* app_ctx, InfraredSignal* signal);
 bool xremote_app_send_signal(XRemoteAppContext* app_ctx, InfraredSignal* signal);
-bool xremote_app_browser_select_file(XRemoteAppContext* app_ctx, const char* extension);
+bool xremote_app_context_select_file(XRemoteAppContext* app_ctx, const char* extension);
+bool xremote_app_browser_select_file(FuriString** file_path, const char* extension);
 
 
 //////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////
 // XRemote buttons and custom button pairs
 // XRemote buttons and custom button pairs
@@ -114,6 +121,7 @@ XRemoteAppButtons* xremote_app_buttons_load(XRemoteAppContext* app_ctx);
 
 
 bool xremote_app_extension_store(XRemoteAppButtons* buttons, FuriString* path);
 bool xremote_app_extension_store(XRemoteAppButtons* buttons, FuriString* path);
 bool xremote_app_extension_load(XRemoteAppButtons* buttons, FuriString* path);
 bool xremote_app_extension_load(XRemoteAppButtons* buttons, FuriString* path);
+bool xremote_app_alt_names_check_and_store();
 
 
 //////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////
 // XRemote application factory
 // XRemote application factory

+ 26 - 0
xremote_settings.c

@@ -22,6 +22,9 @@ typedef struct {
 #define XREMOTE_REPEAT_TEXT "IR Msg Repeat"
 #define XREMOTE_REPEAT_TEXT "IR Msg Repeat"
 #define XREMOTE_REPEAT_MAX 128
 #define XREMOTE_REPEAT_MAX 128
 
 
+#define XREMOTE_ALT_NAMES_TEXT "Alt Names"
+#define XREMOTE_ALT_NAMES_MAX 2
+
 static uint32_t xremote_settings_view_exit_callback(void* context) {
 static uint32_t xremote_settings_view_exit_callback(void* context) {
     UNUSED(context);
     UNUSED(context);
     return XRemoteViewSubmenu;
     return XRemoteViewSubmenu;
@@ -63,6 +66,17 @@ static void infrared_settings_exit_changed(VariableItem* item) {
     xremote_app_settings_store(settings);
     xremote_app_settings_store(settings);
 }
 }
 
 
+static void infrared_settings_alt_names_changed(VariableItem* item) {
+    XRemoteSettingsContext* ctx = variable_item_get_context(item);
+    XRemoteAppSettings* settings = ctx->app_ctx->app_settings;
+
+    settings->alt_names = variable_item_get_current_value_index(item);
+    const char* alt_names_str = xremote_app_get_alt_names_str(settings->alt_names);
+
+    variable_item_set_current_value_text(item, alt_names_str);
+    xremote_app_settings_store(settings);
+}
+
 static XRemoteSettingsContext* xremote_settings_context_alloc(XRemoteAppContext* app_ctx) {
 static XRemoteSettingsContext* xremote_settings_context_alloc(XRemoteAppContext* app_ctx) {
     XRemoteSettingsContext* context = malloc(sizeof(XRemoteSettingsContext));
     XRemoteSettingsContext* context = malloc(sizeof(XRemoteSettingsContext));
     XRemoteAppSettings* settings = app_ctx->app_settings;
     XRemoteAppSettings* settings = app_ctx->app_settings;
@@ -121,6 +135,18 @@ static XRemoteSettingsContext* xremote_settings_context_alloc(XRemoteAppContext*
     variable_item_set_current_value_index(item, exit_index);
     variable_item_set_current_value_index(item, exit_index);
     variable_item_set_current_value_text(item, exit_str);
     variable_item_set_current_value_text(item, exit_str);
 
 
+    /* Add exit behavior to variable item list */
+    item = variable_item_list_add(
+        context->item_list,
+        XREMOTE_ALT_NAMES_TEXT,
+        XREMOTE_ALT_NAMES_MAX,
+        infrared_settings_alt_names_changed,
+        context);
+
+    /* Set exit behavior item index and string */
+    variable_item_set_current_value_index(item, settings->alt_names);
+    variable_item_set_current_value_text(item, xremote_app_get_alt_names_str(settings->alt_names));
+
     return context;
     return context;
 }
 }