#include "../desktop_settings_app.h"
#include "applications.h"
#include "desktop_settings_scene.h"
#include "desktop_settings_scene_i.h"
#include <flipper_application/flipper_application.h>
#include <storage/storage.h>
#include <dialogs/dialogs.h>
 
#define APPS_COUNT (FLIPPER_APPS_COUNT + FLIPPER_EXTERNAL_APPS_COUNT)
 
#define DEFAULT_INDEX                  (0)
#define EXTERNAL_BROWSER_NAME          ("(   ) Apps Menu (Default)")
#define EXTERNAL_BROWSER_NAME_SELECTED ("(*) Apps Menu (Default)")
#define PASSPORT_NAME                  ("(   ) Passport (Default)")
#define PASSPORT_NAME_SELECTED         ("(*) Passport (Default)")
 
#define SELECTED_PREFIX     ("(*) ")
#define NOT_SELECTED_PREFIX ("(   ) ")
 
#define EXTERNAL_APPLICATION_INDEX         (1)
#define EXTERNAL_APPLICATION_NAME          ("(   ) [Select App]")
#define EXTERNAL_APPLICATION_NAME_SELECTED ("(*) [Select App]")
 
#define PRESELECTED_SPECIAL 0xffffffff
 
static const char* favorite_fap_get_app_name(size_t i) {
    const char* name;
    if(i < FLIPPER_APPS_COUNT) {
        name = FLIPPER_APPS[i].name;
    } else {
        name = FLIPPER_EXTERNAL_APPS[i - FLIPPER_APPS_COUNT].name;
    }
 
    return name;
}
 
static bool favorite_fap_selector_item_callback(
    FuriString* file_path,
    void* context,
    uint8_t** icon_ptr,
    FuriString* item_name) {
    UNUSED(context);
    Storage* storage = furi_record_open(RECORD_STORAGE);
    bool success = flipper_application_load_name_and_icon(file_path, storage, icon_ptr, item_name);
    furi_record_close(RECORD_STORAGE);
    return success;
}
 
static bool favorite_fap_selector_file_exists(char* file_path) {
    Storage* storage = furi_record_open(RECORD_STORAGE);
    bool exists = storage_file_exists(storage, file_path);
    furi_record_close(RECORD_STORAGE);
    return exists;
}
 
static void desktop_settings_scene_favorite_submenu_callback(void* context, uint32_t index) {
    DesktopSettingsApp* app = context;
    view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
 
void desktop_settings_scene_favorite_on_enter(void* context) {
    DesktopSettingsApp* app = context;
    Submenu* submenu = app->submenu;
    submenu_reset(submenu);
 
    uint32_t favorite_id =
        scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite);
    uint32_t pre_select_item = PRESELECTED_SPECIAL;
    FavoriteApp* curr_favorite_app = NULL;
    bool default_passport = false;
 
    if((favorite_id & SCENE_STATE_SET_DUMMY_APP) == 0) {
        furi_assert(favorite_id < FavoriteAppNumber);
        curr_favorite_app = &app->settings.favorite_apps[favorite_id];
        if(favorite_id == FavoriteAppRightShort) {
            default_passport = true;
        }
    } else {
        favorite_id &= ~(SCENE_STATE_SET_DUMMY_APP);
        furi_assert(favorite_id < DummyAppNumber);
        curr_favorite_app = &app->settings.dummy_apps[favorite_id];
        default_passport = true;
        favorite_id |= SCENE_STATE_SET_DUMMY_APP;
    }
 
    // Special case: Application browser
    submenu_add_item(
        submenu,
        default_passport ? (PASSPORT_NAME) : (EXTERNAL_BROWSER_NAME),
        DEFAULT_INDEX,
        desktop_settings_scene_favorite_submenu_callback,
        app);
 
    // Special case: Specific application
    submenu_add_item(
        submenu,
        EXTERNAL_APPLICATION_NAME,
        EXTERNAL_APPLICATION_INDEX,
        desktop_settings_scene_favorite_submenu_callback,
        app);
 
    FuriString* full_name = furi_string_alloc();
 
    for(size_t i = 0; i < APPS_COUNT; i++) {
        const char* name = favorite_fap_get_app_name(i);
 
        // Add the prefix
        furi_string_reset(full_name);
        if(!strcmp(name, curr_favorite_app->name_or_path)) {
            furi_string_set_str(full_name, SELECTED_PREFIX);
        } else {
            furi_string_set_str(full_name, NOT_SELECTED_PREFIX);
        }
        furi_string_cat_str(full_name, name);
 
        submenu_add_item(
            submenu,
            furi_string_get_cstr(full_name),
            i + 2,
            desktop_settings_scene_favorite_submenu_callback,
            app);
 
        // Select favorite item in submenu
        if(!strcmp(name, curr_favorite_app->name_or_path)) {
            pre_select_item = i + 2;
        }
    }
 
    if(pre_select_item == PRESELECTED_SPECIAL) {
        if(curr_favorite_app->name_or_path[0] == '\0') {
            pre_select_item = DEFAULT_INDEX;
            submenu_change_item_label(
                submenu,
                DEFAULT_INDEX,
                default_passport ? (PASSPORT_NAME_SELECTED) : (EXTERNAL_BROWSER_NAME_SELECTED));
        } else {
            pre_select_item = EXTERNAL_APPLICATION_INDEX;
            submenu_change_item_label(
                submenu, EXTERNAL_APPLICATION_INDEX, EXTERNAL_APPLICATION_NAME_SELECTED);
        }
    }
 
    switch(favorite_id) {
    case SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftShort:
    case SCENE_STATE_SET_DUMMY_APP | DummyAppLeft:
        submenu_set_header(submenu, "Left - Press");
        break;
    case SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftLong:
        submenu_set_header(submenu, "Left - Hold");
        break;
    case SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightShort:
    case SCENE_STATE_SET_DUMMY_APP | DummyAppRight:
        submenu_set_header(submenu, "Right - Press");
        break;
    case SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightLong:
        submenu_set_header(submenu, "Right - Hold");
        break;
    case SCENE_STATE_SET_DUMMY_APP | DummyAppDown:
        submenu_set_header(submenu, "Down - Press");
        break;
    case SCENE_STATE_SET_DUMMY_APP | DummyAppOk:
        submenu_set_header(submenu, "Middle - Press");
        break;
    default:
        break;
    }
 
    submenu_set_selected_item(submenu, pre_select_item); // If set during loop, visual glitch.
 
    view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu);
}
 
bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent event) {
    DesktopSettingsApp* app = context;
    bool consumed = false;
    FuriString* temp_path = furi_string_alloc_set_str(EXT_PATH("apps"));
 
    uint32_t favorite_id =
        scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite);
    FavoriteApp* curr_favorite_app = NULL;
    if((favorite_id & SCENE_STATE_SET_DUMMY_APP) == 0) {
        furi_assert(favorite_id < FavoriteAppNumber);
        curr_favorite_app = &app->settings.favorite_apps[favorite_id];
    } else {
        favorite_id &= ~(SCENE_STATE_SET_DUMMY_APP);
        furi_assert(favorite_id < DummyAppNumber);
        curr_favorite_app = &app->settings.dummy_apps[favorite_id];
    }
 
    if(event.type == SceneManagerEventTypeCustom) {
        if(event.event == DEFAULT_INDEX) {
            curr_favorite_app->name_or_path[0] = '\0';
            consumed = true;
        } else if(event.event == EXTERNAL_APPLICATION_INDEX) {
            const DialogsFileBrowserOptions browser_options = {
                .extension = ".fap",
                .icon = &I_unknown_10px,
                .skip_assets = true,
                .hide_ext = true,
                .item_loader_callback = favorite_fap_selector_item_callback,
                .item_loader_context = app,
                .base_path = EXT_PATH("apps"),
            };
 
            // Select favorite fap in file browser
            if(favorite_fap_selector_file_exists(curr_favorite_app->name_or_path)) {
                furi_string_set_str(temp_path, curr_favorite_app->name_or_path);
            }
 
            if(dialog_file_browser_show(app->dialogs, temp_path, temp_path, &browser_options)) {
                submenu_reset(app->submenu); // Prevent menu from being shown when we exiting scene
                strlcpy(
                    curr_favorite_app->name_or_path,
                    furi_string_get_cstr(temp_path),
                    sizeof(curr_favorite_app->name_or_path));
                consumed = true;
            }
        } else {
            size_t app_index = event.event - 2;
            const char* name = favorite_fap_get_app_name(app_index);
            if(name)
                strlcpy(
                    curr_favorite_app->name_or_path,
                    name,
                    sizeof(curr_favorite_app->name_or_path));
            consumed = true;
        }
        if(consumed) {
            scene_manager_previous_scene(app->scene_manager);
        };
        consumed = true;
 
        desktop_settings_save(&app->settings);
    }
 
    furi_string_free(temp_path);
    return consumed;
}
 
void desktop_settings_scene_favorite_on_exit(void* context) {
    DesktopSettingsApp* app = context;
    submenu_reset(app->submenu);
}

V784 The size of the bit mask is less than the size of the first operand. This will cause the loss of higher bits.

V784 The size of the bit mask is less than the size of the first operand. This will cause the loss of higher bits.