".fap" extention in file browser and archive tab (#1812)

* Add .fap extention, and Applications tab
* Using new icon, renaming tab to Apps
* Change tabs order
* Add first ugly implementation of in-app icons in archive browser
* Starting using FAPLoader callback
* Getting all metafata from fap
* add app filename fallback
* using fap_loader_item_callback in archive_list_item_cb
* FAP-Loader: removed minimal allocation
* Removed strange code

Co-authored-by: SG <who.just.the.doctor@gmail.com>
Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
Max Andreev 2022-10-06 20:37:53 +05:00 committed by GitHub
parent 11681d8ee8
commit d07c2dbe54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 148 additions and 42 deletions

View File

@ -5,6 +5,7 @@
#include <core/common_defines.h> #include <core/common_defines.h>
#include <core/log.h> #include <core/log.h>
#include "gui/modules/file_browser_worker.h" #include "gui/modules/file_browser_worker.h"
#include <fap_loader/fap_loader_app.h>
#include <math.h> #include <math.h>
static void static void
@ -351,16 +352,32 @@ void archive_add_app_item(ArchiveBrowserView* browser, const char* name) {
ArchiveFile_t_clear(&item); ArchiveFile_t_clear(&item);
} }
static bool archive_get_fap_meta(FuriString* file_path, FuriString* fap_name, uint8_t** icon_ptr) {
Storage* storage = furi_record_open(RECORD_STORAGE);
bool success = false;
if(fap_loader_load_name_and_icon(file_path, storage, icon_ptr, fap_name)) {
success = true;
}
furi_record_close(RECORD_STORAGE);
return success;
}
void archive_add_file_item(ArchiveBrowserView* browser, bool is_folder, const char* name) { void archive_add_file_item(ArchiveBrowserView* browser, bool is_folder, const char* name) {
furi_assert(browser); furi_assert(browser);
furi_assert(name); furi_assert(name);
ArchiveFile_t item; ArchiveFile_t item;
ArchiveFile_t_init(&item); ArchiveFile_t_init(&item);
item.path = furi_string_alloc_set(name);
archive_set_file_type(&item, furi_string_get_cstr(browser->path), is_folder, false);
furi_string_set(item.path, name);
archive_set_file_type(&item, furi_string_get_cstr(browser->path), is_folder, false);
if(item.type == ArchiveFileTypeApplication) {
item.custom_icon_data = malloc(FAP_MANIFEST_MAX_ICON_SIZE);
if(!archive_get_fap_meta(item.path, item.custom_name, &item.custom_icon_data)) {
free(item.custom_icon_data);
item.custom_icon_data = NULL;
}
}
with_view_model( with_view_model(
browser->view, (ArchiveBrowserViewModel * model) { browser->view, (ArchiveBrowserViewModel * model) {
files_array_push_back(model->files, item); files_array_push_back(model->files, item);

View File

@ -16,6 +16,7 @@ static const char* tab_default_paths[] = {
[ArchiveTabInfrared] = ANY_PATH("infrared"), [ArchiveTabInfrared] = ANY_PATH("infrared"),
[ArchiveTabBadUsb] = ANY_PATH("badusb"), [ArchiveTabBadUsb] = ANY_PATH("badusb"),
[ArchiveTabU2f] = "/app:u2f", [ArchiveTabU2f] = "/app:u2f",
[ArchiveTabApplications] = ANY_PATH("apps"),
[ArchiveTabBrowser] = STORAGE_ANY_PATH_PREFIX, [ArchiveTabBrowser] = STORAGE_ANY_PATH_PREFIX,
}; };
@ -27,6 +28,7 @@ static const char* known_ext[] = {
[ArchiveFileTypeInfrared] = ".ir", [ArchiveFileTypeInfrared] = ".ir",
[ArchiveFileTypeBadUsb] = ".txt", [ArchiveFileTypeBadUsb] = ".txt",
[ArchiveFileTypeU2f] = "?", [ArchiveFileTypeU2f] = "?",
[ArchiveFileTypeApplication] = ".fap",
[ArchiveFileTypeUpdateManifest] = ".fuf", [ArchiveFileTypeUpdateManifest] = ".fuf",
[ArchiveFileTypeFolder] = "?", [ArchiveFileTypeFolder] = "?",
[ArchiveFileTypeUnknown] = "*", [ArchiveFileTypeUnknown] = "*",
@ -41,6 +43,7 @@ static const ArchiveFileTypeEnum known_type[] = {
[ArchiveTabInfrared] = ArchiveFileTypeInfrared, [ArchiveTabInfrared] = ArchiveFileTypeInfrared,
[ArchiveTabBadUsb] = ArchiveFileTypeBadUsb, [ArchiveTabBadUsb] = ArchiveFileTypeBadUsb,
[ArchiveTabU2f] = ArchiveFileTypeU2f, [ArchiveTabU2f] = ArchiveFileTypeU2f,
[ArchiveTabApplications] = ArchiveFileTypeApplication,
[ArchiveTabBrowser] = ArchiveFileTypeUnknown, [ArchiveTabBrowser] = ArchiveFileTypeUnknown,
}; };

View File

@ -4,6 +4,8 @@
#include <furi.h> #include <furi.h>
#include <storage/storage.h> #include <storage/storage.h>
#define FAP_MANIFEST_MAX_ICON_SIZE 32
typedef enum { typedef enum {
ArchiveFileTypeIButton, ArchiveFileTypeIButton,
ArchiveFileTypeNFC, ArchiveFileTypeNFC,
@ -13,6 +15,7 @@ typedef enum {
ArchiveFileTypeBadUsb, ArchiveFileTypeBadUsb,
ArchiveFileTypeU2f, ArchiveFileTypeU2f,
ArchiveFileTypeUpdateManifest, ArchiveFileTypeUpdateManifest,
ArchiveFileTypeApplication,
ArchiveFileTypeFolder, ArchiveFileTypeFolder,
ArchiveFileTypeUnknown, ArchiveFileTypeUnknown,
ArchiveFileTypeLoading, ArchiveFileTypeLoading,
@ -21,33 +24,56 @@ typedef enum {
typedef struct { typedef struct {
FuriString* path; FuriString* path;
ArchiveFileTypeEnum type; ArchiveFileTypeEnum type;
uint8_t* custom_icon_data;
FuriString* custom_name;
bool fav; bool fav;
bool is_app; bool is_app;
} ArchiveFile_t; } ArchiveFile_t;
static void ArchiveFile_t_init(ArchiveFile_t* obj) { static void ArchiveFile_t_init(ArchiveFile_t* obj) {
obj->type = ArchiveFileTypeUnknown;
obj->is_app = false;
obj->fav = false;
obj->path = furi_string_alloc(); obj->path = furi_string_alloc();
obj->type = ArchiveFileTypeUnknown;
obj->custom_icon_data = NULL;
obj->custom_name = furi_string_alloc();
obj->fav = false;
obj->is_app = false;
} }
static void ArchiveFile_t_init_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { static void ArchiveFile_t_init_set(ArchiveFile_t* obj, const ArchiveFile_t* src) {
obj->type = src->type;
obj->is_app = src->is_app;
obj->fav = src->fav;
obj->path = furi_string_alloc_set(src->path); obj->path = furi_string_alloc_set(src->path);
obj->type = src->type;
if(src->custom_icon_data) {
obj->custom_icon_data = malloc(FAP_MANIFEST_MAX_ICON_SIZE);
memcpy(obj->custom_icon_data, src->custom_icon_data, FAP_MANIFEST_MAX_ICON_SIZE);
} else {
obj->custom_icon_data = NULL;
}
obj->custom_name = furi_string_alloc_set(src->custom_name);
obj->fav = src->fav;
obj->is_app = src->is_app;
} }
static void ArchiveFile_t_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { static void ArchiveFile_t_set(ArchiveFile_t* obj, const ArchiveFile_t* src) {
obj->type = src->type;
obj->is_app = src->is_app;
obj->fav = src->fav;
furi_string_set(obj->path, src->path); furi_string_set(obj->path, src->path);
obj->type = src->type;
if(src->custom_icon_data) {
obj->custom_icon_data = malloc(FAP_MANIFEST_MAX_ICON_SIZE);
memcpy(obj->custom_icon_data, src->custom_icon_data, FAP_MANIFEST_MAX_ICON_SIZE);
} else {
obj->custom_icon_data = NULL;
}
furi_string_set(obj->custom_name, src->custom_name);
obj->fav = src->fav;
obj->is_app = src->is_app;
} }
static void ArchiveFile_t_clear(ArchiveFile_t* obj) { static void ArchiveFile_t_clear(ArchiveFile_t* obj) {
furi_string_free(obj->path); furi_string_free(obj->path);
if(obj->custom_icon_data) {
free(obj->custom_icon_data);
obj->custom_icon_data = NULL;
}
furi_string_free(obj->custom_name);
} }
ARRAY_DEF( ARRAY_DEF(

View File

@ -20,6 +20,7 @@ static const char* flipper_app_name[] = {
[ArchiveFileTypeBadUsb] = "Bad USB", [ArchiveFileTypeBadUsb] = "Bad USB",
[ArchiveFileTypeU2f] = "U2F", [ArchiveFileTypeU2f] = "U2F",
[ArchiveFileTypeUpdateManifest] = "UpdaterApp", [ArchiveFileTypeUpdateManifest] = "UpdaterApp",
[ArchiveFileTypeApplication] = "Applications",
}; };
static void archive_loader_callback(const void* message, void* context) { static void archive_loader_callback(const void* message, void* context) {

View File

@ -14,6 +14,7 @@ static const char* ArchiveTabNames[] = {
[ArchiveTabInfrared] = "Infrared", [ArchiveTabInfrared] = "Infrared",
[ArchiveTabBadUsb] = "Bad USB", [ArchiveTabBadUsb] = "Bad USB",
[ArchiveTabU2f] = "U2F", [ArchiveTabU2f] = "U2F",
[ArchiveTabApplications] = "Apps",
[ArchiveTabBrowser] = "Browser", [ArchiveTabBrowser] = "Browser",
}; };
@ -29,6 +30,7 @@ static const Icon* ArchiveItemIcons[] = {
[ArchiveFileTypeFolder] = &I_dir_10px, [ArchiveFileTypeFolder] = &I_dir_10px,
[ArchiveFileTypeUnknown] = &I_unknown_10px, [ArchiveFileTypeUnknown] = &I_unknown_10px,
[ArchiveFileTypeLoading] = &I_loading_10px, [ArchiveFileTypeLoading] = &I_loading_10px,
[ArchiveFileTypeApplication] = &I_unknown_10px,
}; };
void archive_browser_set_callback( void archive_browser_set_callback(
@ -124,12 +126,23 @@ static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) {
uint8_t x_offset = (model->move_fav && model->item_idx == idx) ? MOVE_OFFSET : 0; uint8_t x_offset = (model->move_fav && model->item_idx == idx) ? MOVE_OFFSET : 0;
ArchiveFileTypeEnum file_type = ArchiveFileTypeLoading; ArchiveFileTypeEnum file_type = ArchiveFileTypeLoading;
uint8_t* custom_icon_data = NULL;
if(archive_is_item_in_array(model, idx)) { if(archive_is_item_in_array(model, idx)) {
ArchiveFile_t* file = files_array_get( ArchiveFile_t* file = files_array_get(
model->files, CLAMP(idx - model->array_offset, (int32_t)(array_size - 1), 0)); model->files, CLAMP(idx - model->array_offset, (int32_t)(array_size - 1), 0));
path_extract_filename(file->path, str_buf, archive_is_known_app(file->type));
file_type = file->type; file_type = file->type;
if(file_type == ArchiveFileTypeApplication) {
if(file->custom_icon_data) {
custom_icon_data = file->custom_icon_data;
furi_string_set(str_buf, file->custom_name);
} else {
file_type = ArchiveFileTypeUnknown;
path_extract_filename(file->path, str_buf, archive_is_known_app(file->type));
}
} else {
path_extract_filename(file->path, str_buf, archive_is_known_app(file->type));
}
} else { } else {
furi_string_set(str_buf, "---"); furi_string_set(str_buf, "---");
} }
@ -143,7 +156,13 @@ static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) {
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
} }
canvas_draw_icon(canvas, 2 + x_offset, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file_type]); if(custom_icon_data) {
canvas_draw_bitmap(
canvas, 2 + x_offset, 16 + i * FRAME_HEIGHT, 11, 10, custom_icon_data);
} else {
canvas_draw_icon(
canvas, 2 + x_offset, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file_type]);
}
canvas_draw_str( canvas_draw_str(
canvas, 15 + x_offset, 24 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buf)); canvas, 15 + x_offset, 24 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buf));

View File

@ -26,6 +26,7 @@ typedef enum {
ArchiveTabIButton, ArchiveTabIButton,
ArchiveTabBadUsb, ArchiveTabBadUsb,
ArchiveTabU2f, ArchiveTabU2f,
ArchiveTabApplications,
ArchiveTabBrowser, ArchiveTabBrowser,
ArchiveTabTotal, ArchiveTabTotal,
} ArchiveTabEnum; } ArchiveTabEnum;

View File

@ -1,34 +1,31 @@
#include <furi.h> #include <furi.h>
#include <gui/gui.h> #include <gui/gui.h>
#include <gui/view_dispatcher.h> #include <gui/view_dispatcher.h>
#include <gui/modules/loading.h>
#include <storage/storage.h> #include <storage/storage.h>
#include <gui/modules/loading.h>
#include <dialogs/dialogs.h> #include <dialogs/dialogs.h>
#include "elf_cpp/elf_hashtable.h"
#include <flipper_application/flipper_application.h> #include <flipper_application/flipper_application.h>
#include "elf_cpp/elf_hashtable.h"
#include "fap_loader_app.h"
#define TAG "fap_loader_app" #define TAG "fap_loader_app"
typedef struct { struct FapLoader {
FlipperApplication* app; FlipperApplication* app;
Storage* storage; Storage* storage;
DialogsApp* dialogs; DialogsApp* dialogs;
Gui* gui; Gui* gui;
FuriString* fap_path; FuriString* fap_path;
ViewDispatcher* view_dispatcher; ViewDispatcher* view_dispatcher;
Loading* loading; Loading* loading;
} FapLoader; };
static bool fap_loader_item_callback( bool fap_loader_load_name_and_icon(
FuriString* path, FuriString* path,
void* context, Storage* storage,
uint8_t** icon_ptr, uint8_t** icon_ptr,
FuriString* item_name) { FuriString* item_name) {
FapLoader* loader = context; FlipperApplication* app = flipper_application_alloc(storage, &hashtable_api_interface);
furi_assert(loader);
FlipperApplication* app = flipper_application_alloc(loader->storage, &hashtable_api_interface);
FlipperApplicationPreloadStatus preload_res = FlipperApplicationPreloadStatus preload_res =
flipper_application_preload_manifest(app, furi_string_get_cstr(path)); flipper_application_preload_manifest(app, furi_string_get_cstr(path));
@ -51,6 +48,16 @@ static bool fap_loader_item_callback(
return load_success; return load_success;
} }
static bool fap_loader_item_callback(
FuriString* path,
void* context,
uint8_t** icon_ptr,
FuriString* item_name) {
FapLoader* fap_loader = context;
furi_assert(fap_loader);
return fap_loader_load_name_and_icon(path, fap_loader->storage, icon_ptr, item_name);
}
static bool fap_loader_run_selected_app(FapLoader* loader) { static bool fap_loader_run_selected_app(FapLoader* loader) {
furi_assert(loader); furi_assert(loader);
@ -134,7 +141,7 @@ static bool fap_loader_select_app(FapLoader* loader) {
const DialogsFileBrowserOptions browser_options = { const DialogsFileBrowserOptions browser_options = {
.extension = ".fap", .extension = ".fap",
.skip_assets = true, .skip_assets = true,
.icon = &I_badusb_10px, .icon = &I_unknown_10px,
.hide_ext = true, .hide_ext = true,
.item_loader_callback = fap_loader_item_callback, .item_loader_callback = fap_loader_item_callback,
.item_loader_context = loader, .item_loader_context = loader,
@ -144,39 +151,44 @@ static bool fap_loader_select_app(FapLoader* loader) {
loader->dialogs, loader->fap_path, loader->fap_path, &browser_options); loader->dialogs, loader->fap_path, loader->fap_path, &browser_options);
} }
int32_t fap_loader_app(void* p) { static FapLoader* fap_loader_alloc(const char* path) {
FapLoader* loader = malloc(sizeof(FapLoader)); FapLoader* loader = malloc(sizeof(FapLoader));
loader->fap_path = furi_string_alloc_set(path);
loader->storage = furi_record_open(RECORD_STORAGE); loader->storage = furi_record_open(RECORD_STORAGE);
loader->dialogs = furi_record_open(RECORD_DIALOGS); loader->dialogs = furi_record_open(RECORD_DIALOGS);
loader->gui = furi_record_open(RECORD_GUI); loader->gui = furi_record_open(RECORD_GUI);
loader->view_dispatcher = view_dispatcher_alloc(); loader->view_dispatcher = view_dispatcher_alloc();
loader->loading = loading_alloc(); loader->loading = loading_alloc();
view_dispatcher_attach_to_gui( view_dispatcher_attach_to_gui(
loader->view_dispatcher, loader->gui, ViewDispatcherTypeFullscreen); loader->view_dispatcher, loader->gui, ViewDispatcherTypeFullscreen);
view_dispatcher_add_view(loader->view_dispatcher, 0, loading_get_view(loader->loading)); view_dispatcher_add_view(loader->view_dispatcher, 0, loading_get_view(loader->loading));
return loader;
}
static void fap_loader_free(FapLoader* loader) {
view_dispatcher_remove_view(loader->view_dispatcher, 0);
loading_free(loader->loading);
view_dispatcher_free(loader->view_dispatcher);
furi_string_free(loader->fap_path);
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_DIALOGS);
furi_record_close(RECORD_STORAGE);
free(loader);
}
int32_t fap_loader_app(void* p) {
FapLoader* loader;
if(p) { if(p) {
loader->fap_path = furi_string_alloc_set((const char*)p); loader = fap_loader_alloc((const char*)p);
fap_loader_run_selected_app(loader); fap_loader_run_selected_app(loader);
} else { } else {
loader->fap_path = furi_string_alloc_set(EXT_PATH("apps")); loader = fap_loader_alloc(EXT_PATH("apps"));
while(fap_loader_select_app(loader)) { while(fap_loader_select_app(loader)) {
view_dispatcher_switch_to_view(loader->view_dispatcher, 0); view_dispatcher_switch_to_view(loader->view_dispatcher, 0);
fap_loader_run_selected_app(loader); fap_loader_run_selected_app(loader);
}; };
} }
view_dispatcher_remove_view(loader->view_dispatcher, 0); fap_loader_free(loader);
loading_free(loader->loading);
view_dispatcher_free(loader->view_dispatcher);
furi_string_free(loader->fap_path);
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_DIALOGS);
furi_record_close(RECORD_STORAGE);
free(loader);
return 0; return 0;
} }

View File

@ -0,0 +1,27 @@
#pragma once
#include <storage/storage.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct FapLoader FapLoader;
/**
* @brief Load name and icon from FAP file.
*
* @param path Path to FAP file.
* @param storage Storage instance.
* @param icon_ptr Icon pointer.
* @param item_name Application name.
* @return true if icon and name were loaded successfully.
*/
bool fap_loader_load_name_and_icon(
FuriString* path,
Storage* storage,
uint8_t** icon_ptr,
FuriString* item_name);
#ifdef __cplusplus
}
#endif