mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-26 14:30:25 +00:00
[FL-3783] Asynchronous Infrared remote manipulation (#3503)
* Introduce ConcurrentRunner, load universal and regular remotes concurrently * Perform all lengthy operations in a ConcurrentRunner * Fix python formatting * Clean up code * Add usage warning * Remove ConcurrentRunner, use a plain FuriThread instead * Load remotes asynchronously in RPC mode as well * Reorder code for clarity * Clean up, use thread return code to report errors * Improve wording * Fix logical error
This commit is contained in:
parent
adbe4d44f4
commit
022fccf0d7
13 changed files with 269 additions and 200 deletions
|
@ -6,7 +6,8 @@
|
|||
|
||||
#define TAG "InfraredApp"
|
||||
|
||||
#define INFRARED_TX_MIN_INTERVAL_MS 50U
|
||||
#define INFRARED_TX_MIN_INTERVAL_MS (50U)
|
||||
#define INFRARED_TASK_STACK_SIZE (2048UL)
|
||||
|
||||
static const NotificationSequence*
|
||||
infrared_notification_sequences[InfraredNotificationMessageCount] = {
|
||||
|
@ -128,6 +129,8 @@ static void infrared_find_vacant_remote_name(FuriString* name, const char* path)
|
|||
static InfraredApp* infrared_alloc() {
|
||||
InfraredApp* infrared = malloc(sizeof(InfraredApp));
|
||||
|
||||
infrared->task_thread =
|
||||
furi_thread_alloc_ex("InfraredTask", INFRARED_TASK_STACK_SIZE, NULL, infrared);
|
||||
infrared->file_path = furi_string_alloc();
|
||||
infrared->button_name = furi_string_alloc();
|
||||
|
||||
|
@ -203,6 +206,10 @@ static InfraredApp* infrared_alloc() {
|
|||
|
||||
static void infrared_free(InfraredApp* infrared) {
|
||||
furi_assert(infrared);
|
||||
|
||||
furi_thread_join(infrared->task_thread);
|
||||
furi_thread_free(infrared->task_thread);
|
||||
|
||||
ViewDispatcher* view_dispatcher = infrared->view_dispatcher;
|
||||
InfraredAppState* app_state = &infrared->app_state;
|
||||
|
||||
|
@ -377,6 +384,18 @@ void infrared_tx_stop(InfraredApp* infrared) {
|
|||
infrared->app_state.last_transmit_time = furi_get_tick();
|
||||
}
|
||||
|
||||
void infrared_blocking_task_start(InfraredApp* infrared, FuriThreadCallback callback) {
|
||||
view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading));
|
||||
furi_thread_set_callback(infrared->task_thread, callback);
|
||||
furi_thread_start(infrared->task_thread);
|
||||
}
|
||||
|
||||
bool infrared_blocking_task_finalize(InfraredApp* infrared) {
|
||||
furi_thread_join(infrared->task_thread);
|
||||
view_stack_remove_view(infrared->view_stack, loading_get_view(infrared->loading));
|
||||
return furi_thread_get_return_code(infrared->task_thread);
|
||||
}
|
||||
|
||||
void infrared_text_store_set(InfraredApp* infrared, uint32_t bank, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
@ -397,21 +416,6 @@ void infrared_play_notification_message(
|
|||
notification_message(infrared->notifications, infrared_notification_sequences[message]);
|
||||
}
|
||||
|
||||
void infrared_show_loading_popup(const InfraredApp* infrared, bool show) {
|
||||
ViewStack* view_stack = infrared->view_stack;
|
||||
Loading* loading = infrared->loading;
|
||||
|
||||
if(show) {
|
||||
// Raise timer priority so that animations can play
|
||||
furi_timer_set_thread_priority(FuriTimerThreadPriorityElevated);
|
||||
view_stack_add_view(view_stack, loading_get_view(loading));
|
||||
} else {
|
||||
view_stack_remove_view(view_stack, loading_get_view(loading));
|
||||
// Restore default timer priority
|
||||
furi_timer_set_thread_priority(FuriTimerThreadPriorityNormal);
|
||||
}
|
||||
}
|
||||
|
||||
void infrared_show_error_message(const InfraredApp* infrared, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
|
|
@ -19,12 +19,13 @@
|
|||
#include <gui/modules/button_menu.h>
|
||||
#include <gui/modules/button_panel.h>
|
||||
|
||||
#include <rpc/rpc_app.h>
|
||||
#include <storage/storage.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
#include <infrared_worker.h>
|
||||
#include <infrared/worker/infrared_worker.h>
|
||||
|
||||
#include "infrared_app.h"
|
||||
#include "infrared_remote.h"
|
||||
|
@ -36,8 +37,6 @@
|
|||
#include "views/infrared_debug_view.h"
|
||||
#include "views/infrared_move_view.h"
|
||||
|
||||
#include "rpc/rpc_app.h"
|
||||
|
||||
#define INFRARED_FILE_NAME_SIZE 100
|
||||
#define INFRARED_TEXT_STORE_NUM 2
|
||||
#define INFRARED_TEXT_STORE_SIZE 128
|
||||
|
@ -120,6 +119,7 @@ struct InfraredApp {
|
|||
Loading* loading; /**< Standard view for informing about long operations. */
|
||||
InfraredProgressView* progress; /**< Custom view for showing brute force progress. */
|
||||
|
||||
FuriThread* task_thread; /**< Pointer to a FuriThread instance for concurrent tasks. */
|
||||
FuriString* file_path; /**< Full path to the currently loaded file. */
|
||||
FuriString* button_name; /**< Name of the button requested in RPC mode. */
|
||||
/** Arbitrary text storage for various inputs. */
|
||||
|
@ -210,6 +210,28 @@ void infrared_tx_start_button_index(InfraredApp* infrared, size_t button_index);
|
|||
*/
|
||||
void infrared_tx_stop(InfraredApp* infrared);
|
||||
|
||||
/**
|
||||
* @brief Start a blocking task in a separate thread.
|
||||
*
|
||||
* If a ViewStack is currently on screen, a busy "Hourglass" animation
|
||||
* will be shown and no input will be accepted until completion.
|
||||
*
|
||||
* @param[in,out] infrared pointer to the application instance.
|
||||
* @param[in] callback pointer to the function to be run in the thread.
|
||||
*/
|
||||
void infrared_blocking_task_start(InfraredApp* infrared, FuriThreadCallback callback);
|
||||
|
||||
/**
|
||||
* @brief Wait for a blocking task to finish and receive the result.
|
||||
*
|
||||
* Upon the completion of a blocking task, the busy animation will be hidden
|
||||
* and input will be accepted again.
|
||||
*
|
||||
* @param[in,out] infrared pointer to the application instance.
|
||||
* @return true if the blocking task finished successfully, false otherwise.
|
||||
*/
|
||||
bool infrared_blocking_task_finalize(InfraredApp* infrared);
|
||||
|
||||
/**
|
||||
* @brief Set the internal text store with formatted text.
|
||||
*
|
||||
|
@ -239,17 +261,6 @@ void infrared_play_notification_message(
|
|||
const InfraredApp* infrared,
|
||||
InfraredNotificationMessage message);
|
||||
|
||||
/**
|
||||
* @brief Show a loading pop-up screen.
|
||||
*
|
||||
* In order for this to work, a Stack view must be currently active and
|
||||
* the main view must be added to it.
|
||||
*
|
||||
* @param[in] infrared pointer to the application instance.
|
||||
* @param[in] show whether to show or hide the pop-up.
|
||||
*/
|
||||
void infrared_show_loading_popup(const InfraredApp* infrared, bool show);
|
||||
|
||||
/**
|
||||
* @brief Show a formatted error messsage.
|
||||
*
|
||||
|
|
|
@ -14,6 +14,7 @@ enum InfraredCustomEventType {
|
|||
InfraredCustomEventTypePopupClosed,
|
||||
InfraredCustomEventTypeButtonSelected,
|
||||
InfraredCustomEventTypeBackPressed,
|
||||
InfraredCustomEventTypeTaskFinished,
|
||||
|
||||
InfraredCustomEventTypeRpcLoadFile,
|
||||
InfraredCustomEventTypeRpcExit,
|
||||
|
|
|
@ -32,9 +32,24 @@ static void infrared_scene_universal_common_hide_popup(InfraredApp* infrared) {
|
|||
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop);
|
||||
}
|
||||
|
||||
static int32_t infrared_scene_universal_common_task_callback(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
const bool success = infrared_brute_force_calculate_messages(infrared->brute_force);
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher,
|
||||
infrared_custom_event_pack(InfraredCustomEventTypeTaskFinished, 0));
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void infrared_scene_universal_common_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical);
|
||||
view_stack_add_view(infrared->view_stack, button_panel_get_view(infrared->button_panel));
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
|
||||
|
||||
// Load universal remote data in background
|
||||
infrared_blocking_task_start(infrared, infrared_scene_universal_common_task_callback);
|
||||
}
|
||||
|
||||
bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent event) {
|
||||
|
@ -58,26 +73,34 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e
|
|||
if(infrared_custom_event_get_type(event.event) == InfraredCustomEventTypeBackPressed) {
|
||||
infrared_brute_force_stop(brute_force);
|
||||
infrared_scene_universal_common_hide_popup(infrared);
|
||||
consumed = true;
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
} else {
|
||||
if(event.type == SceneManagerEventTypeBack) {
|
||||
scene_manager_previous_scene(scene_manager);
|
||||
consumed = true;
|
||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(infrared_custom_event_get_type(event.event) ==
|
||||
InfraredCustomEventTypeButtonSelected) {
|
||||
uint16_t event_type;
|
||||
int16_t event_value;
|
||||
infrared_custom_event_unpack(event.event, &event_type, &event_value);
|
||||
|
||||
if(event_type == InfraredCustomEventTypeButtonSelected) {
|
||||
uint32_t record_count;
|
||||
if(infrared_brute_force_start(
|
||||
brute_force, infrared_custom_event_get_value(event.event), &record_count)) {
|
||||
if(infrared_brute_force_start(brute_force, event_value, &record_count)) {
|
||||
dolphin_deed(DolphinDeedIrSend);
|
||||
infrared_scene_universal_common_show_popup(infrared, record_count);
|
||||
} else {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases);
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event_type == InfraredCustomEventTypeTaskFinished) {
|
||||
const bool task_success = infrared_blocking_task_finalize(infrared);
|
||||
|
||||
if(!task_success) {
|
||||
scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases);
|
||||
}
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,12 +6,33 @@ static void
|
|||
view_dispatcher_send_custom_event(infrared->view_dispatcher, result);
|
||||
}
|
||||
|
||||
static int32_t infrared_scene_edit_delete_task_callback(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
InfraredAppState* app_state = &infrared->app_state;
|
||||
const InfraredEditTarget edit_target = app_state->edit_target;
|
||||
|
||||
bool success;
|
||||
if(edit_target == InfraredEditTargetButton) {
|
||||
furi_assert(app_state->current_button_index != InfraredButtonIndexNone);
|
||||
success = infrared_remote_delete_signal(infrared->remote, app_state->current_button_index);
|
||||
} else if(edit_target == InfraredEditTargetRemote) {
|
||||
success = infrared_remote_remove(infrared->remote);
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void infrared_scene_edit_delete_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
DialogEx* dialog_ex = infrared->dialog_ex;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
|
||||
const InfraredEditTarget edit_target = infrared->app_state.edit_target;
|
||||
|
||||
if(edit_target == InfraredEditTargetButton) {
|
||||
dialog_ex_set_header(dialog_ex, "Delete Button?", 64, 0, AlignCenter, AlignTop);
|
||||
|
||||
|
@ -84,39 +105,30 @@ bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event)
|
|||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == DialogExResultLeft) {
|
||||
scene_manager_previous_scene(scene_manager);
|
||||
consumed = true;
|
||||
} else if(event.event == DialogExResultRight) {
|
||||
bool success = false;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
// Delete a button or a remote in a separate thread
|
||||
infrared_blocking_task_start(infrared, infrared_scene_edit_delete_task_callback);
|
||||
|
||||
} else if(event.event == InfraredCustomEventTypeTaskFinished) {
|
||||
const bool task_success = infrared_blocking_task_finalize(infrared);
|
||||
|
||||
InfraredAppState* app_state = &infrared->app_state;
|
||||
const InfraredEditTarget edit_target = app_state->edit_target;
|
||||
|
||||
if(edit_target == InfraredEditTargetButton) {
|
||||
furi_assert(app_state->current_button_index != InfraredButtonIndexNone);
|
||||
infrared_show_loading_popup(infrared, true);
|
||||
success = infrared_remote_delete_signal(remote, app_state->current_button_index);
|
||||
infrared_show_loading_popup(infrared, false);
|
||||
app_state->current_button_index = InfraredButtonIndexNone;
|
||||
} else if(edit_target == InfraredEditTargetRemote) {
|
||||
success = infrared_remote_remove(remote);
|
||||
app_state->current_button_index = InfraredButtonIndexNone;
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
|
||||
if(success) {
|
||||
if(task_success) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneEditDeleteDone);
|
||||
} else {
|
||||
infrared_show_error_message(
|
||||
infrared,
|
||||
"Failed to\ndelete %s",
|
||||
edit_target == InfraredEditTargetButton ? "button" : "file");
|
||||
const char* edit_target_text =
|
||||
app_state->edit_target == InfraredEditTargetButton ? "button" : "file";
|
||||
infrared_show_error_message(infrared, "Failed to\ndelete %s", edit_target_text);
|
||||
|
||||
const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart};
|
||||
scene_manager_search_and_switch_to_previous_scene_one_of(
|
||||
scene_manager, possible_scenes, COUNT_OF(possible_scenes));
|
||||
}
|
||||
consumed = true;
|
||||
|
||||
app_state->current_button_index = InfraredButtonIndexNone;
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
|
|
|
@ -1,5 +1,17 @@
|
|||
#include "../infrared_app_i.h"
|
||||
|
||||
static int32_t infrared_scene_edit_move_task_callback(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
const bool success = infrared_remote_move_signal(
|
||||
infrared->remote,
|
||||
infrared->app_state.prev_button_index,
|
||||
infrared->app_state.current_button_index);
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static void infrared_scene_edit_move_button_callback(
|
||||
uint32_t index_old,
|
||||
uint32_t index_new,
|
||||
|
@ -38,25 +50,21 @@ bool infrared_scene_edit_move_on_event(void* context, SceneManagerEvent event) {
|
|||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == InfraredCustomEventTypeButtonSelected) {
|
||||
infrared_show_loading_popup(infrared, true);
|
||||
const bool button_moved = infrared_remote_move_signal(
|
||||
infrared->remote,
|
||||
infrared->app_state.prev_button_index,
|
||||
infrared->app_state.current_button_index);
|
||||
infrared_show_loading_popup(infrared, false);
|
||||
// Move the button in a separate thread
|
||||
infrared_blocking_task_start(infrared, infrared_scene_edit_move_task_callback);
|
||||
|
||||
if(!button_moved) {
|
||||
infrared_show_error_message(
|
||||
infrared,
|
||||
"Failed to move\n\"%s\"",
|
||||
infrared_remote_get_signal_name(
|
||||
infrared->remote, infrared->app_state.current_button_index));
|
||||
} else if(event.event == InfraredCustomEventTypeTaskFinished) {
|
||||
const bool task_success = infrared_blocking_task_finalize(infrared);
|
||||
|
||||
if(!task_success) {
|
||||
const char* signal_name = infrared_remote_get_signal_name(
|
||||
infrared->remote, infrared->app_state.current_button_index);
|
||||
infrared_show_error_message(infrared, "Failed to move\n\"%s\"", signal_name);
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
infrared->scene_manager, InfraredSceneRemoteList);
|
||||
}
|
||||
|
||||
consumed = true;
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
|
|
|
@ -3,6 +3,28 @@
|
|||
#include <string.h>
|
||||
#include <toolbox/path.h>
|
||||
|
||||
static int32_t infrared_scene_edit_rename_task_callback(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
InfraredAppState* app_state = &infrared->app_state;
|
||||
const InfraredEditTarget edit_target = app_state->edit_target;
|
||||
|
||||
bool success;
|
||||
if(edit_target == InfraredEditTargetButton) {
|
||||
furi_assert(app_state->current_button_index != InfraredButtonIndexNone);
|
||||
success = infrared_remote_rename_signal(
|
||||
infrared->remote, app_state->current_button_index, infrared->text_store[0]);
|
||||
} else if(edit_target == InfraredEditTargetRemote) {
|
||||
success = infrared_rename_current_remote(infrared, infrared->text_store[0]);
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void infrared_scene_edit_rename_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
|
@ -61,41 +83,31 @@ void infrared_scene_edit_rename_on_enter(void* context) {
|
|||
|
||||
bool infrared_scene_edit_rename_on_event(void* context, SceneManagerEvent event) {
|
||||
InfraredApp* infrared = context;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
InfraredAppState* app_state = &infrared->app_state;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == InfraredCustomEventTypeTextEditDone) {
|
||||
bool success = false;
|
||||
const InfraredEditTarget edit_target = app_state->edit_target;
|
||||
if(edit_target == InfraredEditTargetButton) {
|
||||
const int32_t current_button_index = app_state->current_button_index;
|
||||
furi_assert(current_button_index != InfraredButtonIndexNone);
|
||||
infrared_show_loading_popup(infrared, true);
|
||||
success = infrared_remote_rename_signal(
|
||||
remote, current_button_index, infrared->text_store[0]);
|
||||
infrared_show_loading_popup(infrared, false);
|
||||
app_state->current_button_index = InfraredButtonIndexNone;
|
||||
} else if(edit_target == InfraredEditTargetRemote) {
|
||||
success = infrared_rename_current_remote(infrared, infrared->text_store[0]);
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
// Rename a button or a remote in a separate thread
|
||||
infrared_blocking_task_start(infrared, infrared_scene_edit_rename_task_callback);
|
||||
|
||||
if(success) {
|
||||
} else if(event.event == InfraredCustomEventTypeTaskFinished) {
|
||||
const bool task_success = infrared_blocking_task_finalize(infrared);
|
||||
InfraredAppState* app_state = &infrared->app_state;
|
||||
|
||||
if(task_success) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneEditRenameDone);
|
||||
} else {
|
||||
infrared_show_error_message(
|
||||
infrared,
|
||||
"Failed to\nrename %s",
|
||||
edit_target == InfraredEditTargetButton ? "button" : "file");
|
||||
const char* edit_target_text =
|
||||
app_state->edit_target == InfraredEditTargetButton ? "button" : "file";
|
||||
infrared_show_error_message(infrared, "Failed to\nrename %s", edit_target_text);
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
scene_manager, InfraredSceneRemoteList);
|
||||
}
|
||||
consumed = true;
|
||||
|
||||
app_state->current_button_index = InfraredButtonIndexNone;
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
|
|
|
@ -1,41 +1,59 @@
|
|||
#include "../infrared_app_i.h"
|
||||
|
||||
void infrared_scene_remote_list_on_enter(void* context) {
|
||||
static int32_t infrared_scene_remote_list_task_callback(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
ViewDispatcher* view_dispatcher = infrared->view_dispatcher;
|
||||
|
||||
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical);
|
||||
view_dispatcher_switch_to_view(view_dispatcher, InfraredViewStack);
|
||||
const bool success =
|
||||
infrared_remote_load(infrared->remote, furi_string_get_cstr(infrared->file_path));
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished);
|
||||
return success;
|
||||
}
|
||||
|
||||
static void infrared_scene_remote_list_select_and_load(InfraredApp* infrared) {
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(&browser_options, INFRARED_APP_EXTENSION, &I_ir_10px);
|
||||
browser_options.base_path = INFRARED_APP_FOLDER;
|
||||
|
||||
while(dialog_file_browser_show(
|
||||
infrared->dialogs, infrared->file_path, infrared->file_path, &browser_options)) {
|
||||
const char* file_path = furi_string_get_cstr(infrared->file_path);
|
||||
const bool file_selected = dialog_file_browser_show(
|
||||
infrared->dialogs, infrared->file_path, infrared->file_path, &browser_options);
|
||||
|
||||
infrared_show_loading_popup(infrared, true);
|
||||
const bool remote_loaded = infrared_remote_load(infrared->remote, file_path);
|
||||
infrared_show_loading_popup(infrared, false);
|
||||
if(file_selected) {
|
||||
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical);
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
|
||||
|
||||
if(remote_loaded) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneRemote);
|
||||
return;
|
||||
} else {
|
||||
infrared_show_error_message(infrared, "Failed to load\n\"%s\"", file_path);
|
||||
}
|
||||
// Load the remote in a separate thread
|
||||
infrared_blocking_task_start(infrared, infrared_scene_remote_list_task_callback);
|
||||
|
||||
} else {
|
||||
scene_manager_previous_scene(infrared->scene_manager);
|
||||
}
|
||||
}
|
||||
|
||||
scene_manager_previous_scene(scene_manager);
|
||||
void infrared_scene_remote_list_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
infrared_scene_remote_list_select_and_load(infrared);
|
||||
}
|
||||
|
||||
bool infrared_scene_remote_list_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
UNUSED(event);
|
||||
InfraredApp* infrared = context;
|
||||
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == InfraredCustomEventTypeTaskFinished) {
|
||||
const bool task_success = infrared_blocking_task_finalize(infrared);
|
||||
|
||||
if(task_success) {
|
||||
scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote);
|
||||
} else {
|
||||
infrared_show_error_message(
|
||||
infrared, "Failed to load\n\"%s\"", furi_string_get_cstr(infrared->file_path));
|
||||
infrared_scene_remote_list_select_and_load(infrared);
|
||||
}
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,15 @@ typedef enum {
|
|||
InfraredRpcStateSending,
|
||||
} InfraredRpcState;
|
||||
|
||||
static int32_t infrared_scene_rpc_task_callback(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
const bool success =
|
||||
infrared_remote_load(infrared->remote, furi_string_get_cstr(infrared->file_path));
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished);
|
||||
return success;
|
||||
}
|
||||
|
||||
void infrared_scene_rpc_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
|
@ -21,7 +30,8 @@ void infrared_scene_rpc_on_enter(void* context) {
|
|||
popup_set_context(popup, context);
|
||||
popup_set_callback(popup, infrared_popup_closed_callback);
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup);
|
||||
view_stack_add_view(infrared->view_stack, popup_get_view(infrared->popup));
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
|
||||
|
||||
scene_manager_set_scene_state(infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateIdle);
|
||||
|
||||
|
@ -33,76 +43,88 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
|||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
InfraredRpcState state =
|
||||
InfraredAppState* app_state = &infrared->app_state;
|
||||
InfraredRpcState rpc_state =
|
||||
scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneRpc);
|
||||
if(event.event == InfraredCustomEventTypeBackPressed) {
|
||||
view_dispatcher_stop(infrared->view_dispatcher);
|
||||
} else if(event.event == InfraredCustomEventTypePopupClosed) {
|
||||
view_dispatcher_stop(infrared->view_dispatcher);
|
||||
} else if(event.event == InfraredCustomEventTypeRpcLoadFile) {
|
||||
bool result = false;
|
||||
if(state == InfraredRpcStateIdle) {
|
||||
result = infrared_remote_load(
|
||||
infrared->remote, furi_string_get_cstr(infrared->file_path));
|
||||
if(result) {
|
||||
scene_manager_set_scene_state(
|
||||
infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded);
|
||||
}
|
||||
}
|
||||
const char* remote_name = infrared_remote_get_name(infrared->remote);
|
||||
|
||||
infrared_text_store_set(infrared, 0, "loaded\n%s", remote_name);
|
||||
if(event.event == InfraredCustomEventTypeRpcLoadFile) {
|
||||
if(rpc_state == InfraredRpcStateIdle) {
|
||||
// Load the remote in a separate thread
|
||||
infrared_blocking_task_start(infrared, infrared_scene_rpc_task_callback);
|
||||
}
|
||||
|
||||
} else if(event.event == InfraredCustomEventTypeTaskFinished) {
|
||||
const bool task_success = infrared_blocking_task_finalize(infrared);
|
||||
|
||||
if(task_success) {
|
||||
const char* remote_name = infrared_remote_get_name(infrared->remote);
|
||||
infrared_text_store_set(infrared, 0, "loaded\n%s", remote_name);
|
||||
scene_manager_set_scene_state(
|
||||
infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded);
|
||||
|
||||
} else {
|
||||
infrared_text_store_set(
|
||||
infrared, 0, "failed to load\n%s", furi_string_get_cstr(infrared->file_path));
|
||||
}
|
||||
|
||||
popup_set_text(
|
||||
infrared->popup, infrared->text_store[0], 89, 44, AlignCenter, AlignTop);
|
||||
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, result);
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, task_success);
|
||||
|
||||
} else if(
|
||||
event.event == InfraredCustomEventTypeRpcButtonPressName ||
|
||||
event.event == InfraredCustomEventTypeRpcButtonPressIndex) {
|
||||
bool result = false;
|
||||
if(state == InfraredRpcStateLoaded) {
|
||||
if(rpc_state == InfraredRpcStateLoaded) {
|
||||
if(event.event == InfraredCustomEventTypeRpcButtonPressName) {
|
||||
const char* button_name = furi_string_get_cstr(infrared->button_name);
|
||||
size_t index;
|
||||
const bool index_found =
|
||||
infrared_remote_get_signal_index(infrared->remote, button_name, &index);
|
||||
infrared->app_state.current_button_index =
|
||||
index_found ? (signed)index : InfraredButtonIndexNone;
|
||||
app_state->current_button_index = index_found ? (signed)index :
|
||||
InfraredButtonIndexNone;
|
||||
FURI_LOG_D(TAG, "Sending signal with name \"%s\"", button_name);
|
||||
} else {
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"Sending signal with index \"%ld\"",
|
||||
infrared->app_state.current_button_index);
|
||||
TAG, "Sending signal with index \"%ld\"", app_state->current_button_index);
|
||||
}
|
||||
if(infrared->app_state.current_button_index != InfraredButtonIndexNone) {
|
||||
infrared_tx_start_button_index(
|
||||
infrared, infrared->app_state.current_button_index);
|
||||
infrared_tx_start_button_index(infrared, app_state->current_button_index);
|
||||
scene_manager_set_scene_state(
|
||||
infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateSending);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, result);
|
||||
|
||||
} else if(event.event == InfraredCustomEventTypeRpcButtonRelease) {
|
||||
bool result = false;
|
||||
if(state == InfraredRpcStateSending) {
|
||||
|
||||
if(rpc_state == InfraredRpcStateSending) {
|
||||
infrared_tx_stop(infrared);
|
||||
result = true;
|
||||
scene_manager_set_scene_state(
|
||||
infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded);
|
||||
}
|
||||
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, result);
|
||||
} else if(event.event == InfraredCustomEventTypeRpcExit) {
|
||||
scene_manager_stop(infrared->scene_manager);
|
||||
view_dispatcher_stop(infrared->view_dispatcher);
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, true);
|
||||
} else if(event.event == InfraredCustomEventTypeRpcSessionClose) {
|
||||
|
||||
} else if(
|
||||
event.event == InfraredCustomEventTypeRpcExit ||
|
||||
event.event == InfraredCustomEventTypeRpcSessionClose ||
|
||||
event.event == InfraredCustomEventTypePopupClosed) {
|
||||
scene_manager_stop(infrared->scene_manager);
|
||||
view_dispatcher_stop(infrared->view_dispatcher);
|
||||
|
||||
if(event.event == InfraredCustomEventTypeRpcExit) {
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, true);
|
||||
}
|
||||
}
|
||||
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
|
@ -112,5 +134,7 @@ void infrared_scene_rpc_on_exit(void* context) {
|
|||
InfraredRpcStateSending) {
|
||||
infrared_tx_stop(infrared);
|
||||
}
|
||||
|
||||
view_stack_remove_view(infrared->view_stack, popup_get_view(infrared->popup));
|
||||
popup_reset(infrared->popup);
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
#include "common/infrared_scene_universal_common.h"
|
||||
|
||||
void infrared_scene_universal_ac_on_enter(void* context) {
|
||||
infrared_scene_universal_common_on_enter(context);
|
||||
|
||||
InfraredApp* infrared = context;
|
||||
ButtonPanel* button_panel = infrared->button_panel;
|
||||
InfraredBruteForce* brute_force = infrared->brute_force;
|
||||
|
@ -122,16 +120,7 @@ void infrared_scene_universal_ac_on_enter(void* context) {
|
|||
|
||||
button_panel_add_label(button_panel, 4, 10, FontPrimary, "AC remote");
|
||||
|
||||
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical);
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
|
||||
|
||||
infrared_show_loading_popup(infrared, true);
|
||||
bool success = infrared_brute_force_calculate_messages(brute_force);
|
||||
infrared_show_loading_popup(infrared, false);
|
||||
|
||||
if(!success) {
|
||||
scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases);
|
||||
}
|
||||
infrared_scene_universal_common_on_enter(context);
|
||||
}
|
||||
|
||||
bool infrared_scene_universal_ac_on_event(void* context, SceneManagerEvent event) {
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
#include "common/infrared_scene_universal_common.h"
|
||||
|
||||
void infrared_scene_universal_audio_on_enter(void* context) {
|
||||
infrared_scene_universal_common_on_enter(context);
|
||||
|
||||
InfraredApp* infrared = context;
|
||||
ButtonPanel* button_panel = infrared->button_panel;
|
||||
InfraredBruteForce* brute_force = infrared->brute_force;
|
||||
|
@ -119,16 +117,7 @@ void infrared_scene_universal_audio_on_enter(void* context) {
|
|||
button_panel_add_label(button_panel, 1, 10, FontPrimary, "Mus. remote");
|
||||
button_panel_add_icon(button_panel, 34, 56, &I_vol_ac_text_30x30);
|
||||
|
||||
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical);
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
|
||||
|
||||
infrared_show_loading_popup(infrared, true);
|
||||
bool success = infrared_brute_force_calculate_messages(brute_force);
|
||||
infrared_show_loading_popup(infrared, false);
|
||||
|
||||
if(!success) {
|
||||
scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases);
|
||||
}
|
||||
infrared_scene_universal_common_on_enter(context);
|
||||
}
|
||||
|
||||
bool infrared_scene_universal_audio_on_event(void* context, SceneManagerEvent event) {
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
#include "common/infrared_scene_universal_common.h"
|
||||
|
||||
void infrared_scene_universal_projector_on_enter(void* context) {
|
||||
infrared_scene_universal_common_on_enter(context);
|
||||
|
||||
InfraredApp* infrared = context;
|
||||
ButtonPanel* button_panel = infrared->button_panel;
|
||||
InfraredBruteForce* brute_force = infrared->brute_force;
|
||||
|
@ -68,16 +66,7 @@ void infrared_scene_universal_projector_on_enter(void* context) {
|
|||
button_panel_add_label(button_panel, 3, 11, FontPrimary, "Proj. remote");
|
||||
button_panel_add_icon(button_panel, 17, 72, &I_vol_ac_text_30x30);
|
||||
|
||||
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical);
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
|
||||
|
||||
infrared_show_loading_popup(infrared, true);
|
||||
bool success = infrared_brute_force_calculate_messages(brute_force);
|
||||
infrared_show_loading_popup(infrared, false);
|
||||
|
||||
if(!success) {
|
||||
scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases);
|
||||
}
|
||||
infrared_scene_universal_common_on_enter(context);
|
||||
}
|
||||
|
||||
bool infrared_scene_universal_projector_on_event(void* context, SceneManagerEvent event) {
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
#include "common/infrared_scene_universal_common.h"
|
||||
|
||||
void infrared_scene_universal_tv_on_enter(void* context) {
|
||||
infrared_scene_universal_common_on_enter(context);
|
||||
|
||||
InfraredApp* infrared = context;
|
||||
ButtonPanel* button_panel = infrared->button_panel;
|
||||
InfraredBruteForce* brute_force = infrared->brute_force;
|
||||
|
@ -95,16 +93,7 @@ void infrared_scene_universal_tv_on_enter(void* context) {
|
|||
|
||||
button_panel_add_label(button_panel, 5, 10, FontPrimary, "TV remote");
|
||||
|
||||
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical);
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
|
||||
|
||||
infrared_show_loading_popup(infrared, true);
|
||||
bool success = infrared_brute_force_calculate_messages(brute_force);
|
||||
infrared_show_loading_popup(infrared, false);
|
||||
|
||||
if(!success) {
|
||||
scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases);
|
||||
}
|
||||
infrared_scene_universal_common_on_enter(context);
|
||||
}
|
||||
|
||||
bool infrared_scene_universal_tv_on_event(void* context, SceneManagerEvent event) {
|
||||
|
|
Loading…
Reference in a new issue