#include "subbrute_attack_view.h"
#include "../subbrute_i.h"
#include
#include
#include
#include
#include
#define TAG "SubBruteAttackView"
struct SubBruteAttackView {
View* view;
SubBruteAttackViewCallback callback;
void* context;
SubBruteAttacks attack_type;
uint64_t max_value;
uint64_t current_step;
bool is_attacking;
};
typedef struct {
SubBruteAttacks attack_type;
uint64_t max_value;
uint64_t current_step;
uint8_t repeat_count;
bool is_attacking;
IconAnimation* icon;
} SubBruteAttackViewModel;
void subbrute_attack_view_set_callback(
SubBruteAttackView* instance,
SubBruteAttackViewCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
bool subbrute_attack_view_input(InputEvent* event, void* context) {
furi_assert(event);
furi_assert(context);
SubBruteAttackView* instance = context;
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "InputKey: %d", event->key);
#endif
if(event->key == InputKeyBack && event->type == InputTypeShort) {
instance->is_attacking = false;
with_view_model(
instance->view,
SubBruteAttackViewModel * model,
{ model->is_attacking = false; },
true);
instance->callback(SubBruteCustomEventTypeBackPressed, instance->context);
return true;
}
bool update = false;
if(!instance->is_attacking) {
if(event->type == InputTypeShort && event->key == InputKeyOk) {
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "InputKey: %d OK", event->key);
#endif
instance->is_attacking = true;
instance->callback(SubBruteCustomEventTypeTransmitStarted, instance->context);
update = true;
} else if(event->key == InputKeyUp && event->type == InputTypeShort) {
instance->callback(SubBruteCustomEventTypeSaveFile, instance->context);
update = true;
} else if(event->key == InputKeyUp && event->type == InputTypeLong) {
instance->callback(SubBruteCustomEventTypeExtraSettings, instance->context);
update = true;
} else if(event->key == InputKeyDown) {
instance->callback(SubBruteCustomEventTypeTransmitCustom, instance->context);
update = true;
} else if(event->type == InputTypeShort) {
if(event->key == InputKeyLeft) {
instance->callback(SubBruteCustomEventTypeChangeStepDown, instance->context);
} else if(event->key == InputKeyRight) {
instance->callback(SubBruteCustomEventTypeChangeStepUp, instance->context);
}
update = true;
} else if(event->type == InputTypeRepeat) {
if(event->key == InputKeyLeft) {
instance->callback(SubBruteCustomEventTypeChangeStepDownMore, instance->context);
} else if(event->key == InputKeyRight) {
instance->callback(SubBruteCustomEventTypeChangeStepUpMore, instance->context);
}
update = true;
}
} else {
// ATTACK Mode!
if((event->type == InputTypeShort || event->type == InputTypeRepeat) &&
(event->key == InputKeyOk || event->key == InputKeyBack)) {
instance->is_attacking = false;
instance->callback(SubBruteCustomEventTypeTransmitNotStarted, instance->context);
update = true;
}
}
if(update) {
with_view_model(
instance->view,
SubBruteAttackViewModel * model,
{
if(model->is_attacking != instance->is_attacking) {
if(instance->is_attacking) {
icon_animation_stop(model->icon);
icon_animation_start(model->icon);
} else {
icon_animation_stop(model->icon);
}
}
model->attack_type = instance->attack_type;
model->max_value = instance->max_value;
model->current_step = instance->current_step;
model->is_attacking = instance->is_attacking;
},
update);
}
return update;
}
SubBruteAttackView* subbrute_attack_view_alloc() {
SubBruteAttackView* instance = malloc(sizeof(SubBruteAttackView));
instance->view = view_alloc();
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubBruteAttackViewModel));
view_set_context(instance->view, instance);
with_view_model(
instance->view,
SubBruteAttackViewModel * model,
{
model->icon = icon_animation_alloc(&A_Sub1ghz_14);
view_tie_icon_animation(instance->view, model->icon);
},
false);
view_set_draw_callback(instance->view, (ViewDrawCallback)subbrute_attack_view_draw);
view_set_input_callback(instance->view, subbrute_attack_view_input);
view_set_enter_callback(instance->view, subbrute_attack_view_enter);
view_set_exit_callback(instance->view, subbrute_attack_view_exit);
instance->attack_type = SubBruteAttackTotalCount;
instance->max_value = 0x00;
instance->current_step = 0;
instance->is_attacking = false;
return instance;
}
void subbrute_attack_view_enter(void* context) {
furi_assert(context);
SubBruteAttackView* instance = (SubBruteAttackView*)context;
with_view_model(
instance->view,
SubBruteAttackViewModel * model,
{
if(model->is_attacking) {
icon_animation_start(model->icon);
}
},
true);
}
void subbrute_attack_view_free(SubBruteAttackView* instance) {
furi_assert(instance);
with_view_model(
instance->view,
SubBruteAttackViewModel * model,
{ icon_animation_free(model->icon); },
false);
view_free(instance->view);
free(instance);
}
View* subbrute_attack_view_get_view(SubBruteAttackView* instance) {
furi_assert(instance);
return instance->view;
}
void subbrute_attack_view_set_current_step(SubBruteAttackView* instance, uint64_t current_step) {
furi_assert(instance);
instance->current_step = current_step;
with_view_model(
instance->view,
SubBruteAttackViewModel * model,
{ model->current_step = current_step; },
true);
}
// We need to call init every time, because not every time we call "enter"
// normally, so call "enter" only once
void subbrute_attack_view_init_values(
SubBruteAttackView* instance,
uint8_t index,
uint64_t max_value,
uint64_t current_step,
bool is_attacking,
uint8_t extra_repeats) {
#ifdef FURI_DEBUG
FURI_LOG_I(
TAG,
"INIT, attack_type: %d, max_value: %lld, current_step: %lld, extra_repeats: %d",
index,
max_value,
current_step,
extra_repeats);
#endif
instance->attack_type = index;
instance->max_value = max_value;
instance->current_step = current_step;
instance->is_attacking = is_attacking;
with_view_model(
instance->view,
SubBruteAttackViewModel * model,
{
model->max_value = max_value;
model->attack_type = index;
model->current_step = current_step;
model->is_attacking = is_attacking;
model->repeat_count = extra_repeats;
if(is_attacking) {
icon_animation_start(model->icon);
} else {
icon_animation_stop(model->icon);
}
},
true);
}
void subbrute_attack_view_exit(void* context) {
furi_assert(context);
SubBruteAttackView* instance = context;
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "subbrute_attack_view_exit");
#endif
with_view_model(
instance->view,
SubBruteAttackViewModel * model,
{ icon_animation_stop(model->icon); },
false);
}
void subbrute_attack_view_draw(Canvas* canvas, void* context) {
furi_assert(context);
SubBruteAttackViewModel* model = (SubBruteAttackViewModel*)context;
char buffer[64] = {0};
const char* attack_name = NULL;
attack_name = subbrute_protocol_name(model->attack_type);
// Title
if(model->is_attacking) {
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, attack_name);
}
// Current Step / Max value
const uint8_t y_frequency = 17;
if(model->max_value > 9999) {
canvas_set_font(canvas, FontBigNumbers);
snprintf(buffer, sizeof(buffer), "%05d/", (int)model->current_step);
canvas_draw_str_aligned(canvas, 5, y_frequency, AlignLeft, AlignTop, buffer);
// Second part with another font, because BigNumber is out of screen bounds
canvas_set_font(canvas, FontPrimary);
snprintf(buffer, sizeof(buffer), "%05d", (int)model->max_value);
canvas_draw_str_aligned(canvas, 107, y_frequency + 13, AlignRight, AlignBottom, buffer);
} else if(model->max_value <= 0xFF) {
canvas_set_font(canvas, FontBigNumbers);
snprintf(
buffer, sizeof(buffer), "%03d/%03d", (int)model->current_step, (int)model->max_value);
canvas_draw_str_aligned(canvas, 64, y_frequency, AlignCenter, AlignTop, buffer);
} else {
canvas_set_font(canvas, FontBigNumbers);
snprintf(
buffer, sizeof(buffer), "%04d/%04d", (int)model->current_step, (int)model->max_value);
canvas_draw_str_aligned(canvas, 64, y_frequency, AlignCenter, AlignTop, buffer);
}
canvas_set_font(canvas, FontSecondary);
memset(buffer, 0, sizeof(buffer));
if(!model->is_attacking) {
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(canvas, 64, 44, AlignCenter, AlignBottom, attack_name);
snprintf(
buffer,
sizeof(buffer),
"x%d",
model->repeat_count); // + subbrute_protocol_repeats_count(model->attack_type));
canvas_draw_str_aligned(canvas, 60, 6, AlignCenter, AlignCenter, buffer);
elements_button_left(canvas, "-1");
elements_button_right(canvas, "+1");
elements_button_center(canvas, "Start");
elements_button_up(canvas, "Save");
elements_button_down(canvas, "Resend");
} else {
// canvas_draw_icon_animation
const uint8_t icon_h_offset = 0;
const uint8_t icon_width_with_offset =
icon_animation_get_width(model->icon) + icon_h_offset;
const uint8_t icon_v_offset = icon_animation_get_height(model->icon); // + vertical_offset;
const uint8_t x = canvas_width(canvas);
const uint8_t y = canvas_height(canvas);
canvas_draw_icon_animation(
canvas, x - icon_width_with_offset, y - icon_v_offset, model->icon);
// Progress bar
// Resolution: 128x64 px
float progress_value = (float)model->current_step / (float)model->max_value;
elements_progress_bar(canvas, 8, 37, 110, progress_value > 1 ? 1 : progress_value);
snprintf(
buffer,
sizeof(buffer),
"x%d",
model->repeat_count); // + subbrute_protocol_repeats_count(model->attack_type));
canvas_draw_str(canvas, 4, y - 8, buffer);
canvas_draw_str(canvas, 4, y - 1, "repeats");
elements_button_center(canvas, "Stop");
}
}