mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-07-17 00:28:11 -07:00
438 lines
17 KiB
C
438 lines
17 KiB
C
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
//
|
|
// VB Lab Migration Assistant for Flipper Zero
|
|
// Copyright (C) 2022 cyanic
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
#include <notification/notification_messages.h>
|
|
|
|
#include "../vb_tag.h"
|
|
#include "../vb_migrate_i.h"
|
|
#include "vb_migrate_icons.h"
|
|
|
|
typedef enum {
|
|
RegisterStateInitial,
|
|
RegisterStateInstructionInitial,
|
|
RegisterStateInstructionConnect,
|
|
RegisterStateCaptureInitial,
|
|
RegisterStateCaptureInvalidTag,
|
|
RegisterStateCapturePwd,
|
|
RegisterStateCaptureFull,
|
|
RegisterStateCaptureFailed,
|
|
RegisterStateCaptureIncorrectTag,
|
|
} RegisterState;
|
|
|
|
typedef enum {
|
|
RegisterEventTypeNextButton,
|
|
RegisterEventTypePrevButton,
|
|
RegisterEventTypeVbReadInitial,
|
|
RegisterEventTypeVbPwdAuth,
|
|
RegisterEventTypeVbReadFullSuccess,
|
|
RegisterEventTypeVbReadFullFail,
|
|
} RegisterEventType;
|
|
|
|
static void
|
|
vb_migrate_scene_register_widget_callback(GuiButtonType result, InputType type, void* context) {
|
|
VbMigrate* inst = context;
|
|
|
|
if(type == InputTypeShort) {
|
|
if(result == GuiButtonTypeRight)
|
|
view_dispatcher_send_custom_event(inst->view_dispatcher, RegisterEventTypeNextButton);
|
|
else if(result == GuiButtonTypeLeft)
|
|
view_dispatcher_send_custom_event(inst->view_dispatcher, RegisterEventTypePrevButton);
|
|
}
|
|
}
|
|
|
|
static bool
|
|
vb_migrate_scene_register_worker_read_initial_callback(NfcWorkerEvent event, void* context) {
|
|
VbMigrate* inst = context;
|
|
bool consumed = false;
|
|
|
|
if(event == NfcWorkerEventReadMfUltralight) {
|
|
view_dispatcher_send_custom_event(inst->view_dispatcher, RegisterEventTypeVbReadInitial);
|
|
consumed = true;
|
|
}
|
|
|
|
return consumed;
|
|
}
|
|
|
|
static bool vb_migrate_scene_register_worker_auth_callback(NfcWorkerEvent event, void* context) {
|
|
VbMigrate* inst = context;
|
|
bool consumed = false;
|
|
|
|
if(event == NfcWorkerEventMfUltralightPwdAuth) {
|
|
view_dispatcher_send_custom_event(inst->view_dispatcher, RegisterEventTypeVbPwdAuth);
|
|
consumed = true;
|
|
}
|
|
|
|
return consumed;
|
|
}
|
|
|
|
static bool
|
|
vb_migrate_scene_register_worker_full_capture_callback(NfcWorkerEvent event, void* context) {
|
|
VbMigrate* inst = context;
|
|
bool consumed = false;
|
|
|
|
if(event == NfcWorkerEventMfUltralightPassKey) {
|
|
memcpy(
|
|
inst->nfc_dev->dev_data.mf_ul_data.auth_key,
|
|
inst->captured_pwd,
|
|
sizeof(inst->captured_pwd));
|
|
consumed = true;
|
|
} else if(event == NfcWorkerEventSuccess) {
|
|
view_dispatcher_send_custom_event(
|
|
inst->view_dispatcher, RegisterEventTypeVbReadFullSuccess);
|
|
consumed = true;
|
|
} else if(event == NfcWorkerEventFail) {
|
|
view_dispatcher_send_custom_event(inst->view_dispatcher, RegisterEventTypeVbReadFullFail);
|
|
consumed = true;
|
|
}
|
|
|
|
return consumed;
|
|
}
|
|
|
|
static void vb_migrate_scene_register_cleanup_state(VbMigrate* inst, RegisterState state) {
|
|
if(state == RegisterStateCaptureInvalidTag || state == RegisterStateCaptureFailed ||
|
|
state == RegisterStateCaptureIncorrectTag) {
|
|
notification_message(inst->notifications, &sequence_reset_red);
|
|
} else if(
|
|
state == RegisterStateCaptureInitial || state == RegisterStateCapturePwd ||
|
|
state == RegisterStateCaptureFull) {
|
|
vb_migrate_blink_stop(inst);
|
|
nfc_worker_stop(inst->worker);
|
|
}
|
|
}
|
|
|
|
static void vb_migrate_scene_register_set_state(VbMigrate* inst, RegisterState state) {
|
|
uint32_t curr_state =
|
|
scene_manager_get_scene_state(inst->scene_manager, VbMigrateSceneRegister);
|
|
if(state != curr_state) {
|
|
vb_migrate_scene_register_cleanup_state(inst, curr_state);
|
|
Widget* widget = inst->widget;
|
|
|
|
if(state == RegisterStateInstructionInitial) {
|
|
widget_reset(widget);
|
|
widget_add_string_multiline_element(
|
|
widget,
|
|
0,
|
|
0,
|
|
AlignLeft,
|
|
AlignTop,
|
|
FontSecondary,
|
|
"Please make sure your current\ncharacter has been sent to\nVB Lab before continuing.");
|
|
widget_add_button_element(
|
|
widget,
|
|
GuiButtonTypeLeft,
|
|
"Cancel",
|
|
vb_migrate_scene_register_widget_callback,
|
|
inst);
|
|
widget_add_button_element(
|
|
widget,
|
|
GuiButtonTypeRight,
|
|
"Next",
|
|
vb_migrate_scene_register_widget_callback,
|
|
inst);
|
|
|
|
view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget);
|
|
} else if(state == RegisterStateInstructionConnect) {
|
|
widget_reset(widget);
|
|
widget_add_string_multiline_element(
|
|
widget,
|
|
0,
|
|
0,
|
|
AlignLeft,
|
|
AlignTop,
|
|
FontSecondary,
|
|
"Prepare VB Lab:\n"
|
|
"1. Ensure current character in\n"
|
|
"VB Lab has been put in storage\n"
|
|
"2. Open the \"Scan\" screen\n"
|
|
"3. Tap \"Vital Bracelet -> App\"");
|
|
widget_add_button_element(
|
|
widget,
|
|
GuiButtonTypeLeft,
|
|
"Cancel",
|
|
vb_migrate_scene_register_widget_callback,
|
|
inst);
|
|
widget_add_button_element(
|
|
widget,
|
|
GuiButtonTypeRight,
|
|
"Next",
|
|
vb_migrate_scene_register_widget_callback,
|
|
inst);
|
|
|
|
view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget);
|
|
} else if(state == RegisterStateCaptureInitial) {
|
|
widget_reset(widget);
|
|
widget_add_string_multiline_element(
|
|
widget,
|
|
80,
|
|
31,
|
|
AlignCenter,
|
|
AlignCenter,
|
|
FontPrimary,
|
|
"Tap Flipper to\nyour Vital\nBracelet");
|
|
widget_add_button_element(
|
|
widget,
|
|
GuiButtonTypeLeft,
|
|
"Previous",
|
|
vb_migrate_scene_register_widget_callback,
|
|
inst);
|
|
|
|
view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget);
|
|
nfc_device_clear(inst->nfc_dev);
|
|
inst->nfc_dev->dev_data.read_mode = NfcReadModeMfUltralight;
|
|
nfc_worker_start(
|
|
inst->worker,
|
|
NfcWorkerStateRead,
|
|
&inst->nfc_dev->dev_data,
|
|
vb_migrate_scene_register_worker_read_initial_callback,
|
|
inst);
|
|
vb_migrate_blink_read(inst);
|
|
} else if(state == RegisterStateCaptureInvalidTag) {
|
|
widget_reset(widget);
|
|
widget_add_string_multiline_element(
|
|
widget,
|
|
80,
|
|
31,
|
|
AlignCenter,
|
|
AlignCenter,
|
|
FontSecondary,
|
|
"Tag is not a valid\nVital Bracelet,\nplease try again");
|
|
widget_add_icon_element(widget, 15, 22, &I_Error_18x18);
|
|
widget_add_button_element(
|
|
widget,
|
|
GuiButtonTypeLeft,
|
|
"Retry",
|
|
vb_migrate_scene_register_widget_callback,
|
|
inst);
|
|
|
|
view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget);
|
|
notification_message(inst->notifications, &sequence_set_red_255);
|
|
} else if(state == RegisterStateCapturePwd) {
|
|
widget_reset(widget);
|
|
widget_add_string_multiline_element(
|
|
widget,
|
|
80,
|
|
31,
|
|
AlignCenter,
|
|
AlignCenter,
|
|
FontPrimary,
|
|
"Tap \"Send\" on\nVB Lab, then tap\nto Flipper");
|
|
widget_add_icon_element(widget, 7, 18, &I_Touch_26x26);
|
|
widget_add_button_element(
|
|
widget,
|
|
GuiButtonTypeLeft,
|
|
"Previous",
|
|
vb_migrate_scene_register_widget_callback,
|
|
inst);
|
|
|
|
view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget);
|
|
|
|
BantBlock* bant = vb_tag_get_bant_block(&inst->nfc_dev->dev_data);
|
|
vb_tag_set_operation(bant, VbTagOperationReady);
|
|
vb_tag_set_status(bant, VbTagStatusReady);
|
|
nfc_worker_start(
|
|
inst->worker,
|
|
NfcWorkerStateMfUltralightEmulate,
|
|
&inst->nfc_dev->dev_data,
|
|
vb_migrate_scene_register_worker_auth_callback,
|
|
inst);
|
|
vb_migrate_blink_emulate(inst);
|
|
} else if(state == RegisterStateCaptureFull) {
|
|
widget_reset(widget);
|
|
widget_add_string_multiline_element(
|
|
widget,
|
|
80,
|
|
31,
|
|
AlignCenter,
|
|
AlignCenter,
|
|
FontPrimary,
|
|
"Tap Flipper to\nVital Bracelet\nagain");
|
|
widget_add_button_element(
|
|
widget,
|
|
GuiButtonTypeLeft,
|
|
"Previous",
|
|
vb_migrate_scene_register_widget_callback,
|
|
inst);
|
|
|
|
view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget);
|
|
|
|
// inst->nfc_dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodAuto;
|
|
inst->nfc_dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethod; // HOPE THIS WORKS...
|
|
nfc_worker_start(
|
|
inst->worker,
|
|
NfcWorkerStateReadMfUltralightReadAuth,
|
|
&inst->nfc_dev->dev_data,
|
|
vb_migrate_scene_register_worker_full_capture_callback,
|
|
inst);
|
|
vb_migrate_blink_read(inst);
|
|
} else if(state == RegisterStateCaptureFailed) {
|
|
widget_reset(widget);
|
|
widget_add_string_multiline_element(
|
|
widget,
|
|
80,
|
|
31,
|
|
AlignCenter,
|
|
AlignCenter,
|
|
FontSecondary,
|
|
"Failed to read\nVital Bracelet,\nplease try again");
|
|
widget_add_icon_element(widget, 15, 22, &I_Error_18x18);
|
|
widget_add_button_element(
|
|
widget,
|
|
GuiButtonTypeLeft,
|
|
"Retry",
|
|
vb_migrate_scene_register_widget_callback,
|
|
inst);
|
|
|
|
view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget);
|
|
notification_message(inst->notifications, &sequence_set_red_255);
|
|
} else if(state == RegisterStateCaptureIncorrectTag) {
|
|
widget_reset(widget);
|
|
widget_add_string_multiline_element(
|
|
widget,
|
|
80,
|
|
31,
|
|
AlignCenter,
|
|
AlignCenter,
|
|
FontSecondary,
|
|
"Different tag read,\nplease try again");
|
|
widget_add_icon_element(widget, 15, 22, &I_Error_18x18);
|
|
widget_add_button_element(
|
|
widget,
|
|
GuiButtonTypeLeft,
|
|
"Retry",
|
|
vb_migrate_scene_register_widget_callback,
|
|
inst);
|
|
|
|
view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget);
|
|
notification_message(inst->notifications, &sequence_set_red_255);
|
|
} else {
|
|
furi_crash("Unknown new state in vb_migrate_scene_register_set_state");
|
|
}
|
|
|
|
scene_manager_set_scene_state(inst->scene_manager, VbMigrateSceneRegister, state);
|
|
}
|
|
}
|
|
|
|
static bool vb_migrate_scene_register_next_state(VbMigrate* inst, RegisterState state) {
|
|
if(state == RegisterStateInstructionInitial) {
|
|
vb_migrate_scene_register_set_state(inst, RegisterStateInstructionConnect);
|
|
return true;
|
|
} else if(state == RegisterStateInstructionConnect) {
|
|
vb_migrate_scene_register_set_state(inst, RegisterStateCaptureInitial);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
vb_migrate_scene_register_prev_state(VbMigrate* inst, RegisterState state, bool is_back) {
|
|
UNUSED(is_back);
|
|
|
|
if(state == RegisterStateInstructionInitial || state == RegisterStateInstructionConnect) {
|
|
return scene_manager_previous_scene(inst->scene_manager);
|
|
} else if(state == RegisterStateCaptureInitial) {
|
|
vb_migrate_scene_register_set_state(inst, RegisterStateInstructionConnect);
|
|
return true;
|
|
} else if(
|
|
state == RegisterStateCaptureInvalidTag || state == RegisterStateCapturePwd ||
|
|
state == RegisterStateCaptureFull) {
|
|
vb_migrate_scene_register_set_state(inst, RegisterStateCaptureInitial);
|
|
return true;
|
|
} else if(state == RegisterStateCaptureFailed || state == RegisterStateCaptureIncorrectTag) {
|
|
vb_migrate_scene_register_set_state(inst, RegisterStateCaptureFull);
|
|
return true;
|
|
}
|
|
|
|
return is_back;
|
|
}
|
|
|
|
void vb_migrate_scene_register_on_enter(void* context) {
|
|
VbMigrate* inst = context;
|
|
|
|
scene_manager_set_scene_state(
|
|
inst->scene_manager, VbMigrateSceneRegister, RegisterStateInitial);
|
|
vb_migrate_scene_register_set_state(inst, RegisterStateInstructionInitial);
|
|
}
|
|
|
|
bool vb_migrate_scene_register_on_event(void* context, SceneManagerEvent event) {
|
|
VbMigrate* inst = context;
|
|
bool consumed = false;
|
|
RegisterState state =
|
|
scene_manager_get_scene_state(inst->scene_manager, VbMigrateSceneRegister);
|
|
|
|
if(event.type == SceneManagerEventTypeCustom) {
|
|
if(event.event == RegisterEventTypeNextButton) {
|
|
consumed = vb_migrate_scene_register_next_state(inst, state);
|
|
} else if(event.event == RegisterEventTypePrevButton) {
|
|
consumed = vb_migrate_scene_register_prev_state(inst, state, false);
|
|
} else if(event.event == RegisterEventTypeVbReadInitial) {
|
|
if(vb_tag_validate_product(&inst->nfc_dev->dev_data)) {
|
|
memcpy(
|
|
inst->captured_uid,
|
|
inst->nfc_dev->dev_data.nfc_data.uid,
|
|
sizeof(inst->captured_uid));
|
|
notification_message(inst->notifications, &sequence_success);
|
|
vb_migrate_scene_register_set_state(inst, RegisterStateCapturePwd);
|
|
} else {
|
|
notification_message(inst->notifications, &sequence_error);
|
|
vb_migrate_scene_register_set_state(inst, RegisterStateCaptureInvalidTag);
|
|
}
|
|
consumed = true;
|
|
} else if(event.event == RegisterEventTypeVbPwdAuth) {
|
|
// Set up for auth
|
|
memcpy(
|
|
inst->captured_pwd,
|
|
inst->nfc_dev->dev_data.mf_ul_auth.pwd.raw,
|
|
sizeof(inst->captured_pwd));
|
|
|
|
notification_message(inst->notifications, &sequence_success);
|
|
vb_migrate_scene_register_set_state(inst, RegisterStateCaptureFull);
|
|
consumed = true;
|
|
} else if(event.event == RegisterEventTypeVbReadFullSuccess) {
|
|
NfcDeviceData* dev_data = &inst->nfc_dev->dev_data;
|
|
if(memcmp(dev_data->nfc_data.uid, inst->captured_uid, sizeof(inst->captured_uid)) ||
|
|
dev_data->mf_ul_data.data_read != dev_data->mf_ul_data.data_size) {
|
|
notification_message(inst->notifications, &sequence_error);
|
|
vb_migrate_scene_register_set_state(inst, RegisterStateCaptureIncorrectTag);
|
|
} else {
|
|
notification_message(inst->notifications, &sequence_success);
|
|
scene_manager_next_scene(inst->scene_manager, VbMigrateSceneRegisterSave);
|
|
}
|
|
consumed = true;
|
|
} else if(event.event == RegisterEventTypeVbReadFullFail) {
|
|
notification_message(inst->notifications, &sequence_error);
|
|
vb_migrate_scene_register_set_state(inst, RegisterStateCaptureFailed);
|
|
consumed = true;
|
|
} else {
|
|
furi_crash("Unknown event in vb_migrate_scene_register_on_event");
|
|
}
|
|
} else if(event.type == SceneManagerEventTypeBack) {
|
|
consumed = vb_migrate_scene_register_prev_state(inst, state, true);
|
|
}
|
|
return consumed;
|
|
}
|
|
|
|
void vb_migrate_scene_register_on_exit(void* context) {
|
|
VbMigrate* inst = context;
|
|
RegisterState state =
|
|
scene_manager_get_scene_state(inst->scene_manager, VbMigrateSceneRegister);
|
|
|
|
vb_migrate_scene_register_cleanup_state(inst, state);
|
|
widget_reset(inst->widget);
|
|
}
|