Struan Clark 2 лет назад
Родитель
Сommit
c0a493a947

+ 15 - 0
application.fam

@@ -0,0 +1,15 @@
+App(
+    appid="flipbip39",
+    name="Flip-BIP39",
+    apptype=FlipperAppType.PLUGIN,
+    entry_point="flipbip39_app",
+    cdefines=["APP_BOILERPLATE"],
+    requires=[
+        "gui",
+    ],
+    stack_size=2 * 1024,
+    order=10,
+    fap_icon="flipbip39_10px.png",
+    fap_icon_assets="icons",
+    fap_category="Misc",
+)

+ 109 - 0
flipbip39.c

@@ -0,0 +1,109 @@
+#include "flipbip39.h"
+
+bool flipbip39_custom_event_callback(void* context, uint32_t event) {
+    furi_assert(context);
+    FlipBip39* app = context;
+    return scene_manager_handle_custom_event(app->scene_manager, event);
+}
+
+void flipbip39_tick_event_callback(void* context) {
+    furi_assert(context);
+    FlipBip39* app = context;
+    scene_manager_handle_tick_event(app->scene_manager);
+}
+
+//leave app if back button pressed
+bool flipbip39_navigation_event_callback(void* context) {
+    furi_assert(context);
+    FlipBip39* app = context;
+    return scene_manager_handle_back_event(app->scene_manager);
+}
+
+FlipBip39* flipbip39_app_alloc() {
+    FlipBip39* app = malloc(sizeof(FlipBip39));
+    app->gui = furi_record_open(RECORD_GUI);
+    app->notification = furi_record_open(RECORD_NOTIFICATION);
+    
+    //Turn backlight on, believe me this makes testing your app easier
+    notification_message(app->notification, &sequence_display_backlight_on);
+
+    //Scene additions
+    app->view_dispatcher = view_dispatcher_alloc();
+    view_dispatcher_enable_queue(app->view_dispatcher);
+
+    app->scene_manager = scene_manager_alloc(&flipbip39_scene_handlers, app);
+    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
+    view_dispatcher_set_navigation_event_callback(app->view_dispatcher, flipbip39_navigation_event_callback);
+    view_dispatcher_set_tick_event_callback(app->view_dispatcher, flipbip39_tick_event_callback, 100);
+    view_dispatcher_set_custom_event_callback(app->view_dispatcher, flipbip39_custom_event_callback);
+    app->submenu = submenu_alloc();
+
+    app->haptic = 1;
+    app->speaker = 1;
+    app->led = 1;
+
+    view_dispatcher_add_view(app->view_dispatcher, FlipBip39ViewIdMenu, submenu_get_view(app->submenu));
+    app->flipbip39_startscreen = flipbip39_startscreen_alloc();
+    view_dispatcher_add_view(app->view_dispatcher, FlipBip39ViewIdStartscreen, flipbip39_startscreen_get_view(app->flipbip39_startscreen));
+    app->flipbip39_scene_1 = flipbip39_scene_1_alloc();
+    view_dispatcher_add_view(app->view_dispatcher, FlipBip39ViewIdScene1, flipbip39_scene_1_get_view(app->flipbip39_scene_1));
+    app->flipbip39_scene_2 = flipbip39_scene_2_alloc();
+    view_dispatcher_add_view(app->view_dispatcher, FlipBip39ViewIdScene2, flipbip39_scene_2_get_view(app->flipbip39_scene_2));
+    app->variable_item_list = variable_item_list_alloc();
+    view_dispatcher_add_view(app->view_dispatcher, FlipBip39ViewIdSettings, variable_item_list_get_view(app->variable_item_list));
+
+    //End Scene Additions
+
+    return app;
+}
+
+void flipbip39_app_free(FlipBip39* app) {
+    furi_assert(app);
+    
+    // Scene manager
+    scene_manager_free(app->scene_manager);
+
+    // View Dispatcher
+    view_dispatcher_remove_view(app->view_dispatcher, FlipBip39ViewIdMenu);
+    view_dispatcher_remove_view(app->view_dispatcher, FlipBip39ViewIdScene1);
+    view_dispatcher_remove_view(app->view_dispatcher, FlipBip39ViewIdScene2);
+    view_dispatcher_remove_view(app->view_dispatcher, FlipBip39ViewIdSettings);
+    submenu_free(app->submenu);
+
+    view_dispatcher_free(app->view_dispatcher);
+    furi_record_close(RECORD_GUI);
+    
+    app->gui = NULL;
+    app->notification = NULL;
+
+    //Remove whatever is left
+    free(app);
+}
+
+int32_t flipbip39_app(void* p) {
+    UNUSED(p);
+    FlipBip39* app = flipbip39_app_alloc();
+    
+    // Disabled because causes exit on customer firmwares such as RM
+    /*if(!furi_hal_region_is_provisioned()) {
+        flipbip39_app_free(app);
+        return 1;
+    }*/
+    
+    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
+    
+    scene_manager_next_scene(app->scene_manager, FlipBip39SceneStartscreen); //Start with start screen
+    //scene_manager_next_scene(app->scene_manager, FlipBip39SceneMenu); //if you want to directly start with Menu
+
+    furi_hal_power_suppress_charge_enter();
+
+    view_dispatcher_run(app->view_dispatcher);
+    
+    furi_hal_power_suppress_charge_exit();
+    flipbip39_app_free(app);
+
+    return 0;
+}
+
+
+

+ 54 - 0
flipbip39.h

@@ -0,0 +1,54 @@
+#pragma once
+
+#include <furi.h>
+#include <furi_hal.h>
+#include <gui/gui.h>
+#include <input/input.h>
+#include <stdlib.h>
+#include <notification/notification_messages.h>
+#include <gui/view_dispatcher.h>
+#include <gui/modules/submenu.h>
+#include <gui/scene_manager.h>
+#include <gui/modules/variable_item_list.h>
+#include "scenes/flipbip39_scene.h"
+#include "views/flipbip39_startscreen.h"
+#include "views/flipbip39_scene_1.h"
+#include "views/flipbip39_scene_2.h"
+
+typedef struct {
+    Gui* gui;
+    NotificationApp* notification;
+    ViewDispatcher* view_dispatcher;
+    Submenu* submenu;
+    SceneManager* scene_manager;
+    VariableItemList* variable_item_list;
+    FlipBip39Startscreen* flipbip39_startscreen;
+    FlipBip39Scene1* flipbip39_scene_1;
+    FlipBip39Scene2* flipbip39_scene_2;
+    int haptic; 
+    int speaker;
+    int led;
+} FlipBip39;
+
+typedef enum {
+    FlipBip39ViewIdStartscreen,
+    FlipBip39ViewIdMenu,
+    FlipBip39ViewIdScene1,
+    FlipBip39ViewIdScene2,
+    FlipBip39ViewIdSettings,
+} FlipBip39ViewId;
+
+typedef enum {
+    FlipBip39HapticOff,
+    FlipBip39HapticOn,
+} FlipBip39HapticState;
+
+typedef enum {
+    FlipBip39SpeakerOff,
+    FlipBip39SpeakerOn,
+} FlipBip39SpeakerState;
+
+typedef enum {
+    FlipBip39LedOff,
+    FlipBip39LedOn,
+} FlipBip39LedState;

BIN
flipbip39_10px.png


+ 22 - 0
helpers/flipbip39_custom_event.h

@@ -0,0 +1,22 @@
+#pragma once
+
+typedef enum {
+    FlipBip39CustomEventStartscreenUp,
+    FlipBip39CustomEventStartscreenDown,
+    FlipBip39CustomEventStartscreenLeft,
+    FlipBip39CustomEventStartscreenRight,
+    FlipBip39CustomEventStartscreenOk,
+    FlipBip39CustomEventStartscreenBack,
+    FlipBip39CustomEventScene1Up,
+    FlipBip39CustomEventScene1Down,
+    FlipBip39CustomEventScene1Left,
+    FlipBip39CustomEventScene1Right,
+    FlipBip39CustomEventScene1Ok,
+    FlipBip39CustomEventScene1Back,
+    FlipBip39CustomEventScene2Up,
+    FlipBip39CustomEventScene2Down,
+    FlipBip39CustomEventScene2Left,
+    FlipBip39CustomEventScene2Right,
+    FlipBip39CustomEventScene2Ok,
+    FlipBip39CustomEventScene2Back,
+} FlipBip39CustomEvent;

+ 36 - 0
helpers/flipbip39_haptic.c

@@ -0,0 +1,36 @@
+#include "flipbip39_haptic.h"
+#include "../flipbip39.h"
+
+
+void flipbip39_play_happy_bump(void* context) {
+    FlipBip39* app = context;
+    if (app->haptic != 1) {
+        return;
+    }
+    notification_message(app->notification, &sequence_set_vibro_on);
+    furi_thread_flags_wait(0, FuriFlagWaitAny, 20);
+    notification_message(app->notification, &sequence_reset_vibro);
+}
+
+void flipbip39_play_bad_bump(void* context) {
+    FlipBip39* app = context;
+    if (app->haptic != 1) {
+        return;
+    }
+    notification_message(app->notification, &sequence_set_vibro_on);
+    furi_thread_flags_wait(0, FuriFlagWaitAny, 100);
+    notification_message(app->notification, &sequence_reset_vibro);
+}
+
+void flipbip39_play_long_bump(void* context) {
+    FlipBip39* app = context;
+    if (app->haptic != 1) {
+        return;
+    }
+    for (int i = 0; i < 4; i++) {
+        notification_message(app->notification, &sequence_set_vibro_on);
+        furi_thread_flags_wait(0, FuriFlagWaitAny, 50);
+        notification_message(app->notification, &sequence_reset_vibro);
+        furi_thread_flags_wait(0, FuriFlagWaitAny, 100);
+    }
+}

+ 8 - 0
helpers/flipbip39_haptic.h

@@ -0,0 +1,8 @@
+#include <notification/notification_messages.h>
+
+void flipbip39_play_happy_bump(void* context);
+
+void flipbip39_play_bad_bump(void* context);
+
+void flipbip39_play_long_bump(void* context);
+

+ 39 - 0
helpers/flipbip39_led.c

@@ -0,0 +1,39 @@
+#include "flipbip39_led.h"
+#include "../flipbip39.h"
+
+
+
+void flipbip39_led_set_rgb(void* context, int red, int green, int blue) {
+    FlipBip39* app = context;
+    if (app->led != 1) {
+        return;
+    }
+    NotificationMessage notification_led_message_1;
+    notification_led_message_1.type = NotificationMessageTypeLedRed;
+    NotificationMessage notification_led_message_2;
+    notification_led_message_2.type = NotificationMessageTypeLedGreen;
+    NotificationMessage notification_led_message_3;
+    notification_led_message_3.type = NotificationMessageTypeLedBlue;
+
+    notification_led_message_1.data.led.value = red;
+    notification_led_message_2.data.led.value = green;
+    notification_led_message_3.data.led.value = blue;
+    const NotificationSequence notification_sequence = {
+        &notification_led_message_1,
+        &notification_led_message_2,
+        &notification_led_message_3,
+        &message_do_not_reset,
+        NULL,
+    };
+    notification_message(app->notification, &notification_sequence);
+    furi_thread_flags_wait(0, FuriFlagWaitAny, 10); //Delay, prevent removal from RAM before LED value set    
+}
+
+void flipbip39_led_reset(void* context) {
+    FlipBip39* app = context;
+    notification_message(app->notification, &sequence_reset_red);
+    notification_message(app->notification, &sequence_reset_green);
+    notification_message(app->notification, &sequence_reset_blue);
+    
+    furi_thread_flags_wait(0, FuriFlagWaitAny, 300); //Delay, prevent removal from RAM before LED value set    
+}

+ 6 - 0
helpers/flipbip39_led.h

@@ -0,0 +1,6 @@
+
+
+void flipbip39_led_set_rgb(void* context, int red, int green, int blue);
+
+void flipbip39_led_reset(void* context);
+

+ 27 - 0
helpers/flipbip39_speaker.c

@@ -0,0 +1,27 @@
+#include "flipbip39_speaker.h"
+#include "../flipbip39.h"
+
+#define NOTE_INPUT 587.33f
+
+void flipbip39_play_input_sound(void* context) {
+    FlipBip39* app = context;
+    if (app->speaker != 1) {
+        return;
+    }
+    float volume = 1.0f;
+    if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) {
+        furi_hal_speaker_start(NOTE_INPUT, volume);
+    }
+    
+}
+
+void flipbip39_stop_all_sound(void* context) {
+    FlipBip39* app = context;
+    if (app->speaker != 1) {
+        return;
+    }
+    if(furi_hal_speaker_is_mine()) {
+        furi_hal_speaker_stop();
+        furi_hal_speaker_release();
+    }
+}

+ 4 - 0
helpers/flipbip39_speaker.h

@@ -0,0 +1,4 @@
+#define NOTE_INPUT 587.33f
+
+void flipbip39_play_input_sound(void* context);
+void flipbip39_stop_all_sound(void* context);

BIN
icons/ButtonCenter_7x7.png


BIN
icons/ButtonDown_10x5.png


BIN
icons/ButtonUp_10x5.png


+ 30 - 0
scenes/flipbip39_scene.c

@@ -0,0 +1,30 @@
+#include "flipbip39_scene.h"
+
+// Generate scene on_enter handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
+void (*const flipbip39_on_enter_handlers[])(void*) = {
+#include "flipbip39_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 flipbip39_on_event_handlers[])(void* context, SceneManagerEvent event) = {
+#include "flipbip39_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 flipbip39_on_exit_handlers[])(void* context) = {
+#include "flipbip39_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Initialize scene handlers configuration structure
+const SceneManagerHandlers flipbip39_scene_handlers = {
+    .on_enter_handlers = flipbip39_on_enter_handlers,
+    .on_event_handlers = flipbip39_on_event_handlers,
+    .on_exit_handlers = flipbip39_on_exit_handlers,
+    .scene_num = FlipBip39SceneNum,
+};

+ 29 - 0
scenes/flipbip39_scene.h

@@ -0,0 +1,29 @@
+#pragma once
+
+#include <gui/scene_manager.h>
+
+// Generate scene id and total number
+#define ADD_SCENE(prefix, name, id) FlipBip39Scene##id,
+typedef enum {
+#include "flipbip39_scene_config.h"
+    FlipBip39SceneNum,
+} FlipBip39Scene;
+#undef ADD_SCENE
+
+extern const SceneManagerHandlers flipbip39_scene_handlers;
+
+// Generate scene on_enter handlers declaration
+#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
+#include "flipbip39_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 "flipbip39_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 "flipbip39_scene_config.h"
+#undef ADD_SCENE

+ 5 - 0
scenes/flipbip39_scene_config.h

@@ -0,0 +1,5 @@
+ADD_SCENE(flipbip39, startscreen, Startscreen)
+ADD_SCENE(flipbip39, menu, Menu)
+ADD_SCENE(flipbip39, scene_1, Scene_1)
+ADD_SCENE(flipbip39, scene_2, Scene_2)
+ADD_SCENE(flipbip39, settings, Settings)

+ 58 - 0
scenes/flipbip39_scene_menu.c

@@ -0,0 +1,58 @@
+#include "../flipbip39.h"
+
+enum SubmenuIndex {
+    SubmenuIndexScene1 = 10,
+    SubmenuIndexScene2,
+    SubmenuIndexSettings,
+};
+
+void flipbip39_scene_menu_submenu_callback(void* context, uint32_t index) {
+    FlipBip39* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+void flipbip39_scene_menu_on_enter(void* context) {
+    FlipBip39* app = context;
+
+    submenu_add_item(app->submenu, "Scene 1", SubmenuIndexScene1, flipbip39_scene_menu_submenu_callback, app);
+    submenu_add_item(app->submenu, "Scene 2", SubmenuIndexScene2, flipbip39_scene_menu_submenu_callback, app);
+    submenu_add_item(app->submenu, "Settings", SubmenuIndexSettings, flipbip39_scene_menu_submenu_callback, app);
+
+    submenu_set_selected_item(app->submenu, scene_manager_get_scene_state(app->scene_manager, FlipBip39SceneMenu));
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, FlipBip39ViewIdMenu);
+}
+
+bool flipbip39_scene_menu_on_event(void* context, SceneManagerEvent event) {
+    FlipBip39* app = context;
+    UNUSED(app);
+    if(event.type == SceneManagerEventTypeBack) {
+        //exit app
+        scene_manager_stop(app->scene_manager);
+        view_dispatcher_stop(app->view_dispatcher);
+        return true;
+    } else if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == SubmenuIndexScene1) {
+            scene_manager_set_scene_state(
+                app->scene_manager, FlipBip39SceneMenu, SubmenuIndexScene1);
+            scene_manager_next_scene(app->scene_manager, FlipBip39SceneScene_1);
+            return true;
+        } else if (event.event == SubmenuIndexScene2) {
+            scene_manager_set_scene_state(
+                app->scene_manager, FlipBip39SceneMenu, SubmenuIndexScene2);
+            scene_manager_next_scene(app->scene_manager, FlipBip39SceneScene_2);
+            return true;
+        } else if (event.event == SubmenuIndexSettings) {
+            scene_manager_set_scene_state(
+                app->scene_manager, FlipBip39SceneMenu, SubmenuIndexSettings);
+            scene_manager_next_scene(app->scene_manager, FlipBip39SceneSettings);
+            return true;
+        }
+    }
+    return false;
+}
+
+void flipbip39_scene_menu_on_exit(void* context) {
+    FlipBip39* app = context;
+    submenu_reset(app->submenu);
+}

+ 50 - 0
scenes/flipbip39_scene_scene_1.c

@@ -0,0 +1,50 @@
+#include "../flipbip39.h"
+#include "../helpers/flipbip39_custom_event.h"
+#include "../views/flipbip39_scene_1.h"
+
+void flipbip39_scene_1_callback(FlipBip39CustomEvent event, void* context) {
+    furi_assert(context);
+    FlipBip39* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, event);
+}
+
+void flipbip39_scene_scene_1_on_enter(void* context) {
+    furi_assert(context);
+    FlipBip39* app = context;
+    flipbip39_scene_1_set_callback(app->flipbip39_scene_1, flipbip39_scene_1_callback, app);
+    view_dispatcher_switch_to_view(app->view_dispatcher, FlipBip39ViewIdScene1);
+}
+
+bool flipbip39_scene_scene_1_on_event(void* context, SceneManagerEvent event) {
+    FlipBip39* app = context;
+    bool consumed = false;
+    
+    if(event.type == SceneManagerEventTypeCustom) {
+        switch(event.event) {
+            case FlipBip39CustomEventScene1Left:
+            case FlipBip39CustomEventScene1Right:
+                break;
+            case FlipBip39CustomEventScene1Up:
+            case FlipBip39CustomEventScene1Down:
+                break;
+            case FlipBip39CustomEventScene1Back:
+                notification_message(app->notification, &sequence_reset_red);
+                notification_message(app->notification, &sequence_reset_green);
+                notification_message(app->notification, &sequence_reset_blue);
+                if(!scene_manager_search_and_switch_to_previous_scene(
+                    app->scene_manager, FlipBip39SceneMenu)) {
+                        scene_manager_stop(app->scene_manager);
+                        view_dispatcher_stop(app->view_dispatcher);
+                    }
+                consumed = true;
+                break;
+        }
+    }
+    
+    return consumed;
+}
+
+void flipbip39_scene_scene_1_on_exit(void* context) {
+    FlipBip39* app = context;
+    UNUSED(app);
+}

+ 53 - 0
scenes/flipbip39_scene_scene_2.c

@@ -0,0 +1,53 @@
+#include "../flipbip39.h"
+#include "../helpers/flipbip39_custom_event.h"
+#include "../helpers/flipbip39_haptic.h"
+#include "../helpers/flipbip39_led.h"
+#include "../views/flipbip39_scene_2.h"
+
+void flipbip39_scene_2_callback(FlipBip39CustomEvent event, void* context) {
+    furi_assert(context);
+    FlipBip39* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, event);
+}
+
+void flipbip39_scene_scene_2_on_enter(void* context) {
+    furi_assert(context);
+    FlipBip39* app = context;
+    flipbip39_scene_2_set_callback(app->flipbip39_scene_2, flipbip39_scene_2_callback, app);
+    view_dispatcher_switch_to_view(app->view_dispatcher, FlipBip39ViewIdScene2);
+}
+
+bool flipbip39_scene_scene_2_on_event(void* context, SceneManagerEvent event) {
+    FlipBip39* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        switch(event.event) {
+            case FlipBip39CustomEventScene2Left:
+            case FlipBip39CustomEventScene2Right:
+                break;
+            case FlipBip39CustomEventScene2Up:
+            case FlipBip39CustomEventScene2Down:
+                break;
+            case FlipBip39CustomEventScene2Back:
+                notification_message(app->notification, &sequence_reset_red);
+                notification_message(app->notification, &sequence_reset_green);
+                notification_message(app->notification, &sequence_reset_blue);
+                if(!scene_manager_search_and_switch_to_previous_scene(
+                    app->scene_manager, FlipBip39SceneMenu)) {
+                        scene_manager_stop(app->scene_manager);
+                        view_dispatcher_stop(app->view_dispatcher);
+                    }
+                consumed = true;
+                break;
+        }
+    }
+
+    return consumed;
+}
+
+void flipbip39_scene_scene_2_on_exit(void* context) {
+    FlipBip39* app = context;
+    UNUSED(app);
+}
+

+ 119 - 0
scenes/flipbip39_scene_settings.c

@@ -0,0 +1,119 @@
+#include "../flipbip39.h"
+#include <lib/toolbox/value_index.h>
+
+enum SettingsIndex {
+    SettingsIndexHaptic = 10,
+    SettingsIndexValue1,
+    SettingsIndexValue2,
+};
+
+const char* const haptic_text[2] = {
+    "OFF",
+    "ON",
+};
+const uint32_t haptic_value[2] = {
+    FlipBip39HapticOff,
+    FlipBip39HapticOn,
+};
+
+const char* const speaker_text[2] = {
+    "OFF",
+    "ON",
+};
+const uint32_t speaker_value[2] = {
+    FlipBip39SpeakerOff,
+    FlipBip39SpeakerOn,
+};
+
+const char* const led_text[2] = {
+    "OFF",
+    "ON",
+};
+const uint32_t led_value[2] = {
+    FlipBip39LedOff,
+    FlipBip39LedOn,
+};
+
+static void flipbip39_scene_settings_set_haptic(VariableItem* item) {
+    FlipBip39* app = variable_item_get_context(item);
+    uint8_t index = variable_item_get_current_value_index(item);
+
+    variable_item_set_current_value_text(item, haptic_text[index]);
+    app->haptic = haptic_value[index];
+}
+
+static void flipbip39_scene_settings_set_speaker(VariableItem* item) {
+    FlipBip39* app = variable_item_get_context(item);
+    uint8_t index = variable_item_get_current_value_index(item);
+    variable_item_set_current_value_text(item, speaker_text[index]);
+    app->speaker = speaker_value[index];
+}
+
+static void flipbip39_scene_settings_set_led(VariableItem* item) {
+    FlipBip39* app = variable_item_get_context(item);
+    uint8_t index = variable_item_get_current_value_index(item);
+    variable_item_set_current_value_text(item, led_text[index]);
+    app->led = led_value[index];
+}
+
+void flipbip39_scene_settings_submenu_callback(void* context, uint32_t index) {
+    FlipBip39* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+void flipbip39_scene_settings_on_enter(void* context) {
+    FlipBip39* app = context;
+    VariableItem* item;
+    uint8_t value_index;
+
+    // Vibro on/off
+    item = variable_item_list_add(
+        app->variable_item_list,
+        "Vibro/Haptic:",
+        2,
+        flipbip39_scene_settings_set_haptic,
+        app);
+    value_index = value_index_uint32(app->haptic, haptic_value, 2);
+    variable_item_set_current_value_index(item, value_index);
+    variable_item_set_current_value_text(item, haptic_text[value_index]);
+
+    // Sound on/off
+    item = variable_item_list_add(
+        app->variable_item_list,
+        "Sound:",
+        2,
+        flipbip39_scene_settings_set_speaker,
+        app);
+    value_index = value_index_uint32(app->speaker, speaker_value, 2);
+    variable_item_set_current_value_index(item, value_index);
+    variable_item_set_current_value_text(item, speaker_text[value_index]);
+
+    // LED Effects on/off
+    item = variable_item_list_add(
+        app->variable_item_list,
+        "LED FX:",
+        2,
+        flipbip39_scene_settings_set_led,
+        app);
+    value_index = value_index_uint32(app->led, led_value, 2);
+    variable_item_set_current_value_index(item, value_index);
+    variable_item_set_current_value_text(item, led_text[value_index]);
+    
+    view_dispatcher_switch_to_view(app->view_dispatcher, FlipBip39ViewIdSettings);
+}
+
+bool flipbip39_scene_settings_on_event(void* context, SceneManagerEvent event) {
+    FlipBip39* app = context;
+    UNUSED(app);
+    bool consumed = false;
+    if(event.type == SceneManagerEventTypeCustom) {
+        
+    }
+    return consumed;
+}
+
+void flipbip39_scene_settings_on_exit(void* context) {
+    FlipBip39* app = context;
+    variable_item_list_set_selected_item(app->variable_item_list, 0);
+    variable_item_list_reset(app->variable_item_list);
+}

+ 54 - 0
scenes/flipbip39_scene_startscreen.c

@@ -0,0 +1,54 @@
+#include "../flipbip39.h"
+#include "../helpers/flipbip39_custom_event.h"
+#include "../views/flipbip39_startscreen.h"
+
+void flipbip39_scene_startscreen_callback(FlipBip39CustomEvent event, void* context) {
+    furi_assert(context);
+    FlipBip39* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, event);
+}
+
+void flipbip39_scene_startscreen_on_enter(void* context) {
+    furi_assert(context);
+    FlipBip39* app = context;
+    flipbip39_startscreen_set_callback(app->flipbip39_startscreen, flipbip39_scene_startscreen_callback, app);
+    view_dispatcher_switch_to_view(app->view_dispatcher, FlipBip39ViewIdStartscreen);
+}
+
+bool flipbip39_scene_startscreen_on_event(void* context, SceneManagerEvent event) {
+    FlipBip39* app = context;
+    bool consumed = false;
+    
+    if(event.type == SceneManagerEventTypeCustom) {
+        switch(event.event) {
+            case FlipBip39CustomEventStartscreenLeft:
+            case FlipBip39CustomEventStartscreenRight:
+                break;
+            case FlipBip39CustomEventStartscreenUp:
+            case FlipBip39CustomEventStartscreenDown:
+                break;
+            case FlipBip39CustomEventStartscreenOk:
+                scene_manager_next_scene(app->scene_manager, FlipBip39SceneMenu);
+                consumed = true;
+                break;
+            case FlipBip39CustomEventStartscreenBack:
+                notification_message(app->notification, &sequence_reset_red);
+                notification_message(app->notification, &sequence_reset_green);
+                notification_message(app->notification, &sequence_reset_blue);
+                if(!scene_manager_search_and_switch_to_previous_scene(
+                    app->scene_manager, FlipBip39SceneStartscreen)) {
+                        scene_manager_stop(app->scene_manager);
+                        view_dispatcher_stop(app->view_dispatcher);
+                    }
+                consumed = true;
+                break;
+        }
+    }
+    
+    return consumed;
+}
+
+void flipbip39_scene_startscreen_on_exit(void* context) {
+    FlipBip39* app = context;
+    UNUSED(app);
+}

+ 136 - 0
views/flipbip39_scene_1.c

@@ -0,0 +1,136 @@
+#include "../flipbip39.h"
+#include <furi.h>
+#include <furi_hal.h>
+#include <input/input.h>
+#include <gui/elements.h>
+#include <dolphin/dolphin.h>
+
+struct FlipBip39Scene1 {
+    View* view;
+    FlipBip39Scene1Callback callback;
+    void* context;
+};
+
+
+typedef struct {
+    int some_value;
+} FlipBip39Scene1Model;
+
+void flipbip39_scene_1_set_callback(
+    FlipBip39Scene1* instance,
+    FlipBip39Scene1Callback callback,
+    void* context) {
+    furi_assert(instance);
+    furi_assert(callback);
+    instance->callback = callback;
+    instance->context = context;
+}
+
+void flipbip39_scene_1_draw(Canvas* canvas, FlipBip39Scene1Model* model) {
+    UNUSED(model);
+    canvas_clear(canvas);
+    canvas_set_color(canvas, ColorBlack);
+    canvas_set_font(canvas, FontPrimary);
+    canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, "This is Scene 1"); 
+    canvas_set_font(canvas, FontSecondary);
+    canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, "An empty scene to be"); 
+    canvas_draw_str_aligned(canvas, 0, 32, AlignLeft, AlignTop, "used as flipbip39"); 
+}
+
+static void flipbip39_scene_1_model_init(FlipBip39Scene1Model* const model) {
+    model->some_value = 1;
+}
+
+bool flipbip39_scene_1_input(InputEvent* event, void* context) {
+    furi_assert(context); 
+    FlipBip39Scene1* instance = context;
+    if (event->type == InputTypeRelease) {
+        switch(event->key) {
+            case InputKeyBack:
+                with_view_model(
+                    instance->view,
+                    FlipBip39Scene1Model * model,
+                    {
+                        UNUSED(model);
+                        instance->callback(FlipBip39CustomEventScene1Back, instance->context);
+                    },
+                    true);
+                break;
+            case InputKeyLeft:
+            case InputKeyRight:
+            case InputKeyUp:
+            case InputKeyDown:
+            case InputKeyOk:
+                with_view_model(
+                    instance->view,
+                    FlipBip39Scene1Model* model,
+                    {
+                        UNUSED(model);
+                    },
+                    true);
+                break;
+            case InputKeyMAX:
+                break;
+        }
+    }
+    return true;
+}
+
+void flipbip39_scene_1_exit(void* context) {
+    furi_assert(context);
+}
+
+void flipbip39_scene_1_enter(void* context) {
+    furi_assert(context);
+    FlipBip39Scene1* instance = (FlipBip39Scene1*)context;
+    with_view_model(
+        instance->view,
+        FlipBip39Scene1Model * model,
+        {
+            flipbip39_scene_1_model_init(model);
+        },
+        true
+    );
+}
+
+FlipBip39Scene1* flipbip39_scene_1_alloc() {
+    FlipBip39Scene1* instance = malloc(sizeof(FlipBip39Scene1));
+    instance->view = view_alloc();
+    view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(FlipBip39Scene1Model));
+    view_set_context(instance->view, instance); // furi_assert crashes in events without this
+    view_set_draw_callback(instance->view, (ViewDrawCallback)flipbip39_scene_1_draw);
+    view_set_input_callback(instance->view, flipbip39_scene_1_input);
+    view_set_enter_callback(instance->view, flipbip39_scene_1_enter);
+    view_set_exit_callback(instance->view, flipbip39_scene_1_exit);
+
+    with_view_model(
+        instance->view,
+        FlipBip39Scene1Model * model,
+        {
+            flipbip39_scene_1_model_init(model);
+        },
+        true
+    );
+    
+    return instance;
+}
+
+void flipbip39_scene_1_free(FlipBip39Scene1* instance) {
+    furi_assert(instance);
+
+    with_view_model(
+        instance->view,
+        FlipBip39Scene1Model * model,
+        {
+            UNUSED(model);
+        },
+        true);
+    view_free(instance->view);
+    free(instance);
+}
+
+View* flipbip39_scene_1_get_view(FlipBip39Scene1* instance) {
+    furi_assert(instance);
+    return instance->view;
+}
+

+ 19 - 0
views/flipbip39_scene_1.h

@@ -0,0 +1,19 @@
+#pragma once
+
+#include <gui/view.h>
+#include "../helpers/flipbip39_custom_event.h"
+
+typedef struct FlipBip39Scene1 FlipBip39Scene1;
+
+typedef void (*FlipBip39Scene1Callback)(FlipBip39CustomEvent event, void* context);
+
+void flipbip39_scene_1_set_callback(
+    FlipBip39Scene1* flipbip39_scene_1,
+    FlipBip39Scene1Callback callback,
+    void* context);
+
+View* flipbip39_scene_1_get_view(FlipBip39Scene1* flipbip39_static);
+
+FlipBip39Scene1* flipbip39_scene_1_alloc();
+
+void flipbip39_scene_1_free(FlipBip39Scene1* flipbip39_static);

+ 254 - 0
views/flipbip39_scene_2.c

@@ -0,0 +1,254 @@
+#include "../flipbip39.h"
+#include <furi.h>
+#include <furi_hal.h>
+#include <input/input.h>
+#include <gui/elements.h>
+#include <dolphin/dolphin.h>
+#include "../helpers/flipbip39_haptic.h"
+#include "../helpers/flipbip39_speaker.h"
+#include "../helpers/flipbip39_led.h"
+
+struct FlipBip39Scene2 {
+    View* view;
+    FlipBip39Scene2Callback callback;
+    void* context;
+};
+
+typedef struct {
+    int screen_text;
+} FlipBip39Scene2Model;
+
+char buttonText[11][14] = {
+    "",
+    "Press Up",
+    "Press Down",
+    "Press Left",
+    "Press Right",
+    "Press Ok",
+    "Release Up",
+    "Release Down",
+    "Release Left",
+    "Release Right",
+    "Release Ok",
+};
+
+void flipbip39_scene_2_set_callback(
+    FlipBip39Scene2* instance,
+    FlipBip39Scene2Callback callback,
+    void* context) {
+    furi_assert(instance);
+    furi_assert(callback);
+    instance->callback = callback;
+    instance->context = context;
+}
+
+void flipbip39_scene_2_draw(Canvas* canvas, FlipBip39Scene2Model* model) {
+    canvas_clear(canvas);
+    canvas_set_color(canvas, ColorBlack);
+    canvas_set_font(canvas, FontPrimary);
+    canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, "Scene 2: Input Examples"); 
+    canvas_set_font(canvas, FontSecondary);
+    char *strInput = malloc(15);
+    strcpy(strInput, buttonText[model->screen_text]);
+    canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, strInput); 
+    free(strInput);
+}
+
+static void flipbip39_scene_2_model_init(FlipBip39Scene2Model* const model) {
+    model->screen_text = 0;
+}
+
+bool flipbip39_scene_2_input(InputEvent* event, void* context) {
+    furi_assert(context);
+    FlipBip39Scene2* instance = context;
+    if (event->type == InputTypeRelease) {
+        switch(event->key) {
+            case InputKeyBack:
+                with_view_model(
+                    instance->view,
+                    FlipBip39Scene2Model * model,
+                    {
+                        UNUSED(model);
+                        flipbip39_stop_all_sound(instance->context);
+                        instance->callback(FlipBip39CustomEventScene2Back, instance->context);
+                        flipbip39_play_long_bump(instance->context);
+                    },
+                    true);
+                break;
+            case InputKeyUp:
+                with_view_model(
+                    instance->view,
+                    FlipBip39Scene2Model * model,
+                    {
+                        model->screen_text = 6;
+                        flipbip39_play_bad_bump(instance->context);
+                        flipbip39_stop_all_sound(instance->context);
+                        flipbip39_led_set_rgb(instance->context, 255, 0, 255);
+                    },
+                    true);
+                break;
+            case InputKeyDown:
+                with_view_model(
+                    instance->view,
+                    FlipBip39Scene2Model * model,
+                    {
+                        model->screen_text = 7;
+                        flipbip39_play_bad_bump(instance->context);
+                        flipbip39_stop_all_sound(instance->context);
+                        flipbip39_led_set_rgb(instance->context, 255, 255, 0);
+                    },
+                    true);
+                break;
+            case InputKeyLeft:
+                with_view_model(
+                    instance->view,
+                    FlipBip39Scene2Model * model,
+                    {
+                        model->screen_text = 8;
+                        flipbip39_play_bad_bump(instance->context);
+                        flipbip39_stop_all_sound(instance->context);
+                        flipbip39_led_set_rgb(instance->context, 0, 255, 255);
+                    },
+                    true);
+                break;
+            case InputKeyRight:
+                with_view_model(
+                    instance->view,
+                    FlipBip39Scene2Model * model,
+                    {
+                        model->screen_text = 9;
+                        flipbip39_play_bad_bump(instance->context);
+                        flipbip39_stop_all_sound(instance->context);
+                        flipbip39_led_set_rgb(instance->context, 255, 0, 0);
+                    },
+                    true);
+                break;
+            case InputKeyOk:
+                with_view_model(
+                    instance->view,
+                    FlipBip39Scene2Model * model,
+                    {
+                        model->screen_text = 10;
+                        flipbip39_play_bad_bump(instance->context);
+                        flipbip39_stop_all_sound(instance->context);
+                        flipbip39_led_set_rgb(instance->context, 255, 255, 255);
+                    },
+                    true);
+                break;
+            case InputKeyMAX:
+                break;
+        }
+    } else if (event->type == InputTypePress) {
+         switch(event->key) {
+            case InputKeyUp:
+                with_view_model(
+                    instance->view,
+                    FlipBip39Scene2Model * model,
+                    {
+                        model->screen_text = 1;
+                        flipbip39_play_happy_bump(instance->context);
+                        flipbip39_play_input_sound(instance->context);
+                    },
+                    true);
+                break;
+            case InputKeyDown:
+                with_view_model(
+                    instance->view,
+                    FlipBip39Scene2Model * model,
+                    {
+                        model->screen_text = 2;
+                        flipbip39_play_happy_bump(instance->context);
+                        flipbip39_play_input_sound(instance->context);
+                    },
+                    true);
+                break;
+            case InputKeyLeft:
+                with_view_model(
+                    instance->view,
+                    FlipBip39Scene2Model * model,
+                    {
+                        model->screen_text = 3;
+                        flipbip39_play_happy_bump(instance->context);
+                        flipbip39_play_input_sound(instance->context);
+                    },
+                    true);
+                break;
+            case InputKeyRight:
+                with_view_model(
+                    instance->view,
+                    FlipBip39Scene2Model * model,
+                    {
+                        model->screen_text = 4;
+                        flipbip39_play_happy_bump(instance->context);
+                        flipbip39_play_input_sound(instance->context);
+                    },
+                    true);
+                break;
+            case InputKeyOk:
+                with_view_model(
+                    instance->view,
+                    FlipBip39Scene2Model * model,
+                    {
+                        model->screen_text = 5;
+                        flipbip39_play_happy_bump(instance->context);
+                        flipbip39_play_input_sound(instance->context);
+                    },
+                    true);
+                break;
+            case InputKeyBack:
+            case InputKeyMAX:
+                break;
+        }
+    }
+    
+    return true;
+}
+
+void flipbip39_scene_2_exit(void* context) {
+    furi_assert(context);
+    FlipBip39* app = context;
+    flipbip39_stop_all_sound(app);
+    //flipbip39_led_reset(app);
+}
+
+void flipbip39_scene_2_enter(void* context) {
+    furi_assert(context);
+    DOLPHIN_DEED(DolphinDeedPluginStart);
+}
+
+FlipBip39Scene2* flipbip39_scene_2_alloc() {
+    FlipBip39Scene2* instance = malloc(sizeof(FlipBip39Scene2));
+    instance->view = view_alloc();
+    view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(FlipBip39Scene2Model));
+    view_set_context(instance->view, instance);
+    view_set_draw_callback(instance->view, (ViewDrawCallback)flipbip39_scene_2_draw);
+    view_set_input_callback(instance->view, flipbip39_scene_2_input);
+    //view_set_enter_callback(instance->view, flipbip39_scene_2_enter);
+    view_set_exit_callback(instance->view, flipbip39_scene_2_exit);
+
+    with_view_model(
+        instance->view,
+        FlipBip39Scene2Model * model,
+        {
+            flipbip39_scene_2_model_init(model);
+        },
+        true);
+    
+    return instance;
+}
+
+void flipbip39_scene_2_free(FlipBip39Scene2* instance) {
+    furi_assert(instance);
+
+
+    view_free(instance->view);
+    free(instance);
+}
+
+View* flipbip39_scene_2_get_view(FlipBip39Scene2* instance) {
+    furi_assert(instance);
+
+
+    return instance->view;
+}
+

+ 19 - 0
views/flipbip39_scene_2.h

@@ -0,0 +1,19 @@
+#pragma once
+
+#include <gui/view.h>
+#include "../helpers/flipbip39_custom_event.h"
+
+typedef struct FlipBip39Scene2 FlipBip39Scene2;
+
+typedef void (*FlipBip39Scene2Callback)(FlipBip39CustomEvent event, void* context);
+
+void flipbip39_scene_2_set_callback(
+    FlipBip39Scene2* instance,
+    FlipBip39Scene2Callback callback,
+    void * context);
+
+FlipBip39Scene2* flipbip39_scene_2_alloc();
+
+void flipbip39_scene_2_free(FlipBip39Scene2* flipbip39_static);
+
+View* flipbip39_scene_2_get_view(FlipBip39Scene2* boilerpate_static);

+ 137 - 0
views/flipbip39_startscreen.c

@@ -0,0 +1,137 @@
+#include "../flipbip39.h"
+#include <furi.h>
+#include <furi_hal.h>
+#include <input/input.h>
+#include <gui/elements.h>
+
+struct FlipBip39Startscreen {
+    View* view;
+    FlipBip39StartscreenCallback callback;
+    void* context;
+};
+
+
+typedef struct {
+    int some_value;
+} FlipBip39StartscreenModel;
+
+void flipbip39_startscreen_set_callback(
+    FlipBip39Startscreen* instance,
+    FlipBip39StartscreenCallback callback,
+    void* context) {
+    furi_assert(instance);
+    furi_assert(callback);
+    instance->callback = callback;
+    instance->context = context;
+}
+
+void flipbip39_startscreen_draw(Canvas* canvas, FlipBip39StartscreenModel* model) {
+    UNUSED(model);
+    canvas_clear(canvas);
+    canvas_set_color(canvas, ColorBlack);
+    canvas_set_font(canvas, FontPrimary);
+    canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignTop, "Start Screen"); 
+    canvas_set_font(canvas, FontSecondary);
+    canvas_draw_str_aligned(canvas, 64, 22, AlignCenter, AlignTop, "Explain your app"); 
+    canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignTop, "on this screen");
+    elements_button_center(canvas, "Start"); 
+}
+
+static void flipbip39_startscreen_model_init(FlipBip39StartscreenModel* const model) {
+    model->some_value = 1;
+}
+
+bool flipbip39_startscreen_input(InputEvent* event, void* context) {
+    furi_assert(context); 
+    FlipBip39Startscreen* instance = context;
+    if (event->type == InputTypeRelease) {
+        switch(event->key) {
+            case InputKeyBack:
+                with_view_model(
+                    instance->view,
+                    FlipBip39StartscreenModel * model,
+                    {
+                        UNUSED(model);
+                        instance->callback(FlipBip39CustomEventStartscreenBack, instance->context);
+                    },
+                    true);
+                break;
+            case InputKeyLeft:
+            case InputKeyRight:
+            case InputKeyUp:
+            case InputKeyDown:
+            case InputKeyOk:
+                with_view_model(
+                    instance->view,
+                    FlipBip39StartscreenModel* model,
+                    {
+                        UNUSED(model);
+                        instance->callback(FlipBip39CustomEventStartscreenOk, instance->context);
+                    },
+                    true);
+                break;
+            case InputKeyMAX:
+                break;
+        }
+    }
+    return true;
+}
+
+void flipbip39_startscreen_exit(void* context) {
+    furi_assert(context);
+}
+
+void flipbip39_startscreen_enter(void* context) {
+    furi_assert(context);
+    FlipBip39Startscreen* instance = (FlipBip39Startscreen*)context;
+    with_view_model(
+        instance->view,
+        FlipBip39StartscreenModel * model,
+        {
+            flipbip39_startscreen_model_init(model);
+        },
+        true
+    );
+}
+
+FlipBip39Startscreen* flipbip39_startscreen_alloc() {
+    FlipBip39Startscreen* instance = malloc(sizeof(FlipBip39Startscreen));
+    instance->view = view_alloc();
+    view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(FlipBip39StartscreenModel));
+    view_set_context(instance->view, instance); // furi_assert crashes in events without this
+    view_set_draw_callback(instance->view, (ViewDrawCallback)flipbip39_startscreen_draw);
+    view_set_input_callback(instance->view, flipbip39_startscreen_input);
+    //view_set_enter_callback(instance->view, flipbip39_startscreen_enter);
+    //view_set_exit_callback(instance->view, flipbip39_startscreen_exit);
+
+    with_view_model(
+        instance->view,
+        FlipBip39StartscreenModel * model,
+        {
+            flipbip39_startscreen_model_init(model);
+        },
+        true
+    );
+    
+    return instance;
+}
+
+void flipbip39_startscreen_free(FlipBip39Startscreen* instance) {
+    furi_assert(instance);
+
+    with_view_model(
+        instance->view,
+        FlipBip39StartscreenModel * model,
+        {
+            UNUSED(model);
+        },
+        true);
+    view_free(instance->view);
+    free(instance);
+}
+
+View* flipbip39_startscreen_get_view(FlipBip39Startscreen* instance) {
+    furi_assert(instance);
+    return instance->view;
+}
+

+ 19 - 0
views/flipbip39_startscreen.h

@@ -0,0 +1,19 @@
+#pragma once
+
+#include <gui/view.h>
+#include "../helpers/flipbip39_custom_event.h"
+
+typedef struct FlipBip39Startscreen FlipBip39Startscreen;
+
+typedef void (*FlipBip39StartscreenCallback)(FlipBip39CustomEvent event, void* context);
+
+void flipbip39_startscreen_set_callback(
+    FlipBip39Startscreen* flipbip39_startscreen,
+    FlipBip39StartscreenCallback callback,
+    void* context);
+
+View* flipbip39_startscreen_get_view(FlipBip39Startscreen* flipbip39_static);
+
+FlipBip39Startscreen* flipbip39_startscreen_alloc();
+
+void flipbip39_startscreen_free(FlipBip39Startscreen* flipbip39_static);