[FL-1479] New NFC app (#544)
* view: add custom event callback * nfc: rework nfc worker * gui: introduce view navigator * nfc_scences: introduce nfc scenes * nfc: add start scene * lib: add C application scene template * nfc: move nfc views to separate directory * view_dispatcher: add support for view_navigator * nfc views: rework nfc views * nfc scenes: add nfc application scenes * nfc: rework nfc main thread * view_dispatcher: add separate event for search back scene * nfc: set worker result address at worker start * nfc: update read nfc scenes * view_navigator: rework with M-LIB container * view_dispatcher: check that all views were freed * nfc: add debug menu with all functions * nfc read scene: add notification on success * api-hal-nfc: add API for UID emulation * nfc: add nfc emulation UID scene * assets: add NFC assets * nfc: update read and emulate scenes UI * nfc: fix memory leak * rfal: set custom analog configuration
This commit is contained in:
@@ -24,6 +24,12 @@ void view_set_input_callback(View* view, ViewInputCallback callback) {
|
||||
view->input_callback = callback;
|
||||
}
|
||||
|
||||
void view_set_custom_callback(View* view, ViewCustomCallback callback) {
|
||||
furi_assert(view);
|
||||
furi_assert(callback);
|
||||
view->custom_callback = callback;
|
||||
}
|
||||
|
||||
void view_set_previous_callback(View* view, ViewNavigationCallback callback) {
|
||||
furi_assert(view);
|
||||
view->previous_callback = callback;
|
||||
@@ -145,6 +151,15 @@ bool view_input(View* view, InputEvent* event) {
|
||||
}
|
||||
}
|
||||
|
||||
bool view_custom(View* view, uint32_t event) {
|
||||
furi_assert(view);
|
||||
if(view->custom_callback) {
|
||||
return view->custom_callback(event, view->context);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t view_previous(View* view) {
|
||||
furi_assert(view);
|
||||
if(view->previous_callback) {
|
||||
|
@@ -43,6 +43,13 @@ typedef void (*ViewDrawCallback)(Canvas* canvas, void* model);
|
||||
*/
|
||||
typedef bool (*ViewInputCallback)(InputEvent* event, void* context);
|
||||
|
||||
/* View Custom callback
|
||||
* @param event, number of custom event
|
||||
* @param context, pointer to context
|
||||
* @return true if event handled, false if event ignored
|
||||
*/
|
||||
typedef bool (*ViewCustomCallback)(uint32_t event, void* context);
|
||||
|
||||
/* View navigation callback
|
||||
* @param context, pointer to context
|
||||
* @return next view id
|
||||
@@ -94,12 +101,18 @@ void view_free(View* view);
|
||||
*/
|
||||
void view_set_draw_callback(View* view, ViewDrawCallback callback);
|
||||
|
||||
/* Set View Draw callback
|
||||
/* Set View Input callback
|
||||
* @param view, pointer to View
|
||||
* @param callback, input callback
|
||||
*/
|
||||
void view_set_input_callback(View* view, ViewInputCallback callback);
|
||||
|
||||
/* Set View Custom callback
|
||||
* @param view, pointer to View
|
||||
* @param callback, input callback
|
||||
*/
|
||||
void view_set_custom_callback(View* view, ViewCustomCallback callback);
|
||||
|
||||
/* Set Navigation Previous callback
|
||||
* @param view, pointer to View
|
||||
* @param callback, input callback
|
||||
|
86
applications/gui/view_dispatcher.c
Executable file → Normal file
86
applications/gui/view_dispatcher.c
Executable file → Normal file
@@ -25,7 +25,8 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher) {
|
||||
ViewDict_it(it, view_dispatcher->views);
|
||||
while(!ViewDict_end_p(it)) {
|
||||
ViewDict_itref_t* ref = ViewDict_ref(it);
|
||||
view_free(ref->value);
|
||||
// Crash if view wasn't freed
|
||||
furi_assert(ref->value);
|
||||
ViewDict_next(it);
|
||||
}
|
||||
ViewDict_clear(view_dispatcher->views);
|
||||
@@ -35,6 +36,10 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher) {
|
||||
if(view_dispatcher->queue) {
|
||||
osMessageQueueDelete(view_dispatcher->queue);
|
||||
}
|
||||
// Free View Navigator
|
||||
if(view_dispatcher->view_navigator) {
|
||||
view_navigator_free(view_dispatcher->view_navigator);
|
||||
}
|
||||
// Free dispatcher
|
||||
free(view_dispatcher);
|
||||
}
|
||||
@@ -45,10 +50,26 @@ void view_dispatcher_enable_queue(ViewDispatcher* view_dispatcher) {
|
||||
view_dispatcher->queue = osMessageQueueNew(8, sizeof(ViewDispatcherMessage), NULL);
|
||||
}
|
||||
|
||||
void view_dispatcher_enable_navigation(ViewDispatcher* view_dispatcher, void* context) {
|
||||
furi_assert(view_dispatcher);
|
||||
view_dispatcher->view_navigator = view_navigator_alloc(context);
|
||||
}
|
||||
|
||||
void view_dispatcher_add_scene(ViewDispatcher* view_dispatcher, AppScene* scene) {
|
||||
furi_assert(view_dispatcher);
|
||||
furi_assert(view_dispatcher->view_navigator);
|
||||
furi_assert(scene);
|
||||
view_navigator_add_next_scene(view_dispatcher->view_navigator, scene);
|
||||
}
|
||||
|
||||
void view_dispatcher_run(ViewDispatcher* view_dispatcher) {
|
||||
furi_assert(view_dispatcher);
|
||||
furi_assert(view_dispatcher->queue);
|
||||
|
||||
if(view_dispatcher->view_navigator) {
|
||||
view_navigator_start(view_dispatcher->view_navigator);
|
||||
}
|
||||
|
||||
ViewDispatcherMessage message;
|
||||
while(osMessageQueueGet(view_dispatcher->queue, &message, NULL, osWaitForever) == osOK) {
|
||||
if(message.type == ViewDispatcherMessageTypeStop) {
|
||||
@@ -57,6 +78,12 @@ void view_dispatcher_run(ViewDispatcher* view_dispatcher) {
|
||||
view_dispatcher_handle_input(view_dispatcher, &message.input);
|
||||
} else if(message.type == ViewDispatcherMessageTypeCustomEvent) {
|
||||
view_dispatcher_handle_custom_event(view_dispatcher, message.custom_event);
|
||||
} else if(message.type == ViewDispatcherMessageTypeNavigationEvent) {
|
||||
view_navigator_handle_navigation_event(
|
||||
view_dispatcher->view_navigator, message.navigator_event);
|
||||
} else if(message.type == ViewDispatcherMessageTypeBackSearchScene) {
|
||||
view_navigator_handle_back_search_scene_event(
|
||||
view_dispatcher->view_navigator, message.navigator_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,30 +202,35 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e
|
||||
is_consumed = view_input(view_dispatcher->current_view, event);
|
||||
}
|
||||
if(!is_consumed && event->type == InputTypeShort) {
|
||||
// TODO remove view navigation handlers
|
||||
uint32_t view_id = VIEW_IGNORE;
|
||||
if(event->key == InputKeyBack) {
|
||||
view_id = view_previous(view_dispatcher->current_view);
|
||||
if((view_id == VIEW_IGNORE) && (view_dispatcher->view_navigator)) {
|
||||
is_consumed = view_navigator_handle_navigation_event(
|
||||
view_dispatcher->view_navigator, ViewNavigatorEventBack);
|
||||
if(!is_consumed) {
|
||||
view_dispatcher_stop(view_dispatcher);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if(event->key == InputKeyOk) {
|
||||
view_id = view_next(view_dispatcher->current_view);
|
||||
}
|
||||
view_dispatcher_switch_to_view(view_dispatcher, view_id);
|
||||
if(!is_consumed) {
|
||||
view_dispatcher_switch_to_view(view_dispatcher, view_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void view_dispatcher_set_custom_callback(
|
||||
ViewDispatcher* view_dispatcher,
|
||||
CustomEventCallback callback,
|
||||
void* context) {
|
||||
furi_assert(view_dispatcher);
|
||||
furi_assert(callback);
|
||||
|
||||
view_dispatcher->custom_event_cb = callback;
|
||||
view_dispatcher->custom_event_ctx = context;
|
||||
}
|
||||
|
||||
void view_dispatcher_handle_custom_event(ViewDispatcher* view_dispatcher, uint32_t event) {
|
||||
if(view_dispatcher->custom_event_cb) {
|
||||
view_dispatcher->custom_event_cb(event, view_dispatcher->custom_event_ctx);
|
||||
bool is_consumed = false;
|
||||
if(view_dispatcher->current_view) {
|
||||
is_consumed = view_custom(view_dispatcher->current_view, event);
|
||||
}
|
||||
// If custom event is not consumed in View, handle it in Scene
|
||||
if(!is_consumed) {
|
||||
is_consumed = view_navigator_handle_custom_event(view_dispatcher->view_navigator, event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,6 +245,30 @@ void view_dispatcher_send_custom_event(ViewDispatcher* view_dispatcher, uint32_t
|
||||
furi_check(osMessageQueuePut(view_dispatcher->queue, &message, 0, osWaitForever) == osOK);
|
||||
}
|
||||
|
||||
void view_dispatcher_send_navigation_event(ViewDispatcher* view_dispatcher, uint32_t event) {
|
||||
furi_assert(view_dispatcher);
|
||||
furi_assert(view_dispatcher->queue);
|
||||
furi_assert(view_dispatcher->view_navigator);
|
||||
|
||||
ViewDispatcherMessage message;
|
||||
message.type = ViewDispatcherMessageTypeNavigationEvent;
|
||||
message.custom_event = event;
|
||||
|
||||
furi_check(osMessageQueuePut(view_dispatcher->queue, &message, 0, osWaitForever) == osOK);
|
||||
}
|
||||
|
||||
void view_dispatcher_send_back_search_scene_event(ViewDispatcher* view_dispatcher, uint32_t event) {
|
||||
furi_assert(view_dispatcher);
|
||||
furi_assert(view_dispatcher->queue);
|
||||
furi_assert(view_dispatcher->view_navigator);
|
||||
|
||||
ViewDispatcherMessage message;
|
||||
message.type = ViewDispatcherMessageTypeBackSearchScene;
|
||||
message.custom_event = event;
|
||||
|
||||
furi_check(osMessageQueuePut(view_dispatcher->queue, &message, 0, osWaitForever) == osOK);
|
||||
}
|
||||
|
||||
void view_dispatcher_set_current_view(ViewDispatcher* view_dispatcher, View* view) {
|
||||
furi_assert(view_dispatcher);
|
||||
// Dispatch view exit event
|
||||
|
@@ -2,15 +2,12 @@
|
||||
|
||||
#include "view.h"
|
||||
#include "gui.h"
|
||||
#include "view_navigator.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Prototype for custom event callback
|
||||
*/
|
||||
typedef void (*CustomEventCallback)(uint32_t custom_event, void* context);
|
||||
|
||||
/** ViewDispatcher view_port placement
|
||||
*/
|
||||
typedef enum {
|
||||
@@ -37,18 +34,36 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher);
|
||||
*/
|
||||
void view_dispatcher_enable_queue(ViewDispatcher* view_dispatcher);
|
||||
|
||||
/** Set custom event callback
|
||||
* Custom callback is called when custom event in internal queue received
|
||||
*/
|
||||
void view_dispatcher_set_custom_callback(
|
||||
ViewDispatcher* view_dispatcher,
|
||||
CustomEventCallback callback,
|
||||
void* context);
|
||||
|
||||
/** Send custom event
|
||||
* @param view_dispatcher ViewDispatcher instance
|
||||
*/
|
||||
void view_dispatcher_send_custom_event(ViewDispatcher* view_dispatcher, uint32_t event);
|
||||
|
||||
/** Enable View Navigator to handle custom events and scene navigation
|
||||
* @param view_dispatcher ViewDispatcher instance
|
||||
* @param context context for all scenes
|
||||
*/
|
||||
void view_dispatcher_enable_navigation(ViewDispatcher* view_dispatcher, void* context);
|
||||
|
||||
/** Add Scene to view navigator
|
||||
* Use only after navigation enabled
|
||||
* @param view_dispatcher ViewDispatcher instance
|
||||
* @param scene AppScene instance
|
||||
*/
|
||||
void view_dispatcher_add_scene(ViewDispatcher* view_dispatcher, AppScene* scene);
|
||||
|
||||
/** Send navigation event
|
||||
* @param view_dispatcher ViewDispatcher instance
|
||||
* @param event event
|
||||
*/
|
||||
void view_dispatcher_send_navigation_event(ViewDispatcher* view_dispatcher, uint32_t event);
|
||||
|
||||
/** Send search scene event
|
||||
* @param view_dispatcher ViewDispatcher instance
|
||||
* @param event event
|
||||
*/
|
||||
void view_dispatcher_send_back_search_scene_event(ViewDispatcher* view_dispatcher, uint32_t event);
|
||||
|
||||
/** Run ViewDispatcher
|
||||
* Use only after queue enabled
|
||||
* @param view_dispatcher ViewDispatcher instance
|
||||
|
@@ -15,13 +15,14 @@ struct ViewDispatcher {
|
||||
ViewPort* view_port;
|
||||
ViewDict_t views;
|
||||
View* current_view;
|
||||
CustomEventCallback custom_event_cb;
|
||||
void* custom_event_ctx;
|
||||
ViewNavigator* view_navigator;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
ViewDispatcherMessageTypeInput,
|
||||
ViewDispatcherMessageTypeCustomEvent,
|
||||
ViewDispatcherMessageTypeNavigationEvent,
|
||||
ViewDispatcherMessageTypeBackSearchScene,
|
||||
ViewDispatcherMessageTypeStop,
|
||||
} ViewDispatcherMessageType;
|
||||
|
||||
@@ -30,6 +31,8 @@ typedef struct {
|
||||
union {
|
||||
InputEvent input;
|
||||
uint32_t custom_event;
|
||||
ViewNavigatorEvent navigator_event;
|
||||
uint32_t scene_id;
|
||||
};
|
||||
} ViewDispatcherMessage;
|
||||
|
||||
|
@@ -11,6 +11,7 @@ typedef struct {
|
||||
struct View {
|
||||
ViewDrawCallback draw_callback;
|
||||
ViewInputCallback input_callback;
|
||||
ViewCustomCallback custom_callback;
|
||||
|
||||
ViewModelType model_type;
|
||||
ViewNavigationCallback previous_callback;
|
||||
@@ -35,6 +36,9 @@ void view_draw(View* view, Canvas* canvas);
|
||||
/* Input Callback for View dispatcher */
|
||||
bool view_input(View* view, InputEvent* event);
|
||||
|
||||
/* Custom Callback for View dispatcher */
|
||||
bool view_custom(View* view, uint32_t event);
|
||||
|
||||
/* Previous Callback for View dispatcher */
|
||||
uint32_t view_previous(View* view);
|
||||
|
||||
|
109
applications/gui/view_navigator.c
Executable file
109
applications/gui/view_navigator.c
Executable file
@@ -0,0 +1,109 @@
|
||||
#include "view_navigator_i.h"
|
||||
|
||||
ViewNavigator* view_navigator_alloc(void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
ViewNavigator* view_navigator = furi_alloc(sizeof(ViewNavigator));
|
||||
view_navigator->context = context;
|
||||
ViewNavSceneArray_init(view_navigator->scene_array);
|
||||
|
||||
return view_navigator;
|
||||
}
|
||||
|
||||
void view_navigator_free(ViewNavigator* view_navigator) {
|
||||
furi_assert(view_navigator);
|
||||
ViewNavSceneArray_clear(view_navigator->scene_array);
|
||||
|
||||
free(view_navigator);
|
||||
}
|
||||
|
||||
bool view_navigator_handle_custom_event(ViewNavigator* view_navigator, uint32_t event) {
|
||||
AppScene* scene = *ViewNavSceneArray_back(view_navigator->scene_array);
|
||||
return scene->on_event(view_navigator->context, event);
|
||||
}
|
||||
|
||||
bool view_navigator_handle_navigation_event(ViewNavigator* view_navigator, uint32_t event) {
|
||||
if(event == ViewNavigatorEventNext) {
|
||||
return view_navigator_next_scene(view_navigator);
|
||||
} else if(event == ViewNavigatorEventBack) {
|
||||
AppScene* scene = *ViewNavSceneArray_back(view_navigator->scene_array);
|
||||
if(scene->on_event(view_navigator->context, ViewNavigatorEventBack)) {
|
||||
return true;
|
||||
} else {
|
||||
return view_navigator_previous_scene(view_navigator);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool view_navigator_handle_back_search_scene_event(ViewNavigator* view_navigator, uint32_t event) {
|
||||
return view_navigator_search_previous_scene(view_navigator, event);
|
||||
}
|
||||
|
||||
void view_navigator_add_next_scene(ViewNavigator* view_navigator, AppScene* scene) {
|
||||
furi_assert(view_navigator);
|
||||
furi_assert(scene);
|
||||
|
||||
ViewNavSceneArray_push_back(view_navigator->scene_array, scene);
|
||||
}
|
||||
|
||||
void view_navigator_start(ViewNavigator* view_navigator) {
|
||||
furi_assert(view_navigator);
|
||||
AppScene* scene = *ViewNavSceneArray_front(view_navigator->scene_array);
|
||||
furi_assert(scene);
|
||||
scene->on_enter(view_navigator->context);
|
||||
}
|
||||
|
||||
bool view_navigator_next_scene(ViewNavigator* view_navigator) {
|
||||
ViewNavSceneArray_it_t scene_it;
|
||||
ViewNavSceneArray_it_last(scene_it, view_navigator->scene_array);
|
||||
ViewNavSceneArray_previous(scene_it);
|
||||
AppScene* current_scene = *ViewNavSceneArray_ref(scene_it);
|
||||
AppScene* next_scene = *ViewNavSceneArray_back(view_navigator->scene_array);
|
||||
if(current_scene && next_scene) {
|
||||
current_scene->on_exit(view_navigator->context);
|
||||
next_scene->on_enter(view_navigator->context);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool view_navigator_previous_scene(ViewNavigator* view_navigator) {
|
||||
AppScene* current_scene = NULL;
|
||||
ViewNavSceneArray_pop_back(¤t_scene, view_navigator->scene_array);
|
||||
if(ViewNavSceneArray_size(view_navigator->scene_array) == 0) {
|
||||
// Handle exit from start scene separately
|
||||
current_scene->on_exit(view_navigator->context);
|
||||
return false;
|
||||
}
|
||||
AppScene* previous_scene = *ViewNavSceneArray_back(view_navigator->scene_array);
|
||||
if(current_scene && previous_scene) {
|
||||
current_scene->on_exit(view_navigator->context);
|
||||
previous_scene->on_enter(view_navigator->context);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool view_navigator_search_previous_scene(ViewNavigator* view_navigator, uint32_t scene_id) {
|
||||
AppScene* previous_scene = NULL;
|
||||
AppScene* current_scene = *ViewNavSceneArray_back(view_navigator->scene_array);
|
||||
ViewNavSceneArray_it_t scene_it;
|
||||
ViewNavSceneArray_it_last(scene_it, view_navigator->scene_array);
|
||||
bool scene_found = false;
|
||||
while(!scene_found) {
|
||||
ViewNavSceneArray_previous(scene_it);
|
||||
previous_scene = *ViewNavSceneArray_ref(scene_it);
|
||||
if(previous_scene == NULL) {
|
||||
return false;
|
||||
}
|
||||
if(previous_scene->id == scene_id) {
|
||||
scene_found = true;
|
||||
}
|
||||
}
|
||||
ViewNavSceneArray_next(scene_it);
|
||||
ViewNavSceneArray_pop_until(view_navigator->scene_array, scene_it);
|
||||
current_scene->on_exit(view_navigator->context);
|
||||
previous_scene->on_enter(view_navigator->context);
|
||||
return true;
|
||||
}
|
28
applications/gui/view_navigator.h
Normal file
28
applications/gui/view_navigator.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "app_scene.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
ViewNavigatorEventNext = 0x00000000UL,
|
||||
ViewNavigatorEventBack = 0xFFFFFFFFUL,
|
||||
} ViewNavigatorEvent;
|
||||
|
||||
typedef struct ViewNavigator ViewNavigator;
|
||||
|
||||
ViewNavigator* view_navigator_alloc(void* context);
|
||||
void view_navigator_free(ViewNavigator* view_navigator);
|
||||
|
||||
bool view_navigator_handle_custom_event(ViewNavigator* view_navigator, uint32_t event);
|
||||
bool view_navigator_handle_navigation_event(ViewNavigator* view_navigator, uint32_t event);
|
||||
bool view_navigator_handle_back_search_scene_event(ViewNavigator* view_navigator, uint32_t event);
|
||||
|
||||
void view_navigator_add_next_scene(ViewNavigator* view_navigator, AppScene* scene);
|
||||
void view_navigator_start(ViewNavigator* view_navigator);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
16
applications/gui/view_navigator_i.h
Executable file
16
applications/gui/view_navigator_i.h
Executable file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "view_navigator.h"
|
||||
#include <furi.h>
|
||||
#include <m-array.h>
|
||||
|
||||
ARRAY_DEF(ViewNavSceneArray, AppScene*, M_PTR_OPLIST);
|
||||
|
||||
struct ViewNavigator {
|
||||
ViewNavSceneArray_t scene_array;
|
||||
void* context;
|
||||
};
|
||||
|
||||
bool view_navigator_next_scene(ViewNavigator* view_navigator);
|
||||
bool view_navigator_previous_scene(ViewNavigator* view_navigator);
|
||||
bool view_navigator_search_previous_scene(ViewNavigator* view_navigator, uint32_t scene_id);
|
Reference in New Issue
Block a user