[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:
gornekich
2021-06-30 20:43:29 +03:00
committed by GitHub
parent 7a13391b2b
commit a0e1e42f2d
57 changed files with 2489 additions and 1089 deletions

View File

@@ -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) {

View File

@@ -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
View 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

View File

@@ -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

View File

@@ -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;

View File

@@ -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
View 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(&current_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;
}

View 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

View 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);