Merge remote-tracking branch 'ofw/dev' into yeet-lfs

This commit is contained in:
Willy-JL
2024-08-12 19:57:49 +02:00
139 changed files with 3552 additions and 1678 deletions

View File

@@ -97,7 +97,7 @@ void animation_manager_set_interact_callback(
animation_manager->interact_callback = callback;
}
static void animation_manager_check_blocking_callback(const void* message, void* context) {
static void animation_manager_storage_callback(const void* message, void* context) {
const StorageEvent* storage_event = message;
switch(storage_event->type) {
@@ -116,6 +116,22 @@ static void animation_manager_check_blocking_callback(const void* message, void*
}
}
static void animation_manager_dolphin_callback(const void* message, void* context) {
const DolphinPubsubEvent* dolphin_event = message;
switch(*dolphin_event) {
case DolphinPubsubEventUpdate:
furi_assert(context);
AnimationManager* animation_manager = context;
if(animation_manager->check_blocking_callback) {
animation_manager->check_blocking_callback(animation_manager->context);
}
break;
default:
break;
}
}
static void animation_manager_timer_callback(void* context) {
furi_assert(context);
AnimationManager* animation_manager = context;
@@ -296,12 +312,12 @@ AnimationManager* animation_manager_alloc(void) {
Storage* storage = furi_record_open(RECORD_STORAGE);
animation_manager->pubsub_subscription_storage = furi_pubsub_subscribe(
storage_get_pubsub(storage), animation_manager_check_blocking_callback, animation_manager);
storage_get_pubsub(storage), animation_manager_storage_callback, animation_manager);
furi_record_close(RECORD_STORAGE);
Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);
animation_manager->pubsub_subscription_dolphin = furi_pubsub_subscribe(
dolphin_get_pubsub(dolphin), animation_manager_check_blocking_callback, animation_manager);
dolphin_get_pubsub(dolphin), animation_manager_dolphin_callback, animation_manager);
furi_record_close(RECORD_DOLPHIN);
animation_manager->blocking_shown_sd_ok = true;

View File

@@ -265,7 +265,6 @@ static Desktop* desktop_alloc(void) {
desktop->view_dispatcher = view_dispatcher_alloc();
desktop->scene_manager = scene_manager_alloc(&desktop_scene_handlers, desktop);
view_dispatcher_enable_queue(desktop->view_dispatcher);
view_dispatcher_attach_to_gui(
desktop->view_dispatcher, desktop->gui, ViewDispatcherTypeDesktop);
view_dispatcher_set_tick_event_callback(

View File

@@ -49,12 +49,11 @@ bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrow
file_browser_start(file_browser, data->preselected_filename);
view_holder_set_view(view_holder, file_browser_get_view(file_browser));
view_holder_start(view_holder);
api_lock_wait_unlock(file_browser_context->lock);
ret = file_browser_context->result;
view_holder_stop(view_holder);
view_holder_set_view(view_holder, NULL);
view_holder_free(view_holder);
file_browser_stop(file_browser);
file_browser_free(file_browser);

View File

@@ -88,12 +88,11 @@ DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDa
dialog_ex_set_right_button_text(dialog_ex, message->right_button_text);
view_holder_set_view(view_holder, dialog_ex_get_view(dialog_ex));
view_holder_start(view_holder);
api_lock_wait_unlock(message_context->lock);
ret = message_context->result;
view_holder_stop(view_holder);
view_holder_set_view(view_holder, NULL);
view_holder_free(view_holder);
dialog_ex_free(dialog_ex);
api_lock_free(message_context->lock);

View File

@@ -211,8 +211,8 @@ static void dolphin_reset_butthurt_timer(Dolphin* dolphin) {
}
}
static bool dolphin_process_event(FuriMessageQueue* queue, void* context) {
UNUSED(queue);
static bool dolphin_process_event(FuriEventLoopObject* object, void* context) {
UNUSED(object);
Dolphin* dolphin = context;
DolphinEvent event;
@@ -300,7 +300,7 @@ int32_t dolphin_srv(void* p) {
dolphin_init_state(dolphin);
furi_event_loop_message_queue_subscribe(
furi_event_loop_subscribe_message_queue(
dolphin->event_loop,
dolphin->event_queue,
FuriEventLoopEventIn,

View File

@@ -19,6 +19,7 @@ App(
"view_holder.h",
"modules/button_menu.h",
"modules/byte_input.h",
"modules/number_input.h",
"modules/popup.h",
"modules/text_input.h",
"modules/widget.h",

View File

@@ -137,7 +137,7 @@ static bool browser_filter_by_name(BrowserWorker* browser, FuriString* name, boo
if((furi_string_empty(ext)) || (furi_string_cmp_str(ext, "*") == 0)) {
return true;
}
if(furi_string_end_with(name, ext)) {
if(furi_string_end_withi(name, ext)) {
return true;
}
}

View File

@@ -0,0 +1,443 @@
#include "number_input.h"
#include <gui/elements.h>
#include <furi.h>
#include <assets_icons.h>
struct NumberInput {
View* view;
};
typedef struct {
const char text;
const size_t x;
const size_t y;
} NumberInputKey;
typedef struct {
FuriString* header;
FuriString* text_buffer;
int32_t current_number;
int32_t max_value;
int32_t min_value;
NumberInputCallback callback;
void* callback_context;
size_t selected_row;
size_t selected_column;
} NumberInputModel;
static const size_t keyboard_origin_x = 7;
static const size_t keyboard_origin_y = 31;
static const size_t keyboard_row_count = 2;
static const char enter_symbol = '\r';
static const char backspace_symbol = '\b';
static const char sign_symbol = '-';
static const NumberInputKey keyboard_keys_row_1[] = {
{'0', 0, 12},
{'1', 11, 12},
{'2', 22, 12},
{'3', 33, 12},
{'4', 44, 12},
{backspace_symbol, 103, 4},
};
static const NumberInputKey keyboard_keys_row_2[] = {
{'5', 0, 26},
{'6', 11, 26},
{'7', 22, 26},
{'8', 33, 26},
{'9', 44, 26},
{sign_symbol, 55, 17},
{enter_symbol, 95, 17},
};
static size_t number_input_get_row_size(size_t row_index) {
size_t row_size = 0;
switch(row_index + 1) {
case 1:
row_size = COUNT_OF(keyboard_keys_row_1);
break;
case 2:
row_size = COUNT_OF(keyboard_keys_row_2);
break;
default:
furi_crash();
}
return row_size;
}
static const NumberInputKey* number_input_get_row(size_t row_index) {
const NumberInputKey* row = NULL;
switch(row_index + 1) {
case 1:
row = keyboard_keys_row_1;
break;
case 2:
row = keyboard_keys_row_2;
break;
default:
furi_crash();
}
return row;
}
static void number_input_draw_input(Canvas* canvas, NumberInputModel* model) {
const size_t text_x = 8;
const size_t text_y = 25;
elements_slightly_rounded_frame(canvas, 6, 14, 116, 15);
canvas_draw_str(canvas, text_x, text_y, furi_string_get_cstr(model->text_buffer));
}
static bool number_input_use_sign(NumberInputModel* model) {
//only show sign button if allowed number range needs it
if(model->min_value < 0 && model->max_value >= 0) {
return true;
}
return false;
}
static void number_input_backspace_cb(NumberInputModel* model) {
size_t text_length = furi_string_utf8_length(model->text_buffer);
if(text_length < 1 || (text_length < 2 && model->current_number <= 0)) {
return;
}
furi_string_set_strn(
model->text_buffer, furi_string_get_cstr(model->text_buffer), text_length - 1);
model->current_number = strtol(furi_string_get_cstr(model->text_buffer), NULL, 10);
}
static void number_input_handle_up(NumberInputModel* model) {
if(model->selected_row > 0) {
model->selected_row--;
if(model->selected_column > number_input_get_row_size(model->selected_row) - 1) {
model->selected_column = number_input_get_row_size(model->selected_row) - 1;
}
}
}
static void number_input_handle_down(NumberInputModel* model) {
if(model->selected_row < keyboard_row_count - 1) {
if(model->selected_column >= number_input_get_row_size(model->selected_row) - 1) {
model->selected_column = number_input_get_row_size(model->selected_row + 1) - 1;
}
model->selected_row += 1;
}
const NumberInputKey* keys = number_input_get_row(model->selected_row);
if(keys[model->selected_column].text == sign_symbol && !number_input_use_sign(model)) {
model->selected_column--;
}
}
static void number_input_handle_left(NumberInputModel* model) {
if(model->selected_column > 0) {
model->selected_column--;
} else {
model->selected_column = number_input_get_row_size(model->selected_row) - 1;
}
const NumberInputKey* keys = number_input_get_row(model->selected_row);
if(keys[model->selected_column].text == sign_symbol && !number_input_use_sign(model)) {
model->selected_column--;
}
}
static void number_input_handle_right(NumberInputModel* model) {
if(model->selected_column < number_input_get_row_size(model->selected_row) - 1) {
model->selected_column++;
} else {
model->selected_column = 0;
}
const NumberInputKey* keys = number_input_get_row(model->selected_row);
if(keys[model->selected_column].text == sign_symbol && !number_input_use_sign(model)) {
model->selected_column++;
}
}
static bool is_number_too_large(NumberInputModel* model) {
int64_t value = strtoll(furi_string_get_cstr(model->text_buffer), NULL, 10);
if(value > (int64_t)model->max_value) {
return true;
}
return false;
}
static bool is_number_too_small(NumberInputModel* model) {
int64_t value = strtoll(furi_string_get_cstr(model->text_buffer), NULL, 10);
if(value < (int64_t)model->min_value) {
return true;
}
return false;
}
static void number_input_sign(NumberInputModel* model) {
int32_t number = strtol(furi_string_get_cstr(model->text_buffer), NULL, 10);
if(number == 0 && furi_string_cmp_str(model->text_buffer, "-") != 0) {
furi_string_set_str(model->text_buffer, "-");
return;
}
number = number * -1;
furi_string_printf(model->text_buffer, "%ld", number);
if(is_number_too_large(model) || is_number_too_small(model)) {
furi_string_printf(model->text_buffer, "%ld", model->current_number);
return;
}
model->current_number = strtol(furi_string_get_cstr(model->text_buffer), NULL, 10);
if(model->current_number == 0) {
furi_string_set_str(model->text_buffer, ""); //show empty if 0, better for usability
}
}
static void number_input_add_digit(NumberInputModel* model, char* newChar) {
furi_string_cat_str(model->text_buffer, newChar);
if((model->max_value >= 0 && is_number_too_large(model)) ||
(model->min_value < 0 && is_number_too_small(model))) {
//you still need to be able to type invalid numbers in some cases to reach valid numbers on later keypress
furi_string_printf(model->text_buffer, "%ld", model->current_number);
return;
}
model->current_number = strtol(furi_string_get_cstr(model->text_buffer), NULL, 10);
if(model->current_number == 0) {
furi_string_reset(model->text_buffer);
}
}
static void number_input_handle_ok(NumberInputModel* model) {
char selected = number_input_get_row(model->selected_row)[model->selected_column].text;
char temp_str[2] = {selected, '\0'};
if(selected == enter_symbol) {
if(is_number_too_large(model) || is_number_too_small(model)) {
return; //Do nothing if number outside allowed range
}
model->current_number = strtol(furi_string_get_cstr(model->text_buffer), NULL, 10);
model->callback(model->callback_context, model->current_number);
} else if(selected == backspace_symbol) {
number_input_backspace_cb(model);
} else if(selected == sign_symbol) {
number_input_sign(model);
} else {
number_input_add_digit(model, temp_str);
}
}
static void number_input_view_draw_callback(Canvas* canvas, void* _model) {
NumberInputModel* model = _model;
number_input_draw_input(canvas, model);
if(!furi_string_empty(model->header)) {
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 2, 9, furi_string_get_cstr(model->header));
}
canvas_set_font(canvas, FontKeyboard);
// Draw keyboard
for(size_t row = 0; row < keyboard_row_count; row++) {
const size_t column_count = number_input_get_row_size(row);
const NumberInputKey* keys = number_input_get_row(row);
for(size_t column = 0; column < column_count; column++) {
if(keys[column].text == sign_symbol && !number_input_use_sign(model)) {
continue;
}
if(keys[column].text == enter_symbol) {
if(is_number_too_small(model) || is_number_too_large(model)) {
//in some cases you need to be able to type a number smaller/larger than the limits (expl. min = 50, clear all and editor must allow to type 9 and later 0 for 90)
if(model->selected_row == row && model->selected_column == column) {
canvas_draw_icon(
canvas,
keyboard_origin_x + keys[column].x,
keyboard_origin_y + keys[column].y,
&I_KeySaveBlockedSelected_24x11);
} else {
canvas_draw_icon(
canvas,
keyboard_origin_x + keys[column].x,
keyboard_origin_y + keys[column].y,
&I_KeySaveBlocked_24x11);
}
} else {
if(model->selected_row == row && model->selected_column == column) {
canvas_draw_icon(
canvas,
keyboard_origin_x + keys[column].x,
keyboard_origin_y + keys[column].y,
&I_KeySaveSelected_24x11);
} else {
canvas_draw_icon(
canvas,
keyboard_origin_x + keys[column].x,
keyboard_origin_y + keys[column].y,
&I_KeySave_24x11);
}
}
} else if(keys[column].text == backspace_symbol) {
if(model->selected_row == row && model->selected_column == column) {
canvas_draw_icon(
canvas,
keyboard_origin_x + keys[column].x,
keyboard_origin_y + keys[column].y,
&I_KeyBackspaceSelected_16x9);
} else {
canvas_draw_icon(
canvas,
keyboard_origin_x + keys[column].x,
keyboard_origin_y + keys[column].y,
&I_KeyBackspace_16x9);
}
} else if(keys[column].text == sign_symbol) {
if(model->selected_row == row && model->selected_column == column) {
canvas_draw_icon(
canvas,
keyboard_origin_x + keys[column].x,
keyboard_origin_y + keys[column].y,
&I_KeySignSelected_21x11);
} else {
canvas_draw_icon(
canvas,
keyboard_origin_x + keys[column].x,
keyboard_origin_y + keys[column].y,
&I_KeySign_21x11);
}
} else {
if(model->selected_row == row && model->selected_column == column) {
canvas_draw_box(
canvas,
keyboard_origin_x + keys[column].x - 3,
keyboard_origin_y + keys[column].y - 10,
11,
13);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_glyph(
canvas,
keyboard_origin_x + keys[column].x,
keyboard_origin_y + keys[column].y,
keys[column].text);
canvas_set_color(canvas, ColorBlack);
}
}
}
}
static bool number_input_view_input_callback(InputEvent* event, void* context) {
furi_assert(context);
NumberInput* number_input = context;
bool consumed = false;
// Fetch the model
NumberInputModel* model = view_get_model(number_input->view);
if(event->type == InputTypeShort || event->type == InputTypeLong ||
event->type == InputTypeRepeat) {
consumed = true;
switch(event->key) {
case InputKeyLeft:
number_input_handle_left(model);
break;
case InputKeyRight:
number_input_handle_right(model);
break;
case InputKeyUp:
number_input_handle_up(model);
break;
case InputKeyDown:
number_input_handle_down(model);
break;
case InputKeyOk:
number_input_handle_ok(model);
break;
default:
consumed = false;
break;
}
}
// commit view
view_commit_model(number_input->view, consumed);
return consumed;
}
NumberInput* number_input_alloc(void) {
NumberInput* number_input = malloc(sizeof(NumberInput));
number_input->view = view_alloc();
view_set_context(number_input->view, number_input);
view_allocate_model(number_input->view, ViewModelTypeLocking, sizeof(NumberInputModel));
view_set_draw_callback(number_input->view, number_input_view_draw_callback);
view_set_input_callback(number_input->view, number_input_view_input_callback);
with_view_model(
number_input->view,
NumberInputModel * model,
{
model->header = furi_string_alloc();
model->text_buffer = furi_string_alloc();
},
true);
return number_input;
}
void number_input_free(NumberInput* number_input) {
furi_check(number_input);
with_view_model(
number_input->view,
NumberInputModel * model,
{
furi_string_free(model->header);
furi_string_free(model->text_buffer);
},
true);
view_free(number_input->view);
free(number_input);
}
View* number_input_get_view(NumberInput* number_input) {
furi_check(number_input);
return number_input->view;
}
void number_input_set_result_callback(
NumberInput* number_input,
NumberInputCallback callback,
void* callback_context,
int32_t current_number,
int32_t min_value,
int32_t max_value) {
furi_check(number_input);
current_number = CLAMP(current_number, max_value, min_value);
with_view_model(
number_input->view,
NumberInputModel * model,
{
model->callback = callback;
model->callback_context = callback_context;
model->current_number = current_number;
furi_string_printf(model->text_buffer, "%ld", current_number);
model->min_value = min_value;
model->max_value = max_value;
},
true);
}
void number_input_set_header_text(NumberInput* number_input, const char* text) {
furi_check(number_input);
with_view_model(
number_input->view,
NumberInputModel * model,
{ furi_string_set(model->header, text); },
true);
}

View File

@@ -0,0 +1,69 @@
/**
* @file number_input.h
* GUI: Integer string keyboard view module API
*/
#pragma once
#include <gui/view.h>
#ifdef __cplusplus
extern "C" {
#endif
/** Number input anonymous structure */
typedef struct NumberInput NumberInput;
/** Callback to be called on save button press */
typedef void (*NumberInputCallback)(void* context, int32_t number);
/** Allocate and initialize Number input.
*
* This Number input is used to enter Numbers (Integers).
*
* @return NumberInput instance pointer
*/
NumberInput* number_input_alloc(void);
/** Deinitialize and free byte input
*
* @param number_input Number input instance
*/
void number_input_free(NumberInput* number_input);
/** Get byte input view
*
* @param number_input byte input instance
*
* @return View instance that can be used for embedding
*/
View* number_input_get_view(NumberInput* number_input);
/** Set byte input result callback
*
* @param number_input byte input instance
* @param input_callback input callback fn
* @param callback_context callback context
* @param[in] current_number The current number
* @param min_value Min number value
* @param max_value Max number value
*/
void number_input_set_result_callback(
NumberInput* number_input,
NumberInputCallback input_callback,
void* callback_context,
int32_t current_number,
int32_t min_value,
int32_t max_value);
/** Set byte input header text
*
* @param number_input byte input instance
* @param text text to be shown
*/
void number_input_set_header_text(NumberInput* number_input, const char* text);
#ifdef __cplusplus
}
#endif

View File

@@ -2,6 +2,8 @@
#define TAG "ViewDispatcher"
#define VIEW_DISPATCHER_QUEUE_LEN (16U)
ViewDispatcher* view_dispatcher_alloc(void) {
ViewDispatcher* view_dispatcher = malloc(sizeof(ViewDispatcher));
@@ -16,6 +18,35 @@ ViewDispatcher* view_dispatcher_alloc(void) {
ViewDict_init(view_dispatcher->views);
view_dispatcher->event_loop = furi_event_loop_alloc();
view_dispatcher->input_queue =
furi_message_queue_alloc(VIEW_DISPATCHER_QUEUE_LEN, sizeof(InputEvent));
furi_event_loop_subscribe_message_queue(
view_dispatcher->event_loop,
view_dispatcher->input_queue,
FuriEventLoopEventIn,
view_dispatcher_run_input_callback,
view_dispatcher);
view_dispatcher->ascii_queue =
furi_message_queue_alloc(VIEW_DISPATCHER_QUEUE_LEN, sizeof(AsciiEvent));
furi_event_loop_subscribe_message_queue(
view_dispatcher->event_loop,
view_dispatcher->ascii_queue,
FuriEventLoopEventIn,
view_dispatcher_run_ascii_callback,
view_dispatcher);
view_dispatcher->event_queue =
furi_message_queue_alloc(VIEW_DISPATCHER_QUEUE_LEN, sizeof(uint32_t));
furi_event_loop_subscribe_message_queue(
view_dispatcher->event_loop,
view_dispatcher->event_queue,
FuriEventLoopEventIn,
view_dispatcher_run_event_callback,
view_dispatcher);
return view_dispatcher;
}
@@ -31,57 +62,21 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher) {
// Free ViewPort
view_port_free(view_dispatcher->view_port);
// Free internal queue
if(view_dispatcher->input_queue) {
furi_event_loop_message_queue_unsubscribe(
view_dispatcher->event_loop, view_dispatcher->input_queue);
furi_message_queue_free(view_dispatcher->input_queue);
}
if(view_dispatcher->ascii_queue) {
furi_event_loop_message_queue_unsubscribe(
view_dispatcher->event_loop, view_dispatcher->ascii_queue);
furi_message_queue_free(view_dispatcher->ascii_queue);
}
if(view_dispatcher->event_queue) {
furi_event_loop_message_queue_unsubscribe(
view_dispatcher->event_loop, view_dispatcher->event_queue);
furi_message_queue_free(view_dispatcher->event_queue);
}
if(view_dispatcher->event_loop) {
furi_event_loop_free(view_dispatcher->event_loop);
}
furi_event_loop_unsubscribe(view_dispatcher->event_loop, view_dispatcher->input_queue);
furi_event_loop_unsubscribe(view_dispatcher->event_loop, view_dispatcher->ascii_queue);
furi_event_loop_unsubscribe(view_dispatcher->event_loop, view_dispatcher->event_queue);
furi_message_queue_free(view_dispatcher->input_queue);
furi_message_queue_free(view_dispatcher->ascii_queue);
furi_message_queue_free(view_dispatcher->event_queue);
furi_event_loop_free(view_dispatcher->event_loop);
// Free dispatcher
free(view_dispatcher);
}
void view_dispatcher_enable_queue(ViewDispatcher* view_dispatcher) {
furi_check(view_dispatcher);
furi_check(view_dispatcher->event_loop == NULL);
view_dispatcher->event_loop = furi_event_loop_alloc();
view_dispatcher->input_queue = furi_message_queue_alloc(16, sizeof(InputEvent));
furi_event_loop_message_queue_subscribe(
view_dispatcher->event_loop,
view_dispatcher->input_queue,
FuriEventLoopEventIn,
view_dispatcher_run_input_callback,
view_dispatcher);
view_dispatcher->ascii_queue = furi_message_queue_alloc(16, sizeof(AsciiEvent));
furi_event_loop_message_queue_subscribe(
view_dispatcher->event_loop,
view_dispatcher->ascii_queue,
FuriEventLoopEventIn,
view_dispatcher_run_ascii_callback,
view_dispatcher);
view_dispatcher->event_queue = furi_message_queue_alloc(16, sizeof(uint32_t));
furi_event_loop_message_queue_subscribe(
view_dispatcher->event_loop,
view_dispatcher->event_queue,
FuriEventLoopEventIn,
view_dispatcher_run_event_callback,
view_dispatcher);
UNUSED(view_dispatcher);
}
void view_dispatcher_set_navigation_event_callback(
@@ -114,14 +109,12 @@ void view_dispatcher_set_event_callback_context(ViewDispatcher* view_dispatcher,
FuriEventLoop* view_dispatcher_get_event_loop(ViewDispatcher* view_dispatcher) {
furi_check(view_dispatcher);
furi_check(view_dispatcher->event_loop);
return view_dispatcher->event_loop;
}
void view_dispatcher_run(ViewDispatcher* view_dispatcher) {
furi_check(view_dispatcher);
furi_check(view_dispatcher->event_loop);
uint32_t tick_period = view_dispatcher->tick_period == 0 ? FuriWaitForever :
view_dispatcher->tick_period;
@@ -149,7 +142,6 @@ void view_dispatcher_run(ViewDispatcher* view_dispatcher) {
void view_dispatcher_stop(ViewDispatcher* view_dispatcher) {
furi_check(view_dispatcher);
furi_check(view_dispatcher->event_loop);
furi_event_loop_stop(view_dispatcher->event_loop);
}
@@ -257,13 +249,9 @@ void view_dispatcher_draw_callback(Canvas* canvas, void* context) {
void view_dispatcher_input_callback(InputEvent* event, void* context) {
ViewDispatcher* view_dispatcher = context;
if(view_dispatcher->input_queue) {
furi_check(
furi_message_queue_put(view_dispatcher->input_queue, event, FuriWaitForever) ==
FuriStatusOk);
} else {
view_dispatcher_handle_input(view_dispatcher, event);
}
furi_check(
furi_message_queue_put(view_dispatcher->input_queue, event, FuriWaitForever) ==
FuriStatusOk);
}
bool view_dispatcher_ascii_callback(AsciiEvent* event, void* context) {
@@ -271,13 +259,9 @@ bool view_dispatcher_ascii_callback(AsciiEvent* event, void* context) {
// So instead ViewDispatcher tells ViewPort that all events are consumed
// Then ViewDispatcher handles fallbacks the same way as ViewPort would have done
ViewDispatcher* view_dispatcher = context;
if(view_dispatcher->ascii_queue) {
furi_check(
furi_message_queue_put(view_dispatcher->ascii_queue, event, FuriWaitForever) ==
FuriStatusOk);
} else {
view_dispatcher_handle_ascii(view_dispatcher, event);
}
furi_check(
furi_message_queue_put(view_dispatcher->ascii_queue, event, FuriWaitForever) ==
FuriStatusOk);
return true;
}
@@ -400,7 +384,6 @@ void view_dispatcher_handle_custom_event(ViewDispatcher* view_dispatcher, uint32
void view_dispatcher_send_custom_event(ViewDispatcher* view_dispatcher, uint32_t event) {
furi_check(view_dispatcher);
furi_check(view_dispatcher->event_loop);
furi_check(
furi_message_queue_put(view_dispatcher->event_queue, &event, FuriWaitForever) ==
@@ -436,9 +419,7 @@ void view_dispatcher_set_current_view(ViewDispatcher* view_dispatcher, View* vie
view_port_update(view_dispatcher->view_port);
} else {
view_port_enabled_set(view_dispatcher->view_port, false);
if(view_dispatcher->event_loop) {
view_dispatcher_stop(view_dispatcher);
}
view_dispatcher_stop(view_dispatcher);
}
}
@@ -453,10 +434,10 @@ void view_dispatcher_update(View* view, void* context) {
}
}
bool view_dispatcher_run_event_callback(FuriMessageQueue* queue, void* context) {
bool view_dispatcher_run_event_callback(FuriEventLoopObject* object, void* context) {
furi_assert(context);
ViewDispatcher* instance = context;
furi_assert(instance->event_queue == queue);
furi_assert(instance->event_queue == object);
uint32_t event;
furi_check(furi_message_queue_get(instance->event_queue, &event, 0) == FuriStatusOk);
@@ -465,10 +446,10 @@ bool view_dispatcher_run_event_callback(FuriMessageQueue* queue, void* context)
return true;
}
bool view_dispatcher_run_input_callback(FuriMessageQueue* queue, void* context) {
bool view_dispatcher_run_input_callback(FuriEventLoopObject* object, void* context) {
furi_assert(context);
ViewDispatcher* instance = context;
furi_assert(instance->input_queue == queue);
furi_assert(instance->input_queue == object);
InputEvent input;
furi_check(furi_message_queue_get(instance->input_queue, &input, 0) == FuriStatusOk);
@@ -477,10 +458,10 @@ bool view_dispatcher_run_input_callback(FuriMessageQueue* queue, void* context)
return true;
}
bool view_dispatcher_run_ascii_callback(FuriMessageQueue* queue, void* context) {
bool view_dispatcher_run_ascii_callback(FuriEventLoopObject* object, void* context) {
furi_assert(context);
ViewDispatcher* instance = context;
furi_assert(instance->ascii_queue == queue);
furi_assert(instance->ascii_queue == object);
AsciiEvent ascii;
furi_check(furi_message_queue_get(instance->ascii_queue, &ascii, 0) == FuriStatusOk);

View File

@@ -2,6 +2,14 @@
* @file view_dispatcher.h
* @brief GUI: ViewDispatcher API
*
* ViewDispatcher is used to connect several Views to a Gui instance, switch between them and handle various events.
* This is useful in applications featuring an advanced graphical user interface.
*
* Internally, ViewDispatcher employs a FuriEventLoop instance together with two separate
* message queues for input and custom event handling. See FuriEventLoop for more information.
*
* If no multi-view or complex event handling capabilities are required, consider using ViewHolder instead.
*
* @warning Views added to a ViewDispatcher MUST NOT be in a ViewStack at the same time.
*/
@@ -40,6 +48,9 @@ typedef void (*ViewDispatcherTickEventCallback)(void* context);
ViewDispatcher* view_dispatcher_alloc(void);
/** Free ViewDispatcher instance
*
* @warning All added views MUST be removed using view_dispatcher_remove_view()
* before calling this function.
*
* @param view_dispatcher pointer to ViewDispatcher
*/
@@ -47,12 +58,13 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher);
/** Enable queue support
*
* Allocates event_loop, input and event message queues. Must be used with
* `view_dispatcher_run`
* @deprecated Do NOT use in new code and remove all calls to it from existing code.
* The queue support is now always enabled during construction. If no queue support
* is required, consider using ViewHolder instead.
*
* @param view_dispatcher ViewDispatcher instance
*/
void view_dispatcher_enable_queue(ViewDispatcher* view_dispatcher);
FURI_DEPRECATED void view_dispatcher_enable_queue(ViewDispatcher* view_dispatcher);
/** Send custom event
*
@@ -103,11 +115,11 @@ void view_dispatcher_set_event_callback_context(ViewDispatcher* view_dispatcher,
/** Get event_loop instance
*
* event_loop instance is allocated on `view_dispatcher_enable_queue` and used
* in view_dispatcher_run.
* Use the return value to connect additional supported primitives (message queues, timers, etc)
* to this ViewDispatcher instance's event loop.
*
* You can add your objects into event_loop instance, but don't run the loop on
* your side as it will cause issues with input processing on dispatcher stop.
* @warning Do NOT call furi_event_loop_run() on the returned instance, it is done internally
* in the view_dispatcher_run() call.
*
* @param view_dispatcher ViewDispatcher instance
*
@@ -117,15 +129,14 @@ FuriEventLoop* view_dispatcher_get_event_loop(ViewDispatcher* view_dispatcher);
/** Run ViewDispatcher
*
* Use only after queue enabled
* This function will start the event loop and block until view_dispatcher_stop() is called
* or the current thread receives a FuriSignalExit signal.
*
* @param view_dispatcher ViewDispatcher instance
*/
void view_dispatcher_run(ViewDispatcher* view_dispatcher);
/** Stop ViewDispatcher
*
* Use only after queue enabled
*
* @param view_dispatcher ViewDispatcher instance
*/

View File

@@ -64,10 +64,10 @@ void view_dispatcher_set_current_view(ViewDispatcher* view_dispatcher, View* vie
void view_dispatcher_update(View* view, void* context);
/** ViewDispatcher run event loop event callback */
bool view_dispatcher_run_event_callback(FuriMessageQueue* queue, void* context);
bool view_dispatcher_run_event_callback(FuriEventLoopObject* object, void* context);
/** ViewDispatcher run event loop input callback */
bool view_dispatcher_run_input_callback(FuriMessageQueue* queue, void* context);
bool view_dispatcher_run_input_callback(FuriEventLoopObject* object, void* context);
/** ViewDispatcher run event loop ascii callback */
bool view_dispatcher_run_ascii_callback(FuriMessageQueue* queue, void* context);
bool view_dispatcher_run_ascii_callback(FuriEventLoopObject* object, void* context);

View File

@@ -34,7 +34,8 @@ ViewHolder* view_holder_alloc(void) {
}
void view_holder_free(ViewHolder* view_holder) {
furi_assert(view_holder);
furi_check(view_holder);
furi_check(view_holder->view == NULL);
if(view_holder->gui) {
gui_remove_view_port(view_holder->gui, view_holder->view_port);
@@ -50,12 +51,14 @@ void view_holder_free(ViewHolder* view_holder) {
}
void view_holder_set_view(ViewHolder* view_holder, View* view) {
furi_assert(view_holder);
furi_check(view_holder);
if(view_holder->view) {
if(view_holder->view->exit_callback) {
view_holder->view->exit_callback(view_holder->view->context);
while(view_holder->ongoing_input) {
furi_delay_tick(1);
}
view_exit(view_holder->view);
view_set_update_callback(view_holder->view, NULL);
view_set_update_callback_context(view_holder->view, NULL);
}
@@ -63,12 +66,23 @@ void view_holder_set_view(ViewHolder* view_holder, View* view) {
view_holder->view = view;
if(view_holder->view) {
const ViewPortOrientation orientation = (ViewPortOrientation)view->orientation;
furi_assert(orientation < ViewPortOrientationMAX);
if(view_port_get_orientation(view_holder->view_port) != orientation) {
view_port_set_orientation(view_holder->view_port, orientation);
// we just rotated input keys, now it's time to sacrifice some input
view_holder->ongoing_input = 0;
}
view_set_update_callback(view_holder->view, view_holder_update);
view_set_update_callback_context(view_holder->view, view_holder);
if(view_holder->view->enter_callback) {
view_holder->view->enter_callback(view_holder->view->context);
}
view_enter(view_holder->view);
view_port_enabled_set(view_holder->view_port, true);
view_port_update(view_holder->view_port);
} else {
view_port_enabled_set(view_holder->view_port, false);
}
}
@@ -76,7 +90,7 @@ void view_holder_set_free_callback(
ViewHolder* view_holder,
FreeCallback free_callback,
void* free_context) {
furi_assert(view_holder);
furi_check(view_holder);
view_holder->free_callback = free_callback;
view_holder->free_context = free_context;
}
@@ -89,31 +103,22 @@ void view_holder_set_back_callback(
ViewHolder* view_holder,
BackCallback back_callback,
void* back_context) {
furi_assert(view_holder);
furi_check(view_holder);
view_holder->back_callback = back_callback;
view_holder->back_context = back_context;
}
void view_holder_attach_to_gui(ViewHolder* view_holder, Gui* gui) {
furi_assert(gui);
furi_assert(view_holder);
view_holder->gui = gui;
furi_check(view_holder);
furi_check(view_holder->gui == NULL);
furi_check(gui);
gui_add_view_port(gui, view_holder->view_port, GuiLayerFullscreen);
}
void view_holder_start(ViewHolder* view_holder) {
view_port_enabled_set(view_holder->view_port, true);
}
void view_holder_stop(ViewHolder* view_holder) {
while(view_holder->ongoing_input)
furi_delay_tick(1);
view_port_enabled_set(view_holder->view_port, false);
view_holder->gui = gui;
}
void view_holder_update(View* view, void* context) {
furi_assert(view);
furi_assert(context);
furi_check(view);
furi_check(context);
ViewHolder* view_holder = context;
if(view == view_holder->view) {
@@ -121,6 +126,18 @@ void view_holder_update(View* view, void* context) {
}
}
void view_holder_send_to_front(ViewHolder* view_holder) {
furi_check(view_holder);
furi_check(view_holder->gui);
gui_view_port_send_to_front(view_holder->gui, view_holder->view_port);
}
void view_holder_send_to_back(ViewHolder* view_holder) {
furi_check(view_holder);
furi_check(view_holder->gui);
gui_view_port_send_to_back(view_holder->gui, view_holder->view_port);
}
static void view_holder_draw_callback(Canvas* canvas, void* context) {
ViewHolder* view_holder = context;
if(view_holder->view) {

View File

@@ -2,7 +2,10 @@
* @file view_holder.h
* @brief GUI: ViewHolder API
*
* @warning View added to a ViewHolder MUST NOT be in a ViewStack at the same time.
* ViewHolder is used to connect a single View to a Gui instance. This is useful in smaller applications
* with a simple user interface. If advanced view switching capabilites are required, consider using ViewDispatcher instead.
*
* @warning Views added to a ViewHolder MUST NOT be in a ViewStack at the same time.
*/
#pragma once
@@ -22,7 +25,8 @@ typedef void (*FreeCallback)(void* free_context);
/**
* @brief Back callback type
* @warning comes from GUI thread
*
* @warning Will be called from the GUI thread
*/
typedef void (*BackCallback)(void* back_context);
@@ -34,12 +38,17 @@ ViewHolder* view_holder_alloc(void);
/**
* @brief Free ViewHolder and call Free callback
*
* @warning The current view must be unset prior to freeing a ViewHolder instance.
*
* @param view_holder pointer to ViewHolder
*/
void view_holder_free(ViewHolder* view_holder);
/**
* @brief Set view for ViewHolder
*
* Pass NULL as the view parameter to unset the current view.
*
* @param view_holder ViewHolder instance
* @param view View instance
@@ -59,13 +68,25 @@ void view_holder_set_free_callback(
void* free_context);
/**
* @brief Free callback context getter. Useful if your Free callback is a module destructor, so you can get an instance of the module using this method.
* @brief Free callback context getter.
*
* Useful if your Free callback is a module destructor, so you can get an instance of the module using this method.
*
* @param view_holder ViewHolder instance
* @return void* free callback context
*/
void* view_holder_get_free_context(ViewHolder* view_holder);
/**
* @brief Set the back key callback.
*
* The callback function will be called if the user has pressed the Back key
* and the current view did not handle this event.
*
* @param view_holder ViewHolder instance
* @param back_callback pointer to the callback function
* @param back_context pointer to a user-specific object, can be NULL
*/
void view_holder_set_back_callback(
ViewHolder* view_holder,
BackCallback back_callback,
@@ -80,26 +101,27 @@ void view_holder_set_back_callback(
void view_holder_attach_to_gui(ViewHolder* view_holder, Gui* gui);
/**
* @brief Enable view processing
*
* @param view_holder
*/
void view_holder_start(ViewHolder* view_holder);
/**
* @brief Disable view processing
*
* @param view_holder
*/
void view_holder_stop(ViewHolder* view_holder);
/** View Update Handler
* @brief View Update Handler
*
* @param view View Instance
* @param context ViewHolder instance
* @param view View Instance
* @param context ViewHolder instance
*/
void view_holder_update(View* view, void* context);
/**
* @brief Send ViewPort of this ViewHolder instance to front
*
* @param view_holder ViewHolder instance
*/
void view_holder_send_to_front(ViewHolder* view_holder);
/**
* @brief Send ViewPort of this ViewHolder instance to back
*
* @param view_holder ViewHolder instance
*/
void view_holder_send_to_back(ViewHolder* view_holder);
#ifdef __cplusplus
}
#endif

View File

@@ -61,7 +61,6 @@ static LoaderApplicationsApp* loader_applications_app_alloc(void) {
app->loading = loading_alloc();
view_holder_attach_to_gui(app->view_holder, app->gui);
view_holder_set_view(app->view_holder, loading_get_view(app->loading));
return app;
} //-V773
@@ -152,7 +151,7 @@ static int32_t loader_applications_thread(void* p) {
LoaderApplicationsApp* app = loader_applications_app_alloc();
// start loading animation
view_holder_start(app->view_holder);
view_holder_set_view(app->view_holder, loading_get_view(app->loading));
while(loader_applications_select_app(app)) {
if(!furi_string_end_with(app->file_path, ".js")) {
@@ -164,7 +163,7 @@ static int32_t loader_applications_thread(void* p) {
}
// stop loading animation
view_holder_stop(app->view_holder);
view_holder_set_view(app->view_holder, NULL);
loader_applications_app_free(app);

View File

@@ -102,7 +102,6 @@ LoaderMenu* loader_menu_alloc(void (*closed_cb)(void*), void* context, bool sett
view_holder_attach_to_gui(loader_menu->view_holder, gui);
view_holder_set_back_callback(loader_menu->view_holder, NULL, NULL);
loader_menu_set_view(loader_menu, loader_menu->dummy);
view_holder_start(loader_menu->view_holder);
loader_menu->loader = furi_record_open(RECORD_LOADER);
loader_menu->subscription = furi_pubsub_subscribe(

View File

@@ -17,13 +17,15 @@ void power_cli_off(Cli* cli, FuriString* args) {
void power_cli_reboot(Cli* cli, FuriString* args) {
UNUSED(cli);
UNUSED(args);
power_reboot(PowerBootModeNormal);
Power* power = furi_record_open(RECORD_POWER);
power_reboot(power, PowerBootModeNormal);
}
void power_cli_reboot2dfu(Cli* cli, FuriString* args) {
UNUSED(cli);
UNUSED(args);
power_reboot(PowerBootModeDfu);
Power* power = furi_record_open(RECORD_POWER);
power_reboot(power, PowerBootModeDfu);
}
void power_cli_5v(Cli* cli, FuriString* args) {

View File

@@ -1,9 +1,10 @@
#pragma once
#include <stdint.h>
#include <core/pubsub.h>
#include <stdbool.h>
#include <core/pubsub.h>
#ifdef __cplusplus
extern "C" {
#endif
@@ -67,7 +68,7 @@ void power_off(Power* power);
*
* @param mode PowerBootMode
*/
void power_reboot(PowerBootMode mode);
void power_reboot(Power* power, PowerBootMode mode);
/** Get power info
*

View File

@@ -2,21 +2,17 @@
#include "power.h"
#include <stdint.h>
#include <gui/view_dispatcher.h>
#include <gui/gui.h>
#include <gui/view_holder.h>
#include <toolbox/api_lock.h>
#include <assets_icons.h>
#include <loader/loader.h>
#include <gui/modules/popup.h>
#include "views/power_off.h"
#include <power/power_settings.h>
#include "views/power_unplug_usb.h"
#include <notification/notification_messages.h>
#define POWER_BATTERY_HEALTHY_LEVEL 70
typedef enum {
PowerStateNotCharging,
PowerStateCharging,
@@ -24,26 +20,24 @@ typedef enum {
} PowerState;
struct Power {
ViewDispatcher* view_dispatcher;
PowerOff* power_off;
PowerUnplugUsb* power_unplug_usb;
ViewHolder* view_holder;
FuriPubSub* event_pubsub;
FuriEventLoop* event_loop;
FuriMessageQueue* message_queue;
ViewPort* battery_view_port;
Gui* gui;
NotificationApp* notification;
FuriPubSub* event_pubsub;
PowerEvent event;
PowerOff* view_power_off;
PowerUnplugUsb* view_power_unplug_usb;
PowerEvent event;
PowerState state;
PowerInfo info;
bool battery_low;
bool show_low_bat_level_message;
bool show_battery_low_warning;
uint8_t battery_level;
uint8_t power_off_timeout;
FuriMutex* api_mtx;
FuriPubSub* settings_events;
FuriPubSub* input_events_pubsub;
FuriPubSubSubscription* input_events_subscription;
@@ -60,3 +54,21 @@ typedef enum {
PowerViewOff,
PowerViewUnplugUsb,
} PowerView;
typedef enum {
PowerMessageTypeShutdown,
PowerMessageTypeReboot,
PowerMessageTypeGetInfo,
PowerMessageTypeIsBatteryHealthy,
PowerMessageTypeShowBatteryLowWarning,
} PowerMessageType;
typedef struct {
PowerMessageType type;
union {
PowerBootMode boot_mode;
PowerInfo* power_info;
bool* bool_param;
};
FuriApiLock lock;
} PowerMessage;

View File

@@ -54,18 +54,21 @@ static void rpc_system_system_reboot_process(const PB_Main* request, void* conte
RpcSession* session = (RpcSession*)context;
furi_assert(session);
Power* power = furi_record_open(RECORD_POWER);
const int mode = request->content.system_reboot_request.mode;
if(mode == PB_System_RebootRequest_RebootMode_OS) {
power_reboot(PowerBootModeNormal);
power_reboot(power, PowerBootModeNormal);
} else if(mode == PB_System_RebootRequest_RebootMode_DFU) {
power_reboot(PowerBootModeDfu);
power_reboot(power, PowerBootModeDfu);
} else if(mode == PB_System_RebootRequest_RebootMode_UPDATE) {
power_reboot(PowerBootModeUpdateStart);
power_reboot(power, PowerBootModeUpdateStart);
} else {
rpc_send_and_release_empty(
session, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS);
}
furi_record_close(RECORD_POWER);
}
static void rpc_system_system_device_info_callback(
@@ -181,9 +184,9 @@ static void rpc_system_system_factory_reset_process(const PB_Main* request, void
furi_hal_rtc_reset_registers();
furi_hal_rtc_set_flag(FuriHalRtcFlagStorageFormatInternal);
power_reboot(PowerBootModeNormal);
(void)session;
Power* power = furi_record_open(RECORD_POWER);
power_reboot(power, PowerBootModeNormal);
}
static void

View File

@@ -126,11 +126,6 @@ int32_t storage_srv(void* p) {
Storage* app = storage_app_alloc();
furi_record_create(RECORD_STORAGE, app);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagStorageFormatInternal)) {
FURI_LOG_W(TAG, "Format Internal not supported, clearing flag");
furi_hal_rtc_reset_flag(FuriHalRtcFlagStorageFormatInternal);
}
StorageMessage message;
while(1) {
if(furi_message_queue_get(app->message_queue, &message, STORAGE_TICK) == FuriStatusOk) {

View File

@@ -701,9 +701,12 @@ static void storage_cli_factory_reset(Cli* cli, FuriString* args, void* context)
char c = cli_getc(cli);
if(c == 'y' || c == 'Y') {
printf("Data will be wiped after reboot.\r\n");
furi_hal_rtc_reset_registers();
furi_hal_rtc_set_flag(FuriHalRtcFlagStorageFormatInternal);
power_reboot(PowerBootModeNormal);
Power* power = furi_record_open(RECORD_POWER);
power_reboot(power, PowerBootModeNormal);
} else {
printf("Safe choice.\r\n");
}

View File

@@ -0,0 +1,3 @@
#pragma once
#define STORAGE_INTERNAL_DIR_NAME ".int"

View File

@@ -1,7 +1,9 @@
#include "storage_processing.h"
#include <m-list.h>
#include <m-dict.h>
#include "storage_processing.h"
#include "storage_internal_dirname_i.h"
#define TAG "Storage"
#define STORAGE_PATH_PREFIX_LEN 4u
@@ -604,9 +606,9 @@ void storage_process_alias(
} else if(furi_string_start_with(path, STORAGE_INT_PATH_PREFIX)) {
furi_string_replace_at(
path, 0, strlen(STORAGE_INT_PATH_PREFIX), STORAGE_EXT_PATH_PREFIX "/.int");
path, 0, strlen(STORAGE_INT_PATH_PREFIX), EXT_PATH(STORAGE_INTERNAL_DIR_NAME));
FuriString* int_on_ext_path = furi_string_alloc_set(STORAGE_EXT_PATH_PREFIX "/.int");
FuriString* int_on_ext_path = furi_string_alloc_set(EXT_PATH(STORAGE_INTERNAL_DIR_NAME));
if(storage_process_common_stat(app, int_on_ext_path, NULL) != FSE_OK) {
storage_process_common_mkdir(app, int_on_ext_path);
}

View File

@@ -1,11 +1,14 @@
#include "fatfs.h"
#include "../filesystem_api_internal.h"
#include "storage_ext.h"
#include <fatfs.h>
#include <furi_hal.h>
#include "sd_notify.h"
#include <furi_hal_sd.h>
#include <toolbox/path.h>
#include "sd_notify.h"
#include "storage_ext.h"
#include "../filesystem_api_internal.h"
#include "../storage_internal_dirname_i.h"
typedef FIL SDFile;
typedef DIR SDDir;
typedef FILINFO SDFileInfo;
@@ -94,6 +97,64 @@ static bool sd_mount_card_internal(StorageData* storage, bool notify) {
return result;
}
static bool sd_remove_recursive(const char* path) {
SDDir* current_dir = malloc(sizeof(DIR));
SDFileInfo* file_info = malloc(sizeof(FILINFO));
FuriString* current_path = furi_string_alloc_set(path);
bool go_deeper = false;
SDError status;
while(true) {
status = f_opendir(current_dir, furi_string_get_cstr(current_path));
if(status != FR_OK) break;
while(true) {
status = f_readdir(current_dir, file_info);
if(status != FR_OK || !strlen(file_info->fname)) break;
if(file_info->fattrib & AM_DIR) {
furi_string_cat_printf(current_path, "/%s", file_info->fname);
go_deeper = true;
break;
} else {
FuriString* file_path = furi_string_alloc_printf(
"%s/%s", furi_string_get_cstr(current_path), file_info->fname);
status = f_unlink(furi_string_get_cstr(file_path));
furi_string_free(file_path);
if(status != FR_OK) break;
}
}
status = f_closedir(current_dir);
if(status != FR_OK) break;
if(go_deeper) {
go_deeper = false;
continue;
}
status = f_unlink(furi_string_get_cstr(current_path));
if(status != FR_OK) break;
if(!furi_string_equal(current_path, path)) {
size_t last_char_pos = furi_string_search_rchar(current_path, '/');
furi_assert(last_char_pos != FURI_STRING_FAILURE);
furi_string_left(current_path, last_char_pos);
} else {
break;
}
}
free(current_dir);
free(file_info);
furi_string_free(current_path);
return status == FR_OK;
}
FS_Error sd_unmount_card(StorageData* storage) {
SDData* sd_data = storage->data;
SDError error;
@@ -113,21 +174,32 @@ FS_Error sd_mount_card(StorageData* storage, bool notify) {
if(storage->status != StorageStatusOK) {
FURI_LOG_E(TAG, "sd init error: %s", storage_data_status_text(storage));
if(notify) {
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
sd_notify_error(notification);
furi_record_close(RECORD_NOTIFICATION);
}
error = FSE_INTERNAL;
} else {
FURI_LOG_I(TAG, "card mounted");
if(notify) {
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
sd_notify_success(notification);
furi_record_close(RECORD_NOTIFICATION);
}
#ifndef FURI_RAM_EXEC
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagStorageFormatInternal)) {
FURI_LOG_I(TAG, "deleting internal storage directory");
error = sd_remove_recursive(STORAGE_INTERNAL_DIR_NAME) ? FSE_OK : FSE_INTERNAL;
} else {
error = FSE_OK;
}
#else
UNUSED(sd_remove_recursive);
error = FSE_OK;
#endif
}
if(notify) {
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
if(error != FSE_OK) {
sd_notify_error(notification);
} else {
sd_notify_success(notification);
}
furi_record_close(RECORD_NOTIFICATION);
}
return error;
@@ -714,6 +786,10 @@ void storage_ext_init(StorageData* storage) {
// do not notify on first launch, notifications app is waiting for our thread to read settings
storage_ext_tick_internal(storage, false);
#ifndef FURI_RAM_EXEC
// always reset the flag to prevent accidental wipe on SD card insertion
furi_hal_rtc_reset_flag(FuriHalRtcFlagStorageFormatInternal);
#endif
}
#include "fatfs/ff_gen_drv.h"