Merge remote-tracking branch 'upstream/dev' into feat/subghz-save-hopping-state

# Conflicts:
#	applications/main/subghz/scenes/subghz_scene_receiver.c
This commit is contained in:
DerSkythe
2023-09-09 22:41:08 +04:00
181 changed files with 1557 additions and 4123 deletions

View File

@@ -101,7 +101,6 @@ static void desktop_clock_draw_callback(Canvas* canvas, void* context) {
char buffer[20];
snprintf(buffer, sizeof(buffer), "%02u:%02u", hour, desktop->time_minute);
// TODO FL-3515: never do that, may cause visual glitches
view_port_set_width(
desktop->clock_viewport,
canvas_string_width(canvas, buffer) - 1 + (desktop->time_minute % 10 == 1));
@@ -126,8 +125,6 @@ static bool desktop_custom_event_callback(void* context, uint32_t event) {
return true;
case DesktopGlobalAfterAppFinished:
animation_manager_load_and_continue_animation(desktop->animation_manager);
// TODO FL-3497: Implement a message mechanism for loading settings and (optionally)
// locking and unlocking
DESKTOP_SETTINGS_LOAD(&desktop->settings);
desktop_clock_reconfigure(desktop);

View File

@@ -4,7 +4,6 @@
#include <furi.h>
#include <furi_hal.h>
#include <furi_hal_rtc.h>
#include <stdint.h>
#include <u8g2_glue.h>

View File

@@ -290,7 +290,8 @@ void elements_multiline_text_aligned(
} else if((y + font_height) > canvas_height(canvas)) {
line = furi_string_alloc_printf("%.*s...\n", chars_fit, start);
} else {
line = furi_string_alloc_printf("%.*s-\n", chars_fit, start);
// Account for the added "-" in length
line = furi_string_alloc_printf("%.*s-\n", chars_fit - 1, start);
}
canvas_draw_str_aligned(canvas, x, y, horizontal, vertical, furi_string_get_cstr(line));
furi_string_free(line);

View File

@@ -361,10 +361,11 @@ void gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer) {
furi_assert(view_port);
furi_check(layer < GuiLayerMAX);
// Only fullscreen supports Vertical orientation for now
furi_assert(
ViewPortOrientation view_port_orientation = view_port_get_orientation(view_port);
furi_check(
(layer == GuiLayerFullscreen) ||
((view_port->orientation != ViewPortOrientationVertical) &&
(view_port->orientation != ViewPortOrientationVerticalFlip)));
((view_port_orientation != ViewPortOrientationVertical) &&
(view_port_orientation != ViewPortOrientationVerticalFlip)));
gui_lock(gui);
// Verify that view port is not yet added

View File

@@ -325,6 +325,7 @@ static void text_input_handle_down(TextInput* text_input, TextInputModel* model)
static void text_input_handle_left(TextInput* text_input, TextInputModel* model) {
UNUSED(text_input);
if(model->cursor_select) {
model->clear_default_text = false;
if(model->cursor_pos > 0) {
model->cursor_pos = CLAMP(model->cursor_pos - 1, strlen(model->text_buffer), 0u);
}
@@ -338,6 +339,7 @@ static void text_input_handle_left(TextInput* text_input, TextInputModel* model)
static void text_input_handle_right(TextInput* text_input, TextInputModel* model) {
UNUSED(text_input);
if(model->cursor_select) {
model->clear_default_text = false;
model->cursor_pos = CLAMP(model->cursor_pos + 1, strlen(model->text_buffer), 0u);
} else if(model->selected_column < get_row_size(model->selected_row) - 1) {
model->selected_column++;
@@ -347,7 +349,10 @@ static void text_input_handle_right(TextInput* text_input, TextInputModel* model
}
static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, InputType type) {
if(model->cursor_select) return;
if(model->cursor_select) {
model->clear_default_text = !model->clear_default_text;
return;
}
bool shift = type == InputTypeLong;
bool repeat = type == InputTypeRepeat;
char selected = get_selected_char(model);

View File

@@ -272,7 +272,6 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e
} else if(view_dispatcher->navigation_event_callback) {
// Dispatch navigation event
if(!view_dispatcher->navigation_event_callback(view_dispatcher->event_context)) {
// TODO FL-3514: should we allow view_dispatcher to stop without navigation_event_callback?
view_dispatcher_stop(view_dispatcher);
return;
}

View File

@@ -2,13 +2,10 @@
#include <furi.h>
#include <furi_hal.h>
#include <furi_hal_rtc.h>
#include "gui.h"
#include "gui_i.h"
// TODO FL-3498: add mutex to view_port ops
_Static_assert(ViewPortOrientationMAX == 4, "Incorrect ViewPortOrientation count");
_Static_assert(
(ViewPortOrientationHorizontal == 0 && ViewPortOrientationHorizontalFlip == 1 &&
@@ -95,52 +92,73 @@ ViewPort* view_port_alloc() {
ViewPort* view_port = malloc(sizeof(ViewPort));
view_port->orientation = ViewPortOrientationHorizontal;
view_port->is_enabled = true;
view_port->mutex = furi_mutex_alloc(FuriMutexTypeRecursive);
return view_port;
}
void view_port_free(ViewPort* view_port) {
furi_assert(view_port);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
furi_check(view_port->gui == NULL);
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
furi_mutex_free(view_port->mutex);
free(view_port);
}
void view_port_set_width(ViewPort* view_port, uint8_t width) {
furi_assert(view_port);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
view_port->width = width;
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
}
uint8_t view_port_get_width(const ViewPort* view_port) {
furi_assert(view_port);
return view_port->width;
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
uint8_t width = view_port->width;
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
return width;
}
void view_port_set_height(ViewPort* view_port, uint8_t height) {
furi_assert(view_port);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
view_port->height = height;
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
}
uint8_t view_port_get_height(const ViewPort* view_port) {
furi_assert(view_port);
return view_port->height;
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
uint8_t height = view_port->height;
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
return height;
}
void view_port_enabled_set(ViewPort* view_port, bool enabled) {
furi_assert(view_port);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
if(view_port->is_enabled != enabled) {
view_port->is_enabled = enabled;
if(view_port->gui) gui_update(view_port->gui);
}
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
}
bool view_port_is_enabled(const ViewPort* view_port) {
furi_assert(view_port);
return view_port->is_enabled;
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
bool is_enabled = view_port->is_enabled;
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
return is_enabled;
}
void view_port_draw_callback_set(ViewPort* view_port, ViewPortDrawCallback callback, void* context) {
furi_assert(view_port);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
view_port->draw_callback = callback;
view_port->draw_callback_context = context;
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
}
void view_port_input_callback_set(
@@ -148,34 +166,43 @@ void view_port_input_callback_set(
ViewPortInputCallback callback,
void* context) {
furi_assert(view_port);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
view_port->input_callback = callback;
view_port->input_callback_context = context;
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
}
void view_port_update(ViewPort* view_port) {
furi_assert(view_port);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
if(view_port->gui && view_port->is_enabled) gui_update(view_port->gui);
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
}
void view_port_gui_set(ViewPort* view_port, Gui* gui) {
furi_assert(view_port);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
view_port->gui = gui;
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
}
void view_port_draw(ViewPort* view_port, Canvas* canvas) {
furi_assert(view_port);
furi_assert(canvas);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
furi_check(view_port->gui);
if(view_port->draw_callback) {
view_port_setup_canvas_orientation(view_port, canvas);
view_port->draw_callback(canvas, view_port->draw_callback_context);
}
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
}
void view_port_input(ViewPort* view_port, InputEvent* event) {
furi_assert(view_port);
furi_assert(event);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
furi_check(view_port->gui);
if(view_port->input_callback) {
@@ -183,13 +210,19 @@ void view_port_input(ViewPort* view_port, InputEvent* event) {
view_port_map_input(event, orientation);
view_port->input_callback(event, view_port->input_callback_context);
}
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
}
void view_port_set_orientation(ViewPort* view_port, ViewPortOrientation orientation) {
furi_assert(view_port);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
view_port->orientation = orientation;
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
}
ViewPortOrientation view_port_get_orientation(const ViewPort* view_port) {
return view_port->orientation;
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
ViewPortOrientation orientation = view_port->orientation;
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
return orientation;
}

View File

@@ -10,6 +10,7 @@
struct ViewPort {
Gui* gui;
FuriMutex* mutex;
bool is_enabled;
ViewPortOrientation orientation;

View File

@@ -15,6 +15,8 @@
#include <stdio.h>
#include <m-dict.h>
#include <bt/bt_service/bt.h>
#define TAG "RpcSrv"
typedef enum {
@@ -316,6 +318,15 @@ static int32_t rpc_session_worker(void* context) {
session->closed_callback(session->context);
}
furi_mutex_release(session->callbacks_mutex);
if(session->owner == RpcOwnerBle) {
// Disconnect BLE session
FURI_LOG_E("RPC", "BLE session closed due to a decode error");
Bt* bt = furi_record_open(RECORD_BT);
bt_set_profile(bt, BtProfileSerial);
furi_record_close(RECORD_BT);
FURI_LOG_E("RPC", "Finished disconnecting the BLE session");
}
}
}

View File

@@ -242,6 +242,23 @@ static void rpc_system_storage_list_root(const PB_Main* request, void* context)
rpc_send_and_release(session, &response);
}
static bool rpc_system_storage_list_filter(
const PB_Storage_ListRequest* request,
const FileInfo* fileinfo,
const char* name) {
bool result = false;
do {
if(!path_contains_only_ascii(name)) break;
if(request->filter_max_size) {
if(fileinfo->size > request->filter_max_size) break;
}
result = true;
} while(false);
return result;
}
static void rpc_system_storage_list_process(const PB_Main* request, void* context) {
furi_assert(request);
furi_assert(context);
@@ -253,9 +270,11 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex
RpcSession* session = rpc_storage->session;
furi_assert(session);
const PB_Storage_ListRequest* list_request = &request->content.storage_list_request;
rpc_system_storage_reset_state(rpc_storage, session, true);
if(!strcmp(request->content.storage_list_request.path, "/")) {
if(!strcmp(list_request->path, "/")) {
rpc_system_storage_list_root(request, context);
return;
}
@@ -271,7 +290,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex
};
PB_Storage_ListResponse* list = &response.content.storage_list_response;
bool include_md5 = request->content.storage_list_request.include_md5;
bool include_md5 = list_request->include_md5;
FuriString* md5 = furi_string_alloc();
FuriString* md5_path = furi_string_alloc();
File* file = storage_file_alloc(fs_api);
@@ -279,7 +298,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex
bool finish = false;
int i = 0;
if(!storage_dir_open(dir, request->content.storage_list_request.path)) {
if(!storage_dir_open(dir, list_request->path)) {
response.command_status = rpc_system_storage_get_file_error(dir);
response.which_content = PB_Main_empty_tag;
finish = true;
@@ -289,7 +308,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex
FileInfo fileinfo;
char* name = malloc(MAX_NAME_LENGTH + 1);
if(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) {
if(path_contains_only_ascii(name)) {
if(rpc_system_storage_list_filter(list_request, &fileinfo, name)) {
if(i == COUNT_OF(list->file)) {
list->file_count = i;
response.has_next = true;
@@ -303,11 +322,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex
list->file[i].name = name;
if(include_md5 && !file_info_is_dir(&fileinfo)) {
furi_string_printf( //-V576
md5_path,
"%s/%s",
request->content.storage_list_request.path,
name);
furi_string_printf(md5_path, "%s/%s", list_request->path, name); //-V576
if(md5_string_calc_file(file, furi_string_get_cstr(md5_path), md5, NULL)) {
char* md5sum = list->file[i].md5sum;

View File

@@ -12,7 +12,7 @@
#define ICON_SD_MOUNTED &I_SDcardMounted_11x8
#define ICON_SD_ERROR &I_SDcardFail_11x8
#define TAG RECORD_STORAGE
#define TAG "Storage"
static void storage_app_sd_icon_draw_callback(Canvas* canvas, void* context) {
furi_assert(canvas);

View File

@@ -334,12 +334,20 @@ const char* storage_file_get_error_desc(File* file);
*/
FS_Error storage_sd_format(Storage* api);
/** Will unmount the SD card
/** Will unmount the SD card.
* Will return FSE_NOT_READY if the SD card is not mounted.
* Will return FSE_DENIED if there are open files on the SD card.
* @param api pointer to the api
* @return FS_Error operation result
*/
FS_Error storage_sd_unmount(Storage* api);
/** Will mount the SD card
* @param api pointer to the api
* @return FS_Error operation result
*/
FS_Error storage_sd_mount(Storage* api);
/** Retrieves SD card information
* @param api pointer to the api
* @param info pointer to the info

View File

@@ -11,7 +11,7 @@
#define MAX_EXT_LEN 16
#define FILE_BUFFER_SIZE 512
#define TAG "StorageAPI"
#define TAG "StorageApi"
#define S_API_PROLOGUE FuriApiLock lock = api_lock_alloc_locked();
@@ -781,6 +781,14 @@ FS_Error storage_sd_unmount(Storage* storage) {
return S_RETURN_ERROR;
}
FS_Error storage_sd_mount(Storage* storage) {
S_API_PROLOGUE;
SAData data = {};
S_API_MESSAGE(StorageCommandSDMount);
S_API_EPILOGUE;
return S_RETURN_ERROR;
}
FS_Error storage_sd_info(Storage* storage, SDInfo* info) {
S_API_PROLOGUE;
SAData data = {

View File

@@ -1,6 +1,8 @@
#include "storage_glue.h"
#include <furi_hal.h>
#define TAG "StorageGlue"
/****************** storage file ******************/
void storage_file_init(StorageFile* obj) {
@@ -149,3 +151,8 @@ bool storage_pop_storage_file(File* file, StorageData* storage) {
return result;
}
size_t storage_open_files_count(StorageData* storage) {
size_t count = StorageFileList_size(storage->files);
return count;
}

View File

@@ -68,6 +68,8 @@ void* storage_get_storage_file_data(const File* file, StorageData* storage);
void storage_push_storage_file(File* file, FuriString* path, StorageData* storage);
bool storage_pop_storage_file(File* file, StorageData* storage);
size_t storage_open_files_count(StorageData* storage);
#ifdef __cplusplus
}
#endif

View File

@@ -141,6 +141,7 @@ typedef enum {
StorageCommandSDInfo,
StorageCommandSDStatus,
StorageCommandCommonResolvePath,
StorageCommandSDMount,
} StorageCommand;
typedef struct {

View File

@@ -418,12 +418,38 @@ static FS_Error storage_process_sd_format(Storage* app) {
static FS_Error storage_process_sd_unmount(Storage* app) {
FS_Error ret = FSE_OK;
if(storage_data_status(&app->storage[ST_EXT]) == StorageStatusNotReady) {
ret = FSE_NOT_READY;
} else {
sd_unmount_card(&app->storage[ST_EXT]);
storage_data_timestamp(&app->storage[ST_EXT]);
}
do {
StorageData* storage = &app->storage[ST_EXT];
if(storage_data_status(storage) == StorageStatusNotReady) {
ret = FSE_NOT_READY;
break;
}
if(storage_open_files_count(storage)) {
ret = FSE_DENIED;
break;
}
sd_unmount_card(storage);
storage_data_timestamp(storage);
} while(false);
return ret;
}
static FS_Error storage_process_sd_mount(Storage* app) {
FS_Error ret = FSE_OK;
do {
StorageData* storage = &app->storage[ST_EXT];
if(storage_data_status(storage) != StorageStatusNotReady) {
ret = FSE_NOT_READY;
break;
}
ret = sd_mount_card(storage, true);
storage_data_timestamp(storage);
} while(false);
return ret;
}
@@ -630,6 +656,9 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) {
case StorageCommandSDUnmount:
message->return_data->error_value = storage_process_sd_unmount(app);
break;
case StorageCommandSDMount:
message->return_data->error_value = storage_process_sd_mount(app);
break;
case StorageCommandSDInfo:
message->return_data->error_value =
storage_process_sd_info(app, message->data->sdinfo.info);

View File

@@ -24,7 +24,7 @@ static FS_Error storage_ext_parse_error(SDError error);
/******************* Core Functions *******************/
static bool sd_mount_card(StorageData* storage, bool notify) {
static bool sd_mount_card_internal(StorageData* storage, bool notify) {
bool result = false;
uint8_t counter = sd_max_mount_retry_count();
uint8_t bsp_result;
@@ -106,6 +106,32 @@ FS_Error sd_unmount_card(StorageData* storage) {
return storage_ext_parse_error(error);
}
FS_Error sd_mount_card(StorageData* storage, bool notify) {
sd_mount_card_internal(storage, notify);
FS_Error error;
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);
}
error = FSE_OK;
}
return error;
}
FS_Error sd_format_card(StorageData* storage) {
#ifdef FURI_RAM_EXEC
UNUSED(storage);
@@ -222,25 +248,8 @@ static void storage_ext_tick_internal(StorageData* storage, bool notify) {
if(sd_data->sd_was_present) {
if(hal_sd_detect()) {
FURI_LOG_I(TAG, "card detected");
sd_mount_card(storage, 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);
}
} 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);
}
}
sd_data->sd_was_present = false;
sd_mount_card(storage, notify);
if(!hal_sd_detect()) {
FURI_LOG_I(TAG, "card removed while mounting");

View File

@@ -8,6 +8,7 @@ extern "C" {
#endif
void storage_ext_init(StorageData* storage);
FS_Error sd_mount_card(StorageData* storage, bool notify);
FS_Error sd_unmount_card(StorageData* storage);
FS_Error sd_format_card(StorageData* storage);
FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info);