mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-07 05:29:09 -07:00
Merge branch 'dev' of https://github.com/Sil333033/Flipper-Xtreme into dev
This commit is contained in:
@@ -1,12 +0,0 @@
|
||||
App(
|
||||
appid="mass_storage",
|
||||
name="USB Mass Storage",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="mass_storage_app",
|
||||
cdefines=["APP_MASS_STORAGE"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=2,
|
||||
# fap_icon="",
|
||||
fap_category="USB",
|
||||
)
|
||||
@@ -1,54 +0,0 @@
|
||||
#include "../mass_storage_app_i.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
typedef enum {
|
||||
SubghzCustomEventErrorBack,
|
||||
} MassStorageCustomEvent;
|
||||
|
||||
static void
|
||||
mass_storage_scene_error_event_callback(GuiButtonType result, InputType type, void* context) {
|
||||
furi_assert(context);
|
||||
MassStorageApp* app = context;
|
||||
|
||||
if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, SubghzCustomEventErrorBack);
|
||||
}
|
||||
}
|
||||
|
||||
void mass_storage_scene_error_on_enter(void* context) {
|
||||
MassStorageApp* app = context;
|
||||
|
||||
widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43);
|
||||
|
||||
widget_add_string_multiline_element(
|
||||
app->widget,
|
||||
81,
|
||||
4,
|
||||
AlignCenter,
|
||||
AlignTop,
|
||||
FontSecondary,
|
||||
"No SD card or\napp data found.\nThis app requires a\nmass_storage/\ndirectory.");
|
||||
|
||||
widget_add_button_element(
|
||||
app->widget, GuiButtonTypeLeft, "Back", mass_storage_scene_error_event_callback, app);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewError);
|
||||
}
|
||||
|
||||
bool mass_storage_scene_error_on_event(void* context, SceneManagerEvent event) {
|
||||
MassStorageApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubghzCustomEventErrorBack) {
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void mass_storage_scene_error_on_exit(void* context) {
|
||||
MassStorageApp* app = context;
|
||||
widget_reset(app->widget);
|
||||
}
|
||||
15
applications/external/mass_storage/application.fam
vendored
Normal file
15
applications/external/mass_storage/application.fam
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
App(
|
||||
appid="mass_storage",
|
||||
name="Mass Storage",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="mass_storage_app",
|
||||
requires=[
|
||||
"gui",
|
||||
"dialogs",
|
||||
],
|
||||
stack_size=2 * 1024,
|
||||
order=20,
|
||||
fap_icon="assets/mass_storage_10px.png",
|
||||
fap_icon_assets="assets",
|
||||
fap_category="USB",
|
||||
)
|
||||
BIN
applications/external/mass_storage/assets/mass_storage_10px.png
vendored
Normal file
BIN
applications/external/mass_storage/assets/mass_storage_10px.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 183 B |
@@ -1,5 +1,7 @@
|
||||
#include "mass_storage_scsi.h"
|
||||
|
||||
#include <core/log.h>
|
||||
|
||||
#define TAG "MassStorageSCSI"
|
||||
|
||||
#define SCSI_TEST_UNIT_READY (0x00)
|
||||
@@ -131,17 +133,17 @@ bool scsi_cmd_tx_data(SCSISession* scsi, uint8_t* data, uint32_t* len, uint32_t
|
||||
data[0] = (n_blocks - 1) >> 24;
|
||||
data[1] = (n_blocks - 1) >> 16;
|
||||
data[2] = (n_blocks - 1) >> 8;
|
||||
data[3] = (n_blocks - 1);
|
||||
data[3] = (n_blocks - 1) & 0xFF;
|
||||
data[4] = block_size >> 24;
|
||||
data[5] = block_size >> 16;
|
||||
data[6] = block_size >> 8;
|
||||
data[7] = block_size;
|
||||
data[7] = block_size & 0xFF;
|
||||
*len = 8;
|
||||
scsi->tx_done = true;
|
||||
return true;
|
||||
}; break;
|
||||
case SCSI_MODE_SENSE_6: {
|
||||
FURI_LOG_I(TAG, "SCSI_MODE_SENSE_6 %ld", cap);
|
||||
FURI_LOG_I(TAG, "SCSI_MODE_SENSE_6 %lu", cap);
|
||||
if(cap < 4) return false;
|
||||
data[0] = 3; // mode data length (len - 1)
|
||||
data[1] = 0; // medium type
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define SCSI_BLOCK_SIZE (0x200u)
|
||||
#define SCSI_BLOCK_SIZE (0x200UL)
|
||||
|
||||
#define SCSI_SK_ILLEGAL_REQUEST (5)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "mass_storage_usb.h"
|
||||
|
||||
#include <furi_hal.h>
|
||||
|
||||
#define TAG "MassStorageUsb"
|
||||
@@ -7,8 +6,8 @@
|
||||
#define USB_MSC_RX_EP (0x01)
|
||||
#define USB_MSC_TX_EP (0x82)
|
||||
|
||||
#define USB_MSC_RX_EP_SIZE (64)
|
||||
#define USB_MSC_TX_EP_SIZE (64u)
|
||||
#define USB_MSC_RX_EP_SIZE (64UL)
|
||||
#define USB_MSC_TX_EP_SIZE (64UL)
|
||||
|
||||
#define USB_MSC_BOT_GET_MAX_LUN (0xFE)
|
||||
#define USB_MSC_BOT_RESET (0xFF)
|
||||
@@ -23,7 +22,7 @@
|
||||
|
||||
// must be SCSI_BLOCK_SIZE aligned
|
||||
// larger than 0x10000 exceeds size_t, storage_file_* ops fail
|
||||
#define USB_MSC_BUF_MAX (0x10000u - SCSI_BLOCK_SIZE)
|
||||
#define USB_MSC_BUF_MAX (0x10000UL - SCSI_BLOCK_SIZE)
|
||||
|
||||
static usbd_respond usb_ep_config(usbd_device* dev, uint8_t cfg);
|
||||
static usbd_respond usb_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback);
|
||||
@@ -79,11 +78,10 @@ static int32_t mass_thread_worker(void* context) {
|
||||
StateWriteCSW,
|
||||
} state = StateReadCBW;
|
||||
while(true) {
|
||||
uint32_t flags = furi_thread_flags_wait(EventAll, FuriFlagWaitAny, FuriFlagWaitAny);
|
||||
uint32_t flags = furi_thread_flags_wait(EventAll, FuriFlagWaitAny, FuriWaitForever);
|
||||
if(flags & EventExit) {
|
||||
FURI_LOG_I(TAG, "exit");
|
||||
free(buf);
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
if(flags & EventReset) {
|
||||
FURI_LOG_I(TAG, "reset");
|
||||
@@ -91,8 +89,10 @@ static int32_t mass_thread_worker(void* context) {
|
||||
scsi.asc = 0;
|
||||
memset(&cbw, 0, sizeof(cbw));
|
||||
memset(&csw, 0, sizeof(csw));
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
if(buf) {
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
}
|
||||
buf_len = buf_cap = buf_sent = 0;
|
||||
state = StateReadCBW;
|
||||
}
|
||||
@@ -138,8 +138,10 @@ static int32_t mass_thread_worker(void* context) {
|
||||
}
|
||||
uint32_t buf_clamp = MIN(cbw.len, USB_MSC_BUF_MAX);
|
||||
if(buf_clamp > buf_cap) {
|
||||
// FURI_LOG_I(TAG, "growing buf %d -> %d", buf_cap, buf_clamp);
|
||||
free(buf);
|
||||
FURI_LOG_I(TAG, "growing buf %lu -> %lu", buf_cap, buf_clamp);
|
||||
if(buf) {
|
||||
free(buf);
|
||||
}
|
||||
buf_cap = buf_clamp;
|
||||
buf = malloc(buf_cap);
|
||||
}
|
||||
@@ -177,8 +179,10 @@ static int32_t mass_thread_worker(void* context) {
|
||||
}
|
||||
uint32_t buf_clamp = MIN(cbw.len, USB_MSC_BUF_MAX);
|
||||
if(buf_clamp > buf_cap) {
|
||||
// FURI_LOG_I(TAG, "growing buf %d -> %d", buf_cap, buf_clamp);
|
||||
free(buf);
|
||||
FURI_LOG_I(TAG, "growing buf %lu -> %lu", buf_cap, buf_clamp);
|
||||
if(buf) {
|
||||
free(buf);
|
||||
}
|
||||
buf_cap = buf_clamp;
|
||||
buf = malloc(buf_cap);
|
||||
}
|
||||
@@ -248,6 +252,10 @@ static int32_t mass_thread_worker(void* context) {
|
||||
break;
|
||||
} while(true);
|
||||
}
|
||||
if(buf) {
|
||||
free(buf);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// needed in usb_deinit, usb_suspend, usb_rxtx_ep_callback, usb_control,
|
||||
@@ -307,8 +315,8 @@ static void usb_suspend(usbd_device* dev) {
|
||||
}
|
||||
|
||||
static void usb_rxtx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) {
|
||||
UNUSED(event);
|
||||
UNUSED(ep);
|
||||
UNUSED(event);
|
||||
MassStorageUsb* mass = mass_cur;
|
||||
if(!mass || mass->dev != dev) return;
|
||||
furi_thread_flags_set(furi_thread_get_id(mass->thread), EventRxTx);
|
||||
@@ -21,31 +21,14 @@ static void mass_storage_app_tick_event_callback(void* context) {
|
||||
scene_manager_handle_tick_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static bool mass_storage_check_assets(Storage* fs_api) {
|
||||
File* dir = storage_file_alloc(fs_api);
|
||||
bool ret = false;
|
||||
|
||||
if(storage_dir_open(dir, MASS_STORAGE_APP_PATH_FOLDER)) {
|
||||
ret = true;
|
||||
}
|
||||
|
||||
storage_dir_close(dir);
|
||||
storage_file_free(dir);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
MassStorageApp* mass_storage_app_alloc(char* arg) {
|
||||
MassStorageApp* app = malloc(sizeof(MassStorageApp));
|
||||
memset(app, 0, sizeof(MassStorageApp));
|
||||
app->file_path = furi_string_alloc();
|
||||
|
||||
if(arg != NULL) {
|
||||
FuriString* filename = furi_string_alloc_set(arg);
|
||||
if(furi_string_start_with_str(filename, MASS_STORAGE_APP_PATH_FOLDER)) {
|
||||
furi_string_right(filename, strlen(MASS_STORAGE_APP_PATH_FOLDER) + 1);
|
||||
}
|
||||
strlcpy(app->file_name, furi_string_get_cstr(filename), MASS_STORAGE_FILE_NAME_LEN);
|
||||
furi_string_free(filename);
|
||||
furi_string_set_str(app->file_path, arg);
|
||||
} else {
|
||||
furi_string_set_str(app->file_path, MASS_STORAGE_APP_PATH_FOLDER);
|
||||
}
|
||||
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
@@ -53,8 +36,6 @@ MassStorageApp* mass_storage_app_alloc(char* arg) {
|
||||
app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
app->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
|
||||
storage_simply_mkdir(app->fs_api, MASS_STORAGE_APP_PATH_FOLDER);
|
||||
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
|
||||
@@ -68,11 +49,6 @@ MassStorageApp* mass_storage_app_alloc(char* arg) {
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, mass_storage_app_back_event_callback);
|
||||
|
||||
// Custom Widget
|
||||
app->widget = widget_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, MassStorageAppViewError, widget_get_view(app->widget));
|
||||
|
||||
app->mass_storage_view = mass_storage_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
@@ -81,12 +57,10 @@ MassStorageApp* mass_storage_app_alloc(char* arg) {
|
||||
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
if(*app->file_name != '\0') {
|
||||
if(storage_file_exists(app->fs_api, furi_string_get_cstr(app->file_path))) {
|
||||
scene_manager_next_scene(app->scene_manager, MassStorageSceneWork);
|
||||
} else if(mass_storage_check_assets(app->fs_api)) {
|
||||
scene_manager_next_scene(app->scene_manager, MassStorageSceneFileSelect);
|
||||
} else {
|
||||
scene_manager_next_scene(app->scene_manager, MassStorageSceneError);
|
||||
scene_manager_next_scene(app->scene_manager, MassStorageSceneFileSelect);
|
||||
}
|
||||
|
||||
return app;
|
||||
@@ -99,14 +73,12 @@ void mass_storage_app_free(MassStorageApp* app) {
|
||||
view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewWork);
|
||||
mass_storage_free(app->mass_storage_view);
|
||||
|
||||
// Custom Widget
|
||||
view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewError);
|
||||
widget_free(app->widget);
|
||||
|
||||
// View dispatcher
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
scene_manager_free(app->scene_manager);
|
||||
|
||||
furi_string_free(app->file_path);
|
||||
|
||||
// Close records
|
||||
furi_record_close(RECORD_GUI);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "views/mass_storage_view.h"
|
||||
|
||||
#define MASS_STORAGE_APP_PATH_FOLDER STORAGE_APP_DATA_PATH_PREFIX
|
||||
// #define MASS_STORAGE_APP_EXTENSION ".iso"
|
||||
#define MASS_STORAGE_APP_EXTENSION ".img"
|
||||
#define MASS_STORAGE_FILE_NAME_LEN 40
|
||||
|
||||
struct MassStorageApp {
|
||||
@@ -29,7 +29,7 @@ struct MassStorageApp {
|
||||
DialogsApp* dialogs;
|
||||
Widget* widget;
|
||||
|
||||
char file_name[MASS_STORAGE_FILE_NAME_LEN];
|
||||
FuriString* file_path;
|
||||
File* file;
|
||||
MassStorage* mass_storage_view;
|
||||
|
||||
@@ -39,6 +39,5 @@ struct MassStorageApp {
|
||||
|
||||
typedef enum {
|
||||
MassStorageAppViewError,
|
||||
MassStorageAppViewFileSelect,
|
||||
MassStorageAppViewWork,
|
||||
} MassStorageAppView;
|
||||
@@ -1,3 +1,2 @@
|
||||
ADD_SCENE(mass_storage, file_select, FileSelect)
|
||||
ADD_SCENE(mass_storage, work, Work)
|
||||
ADD_SCENE(mass_storage, error, Error)
|
||||
@@ -1,25 +1,18 @@
|
||||
#include "../mass_storage_app_i.h"
|
||||
#include "furi_hal_power.h"
|
||||
#include <mass_storage_icons.h>
|
||||
|
||||
static bool mass_storage_file_select(MassStorageApp* mass_storage) {
|
||||
furi_assert(mass_storage);
|
||||
|
||||
FuriString* file_path = furi_string_alloc();
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(&browser_options, "*", NULL);
|
||||
dialog_file_browser_set_basic_options(
|
||||
&browser_options, MASS_STORAGE_APP_EXTENSION, &I_mass_storage_10px);
|
||||
browser_options.base_path = MASS_STORAGE_APP_PATH_FOLDER;
|
||||
furi_string_set(file_path, MASS_STORAGE_APP_PATH_FOLDER);
|
||||
|
||||
bool res =
|
||||
dialog_file_browser_show(mass_storage->dialogs, file_path, file_path, &browser_options);
|
||||
|
||||
if(res) {
|
||||
strlcpy(
|
||||
mass_storage->file_name,
|
||||
furi_string_get_cstr(file_path),
|
||||
sizeof(mass_storage->file_name));
|
||||
}
|
||||
furi_string_free(file_path);
|
||||
// Input events and views are managed by file_select
|
||||
bool res = dialog_file_browser_show(
|
||||
mass_storage->dialogs, mass_storage->file_path, mass_storage->file_path, &browser_options);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "../mass_storage_app_i.h"
|
||||
#include "../views/mass_storage_view.h"
|
||||
#include "../helpers/mass_storage_usb.h"
|
||||
#include <lib/toolbox/path.h>
|
||||
|
||||
#define TAG "MassStorageSceneWork"
|
||||
|
||||
@@ -27,7 +28,7 @@ static bool file_write(void* ctx, uint32_t lba, uint16_t count, uint8_t* buf, ui
|
||||
MassStorageApp* app = ctx;
|
||||
// FURI_LOG_I(TAG, "file_write lba=%08lx count=%04x len=%04x", lba, count, len);
|
||||
if(len != count * SCSI_BLOCK_SIZE) {
|
||||
FURI_LOG_W(TAG, "bad write params count=%d len=%ld", count, len);
|
||||
FURI_LOG_W(TAG, "bad write params count=%u len=%lu", count, len);
|
||||
return false;
|
||||
}
|
||||
if(!storage_file_seek(app->file, lba * SCSI_BLOCK_SIZE, true)) {
|
||||
@@ -69,13 +70,15 @@ void mass_storage_scene_work_on_enter(void* context) {
|
||||
app->usb_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
|
||||
FuriString* file_name = furi_string_alloc();
|
||||
path_extract_filename(app->file_path, file_name, true);
|
||||
|
||||
mass_storage_set_file_name(app->mass_storage_view, app->file_name);
|
||||
furi_string_printf(file_name, "%s/%s", MASS_STORAGE_APP_PATH_FOLDER, app->file_name);
|
||||
mass_storage_set_file_name(app->mass_storage_view, file_name);
|
||||
app->file = storage_file_alloc(app->fs_api);
|
||||
furi_assert(storage_file_open(
|
||||
app->file, furi_string_get_cstr(file_name), FSAM_READ | FSAM_WRITE, FSOM_OPEN_EXISTING));
|
||||
furi_string_free(file_name);
|
||||
app->file,
|
||||
furi_string_get_cstr(app->file_path),
|
||||
FSAM_READ | FSAM_WRITE,
|
||||
FSOM_OPEN_EXISTING));
|
||||
|
||||
SCSIDeviceFunc fn = {
|
||||
.ctx = app,
|
||||
@@ -84,7 +87,10 @@ void mass_storage_scene_work_on_enter(void* context) {
|
||||
.num_blocks = file_num_blocks,
|
||||
.eject = file_eject,
|
||||
};
|
||||
app->usb = mass_storage_usb_start(app->file_name, fn);
|
||||
|
||||
app->usb = mass_storage_usb_start(furi_string_get_cstr(file_name), fn);
|
||||
|
||||
furi_string_free(file_name);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewWork);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "mass_storage_view.h"
|
||||
#include <gui/elements.h>
|
||||
#include <mass_storage_icons.h>
|
||||
#include <assets_icons.h>
|
||||
|
||||
struct MassStorage {
|
||||
@@ -7,21 +8,22 @@ struct MassStorage {
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
char* file_name;
|
||||
FuriString* file_name;
|
||||
} MassStorageModel;
|
||||
|
||||
static void mass_storage_draw_callback(Canvas* canvas, void* _model) {
|
||||
MassStorageModel* model = _model;
|
||||
|
||||
FuriString* disp_str = furi_string_alloc_set(model->file_name);
|
||||
elements_string_fit_width(canvas, disp_str, 128 - 2);
|
||||
canvas_draw_icon(canvas, 8, 14, &I_Drive_112x35);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, canvas_width(canvas) / 2, 0, AlignCenter, AlignTop, "USB Mass Storage");
|
||||
|
||||
elements_string_fit_width(canvas, model->file_name, 87 - 2);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str));
|
||||
furi_string_reset(disp_str);
|
||||
|
||||
canvas_draw_icon(canvas, 40, 20, &I_UsbTree_48x22);
|
||||
|
||||
furi_string_free(disp_str);
|
||||
canvas_draw_str(canvas, 12, 25, "Disc image:");
|
||||
canvas_draw_str(canvas, 12, 40, furi_string_get_cstr(model->file_name));
|
||||
}
|
||||
|
||||
MassStorage* mass_storage_alloc() {
|
||||
@@ -29,6 +31,11 @@ MassStorage* mass_storage_alloc() {
|
||||
|
||||
mass_storage->view = view_alloc();
|
||||
view_allocate_model(mass_storage->view, ViewModelTypeLocking, sizeof(MassStorageModel));
|
||||
with_view_model(
|
||||
mass_storage->view,
|
||||
MassStorageModel * model,
|
||||
{ model->file_name = furi_string_alloc(); },
|
||||
false);
|
||||
view_set_context(mass_storage->view, mass_storage);
|
||||
view_set_draw_callback(mass_storage->view, mass_storage_draw_callback);
|
||||
|
||||
@@ -37,6 +44,11 @@ MassStorage* mass_storage_alloc() {
|
||||
|
||||
void mass_storage_free(MassStorage* mass_storage) {
|
||||
furi_assert(mass_storage);
|
||||
with_view_model(
|
||||
mass_storage->view,
|
||||
MassStorageModel * model,
|
||||
{ furi_string_free(model->file_name); },
|
||||
false);
|
||||
view_free(mass_storage->view);
|
||||
free(mass_storage);
|
||||
}
|
||||
@@ -46,8 +58,11 @@ View* mass_storage_get_view(MassStorage* mass_storage) {
|
||||
return mass_storage->view;
|
||||
}
|
||||
|
||||
void mass_storage_set_file_name(MassStorage* mass_storage, char* name) {
|
||||
void mass_storage_set_file_name(MassStorage* mass_storage, FuriString* name) {
|
||||
furi_assert(name);
|
||||
with_view_model(
|
||||
mass_storage->view, MassStorageModel * model, { model->file_name = name; }, true);
|
||||
mass_storage->view,
|
||||
MassStorageModel * model,
|
||||
{ furi_string_set(model->file_name, name); },
|
||||
true);
|
||||
}
|
||||
@@ -10,4 +10,4 @@ void mass_storage_free(MassStorage* mass_storage);
|
||||
|
||||
View* mass_storage_get_view(MassStorage* mass_storage);
|
||||
|
||||
void mass_storage_set_file_name(MassStorage* mass_storage, char* name);
|
||||
void mass_storage_set_file_name(MassStorage* mass_storage, FuriString* name);
|
||||
BIN
assets/resources/badkb/assets/layouts/fr-CA.kl
Normal file
BIN
assets/resources/badkb/assets/layouts/fr-CA.kl
Normal file
Binary file not shown.
Reference in New Issue
Block a user