mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-27 06:50:21 +00:00
Apply desktop favourite apps refactoring
This commit is contained in:
parent
36102b8ee0
commit
12f9b6a89e
3 changed files with 109 additions and 170 deletions
|
@ -43,8 +43,6 @@
|
||||||
#define DISPLAY_BATTERY_RETRO_5 4
|
#define DISPLAY_BATTERY_RETRO_5 4
|
||||||
#define DISPLAY_BATTERY_BAR_PERCENT 5
|
#define DISPLAY_BATTERY_BAR_PERCENT 5
|
||||||
|
|
||||||
#define FAP_LOADER_APP_NAME "Applications"
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
InputKey data[MAX_PIN_SIZE];
|
InputKey data[MAX_PIN_SIZE];
|
||||||
uint8_t length;
|
uint8_t length;
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
|
|
||||||
#define TAG "DesktopSrv"
|
#define TAG "DesktopSrv"
|
||||||
|
|
||||||
|
#define FAP_LOADER_APP_NAME "Applications"
|
||||||
|
|
||||||
static void desktop_scene_main_new_idle_animation_callback(void* context) {
|
static void desktop_scene_main_new_idle_animation_callback(void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
Desktop* desktop = context;
|
Desktop* desktop = context;
|
||||||
|
@ -60,6 +62,34 @@ static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* fl
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void desktop_scene_main_open_app_or_profile(Desktop* desktop, const char* path) {
|
||||||
|
do {
|
||||||
|
LoaderStatus status = loader_start(desktop->loader, FAP_LOADER_APP_NAME, path);
|
||||||
|
if(status == LoaderStatusOk) break;
|
||||||
|
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
||||||
|
|
||||||
|
status = loader_start(desktop->loader, "Passport", NULL);
|
||||||
|
if(status != LoaderStatusOk) {
|
||||||
|
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
||||||
|
}
|
||||||
|
} while(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void desktop_scene_main_start_favorite(Desktop* desktop, FavoriteApp* application) {
|
||||||
|
LoaderStatus status = LoaderStatusErrorInternal;
|
||||||
|
if(application->is_external) {
|
||||||
|
status = loader_start(desktop->loader, FAP_LOADER_APP_NAME, application->name_or_path);
|
||||||
|
} else if(strlen(application->name_or_path) > 0) {
|
||||||
|
status = loader_start(desktop->loader, application->name_or_path, NULL);
|
||||||
|
} else {
|
||||||
|
status = loader_start(desktop->loader, FAP_LOADER_APP_NAME, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(status != LoaderStatusOk) {
|
||||||
|
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void desktop_scene_main_callback(DesktopEvent event, void* context) {
|
void desktop_scene_main_callback(DesktopEvent event, void* context) {
|
||||||
Desktop* desktop = (Desktop*)context;
|
Desktop* desktop = (Desktop*)context;
|
||||||
if(desktop->in_transition) return;
|
if(desktop->in_transition) return;
|
||||||
|
@ -130,59 +160,17 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
|
||||||
case DesktopMainEventOpenFavoritePrimary:
|
case DesktopMainEventOpenFavoritePrimary:
|
||||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||||
if(desktop->settings.favorite_primary.is_external) {
|
desktop_scene_main_start_favorite(desktop, &desktop->settings.favorite_primary);
|
||||||
LoaderStatus status = loader_start(
|
|
||||||
desktop->loader,
|
|
||||||
FAP_LOADER_APP_NAME,
|
|
||||||
desktop->settings.favorite_primary.name_or_path);
|
|
||||||
if(status != LoaderStatusOk) {
|
|
||||||
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LoaderStatus status = loader_start(
|
|
||||||
desktop->loader, desktop->settings.favorite_primary.name_or_path, NULL);
|
|
||||||
if(status != LoaderStatusOk) {
|
|
||||||
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
case DesktopMainEventOpenFavoriteSecondary:
|
case DesktopMainEventOpenFavoriteSecondary:
|
||||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||||
if(desktop->settings.favorite_secondary.is_external) {
|
desktop_scene_main_start_favorite(desktop, &desktop->settings.favorite_secondary);
|
||||||
LoaderStatus status = loader_start(
|
|
||||||
desktop->loader,
|
|
||||||
FAP_LOADER_APP_NAME,
|
|
||||||
desktop->settings.favorite_secondary.name_or_path);
|
|
||||||
if(status != LoaderStatusOk) {
|
|
||||||
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LoaderStatus status = loader_start(
|
|
||||||
desktop->loader, desktop->settings.favorite_secondary.name_or_path, NULL);
|
|
||||||
if(status != LoaderStatusOk) {
|
|
||||||
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
case DesktopMainEventOpenFavoriteTertiary:
|
case DesktopMainEventOpenFavoriteTertiary:
|
||||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||||
if(desktop->settings.favorite_tertiary.is_external) {
|
desktop_scene_main_start_favorite(desktop, &desktop->settings.favorite_tertiary);
|
||||||
LoaderStatus status = loader_start(
|
|
||||||
desktop->loader,
|
|
||||||
FAP_LOADER_APP_NAME,
|
|
||||||
desktop->settings.favorite_tertiary.name_or_path);
|
|
||||||
if(status != LoaderStatusOk) {
|
|
||||||
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LoaderStatus status = loader_start(
|
|
||||||
desktop->loader, desktop->settings.favorite_tertiary.name_or_path, NULL);
|
|
||||||
if(status != LoaderStatusOk) {
|
|
||||||
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
case DesktopAnimationEventCheckAnimation:
|
case DesktopAnimationEventCheckAnimation:
|
||||||
|
@ -210,51 +198,28 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DesktopMainEventOpenGameMenu: {
|
case DesktopMainEventOpenGameMenu: {
|
||||||
LoaderStatus status = loader_start(
|
desktop_scene_main_open_app_or_profile(desktop, EXT_PATH("/apps/Games/Snake.fap"));
|
||||||
desktop->loader, FAP_LOADER_APP_NAME, EXT_PATH("/apps/Games/Snake.fap"));
|
|
||||||
if(status != LoaderStatusOk) {
|
|
||||||
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DesktopMainEventOpenTetris: {
|
case DesktopMainEventOpenTetris: {
|
||||||
LoaderStatus status = loader_start(
|
desktop_scene_main_open_app_or_profile(desktop, EXT_PATH("/apps/Games/Tetris.fap"));
|
||||||
desktop->loader, FAP_LOADER_APP_NAME, EXT_PATH("/apps/Games/Tetris.fap"));
|
|
||||||
if(status != LoaderStatusOk) {
|
|
||||||
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DesktopMainEventOpenArkanoid: {
|
case DesktopMainEventOpenArkanoid: {
|
||||||
LoaderStatus status = loader_start(
|
desktop_scene_main_open_app_or_profile(desktop, EXT_PATH("/apps/Games/Arkanoid.fap"));
|
||||||
desktop->loader, FAP_LOADER_APP_NAME, EXT_PATH("/apps/Games/Arkanoid.fap"));
|
|
||||||
if(status != LoaderStatusOk) {
|
|
||||||
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DesktopMainEventOpenDOOM: {
|
case DesktopMainEventOpenDOOM: {
|
||||||
LoaderStatus status = loader_start(
|
desktop_scene_main_open_app_or_profile(desktop, EXT_PATH("/apps/Games/DOOM.fap"));
|
||||||
desktop->loader, FAP_LOADER_APP_NAME, EXT_PATH("/apps/Games/DOOM.fap"));
|
|
||||||
if(status != LoaderStatusOk) {
|
|
||||||
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DesktopMainEventOpenZombiez: {
|
case DesktopMainEventOpenZombiez: {
|
||||||
LoaderStatus status = loader_start(
|
desktop_scene_main_open_app_or_profile(desktop, EXT_PATH("/apps/Games/Zombiez.fap"));
|
||||||
desktop->loader, FAP_LOADER_APP_NAME, EXT_PATH("/apps/Games/Zombiez.fap"));
|
|
||||||
if(status != LoaderStatusOk) {
|
|
||||||
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DesktopMainEventOpenHeap: {
|
case DesktopMainEventOpenHeap: {
|
||||||
LoaderStatus status = loader_start(
|
desktop_scene_main_open_app_or_profile(
|
||||||
desktop->loader, FAP_LOADER_APP_NAME, EXT_PATH("/apps/Games/heap_defence.fap"));
|
desktop, EXT_PATH("/apps/Games/heap_defence.fap"));
|
||||||
if(status != LoaderStatusOk) {
|
|
||||||
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DesktopLockedEventUpdate:
|
case DesktopLockedEventUpdate:
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
#include <dialogs/dialogs.h>
|
#include <dialogs/dialogs.h>
|
||||||
#include <fap_loader/fap_loader_app.h>
|
#include <fap_loader/fap_loader_app.h>
|
||||||
|
|
||||||
|
#define EXTERNAL_APPLICATION_NAME ("[External Application]")
|
||||||
|
#define EXTERNAL_APPLICATION_INDEX (FLIPPER_APPS_COUNT + 1)
|
||||||
|
|
||||||
static bool favorite_fap_selector_item_callback(
|
static bool favorite_fap_selector_item_callback(
|
||||||
FuriString* file_path,
|
FuriString* file_path,
|
||||||
void* context,
|
void* context,
|
||||||
|
@ -44,6 +47,20 @@ void desktop_settings_scene_favorite_on_enter(void* context) {
|
||||||
uint32_t primary_favorite =
|
uint32_t primary_favorite =
|
||||||
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
||||||
uint32_t pre_select_item = 0;
|
uint32_t pre_select_item = 0;
|
||||||
|
FavoriteApp* curr_favorite_app = NULL;
|
||||||
|
if(primary_favorite == 0) {
|
||||||
|
curr_favorite_app = &app->settings.favorite_primary;
|
||||||
|
} else if(primary_favorite == 1) {
|
||||||
|
curr_favorite_app = &app->settings.favorite_secondary;
|
||||||
|
} else if(primary_favorite == 2) {
|
||||||
|
curr_favorite_app = &app->settings.favorite_tertiary;
|
||||||
|
} else {
|
||||||
|
curr_favorite_app = &app->settings.favorite_primary;
|
||||||
|
}
|
||||||
|
if(curr_favorite_app == NULL) {
|
||||||
|
// This should not happen!
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
|
for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
|
@ -53,30 +70,29 @@ void desktop_settings_scene_favorite_on_enter(void* context) {
|
||||||
desktop_settings_scene_favorite_submenu_callback,
|
desktop_settings_scene_favorite_submenu_callback,
|
||||||
app);
|
app);
|
||||||
|
|
||||||
if(primary_favorite == 0) { // Select favorite item in submenu
|
// Select favorite item in submenu
|
||||||
if((app->settings.favorite_primary.is_external &&
|
if(!curr_favorite_app->is_external &&
|
||||||
!strcmp(FLIPPER_APPS[i].name, FAP_LOADER_APP_NAME)) ||
|
!strcmp(FLIPPER_APPS[i].name, curr_favorite_app->name_or_path)) {
|
||||||
(!strcmp(FLIPPER_APPS[i].name, app->settings.favorite_primary.name_or_path))) {
|
|
||||||
pre_select_item = i;
|
|
||||||
}
|
|
||||||
} else if(primary_favorite == 1) {
|
|
||||||
if((app->settings.favorite_secondary.is_external &&
|
|
||||||
!strcmp(FLIPPER_APPS[i].name, FAP_LOADER_APP_NAME)) ||
|
|
||||||
(!strcmp(FLIPPER_APPS[i].name, app->settings.favorite_secondary.name_or_path))) {
|
|
||||||
pre_select_item = i;
|
|
||||||
}
|
|
||||||
} else if(primary_favorite == 2) {
|
|
||||||
if((app->settings.favorite_tertiary.is_external &&
|
|
||||||
!strcmp(FLIPPER_APPS[i].name, FAP_LOADER_APP_NAME)) ||
|
|
||||||
(!strcmp(FLIPPER_APPS[i].name, app->settings.favorite_tertiary.name_or_path))) {
|
|
||||||
pre_select_item = i;
|
pre_select_item = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef APP_FAP_LOADER
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
EXTERNAL_APPLICATION_NAME,
|
||||||
|
EXTERNAL_APPLICATION_INDEX,
|
||||||
|
desktop_settings_scene_favorite_submenu_callback,
|
||||||
|
app);
|
||||||
|
if(curr_favorite_app->is_external) {
|
||||||
|
pre_select_item = EXTERNAL_APPLICATION_INDEX;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu,
|
submenu,
|
||||||
"None (disable)",
|
"None (disable)",
|
||||||
FLIPPER_APPS_COUNT + 1,
|
FLIPPER_APPS_COUNT + 2,
|
||||||
desktop_settings_scene_favorite_submenu_callback,
|
desktop_settings_scene_favorite_submenu_callback,
|
||||||
app);
|
app);
|
||||||
|
|
||||||
|
@ -99,46 +115,24 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e
|
||||||
|
|
||||||
uint32_t primary_favorite =
|
uint32_t primary_favorite =
|
||||||
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
||||||
|
FavoriteApp* curr_favorite_app = NULL;
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
|
||||||
if(event.event >= (FLIPPER_APPS_COUNT + 1)) {
|
|
||||||
if(primary_favorite == 0) {
|
if(primary_favorite == 0) {
|
||||||
app->settings.favorite_primary.is_external = false;
|
curr_favorite_app = &app->settings.favorite_primary;
|
||||||
strncpy(app->settings.favorite_primary.name_or_path, "", MAX_APP_LENGTH);
|
|
||||||
} else if(primary_favorite == 1) {
|
} else if(primary_favorite == 1) {
|
||||||
app->settings.favorite_secondary.is_external = false;
|
curr_favorite_app = &app->settings.favorite_secondary;
|
||||||
strncpy(app->settings.favorite_secondary.name_or_path, "", MAX_APP_LENGTH);
|
|
||||||
} else if(primary_favorite == 2) {
|
} else if(primary_favorite == 2) {
|
||||||
app->settings.favorite_tertiary.is_external = false;
|
curr_favorite_app = &app->settings.favorite_tertiary;
|
||||||
strncpy(app->settings.favorite_tertiary.name_or_path, "", MAX_APP_LENGTH);
|
} else {
|
||||||
|
curr_favorite_app = &app->settings.favorite_primary;
|
||||||
}
|
}
|
||||||
|
if(curr_favorite_app == NULL) {
|
||||||
scene_manager_previous_scene(app->scene_manager);
|
// This should not happen!
|
||||||
consumed = true;
|
|
||||||
furi_string_free(temp_path);
|
furi_string_free(temp_path);
|
||||||
return consumed;
|
return consumed;
|
||||||
}
|
}
|
||||||
if(strcmp(FLIPPER_APPS[event.event].name, FAP_LOADER_APP_NAME) != 0) {
|
|
||||||
if(primary_favorite == 0) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
app->settings.favorite_primary.is_external = false;
|
if(event.event == EXTERNAL_APPLICATION_INDEX) {
|
||||||
strncpy(
|
|
||||||
app->settings.favorite_primary.name_or_path,
|
|
||||||
FLIPPER_APPS[event.event].name,
|
|
||||||
MAX_APP_LENGTH);
|
|
||||||
} else if(primary_favorite == 1) {
|
|
||||||
app->settings.favorite_secondary.is_external = false;
|
|
||||||
strncpy(
|
|
||||||
app->settings.favorite_secondary.name_or_path,
|
|
||||||
FLIPPER_APPS[event.event].name,
|
|
||||||
MAX_APP_LENGTH);
|
|
||||||
} else if(primary_favorite == 2) {
|
|
||||||
app->settings.favorite_tertiary.is_external = false;
|
|
||||||
strncpy(
|
|
||||||
app->settings.favorite_tertiary.name_or_path,
|
|
||||||
FLIPPER_APPS[event.event].name,
|
|
||||||
MAX_APP_LENGTH);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const DialogsFileBrowserOptions browser_options = {
|
const DialogsFileBrowserOptions browser_options = {
|
||||||
.extension = ".fap",
|
.extension = ".fap",
|
||||||
.icon = &I_unknown_10px,
|
.icon = &I_unknown_10px,
|
||||||
|
@ -149,47 +143,29 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e
|
||||||
.base_path = EXT_PATH("apps"),
|
.base_path = EXT_PATH("apps"),
|
||||||
};
|
};
|
||||||
|
|
||||||
if(primary_favorite == 0) { // Select favorite fap in file browser
|
// Select favorite fap in file browser
|
||||||
if(favorite_fap_selector_file_exists(
|
if(favorite_fap_selector_file_exists(curr_favorite_app->name_or_path)) {
|
||||||
app->settings.favorite_primary.name_or_path)) {
|
furi_string_set_str(temp_path, curr_favorite_app->name_or_path);
|
||||||
furi_string_set_str(temp_path, app->settings.favorite_primary.name_or_path);
|
|
||||||
}
|
|
||||||
} else if(primary_favorite == 1) {
|
|
||||||
if(favorite_fap_selector_file_exists(
|
|
||||||
app->settings.favorite_secondary.name_or_path)) {
|
|
||||||
furi_string_set_str(temp_path, app->settings.favorite_secondary.name_or_path);
|
|
||||||
}
|
|
||||||
} else if(primary_favorite == 2) {
|
|
||||||
if(favorite_fap_selector_file_exists(
|
|
||||||
app->settings.favorite_tertiary.name_or_path)) {
|
|
||||||
furi_string_set_str(temp_path, app->settings.favorite_tertiary.name_or_path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
submenu_reset(app->submenu);
|
|
||||||
if(dialog_file_browser_show(app->dialogs, temp_path, temp_path, &browser_options)) {
|
if(dialog_file_browser_show(app->dialogs, temp_path, temp_path, &browser_options)) {
|
||||||
if(primary_favorite == 0) {
|
submenu_reset(app->submenu); // Prevent menu from being shown when we exiting scene
|
||||||
app->settings.favorite_primary.is_external = true;
|
curr_favorite_app->is_external = true;
|
||||||
strncpy(
|
strncpy(
|
||||||
app->settings.favorite_primary.name_or_path,
|
curr_favorite_app->name_or_path,
|
||||||
furi_string_get_cstr(temp_path),
|
furi_string_get_cstr(temp_path),
|
||||||
MAX_APP_LENGTH);
|
MAX_APP_LENGTH);
|
||||||
} else if(primary_favorite == 1) {
|
consumed = true;
|
||||||
app->settings.favorite_secondary.is_external = true;
|
}
|
||||||
|
} else {
|
||||||
|
curr_favorite_app->is_external = false;
|
||||||
strncpy(
|
strncpy(
|
||||||
app->settings.favorite_secondary.name_or_path,
|
curr_favorite_app->name_or_path, FLIPPER_APPS[event.event].name, MAX_APP_LENGTH);
|
||||||
furi_string_get_cstr(temp_path),
|
consumed = true;
|
||||||
MAX_APP_LENGTH);
|
|
||||||
} else if(primary_favorite == 2) {
|
|
||||||
app->settings.favorite_tertiary.is_external = true;
|
|
||||||
strncpy(
|
|
||||||
app->settings.favorite_tertiary.name_or_path,
|
|
||||||
furi_string_get_cstr(temp_path),
|
|
||||||
MAX_APP_LENGTH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if(consumed) {
|
||||||
scene_manager_previous_scene(app->scene_manager);
|
scene_manager_previous_scene(app->scene_manager);
|
||||||
|
};
|
||||||
consumed = true;
|
consumed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue