[FL-2627] Flipper applications: SDK, build and debug system (#1387)

* Added support for running applications from SD card (FAPs - Flipper Application Packages)
* Added plugin_dist target for fbt to build FAPs
* All apps of type FlipperAppType.EXTERNAL and FlipperAppType.PLUGIN are built as FAPs by default
* Updated VSCode configuration for new fbt features - re-deploy stock configuration to use them
* Added debugging support for FAPs with fbt debug & VSCode
* Added public firmware API with automated versioning

Co-authored-by: hedger <hedger@users.noreply.github.com>
Co-authored-by: SG <who.just.the.doctor@gmail.com>
Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
SG
2022-09-15 02:11:38 +10:00
committed by Aleksandr Kutuzov
parent 0f6f9ad52e
commit b9a766d909
895 changed files with 8862 additions and 1465 deletions

View File

@@ -0,0 +1,91 @@
#include "../../infrared_i.h"
#include <dolphin/dolphin.h>
void infrared_scene_universal_common_item_callback(void* context, uint32_t index) {
Infrared* infrared = context;
uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeButtonSelected, index);
view_dispatcher_send_custom_event(infrared->view_dispatcher, event);
}
static void infrared_scene_universal_common_progress_back_callback(void* context) {
Infrared* infrared = context;
uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeBackPressed, -1);
view_dispatcher_send_custom_event(infrared->view_dispatcher, event);
}
static void infrared_scene_universal_common_show_popup(Infrared* infrared, uint32_t record_count) {
ViewStack* view_stack = infrared->view_stack;
InfraredProgressView* progress = infrared->progress;
infrared_progress_view_set_progress_total(progress, record_count);
infrared_progress_view_set_back_callback(
progress, infrared_scene_universal_common_progress_back_callback, infrared);
view_stack_add_view(view_stack, infrared_progress_view_get_view(progress));
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend);
}
static void infrared_scene_universal_common_hide_popup(Infrared* infrared) {
ViewStack* view_stack = infrared->view_stack;
InfraredProgressView* progress = infrared->progress;
view_stack_remove_view(view_stack, infrared_progress_view_get_view(progress));
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop);
}
void infrared_scene_universal_common_on_enter(void* context) {
Infrared* infrared = context;
view_stack_add_view(infrared->view_stack, button_panel_get_view(infrared->button_panel));
}
bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent event) {
Infrared* infrared = context;
SceneManager* scene_manager = infrared->scene_manager;
InfraredBruteForce* brute_force = infrared->brute_force;
bool consumed = false;
if(infrared_brute_force_is_started(brute_force)) {
if(event.type == SceneManagerEventTypeTick) {
bool success = infrared_brute_force_send_next(brute_force);
if(success) {
success = infrared_progress_view_increase_progress(infrared->progress);
}
if(!success) {
infrared_brute_force_stop(brute_force);
infrared_scene_universal_common_hide_popup(infrared);
}
consumed = true;
} else if(event.type == SceneManagerEventTypeCustom) {
if(infrared_custom_event_get_type(event.event) == InfraredCustomEventTypeBackPressed) {
infrared_brute_force_stop(brute_force);
infrared_scene_universal_common_hide_popup(infrared);
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) {
uint32_t record_count;
if(infrared_brute_force_start(
brute_force, infrared_custom_event_get_value(event.event), &record_count)) {
DOLPHIN_DEED(DolphinDeedIrBruteForce);
infrared_scene_universal_common_show_popup(infrared, record_count);
} else {
scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases);
}
consumed = true;
}
}
}
return consumed;
}
void infrared_scene_universal_common_on_exit(void* context) {
Infrared* infrared = context;
ButtonPanel* button_panel = infrared->button_panel;
view_stack_remove_view(infrared->view_stack, button_panel_get_view(button_panel));
button_panel_reset(button_panel);
}

View File

@@ -0,0 +1,8 @@
#pragma once
#include <gui/scene_manager.h>
void infrared_scene_universal_common_on_enter(void* context);
bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent event);
void infrared_scene_universal_common_on_exit(void* context);
void infrared_scene_universal_common_item_callback(void* context, uint32_t index);

View File

@@ -0,0 +1,30 @@
#include "infrared_scene.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const infrared_on_enter_handlers[])(void*) = {
#include "infrared_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_event handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
bool (*const infrared_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "infrared_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_exit handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
void (*const infrared_on_exit_handlers[])(void* context) = {
#include "infrared_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers infrared_scene_handlers = {
.on_enter_handlers = infrared_on_enter_handlers,
.on_event_handlers = infrared_on_event_handlers,
.on_exit_handlers = infrared_on_exit_handlers,
.scene_num = InfraredSceneNum,
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) InfraredScene##id,
typedef enum {
#include "infrared_scene_config.h"
InfraredSceneNum,
} InfraredScene;
#undef ADD_SCENE
extern const SceneManagerHandlers infrared_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "infrared_scene_config.h"
#undef ADD_SCENE
// Generate scene on_event handlers declaration
#define ADD_SCENE(prefix, name, id) \
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
#include "infrared_scene_config.h"
#undef ADD_SCENE
// Generate scene on_exit handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
#include "infrared_scene_config.h"
#undef ADD_SCENE

View File

@@ -0,0 +1,59 @@
#include "../infrared_i.h"
static void infrared_scene_dialog_result_callback(DialogExResult result, void* context) {
Infrared* infrared = context;
view_dispatcher_send_custom_event(infrared->view_dispatcher, result);
}
void infrared_scene_ask_back_on_enter(void* context) {
Infrared* infrared = context;
DialogEx* dialog_ex = infrared->dialog_ex;
if(infrared->app_state.is_learning_new_remote) {
dialog_ex_set_header(dialog_ex, "Exit to Infrared Menu?", 64, 0, AlignCenter, AlignTop);
} else {
dialog_ex_set_header(dialog_ex, "Exit to Remote Menu?", 64, 0, AlignCenter, AlignTop);
}
dialog_ex_set_text(
dialog_ex, "All unsaved data\nwill be lost!", 64, 31, AlignCenter, AlignCenter);
dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
dialog_ex_set_left_button_text(dialog_ex, "Exit");
dialog_ex_set_center_button_text(dialog_ex, NULL);
dialog_ex_set_right_button_text(dialog_ex, "Stay");
dialog_ex_set_result_callback(dialog_ex, infrared_scene_dialog_result_callback);
dialog_ex_set_context(dialog_ex, context);
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDialogEx);
}
bool infrared_scene_ask_back_on_event(void* context, SceneManagerEvent event) {
Infrared* infrared = context;
SceneManager* scene_manager = infrared->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeBack) {
consumed = true;
} else if(event.type == SceneManagerEventTypeCustom) {
if(event.event == DialogExResultLeft) {
if(infrared->app_state.is_learning_new_remote) {
scene_manager_search_and_switch_to_previous_scene(
scene_manager, InfraredSceneStart);
} else {
scene_manager_search_and_switch_to_previous_scene(
scene_manager, InfraredSceneRemote);
}
consumed = true;
} else if(event.event == DialogExResultRight) {
scene_manager_previous_scene(scene_manager);
consumed = true;
}
}
return consumed;
}
void infrared_scene_ask_back_on_exit(void* context) {
Infrared* infrared = context;
dialog_ex_reset(infrared->dialog_ex);
}

View File

@@ -0,0 +1,48 @@
#include "../infrared_i.h"
static void infrared_scene_dialog_result_callback(DialogExResult result, void* context) {
Infrared* infrared = context;
view_dispatcher_send_custom_event(infrared->view_dispatcher, result);
}
void infrared_scene_ask_retry_on_enter(void* context) {
Infrared* infrared = context;
DialogEx* dialog_ex = infrared->dialog_ex;
dialog_ex_set_header(dialog_ex, "Return to Reading?", 64, 0, AlignCenter, AlignTop);
dialog_ex_set_text(
dialog_ex, "All unsaved data\nwill be lost!", 64, 31, AlignCenter, AlignCenter);
dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
dialog_ex_set_left_button_text(dialog_ex, "Exit");
dialog_ex_set_center_button_text(dialog_ex, NULL);
dialog_ex_set_right_button_text(dialog_ex, "Stay");
dialog_ex_set_result_callback(dialog_ex, infrared_scene_dialog_result_callback);
dialog_ex_set_context(dialog_ex, context);
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDialogEx);
}
bool infrared_scene_ask_retry_on_event(void* context, SceneManagerEvent event) {
Infrared* infrared = context;
SceneManager* scene_manager = infrared->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeBack) {
consumed = true;
} else if(event.type == SceneManagerEventTypeCustom) {
if(event.event == DialogExResultLeft) {
scene_manager_search_and_switch_to_previous_scene(scene_manager, InfraredSceneLearn);
consumed = true;
} else if(event.event == DialogExResultRight) {
scene_manager_previous_scene(scene_manager);
consumed = true;
}
}
return consumed;
}
void infrared_scene_ask_retry_on_exit(void* context) {
Infrared* infrared = context;
dialog_ex_reset(infrared->dialog_ex);
}

View File

@@ -0,0 +1,20 @@
ADD_SCENE(infrared, start, Start)
ADD_SCENE(infrared, ask_back, AskBack)
ADD_SCENE(infrared, ask_retry, AskRetry)
ADD_SCENE(infrared, edit, Edit)
ADD_SCENE(infrared, edit_delete, EditDelete)
ADD_SCENE(infrared, edit_delete_done, EditDeleteDone)
ADD_SCENE(infrared, edit_button_select, EditButtonSelect)
ADD_SCENE(infrared, edit_rename, EditRename)
ADD_SCENE(infrared, edit_rename_done, EditRenameDone)
ADD_SCENE(infrared, learn, Learn)
ADD_SCENE(infrared, learn_done, LearnDone)
ADD_SCENE(infrared, learn_enter_name, LearnEnterName)
ADD_SCENE(infrared, learn_success, LearnSuccess)
ADD_SCENE(infrared, remote, Remote)
ADD_SCENE(infrared, remote_list, RemoteList)
ADD_SCENE(infrared, universal, Universal)
ADD_SCENE(infrared, universal_tv, UniversalTV)
ADD_SCENE(infrared, debug, Debug)
ADD_SCENE(infrared, error_databases, ErrorDatabases)
ADD_SCENE(infrared, rpc, Rpc)

View File

@@ -0,0 +1,69 @@
#include "../infrared_i.h"
void infrared_scene_debug_on_enter(void* context) {
Infrared* infrared = context;
InfraredWorker* worker = infrared->worker;
infrared_worker_rx_set_received_signal_callback(
worker, infrared_signal_received_callback, context);
infrared_worker_rx_enable_blink_on_receiving(worker, true);
infrared_worker_rx_start(worker);
infrared_debug_view_set_text(infrared->debug_view, "Received signals\nwill appear here");
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDebugView);
}
bool infrared_scene_debug_on_event(void* context, SceneManagerEvent event) {
Infrared* infrared = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == InfraredCustomEventTypeSignalReceived) {
InfraredDebugView* debug_view = infrared->debug_view;
InfraredSignal* signal = infrared->received_signal;
if(infrared_signal_is_raw(signal)) {
InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
infrared_debug_view_set_text(debug_view, "RAW\n%d samples\n", raw->timings_size);
printf("RAW, %d samples:\r\n", raw->timings_size);
for(size_t i = 0; i < raw->timings_size; ++i) {
printf("%lu ", raw->timings[i]);
}
printf("\r\n");
} else {
InfraredMessage* message = infrared_signal_get_message(signal);
infrared_debug_view_set_text(
debug_view,
"%s\nA:0x%0*lX\nC:0x%0*lX\n%s\n",
infrared_get_protocol_name(message->protocol),
ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4),
message->address,
ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4),
message->command,
message->repeat ? " R" : "");
printf(
"== %s, A:0x%0*lX, C:0x%0*lX%s ==\r\n",
infrared_get_protocol_name(message->protocol),
ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4),
message->address,
ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4),
message->command,
message->repeat ? " R" : "");
}
consumed = true;
}
}
return consumed;
}
void infrared_scene_debug_on_exit(void* context) {
Infrared* infrared = context;
InfraredWorker* worker = infrared->worker;
infrared_worker_rx_stop(worker);
infrared_worker_rx_enable_blink_on_receiving(worker, false);
infrared_worker_rx_set_received_signal_callback(worker, NULL, NULL);
}

View File

@@ -0,0 +1,101 @@
#include "../infrared_i.h"
typedef enum {
SubmenuIndexAddButton,
SubmenuIndexRenameButton,
SubmenuIndexDeleteButton,
SubmenuIndexRenameRemote,
SubmenuIndexDeleteRemote,
} SubmenuIndex;
static void infrared_scene_edit_submenu_callback(void* context, uint32_t index) {
Infrared* infrared = context;
view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
}
void infrared_scene_edit_on_enter(void* context) {
Infrared* infrared = context;
Submenu* submenu = infrared->submenu;
SceneManager* scene_manager = infrared->scene_manager;
submenu_add_item(
submenu,
"Add Button",
SubmenuIndexAddButton,
infrared_scene_edit_submenu_callback,
context);
submenu_add_item(
submenu,
"Rename Button",
SubmenuIndexRenameButton,
infrared_scene_edit_submenu_callback,
context);
submenu_add_item(
submenu,
"Delete Button",
SubmenuIndexDeleteButton,
infrared_scene_edit_submenu_callback,
context);
submenu_add_item(
submenu,
"Rename Remote",
SubmenuIndexRenameRemote,
infrared_scene_edit_submenu_callback,
context);
submenu_add_item(
submenu,
"Delete Remote",
SubmenuIndexDeleteRemote,
infrared_scene_edit_submenu_callback,
context);
const uint32_t submenu_index = scene_manager_get_scene_state(scene_manager, InfraredSceneEdit);
submenu_set_selected_item(submenu, submenu_index);
scene_manager_set_scene_state(scene_manager, InfraredSceneEdit, SubmenuIndexAddButton);
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu);
}
bool infrared_scene_edit_on_event(void* context, SceneManagerEvent event) {
Infrared* infrared = context;
SceneManager* scene_manager = infrared->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
const uint32_t submenu_index = event.event;
scene_manager_set_scene_state(scene_manager, InfraredSceneEdit, submenu_index);
if(submenu_index == SubmenuIndexAddButton) {
infrared->app_state.is_learning_new_remote = false;
scene_manager_next_scene(scene_manager, InfraredSceneLearn);
consumed = true;
} else if(submenu_index == SubmenuIndexRenameButton) {
infrared->app_state.edit_target = InfraredEditTargetButton;
infrared->app_state.edit_mode = InfraredEditModeRename;
scene_manager_next_scene(scene_manager, InfraredSceneEditButtonSelect);
consumed = true;
} else if(submenu_index == SubmenuIndexDeleteButton) {
infrared->app_state.edit_target = InfraredEditTargetButton;
infrared->app_state.edit_mode = InfraredEditModeDelete;
scene_manager_next_scene(scene_manager, InfraredSceneEditButtonSelect);
consumed = true;
} else if(submenu_index == SubmenuIndexRenameRemote) {
infrared->app_state.edit_target = InfraredEditTargetRemote;
infrared->app_state.edit_mode = InfraredEditModeRename;
scene_manager_next_scene(scene_manager, InfraredSceneEditRename);
consumed = true;
} else if(submenu_index == SubmenuIndexDeleteRemote) {
infrared->app_state.edit_target = InfraredEditTargetRemote;
infrared->app_state.edit_mode = InfraredEditModeDelete;
scene_manager_next_scene(scene_manager, InfraredSceneEditDelete);
consumed = true;
}
}
return consumed;
}
void infrared_scene_edit_on_exit(void* context) {
Infrared* infrared = context;
submenu_reset(infrared->submenu);
}

View File

@@ -0,0 +1,63 @@
#include "../infrared_i.h"
static void infrared_scene_edit_button_select_submenu_callback(void* context, uint32_t index) {
Infrared* infrared = context;
view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
}
void infrared_scene_edit_button_select_on_enter(void* context) {
Infrared* infrared = context;
Submenu* submenu = infrared->submenu;
InfraredRemote* remote = infrared->remote;
InfraredAppState* app_state = &infrared->app_state;
const char* header = infrared->app_state.edit_mode == InfraredEditModeRename ?
"Rename Button:" :
"Delete Button:";
submenu_set_header(submenu, header);
const size_t button_count = infrared_remote_get_button_count(remote);
for(size_t i = 0; i < button_count; ++i) {
InfraredRemoteButton* button = infrared_remote_get_button(remote, i);
submenu_add_item(
submenu,
infrared_remote_button_get_name(button),
i,
infrared_scene_edit_button_select_submenu_callback,
context);
}
if(button_count && app_state->current_button_index != InfraredButtonIndexNone) {
submenu_set_selected_item(submenu, app_state->current_button_index);
app_state->current_button_index = InfraredButtonIndexNone;
}
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu);
}
bool infrared_scene_edit_button_select_on_event(void* context, SceneManagerEvent event) {
Infrared* infrared = context;
InfraredAppState* app_state = &infrared->app_state;
SceneManager* scene_manager = infrared->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
app_state->current_button_index = event.event;
const InfraredEditMode edit_mode = app_state->edit_mode;
if(edit_mode == InfraredEditModeRename) {
scene_manager_next_scene(scene_manager, InfraredSceneEditRename);
} else if(edit_mode == InfraredEditModeDelete) {
scene_manager_next_scene(scene_manager, InfraredSceneEditDelete);
} else {
furi_assert(0);
}
consumed = true;
}
return consumed;
}
void infrared_scene_edit_button_select_on_exit(void* context) {
Infrared* infrared = context;
submenu_reset(infrared->submenu);
}

View File

@@ -0,0 +1,112 @@
#include "../infrared_i.h"
static void
infrared_scene_edit_delete_dialog_result_callback(DialogExResult result, void* context) {
Infrared* infrared = context;
view_dispatcher_send_custom_event(infrared->view_dispatcher, result);
}
void infrared_scene_edit_delete_on_enter(void* context) {
Infrared* 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) {
int32_t current_button_index = infrared->app_state.current_button_index;
furi_assert(current_button_index != InfraredButtonIndexNone);
dialog_ex_set_header(dialog_ex, "Delete Button?", 64, 0, AlignCenter, AlignTop);
InfraredRemoteButton* current_button =
infrared_remote_get_button(remote, current_button_index);
InfraredSignal* signal = infrared_remote_button_get_signal(current_button);
if(infrared_signal_is_raw(signal)) {
const InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
infrared_text_store_set(
infrared,
0,
"%s\nRAW\n%ld samples",
infrared_remote_button_get_name(current_button),
raw->timings_size);
} else {
const InfraredMessage* message = infrared_signal_get_message(signal);
infrared_text_store_set(
infrared,
0,
"%s\n%s\nA=0x%0*lX C=0x%0*lX",
infrared_remote_button_get_name(current_button),
infrared_get_protocol_name(message->protocol),
ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4),
message->address,
ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4),
message->command);
}
} else if(edit_target == InfraredEditTargetRemote) {
dialog_ex_set_header(dialog_ex, "Delete Remote?", 64, 0, AlignCenter, AlignTop);
infrared_text_store_set(
infrared,
0,
"%s\n with %lu buttons",
infrared_remote_get_name(remote),
infrared_remote_get_button_count(remote));
} else {
furi_assert(0);
}
dialog_ex_set_text(dialog_ex, infrared->text_store[0], 64, 31, AlignCenter, AlignCenter);
dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
dialog_ex_set_left_button_text(dialog_ex, "Cancel");
dialog_ex_set_right_button_text(dialog_ex, "Delete");
dialog_ex_set_result_callback(dialog_ex, infrared_scene_edit_delete_dialog_result_callback);
dialog_ex_set_context(dialog_ex, context);
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDialogEx);
}
bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event) {
Infrared* infrared = context;
SceneManager* scene_manager = infrared->scene_manager;
bool consumed = false;
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;
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);
success = infrared_remote_delete_button(remote, app_state->current_button_index);
app_state->current_button_index = InfraredButtonIndexNone;
} else if(edit_target == InfraredEditTargetRemote) {
success = infrared_remote_remove(remote);
app_state->current_button_index = InfraredButtonIndexNone;
} else {
furi_assert(0);
}
if(success) {
scene_manager_next_scene(scene_manager, InfraredSceneEditDeleteDone);
} else {
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;
}
}
return consumed;
}
void infrared_scene_edit_delete_on_exit(void* context) {
Infrared* infrared = context;
UNUSED(infrared);
}

View File

@@ -0,0 +1,48 @@
#include "../infrared_i.h"
void infrared_scene_edit_delete_done_on_enter(void* context) {
Infrared* infrared = context;
Popup* popup = infrared->popup;
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
popup_set_callback(popup, infrared_popup_closed_callback);
popup_set_context(popup, context);
popup_set_timeout(popup, 1500);
popup_enable_timeout(popup);
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup);
}
bool infrared_scene_edit_delete_done_on_event(void* context, SceneManagerEvent event) {
Infrared* infrared = context;
SceneManager* scene_manager = infrared->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == InfraredCustomEventTypePopupClosed) {
const InfraredEditTarget edit_target = infrared->app_state.edit_target;
if(edit_target == InfraredEditTargetButton) {
scene_manager_search_and_switch_to_previous_scene(
scene_manager, InfraredSceneRemote);
} else if(edit_target == InfraredEditTargetRemote) {
const uint32_t possible_scenes[] = {InfraredSceneStart, InfraredSceneRemoteList};
if(!scene_manager_search_and_switch_to_previous_scene_one_of(
scene_manager, possible_scenes, COUNT_OF(possible_scenes))) {
view_dispatcher_stop(infrared->view_dispatcher);
}
} else {
furi_assert(0);
}
consumed = true;
}
}
return consumed;
}
void infrared_scene_edit_delete_done_on_exit(void* context) {
Infrared* infrared = context;
UNUSED(infrared);
}

View File

@@ -0,0 +1,107 @@
#include "../infrared_i.h"
#include <string.h>
#include <toolbox/path.h>
void infrared_scene_edit_rename_on_enter(void* context) {
Infrared* infrared = context;
InfraredRemote* remote = infrared->remote;
TextInput* text_input = infrared->text_input;
size_t enter_name_length = 0;
const InfraredEditTarget edit_target = infrared->app_state.edit_target;
if(edit_target == InfraredEditTargetButton) {
text_input_set_header_text(text_input, "Name the button");
const int32_t current_button_index = infrared->app_state.current_button_index;
furi_assert(current_button_index != InfraredButtonIndexNone);
InfraredRemoteButton* current_button =
infrared_remote_get_button(remote, current_button_index);
enter_name_length = INFRARED_MAX_BUTTON_NAME_LENGTH;
strncpy(
infrared->text_store[0],
infrared_remote_button_get_name(current_button),
enter_name_length);
} else if(edit_target == InfraredEditTargetRemote) {
text_input_set_header_text(text_input, "Name the remote");
enter_name_length = INFRARED_MAX_REMOTE_NAME_LENGTH;
strncpy(infrared->text_store[0], infrared_remote_get_name(remote), enter_name_length);
string_t folder_path;
string_init(folder_path);
if(string_end_with_str_p(infrared->file_path, INFRARED_APP_EXTENSION)) {
path_extract_dirname(string_get_cstr(infrared->file_path), folder_path);
}
ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
string_get_cstr(folder_path),
INFRARED_APP_EXTENSION,
infrared_remote_get_name(remote));
text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
string_clear(folder_path);
} else {
furi_assert(0);
}
text_input_set_result_callback(
text_input,
infrared_text_input_callback,
context,
infrared->text_store[0],
enter_name_length,
false);
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewTextInput);
}
bool infrared_scene_edit_rename_on_event(void* context, SceneManagerEvent event) {
Infrared* 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);
success = infrared_remote_rename_button(
remote, infrared->text_store[0], current_button_index);
app_state->current_button_index = InfraredButtonIndexNone;
} else if(edit_target == InfraredEditTargetRemote) {
success = infrared_rename_current_remote(infrared, infrared->text_store[0]);
} else {
furi_assert(0);
}
if(success) {
scene_manager_next_scene(scene_manager, InfraredSceneEditRenameDone);
} else {
scene_manager_search_and_switch_to_previous_scene(
scene_manager, InfraredSceneRemoteList);
}
consumed = true;
}
}
return consumed;
}
void infrared_scene_edit_rename_on_exit(void* context) {
Infrared* infrared = context;
TextInput* text_input = infrared->text_input;
void* validator_context = text_input_get_validator_callback_context(text_input);
text_input_set_validator(text_input, NULL, NULL);
if(validator_context) {
validator_is_file_free((ValidatorIsFile*)validator_context);
}
}

View File

@@ -0,0 +1,38 @@
#include "../infrared_i.h"
void infrared_scene_edit_rename_done_on_enter(void* context) {
Infrared* infrared = context;
Popup* popup = infrared->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
popup_set_callback(popup, infrared_popup_closed_callback);
popup_set_context(popup, context);
popup_set_timeout(popup, 1500);
popup_enable_timeout(popup);
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup);
}
bool infrared_scene_edit_rename_done_on_event(void* context, SceneManagerEvent event) {
Infrared* infrared = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == InfraredCustomEventTypePopupClosed) {
if(!scene_manager_search_and_switch_to_previous_scene(
infrared->scene_manager, InfraredSceneRemote)) {
scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote);
}
consumed = true;
}
}
return consumed;
}
void infrared_scene_edit_rename_done_on_exit(void* context) {
Infrared* infrared = context;
UNUSED(infrared);
}

View File

@@ -0,0 +1,37 @@
#include "../infrared_i.h"
void infrared_scene_error_databases_on_enter(void* context) {
Infrared* infrared = context;
Popup* popup = infrared->popup;
popup_set_icon(popup, 5, 11, &I_SDQuestion_35x43);
popup_set_text(
popup, "Function requires\nSD card with fresh\ndatabases.", 47, 17, AlignLeft, AlignTop);
popup_set_context(popup, context);
popup_set_callback(popup, infrared_popup_closed_callback);
infrared_play_notification_message(infrared, InfraredNotificationMessageYellowOn);
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup);
}
bool infrared_scene_error_databases_on_event(void* context, SceneManagerEvent event) {
Infrared* infrared = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == InfraredCustomEventTypePopupClosed) {
scene_manager_search_and_switch_to_previous_scene(
infrared->scene_manager, InfraredSceneUniversal);
consumed = true;
}
}
return consumed;
}
void infrared_scene_error_databases_on_exit(void* context) {
Infrared* infrared = context;
popup_reset(infrared->popup);
infrared_play_notification_message(infrared, InfraredNotificationMessageYellowOff);
}

View File

@@ -0,0 +1,45 @@
#include "../infrared_i.h"
void infrared_scene_learn_on_enter(void* context) {
Infrared* infrared = context;
Popup* popup = infrared->popup;
InfraredWorker* worker = infrared->worker;
infrared_worker_rx_set_received_signal_callback(
worker, infrared_signal_received_callback, context);
infrared_worker_rx_start(worker);
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartRead);
popup_set_icon(popup, 0, 32, &I_InfraredLearnShort_128x31);
popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignCenter);
popup_set_text(
popup, "Point the remote at IR port\nand push the button", 5, 10, AlignLeft, AlignCenter);
popup_set_callback(popup, NULL);
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup);
}
bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) {
Infrared* infrared = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == InfraredCustomEventTypeSignalReceived) {
infrared_play_notification_message(infrared, InfraredNotificationMessageSuccess);
scene_manager_next_scene(infrared->scene_manager, InfraredSceneLearnSuccess);
consumed = true;
}
}
return consumed;
}
void infrared_scene_learn_on_exit(void* context) {
Infrared* infrared = context;
Popup* popup = infrared->popup;
infrared_worker_rx_set_received_signal_callback(infrared->worker, NULL, NULL);
infrared_worker_rx_stop(infrared->worker);
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop);
popup_set_icon(popup, 0, 0, NULL);
popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignCenter);
}

View File

@@ -0,0 +1,47 @@
#include "../infrared_i.h"
#include <dolphin/dolphin.h>
void infrared_scene_learn_done_on_enter(void* context) {
Infrared* infrared = context;
Popup* popup = infrared->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
DOLPHIN_DEED(DolphinDeedIrSave);
if(infrared->app_state.is_learning_new_remote) {
popup_set_header(popup, "New remote\ncreated!", 0, 0, AlignLeft, AlignTop);
} else {
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
}
popup_set_callback(popup, infrared_popup_closed_callback);
popup_set_context(popup, context);
popup_set_timeout(popup, 1500);
popup_enable_timeout(popup);
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup);
}
bool infrared_scene_learn_done_on_event(void* context, SceneManagerEvent event) {
Infrared* infrared = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == InfraredCustomEventTypePopupClosed) {
if(!scene_manager_search_and_switch_to_previous_scene(
infrared->scene_manager, InfraredSceneRemote)) {
scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote);
}
consumed = true;
}
}
return consumed;
}
void infrared_scene_learn_done_on_exit(void* context) {
Infrared* infrared = context;
infrared->app_state.is_learning_new_remote = false;
popup_set_header(infrared->popup, NULL, 0, 0, AlignLeft, AlignTop);
}

View File

@@ -0,0 +1,66 @@
#include "../infrared_i.h"
void infrared_scene_learn_enter_name_on_enter(void* context) {
Infrared* infrared = context;
TextInput* text_input = infrared->text_input;
InfraredSignal* signal = infrared->received_signal;
if(infrared_signal_is_raw(signal)) {
InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
infrared_text_store_set(infrared, 0, "RAW_%d", raw->timings_size);
} else {
InfraredMessage* message = infrared_signal_get_message(signal);
infrared_text_store_set(
infrared,
0,
"%.4s_%0*lX",
infrared_get_protocol_name(message->protocol),
ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4),
message->command);
}
text_input_set_header_text(text_input, "Name the button");
text_input_set_result_callback(
text_input,
infrared_text_input_callback,
context,
infrared->text_store[0],
INFRARED_MAX_BUTTON_NAME_LENGTH,
true);
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewTextInput);
}
bool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent event) {
Infrared* infrared = context;
InfraredSignal* signal = infrared->received_signal;
SceneManager* scene_manager = infrared->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == InfraredCustomEventTypeTextEditDone) {
bool success = false;
if(infrared->app_state.is_learning_new_remote) {
success =
infrared_add_remote_with_button(infrared, infrared->text_store[0], signal);
} else {
success =
infrared_remote_add_button(infrared->remote, infrared->text_store[0], signal);
}
if(success) {
scene_manager_next_scene(scene_manager, InfraredSceneLearnDone);
} else {
scene_manager_search_and_switch_to_previous_scene(
scene_manager, InfraredSceneRemoteList);
}
consumed = true;
}
}
return consumed;
}
void infrared_scene_learn_enter_name_on_exit(void* context) {
Infrared* infrared = context;
UNUSED(infrared);
}

View File

@@ -0,0 +1,105 @@
#include "../infrared_i.h"
#include <dolphin/dolphin.h>
static void
infrared_scene_learn_success_dialog_result_callback(DialogExResult result, void* context) {
Infrared* infrared = context;
view_dispatcher_send_custom_event(infrared->view_dispatcher, result);
}
void infrared_scene_learn_success_on_enter(void* context) {
Infrared* infrared = context;
DialogEx* dialog_ex = infrared->dialog_ex;
InfraredSignal* signal = infrared->received_signal;
DOLPHIN_DEED(DolphinDeedIrLearnSuccess);
infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOn);
if(infrared_signal_is_raw(signal)) {
InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
dialog_ex_set_header(dialog_ex, "Unknown", 95, 10, AlignCenter, AlignCenter);
infrared_text_store_set(infrared, 0, "%d samples", raw->timings_size);
dialog_ex_set_text(dialog_ex, infrared->text_store[0], 75, 23, AlignLeft, AlignTop);
} else {
InfraredMessage* message = infrared_signal_get_message(signal);
uint8_t addr_digits =
ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4);
uint8_t cmd_digits =
ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4);
uint8_t max_digits = MAX(addr_digits, cmd_digits);
max_digits = MIN(max_digits, 7);
size_t label_x_offset = 63 + (7 - max_digits) * 3;
infrared_text_store_set(infrared, 0, "%s", infrared_get_protocol_name(message->protocol));
infrared_text_store_set(
infrared,
1,
"A: 0x%0*lX\nC: 0x%0*lX\n",
addr_digits,
message->address,
cmd_digits,
message->command);
dialog_ex_set_header(dialog_ex, infrared->text_store[0], 95, 7, AlignCenter, AlignCenter);
dialog_ex_set_text(
dialog_ex, infrared->text_store[1], label_x_offset, 34, AlignLeft, AlignCenter);
}
dialog_ex_set_left_button_text(dialog_ex, "Retry");
dialog_ex_set_right_button_text(dialog_ex, "Save");
dialog_ex_set_center_button_text(dialog_ex, "Send");
dialog_ex_set_icon(dialog_ex, 0, 1, &I_DolphinReadingSuccess_59x63);
dialog_ex_set_result_callback(dialog_ex, infrared_scene_learn_success_dialog_result_callback);
dialog_ex_set_context(dialog_ex, context);
dialog_ex_enable_extended_events(dialog_ex);
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDialogEx);
}
bool infrared_scene_learn_success_on_event(void* context, SceneManagerEvent event) {
Infrared* infrared = context;
SceneManager* scene_manager = infrared->scene_manager;
const bool is_transmitter_idle = !infrared->app_state.is_transmitting;
bool consumed = false;
if(event.type == SceneManagerEventTypeTick) {
if(is_transmitter_idle) {
infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOn);
}
consumed = true;
} else if(event.type == SceneManagerEventTypeBack) {
if(is_transmitter_idle) {
scene_manager_next_scene(scene_manager, InfraredSceneAskBack);
}
consumed = true;
} else if(event.type == SceneManagerEventTypeCustom) {
if(event.event == DialogExResultLeft) {
if(is_transmitter_idle) {
scene_manager_next_scene(scene_manager, InfraredSceneAskRetry);
}
consumed = true;
} else if(event.event == DialogExResultRight) {
if(is_transmitter_idle) {
scene_manager_next_scene(scene_manager, InfraredSceneLearnEnterName);
}
consumed = true;
} else if(event.event == DialogExPressCenter) {
infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff);
infrared_tx_start_received(infrared);
consumed = true;
} else if(event.event == DialogExReleaseCenter) {
infrared_tx_stop(infrared);
consumed = true;
}
}
return consumed;
}
void infrared_scene_learn_success_on_exit(void* context) {
Infrared* infrared = context;
dialog_ex_reset(infrared->dialog_ex);
infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff);
}

View File

@@ -0,0 +1,121 @@
#include "../infrared_i.h"
typedef enum {
ButtonIndexPlus = -2,
ButtonIndexEdit = -1,
ButtonIndexNA = 0,
} ButtonIndex;
static void
infrared_scene_remote_button_menu_callback(void* context, int32_t index, InputType type) {
Infrared* infrared = context;
uint16_t custom_type;
if(type == InputTypePress) {
custom_type = InfraredCustomEventTypeTransmitStarted;
} else if(type == InputTypeRelease) {
custom_type = InfraredCustomEventTypeTransmitStopped;
} else if(type == InputTypeShort) {
custom_type = InfraredCustomEventTypeMenuSelected;
} else {
furi_crash("Unexpected input type");
}
view_dispatcher_send_custom_event(
infrared->view_dispatcher, infrared_custom_event_pack(custom_type, index));
}
void infrared_scene_remote_on_enter(void* context) {
Infrared* infrared = context;
InfraredRemote* remote = infrared->remote;
ButtonMenu* button_menu = infrared->button_menu;
SceneManager* scene_manager = infrared->scene_manager;
size_t button_count = infrared_remote_get_button_count(remote);
for(size_t i = 0; i < button_count; ++i) {
InfraredRemoteButton* button = infrared_remote_get_button(remote, i);
button_menu_add_item(
button_menu,
infrared_remote_button_get_name(button),
i,
infrared_scene_remote_button_menu_callback,
ButtonMenuItemTypeCommon,
context);
}
button_menu_add_item(
button_menu,
"+",
ButtonIndexPlus,
infrared_scene_remote_button_menu_callback,
ButtonMenuItemTypeControl,
context);
button_menu_add_item(
button_menu,
"Edit",
ButtonIndexEdit,
infrared_scene_remote_button_menu_callback,
ButtonMenuItemTypeControl,
context);
button_menu_set_header(button_menu, infrared_remote_get_name(remote));
const int16_t button_index =
(signed)scene_manager_get_scene_state(scene_manager, InfraredSceneRemote);
button_menu_set_selected_item(button_menu, button_index);
scene_manager_set_scene_state(scene_manager, InfraredSceneRemote, ButtonIndexNA);
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewButtonMenu);
}
bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) {
Infrared* infrared = context;
SceneManager* scene_manager = infrared->scene_manager;
const bool is_transmitter_idle = !infrared->app_state.is_transmitting;
bool consumed = false;
if(event.type == SceneManagerEventTypeBack) {
if(is_transmitter_idle) {
const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart};
consumed = scene_manager_search_and_switch_to_previous_scene_one_of(
scene_manager, possible_scenes, COUNT_OF(possible_scenes));
} else {
consumed = true;
}
} else if(event.type == SceneManagerEventTypeCustom) {
const uint16_t custom_type = infrared_custom_event_get_type(event.event);
const int16_t button_index = infrared_custom_event_get_value(event.event);
if(custom_type == InfraredCustomEventTypeTransmitStarted) {
furi_assert(button_index >= 0);
infrared_tx_start_button_index(infrared, button_index);
consumed = true;
} else if(custom_type == InfraredCustomEventTypeTransmitStopped) {
infrared_tx_stop(infrared);
consumed = true;
} else if(custom_type == InfraredCustomEventTypeMenuSelected) {
furi_assert(button_index < 0);
if(is_transmitter_idle) {
scene_manager_set_scene_state(
scene_manager, InfraredSceneRemote, (unsigned)button_index);
if(button_index == ButtonIndexPlus) {
infrared->app_state.is_learning_new_remote = false;
scene_manager_next_scene(scene_manager, InfraredSceneLearn);
consumed = true;
} else if(button_index == ButtonIndexEdit) {
scene_manager_next_scene(scene_manager, InfraredSceneEdit);
consumed = true;
}
} else {
consumed = true;
}
}
}
return consumed;
}
void infrared_scene_remote_on_exit(void* context) {
Infrared* infrared = context;
button_menu_reset(infrared->button_menu);
}

View File

@@ -0,0 +1,40 @@
#include "../infrared_i.h"
void infrared_scene_remote_list_on_enter(void* context) {
Infrared* infrared = context;
SceneManager* scene_manager = infrared->scene_manager;
ViewDispatcher* view_dispatcher = infrared->view_dispatcher;
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(&browser_options, INFRARED_APP_EXTENSION, &I_ir_10px);
bool success = dialog_file_browser_show(
infrared->dialogs, infrared->file_path, infrared->file_path, &browser_options);
if(success) {
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical);
view_dispatcher_switch_to_view(view_dispatcher, InfraredViewStack);
infrared_show_loading_popup(infrared, true);
success = infrared_remote_load(infrared->remote, infrared->file_path);
infrared_show_loading_popup(infrared, false);
}
if(success) {
scene_manager_next_scene(scene_manager, InfraredSceneRemote);
} else {
scene_manager_previous_scene(scene_manager);
}
}
bool infrared_scene_remote_list_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
bool consumed = false;
return consumed;
}
void infrared_scene_remote_list_on_exit(void* context) {
UNUSED(context);
}

View File

@@ -0,0 +1,100 @@
#include "../infrared_i.h"
#include "gui/canvas.h"
typedef enum {
InfraredRpcStateIdle,
InfraredRpcStateLoaded,
InfraredRpcStateSending,
} InfraredRpcState;
void infrared_scene_rpc_on_enter(void* context) {
Infrared* infrared = context;
Popup* popup = infrared->popup;
popup_set_header(popup, "Infrared", 89, 42, AlignCenter, AlignBottom);
popup_set_text(popup, "RPC mode", 89, 44, AlignCenter, AlignTop);
popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61);
popup_set_context(popup, context);
popup_set_callback(popup, infrared_popup_closed_callback);
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup);
scene_manager_set_scene_state(infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateIdle);
notification_message(infrared->notifications, &sequence_display_backlight_on);
}
bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {
Infrared* infrared = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
InfraredRpcState 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 == InfraredCustomEventTypeRpcLoad) {
bool result = false;
const char* arg = rpc_system_app_get_data(infrared->rpc_ctx);
if(arg && (state == InfraredRpcStateIdle)) {
string_set_str(infrared->file_path, arg);
result = infrared_remote_load(infrared->remote, 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);
popup_set_text(
infrared->popup, infrared->text_store[0], 89, 44, AlignCenter, AlignTop);
rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventLoadFile, result);
} else if(event.event == InfraredCustomEventTypeRpcButtonPress) {
bool result = false;
const char* arg = rpc_system_app_get_data(infrared->rpc_ctx);
if(arg && (state == InfraredRpcStateLoaded)) {
size_t button_index = 0;
if(infrared_remote_find_button_by_name(infrared->remote, arg, &button_index)) {
infrared_tx_start_button_index(infrared, button_index);
result = true;
scene_manager_set_scene_state(
infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateSending);
}
}
rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, result);
} else if(event.event == InfraredCustomEventTypeRpcButtonRelease) {
bool result = false;
if(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, RpcAppEventButtonRelease, 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, RpcAppEventAppExit, true);
} else if(event.event == InfraredCustomEventTypeRpcSessionClose) {
scene_manager_stop(infrared->scene_manager);
view_dispatcher_stop(infrared->view_dispatcher);
}
}
return consumed;
}
void infrared_scene_rpc_on_exit(void* context) {
Infrared* infrared = context;
if(scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneRpc) ==
InfraredRpcStateSending) {
infrared_tx_stop(infrared);
}
popup_reset(infrared->popup);
}

View File

@@ -0,0 +1,84 @@
#include "../infrared_i.h"
enum SubmenuIndex {
SubmenuIndexUniversalRemotes,
SubmenuIndexLearnNewRemote,
SubmenuIndexSavedRemotes,
SubmenuIndexDebug
};
static void infrared_scene_start_submenu_callback(void* context, uint32_t index) {
Infrared* infrared = context;
view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
}
void infrared_scene_start_on_enter(void* context) {
Infrared* infrared = context;
Submenu* submenu = infrared->submenu;
SceneManager* scene_manager = infrared->scene_manager;
submenu_add_item(
submenu,
"Universal Remotes",
SubmenuIndexUniversalRemotes,
infrared_scene_start_submenu_callback,
infrared);
submenu_add_item(
submenu,
"Learn New Remote",
SubmenuIndexLearnNewRemote,
infrared_scene_start_submenu_callback,
infrared);
submenu_add_item(
submenu,
"Saved Remotes",
SubmenuIndexSavedRemotes,
infrared_scene_start_submenu_callback,
infrared);
if(infrared->app_state.is_debug_enabled) {
submenu_add_item(
submenu, "Debug", SubmenuIndexDebug, infrared_scene_start_submenu_callback, infrared);
}
const uint32_t submenu_index =
scene_manager_get_scene_state(scene_manager, InfraredSceneStart);
submenu_set_selected_item(submenu, submenu_index);
scene_manager_set_scene_state(scene_manager, InfraredSceneStart, SubmenuIndexUniversalRemotes);
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu);
}
bool infrared_scene_start_on_event(void* context, SceneManagerEvent event) {
Infrared* infrared = context;
SceneManager* scene_manager = infrared->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
const uint32_t submenu_index = event.event;
scene_manager_set_scene_state(scene_manager, InfraredSceneStart, submenu_index);
if(submenu_index == SubmenuIndexUniversalRemotes) {
scene_manager_next_scene(scene_manager, InfraredSceneUniversal);
consumed = true;
} else if(submenu_index == SubmenuIndexLearnNewRemote) {
infrared->app_state.is_learning_new_remote = true;
scene_manager_next_scene(scene_manager, InfraredSceneLearn);
consumed = true;
} else if(submenu_index == SubmenuIndexSavedRemotes) {
string_set_str(infrared->file_path, INFRARED_APP_FOLDER);
scene_manager_next_scene(scene_manager, InfraredSceneRemoteList);
consumed = true;
} else if(submenu_index == SubmenuIndexDebug) {
scene_manager_next_scene(scene_manager, InfraredSceneDebug);
consumed = true;
}
}
return consumed;
}
void infrared_scene_start_on_exit(void* context) {
Infrared* infrared = context;
submenu_reset(infrared->submenu);
}

View File

@@ -0,0 +1,53 @@
#include "../infrared_i.h"
typedef enum {
SubmenuIndexUniversalTV,
SubmenuIndexUniversalAudio,
SubmenuIndexUniversalAirConditioner,
} SubmenuIndex;
static void infrared_scene_universal_submenu_callback(void* context, uint32_t index) {
Infrared* infrared = context;
view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
}
void infrared_scene_universal_on_enter(void* context) {
Infrared* infrared = context;
Submenu* submenu = infrared->submenu;
submenu_add_item(
submenu,
"TVs",
SubmenuIndexUniversalTV,
infrared_scene_universal_submenu_callback,
context);
submenu_set_selected_item(submenu, 0);
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu);
}
bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) {
Infrared* infrared = context;
SceneManager* scene_manager = infrared->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexUniversalTV) {
scene_manager_next_scene(scene_manager, InfraredSceneUniversalTV);
consumed = true;
} else if(event.event == SubmenuIndexUniversalAudio) {
//TODO Implement Audio universal remote
consumed = true;
} else if(event.event == SubmenuIndexUniversalAirConditioner) {
//TODO Implement A/C universal remote
consumed = true;
}
}
return consumed;
}
void infrared_scene_universal_on_exit(void* context) {
Infrared* infrared = context;
submenu_reset(infrared->submenu);
}

View File

@@ -0,0 +1,111 @@
#include "../infrared_i.h"
#include "common/infrared_scene_universal_common.h"
void infrared_scene_universal_tv_on_enter(void* context) {
infrared_scene_universal_common_on_enter(context);
Infrared* infrared = context;
ButtonPanel* button_panel = infrared->button_panel;
InfraredBruteForce* brute_force = infrared->brute_force;
infrared_brute_force_set_db_filename(brute_force, EXT_PATH("infrared/assets/tv.ir"));
button_panel_reserve(button_panel, 2, 3);
uint32_t i = 0;
button_panel_add_item(
button_panel,
i,
0,
0,
3,
19,
&I_Power_25x27,
&I_Power_hvr_25x27,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "POWER");
button_panel_add_item(
button_panel,
i,
1,
0,
36,
19,
&I_Mute_25x27,
&I_Mute_hvr_25x27,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "MUTE");
button_panel_add_item(
button_panel,
i,
0,
1,
3,
66,
&I_Vol_up_25x27,
&I_Vol_up_hvr_25x27,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "VOL+");
button_panel_add_item(
button_panel,
i,
1,
1,
36,
66,
&I_Up_25x27,
&I_Up_hvr_25x27,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "CH+");
button_panel_add_item(
button_panel,
i,
0,
2,
3,
98,
&I_Vol_down_25x27,
&I_Vol_down_hvr_25x27,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "VOL-");
button_panel_add_item(
button_panel,
i,
1,
2,
36,
98,
&I_Down_25x27,
&I_Down_hvr_25x27,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "CH-");
button_panel_add_label(button_panel, 6, 11, FontPrimary, "TV remote");
button_panel_add_label(button_panel, 9, 64, FontSecondary, "Vol");
button_panel_add_label(button_panel, 43, 64, FontSecondary, "Ch");
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);
}
}
bool infrared_scene_universal_tv_on_event(void* context, SceneManagerEvent event) {
return infrared_scene_universal_common_on_event(context, event);
}
void infrared_scene_universal_tv_on_exit(void* context) {
infrared_scene_universal_common_on_exit(context);
}