| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- #include <furi.h>
- #include <gui/view_dispatcher.h>
- #include <gui/scene_manager.h>
- #include <gui/modules/submenu.h>
- #include <lib/toolbox/path.h>
- #include "quac.h"
- #include "scenes.h"
- #include "scene_action_settings.h"
- #include "../actions/action.h"
- #include "quac_icons.h"
- // Define different settings per Action
- typedef enum {
- ActionSettingsRename, // Rename file or folder
- ActionSettingsDelete, // Delete file or folder on SDcard
- ActionSettingsImport, // Copy a remote file into "current" folder
- ActionSettingsImportLink, // Create a link to a remote file into "current" folder
- ActionSettingsCreateGroup, // Create new empty folder in "current" folder
- ActionSettingsCreatePlaylist, // Turn this folder into a playlist
- ActionSettingsAddToPlaylist, // Append a remote file to this playlist
- } ActionSettingsIndex;
- // Delete the file of the currently selected item
- // Update items_view list before returning so that UI is updated and correct
- bool scene_action_settings_delete(App* app) {
- bool success = false;
- Item* item = ItemArray_get(app->items_view->items, app->selected_item);
- DialogMessage* dialog = dialog_message_alloc();
- dialog_message_set_header(dialog, "Delete?", 64, 0, AlignCenter, AlignTop);
- FuriString* text = furi_string_alloc();
- furi_string_printf(text, "%s\n\n%s", furi_string_get_cstr(item->name), "Are you sure?");
- dialog_message_set_text(dialog, furi_string_get_cstr(text), 64, 18, AlignCenter, AlignTop);
- dialog_message_set_buttons(dialog, "Cancel", NULL, "OK");
- DialogMessageButton button = dialog_message_show(app->dialog, dialog);
- if(button == DialogMessageButtonRight) {
- FuriString* current_path = furi_string_alloc();
- path_extract_dirname(furi_string_get_cstr(item->path), current_path);
- FS_Error fs_result = storage_common_remove(app->storage, furi_string_get_cstr(item->path));
- if(fs_result == FSE_OK) {
- success = true;
- FURI_LOG_I(TAG, "Deleted file: %s", furi_string_get_cstr(item->path));
- // ItemsView* new_items = item_get_items_view_from_path(app, current_path);
- // item_items_view_free(app->items_view);
- // app->items_view = new_items;
- } else {
- FURI_LOG_E(
- TAG, "Error deleting file! Error=%s", filesystem_api_error_get_desc(fs_result));
- FuriString* error_msg = furi_string_alloc();
- furi_string_printf(
- error_msg, "Delete failed!\nError: %s", filesystem_api_error_get_desc(fs_result));
- dialog_message_show_storage_error(app->dialog, furi_string_get_cstr(error_msg));
- furi_string_free(error_msg);
- }
- furi_string_free(current_path);
- } else {
- // FURI_LOG_I(TAG, "Used cancelled Delete");
- }
- furi_string_free(text);
- dialog_message_free(dialog);
- return success;
- }
- static bool scene_action_settings_import_file_browser_callback(
- FuriString* path,
- void* context,
- uint8_t** icon,
- FuriString* item_name) {
- UNUSED(context);
- UNUSED(item_name);
- char ext[MAX_EXT_LEN];
- path_extract_extension(path, ext, MAX_EXT_LEN);
- if(!strcmp(ext, ".sub")) {
- memcpy(*icon, icon_get_frame_data(&I_SubGHz_10px, 0), 32); // TODO: find the right size!
- } else if(!strcmp(ext, ".rfid")) {
- memcpy(*icon, icon_get_frame_data(&I_RFID_10px, 0), 32);
- } else if(!strcmp(ext, ".ir")) {
- memcpy(*icon, icon_get_frame_data(&I_IR_10px, 0), 32);
- } else if(!strcmp(ext, ".nfc")) {
- memcpy(*icon, icon_get_frame_data(&I_NFC_10px, 0), 32);
- } else if(!strcmp(ext, ".ibtn")) {
- memcpy(*icon, icon_get_frame_data(&I_iButton_10px, 0), 32);
- } else if(!strcmp(ext, ".qpl")) {
- memcpy(*icon, icon_get_frame_data(&I_Playlist_10px, 0), 32);
- } else {
- return false;
- }
- return true;
- }
- // Ask user for file to import from elsewhere on the SD card
- FuriString* scene_action_get_file_to_import_alloc(App* app) {
- // Setup our file browser options
- DialogsFileBrowserOptions fb_options;
- dialog_file_browser_set_basic_options(&fb_options, "", NULL);
- fb_options.base_path = STORAGE_EXT_PATH_PREFIX;
- fb_options.skip_assets = true;
- furi_string_set_str(app->temp_str, fb_options.base_path);
- fb_options.item_loader_callback = scene_action_settings_import_file_browser_callback;
- fb_options.item_loader_context = app;
- FuriString* full_path = NULL;
- if(dialog_file_browser_show(app->dialog, app->temp_str, app->temp_str, &fb_options)) {
- full_path = furi_string_alloc_set(app->temp_str);
- }
- return full_path;
- }
- // Import a file from elsewhere on the SD card
- // Update items_view list before returning so that UI is updated and correct
- bool scene_action_settings_import(App* app) {
- bool success = false;
- FuriString* current_path = furi_string_alloc();
- if(app->selected_item != EMPTY_ACTION_INDEX) {
- Item* item = ItemArray_get(app->items_view->items, app->selected_item);
- path_extract_dirname(furi_string_get_cstr(item->path), current_path);
- } else {
- furi_string_set(current_path, app->items_view->path);
- }
- // Setup our file browser options
- DialogsFileBrowserOptions fb_options;
- dialog_file_browser_set_basic_options(&fb_options, "", NULL);
- fb_options.base_path = furi_string_get_cstr(current_path);
- fb_options.skip_assets = true;
- furi_string_set_str(app->temp_str, fb_options.base_path);
- fb_options.item_loader_callback = scene_action_settings_import_file_browser_callback;
- fb_options.item_loader_context = app;
- if(dialog_file_browser_show(app->dialog, app->temp_str, app->temp_str, &fb_options)) {
- // FURI_LOG_I(TAG, "Selected file is %s", furi_string_get_cstr(app->temp_str));
- FuriString* file_name = furi_string_alloc();
- path_extract_filename(app->temp_str, file_name, false);
- // FURI_LOG_I(TAG, "Importing file %s", furi_string_get_cstr(file_name));
- FuriString* full_path;
- full_path = furi_string_alloc_printf(
- "%s/%s", furi_string_get_cstr(current_path), furi_string_get_cstr(file_name));
- // FURI_LOG_I(TAG, "New path is %s", furi_string_get_cstr(full_path));
- FS_Error fs_result = storage_common_copy(
- app->storage, furi_string_get_cstr(app->temp_str), furi_string_get_cstr(full_path));
- if(fs_result == FSE_OK) {
- success = true;
- // FURI_LOG_I(TAG, "File copied / updating items view list");
- // ItemsView* new_items = item_get_items_view_from_path(app, current_path);
- // item_items_view_free(app->items_view);
- // app->items_view = new_items;
- } else {
- FURI_LOG_E(TAG, "File copy failed! %s", filesystem_api_error_get_desc(fs_result));
- FuriString* error_msg = furi_string_alloc_printf(
- "File copy failed!\nError: %s", filesystem_api_error_get_desc(fs_result));
- dialog_message_show_storage_error(app->dialog, furi_string_get_cstr(error_msg));
- furi_string_free(error_msg);
- }
- furi_string_free(file_name);
- furi_string_free(full_path);
- } else {
- // FURI_LOG_I(TAG, "User cancelled");
- }
- furi_string_free(current_path);
- return success;
- }
- // Prompt user for the name of the new Group
- // Update items_view list before returning so that UI is updated and correct
- bool scene_action_settings_create_group(App* app) {
- UNUSED(app);
- return false;
- }
- void scene_action_settings_callback(void* context, uint32_t index) {
- App* app = context;
- view_dispatcher_send_custom_event(app->view_dispatcher, index);
- }
- void scene_action_settings_on_enter(void* context) {
- App* app = context;
- Submenu* menu = app->sub_menu;
- submenu_reset(menu);
- if(app->selected_item >= 0) {
- Item* item = ItemArray_get(app->items_view->items, app->selected_item);
- submenu_set_header(menu, furi_string_get_cstr(item->name));
- submenu_add_item(
- menu, "Rename", ActionSettingsRename, scene_action_settings_callback, app);
- submenu_add_item(
- menu, "Delete", ActionSettingsDelete, scene_action_settings_callback, app);
- } else {
- submenu_set_header(menu, furi_string_get_cstr(app->items_view->name));
- }
- submenu_add_item(
- menu, "Import Here", ActionSettingsImport, scene_action_settings_callback, app);
- submenu_add_item(
- menu, "Import Link Here", ActionSettingsImportLink, scene_action_settings_callback, app);
- submenu_add_item(
- menu, "Create Group", ActionSettingsCreateGroup, scene_action_settings_callback, app);
- view_dispatcher_switch_to_view(app->view_dispatcher, QView_SubMenu);
- }
- bool scene_action_settings_on_event(void* context, SceneManagerEvent event) {
- App* app = context;
- bool consumed = false;
- if(event.type == SceneManagerEventTypeCustom) {
- switch(event.event) {
- case ActionSettingsRename:
- consumed = true;
- scene_manager_next_scene(app->scene_manager, QScene_ActionRename);
- break;
- case ActionSettingsDelete:
- consumed = true;
- if(scene_action_settings_delete(app)) {
- scene_manager_previous_scene(app->scene_manager);
- }
- break;
- case ActionSettingsImport: {
- consumed = true;
- // get the filename to import
- FuriString* import_file = scene_action_get_file_to_import_alloc(app);
- if(import_file) {
- FURI_LOG_I(TAG, "Importing %s", furi_string_get_cstr(import_file));
- // if it's a .ir file, switch to a scene that lets user pick the command from the file
- // only if there's more than one command in the file. then copy that relevant chunk
- // to the local directory
- char ext[MAX_EXT_LEN] = {0};
- path_extract_extension(import_file, ext, MAX_EXT_LEN);
- if(!strcmp(ext, ".ir")) {
- FURI_LOG_I(TAG, "Loading ir file %s", furi_string_get_cstr(app->temp_str));
- // load scene that takes filename and lists all commands
- // the scene should write the new file, eh?
- scene_manager_next_scene(app->scene_manager, QScene_ActionIRList);
- } else {
- // just copy the file here
- FuriString* current_path = furi_string_alloc();
- if(app->selected_item != EMPTY_ACTION_INDEX) {
- Item* item = ItemArray_get(app->items_view->items, app->selected_item);
- path_extract_dirname(furi_string_get_cstr(item->path), current_path);
- } else {
- furi_string_set(current_path, app->items_view->path);
- }
- // TODO: this should be a method
- FuriString* file_name = furi_string_alloc();
- path_extract_filename(import_file, file_name, false);
- // FURI_LOG_I(TAG, "Importing file %s", furi_string_get_cstr(file_name));
- FuriString* full_path;
- full_path = furi_string_alloc_printf(
- "%s/%s",
- furi_string_get_cstr(current_path),
- furi_string_get_cstr(file_name));
- // FURI_LOG_I(TAG, "New path is %s", furi_string_get_cstr(full_path));
- FURI_LOG_I(
- TAG,
- "Copy: %s to %s",
- furi_string_get_cstr(import_file),
- furi_string_get_cstr(full_path));
- FS_Error fs_result = storage_common_copy(
- app->storage,
- furi_string_get_cstr(import_file),
- furi_string_get_cstr(full_path));
- if(fs_result != FSE_OK) {
- FURI_LOG_E(
- TAG, "Copy file failed! %s", filesystem_api_error_get_desc(fs_result));
- FuriString* error_msg = furi_string_alloc_printf(
- "Copy failed!\nError: %s", filesystem_api_error_get_desc(fs_result));
- dialog_message_show_storage_error(
- app->dialog, furi_string_get_cstr(error_msg));
- furi_string_free(error_msg);
- }
- furi_string_free(file_name);
- furi_string_free(full_path);
- }
- furi_string_free(import_file);
- } else {
- scene_manager_previous_scene(app->scene_manager);
- }
- // if(scene_action_settings_import(app)) {
- // scene_manager_previous_scene(app->scene_manager);
- // }
- } break;
- case ActionSettingsImportLink: {
- consumed = true;
- // get the filename to import as a link
- FuriString* import_file = scene_action_get_file_to_import_alloc(app);
- if(import_file) {
- FURI_LOG_I(TAG, "Importing as link %s", furi_string_get_cstr(import_file));
- char ext[MAX_EXT_LEN] = {0};
- path_extract_extension(import_file, ext, MAX_EXT_LEN);
- if(!strcmp(ext, ".ir")) {
- dialog_message_show_storage_error(
- app->dialog, "Can't import IR file as link at this time");
- } else if(!strcmp(ext, ".ql")) {
- FURI_LOG_E(TAG, "Can't import link file as a link!");
- dialog_message_show_storage_error(
- app->dialog, "Can't import link file as a link!");
- } else {
- FuriString* current_path = furi_string_alloc();
- if(app->selected_item != EMPTY_ACTION_INDEX) {
- Item* item = ItemArray_get(app->items_view->items, app->selected_item);
- path_extract_dirname(furi_string_get_cstr(item->path), current_path);
- } else {
- furi_string_set(current_path, app->items_view->path);
- }
- FuriString* file_name = furi_string_alloc();
- path_extract_filename(import_file, file_name, false);
- FuriString* full_path;
- full_path = furi_string_alloc_printf(
- "%s/%s.ql", // path/filename.ext.ql
- furi_string_get_cstr(current_path),
- furi_string_get_cstr(file_name));
- FURI_LOG_I(
- TAG,
- "Copy as link: %s to %s",
- furi_string_get_cstr(import_file),
- furi_string_get_cstr(full_path));
- File* file_link = storage_file_alloc(app->storage);
- if(storage_file_open(
- file_link,
- furi_string_get_cstr(full_path),
- FSAM_WRITE,
- FSOM_CREATE_NEW)) {
- const char* cimport_file = furi_string_get_cstr(import_file);
- size_t bytes_written =
- storage_file_write(file_link, cimport_file, strlen(cimport_file));
- if(bytes_written != strlen(cimport_file)) {
- FURI_LOG_E(
- TAG,
- "Copy as link failure: incorrect bytes written. Expected %d, wrote %d",
- strlen(cimport_file),
- bytes_written);
- }
- } else {
- dialog_message_show_storage_error(app->dialog, "Error writing link file!");
- FURI_LOG_E(
- TAG,
- "Copy file as link failed! File %s already exists",
- furi_string_get_cstr(full_path));
- }
- storage_file_close(file_link);
- storage_file_free(file_link);
- furi_string_free(file_name);
- furi_string_free(full_path);
- }
- furi_string_free(import_file);
- } else {
- scene_manager_previous_scene(app->scene_manager);
- }
- } break;
- case ActionSettingsCreateGroup:
- consumed = true;
- scene_manager_next_scene(app->scene_manager, QScene_ActionCreateGroup);
- break;
- }
- }
- return consumed;
- }
- void scene_action_settings_on_exit(void* context) {
- App* app = context;
- submenu_reset(app->sub_menu);
- // Rebuild our list on exit, to pick up any renames
- ItemsView* new_items = item_get_items_view_from_path(app, app->items_view->path);
- item_items_view_free(app->items_view);
- app->items_view = new_items;
- }
|