Bad BT plugin, Submenu locked elements, API updates, etc.

Thanks to WillyJL, ClaraCrazy, and XFW contributors
This commit is contained in:
MX
2023-05-13 00:14:22 +03:00
parent a7691b2d3b
commit 849f14e480
42 changed files with 3211 additions and 44 deletions

View File

@@ -61,6 +61,7 @@ static ViewPort* bt_pin_code_view_port_alloc(Bt* bt) {
static void bt_pin_code_show(Bt* bt, uint32_t pin_code) {
bt->pin_code = pin_code;
if(bt->suppress_pin_screen) return;
notification_message(bt->notification, &sequence_display_backlight_on);
gui_view_port_send_to_front(bt->gui, bt->pin_code_view_port);
view_port_enabled_set(bt->pin_code_view_port, true);
@@ -75,6 +76,8 @@ static void bt_pin_code_hide(Bt* bt) {
static bool bt_pin_code_verify_event_handler(Bt* bt, uint32_t pin) {
furi_assert(bt);
bt->pin_code = pin;
if(bt->suppress_pin_screen) return true;
notification_message(bt->notification, &sequence_display_backlight_on);
FuriString* pin_str;
dialog_message_set_icon(bt->dialog_message, &I_BLE_Pairing_128x64, 0, 0);
@@ -149,6 +152,8 @@ Bt* bt_alloc() {
// API evnent
bt->api_event = furi_event_flag_alloc();
bt->pin = 0;
return bt;
}
@@ -214,6 +219,7 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) {
furi_assert(context);
Bt* bt = context;
bool ret = false;
bt->pin = 0;
if(event.type == GapEventTypeConnected) {
// Update status bar
@@ -270,12 +276,14 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) {
furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);
ret = true;
} else if(event.type == GapEventTypePinCodeShow) {
bt->pin = event.data.pin_code;
BtMessage message = {
.type = BtMessageTypePinCodeShow, .data.pin_code = event.data.pin_code};
furi_check(
furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);
ret = true;
} else if(event.type == GapEventTypePinCodeVerify) {
bt->pin = event.data.pin_code;
ret = bt_pin_code_verify_event_handler(bt, event.data.pin_code);
} else if(event.type == GapEventTypeUpdateMTU) {
bt->max_packet_size = event.data.max_packet_size;
@@ -368,6 +376,86 @@ static void bt_close_connection(Bt* bt) {
furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT);
}
static inline FuriHalBtProfile get_hal_bt_profile(BtProfile profile) {
if(profile == BtProfileHidKeyboard) {
return FuriHalBtProfileHidKeyboard;
} else {
return FuriHalBtProfileSerial;
}
}
void bt_restart(Bt* bt) {
furi_hal_bt_change_app(get_hal_bt_profile(bt->profile), bt_on_gap_event_callback, bt);
furi_hal_bt_start_advertising();
}
void bt_set_profile_adv_name(Bt* bt, const char* fmt, ...) {
furi_assert(bt);
furi_assert(fmt);
char name[FURI_HAL_BT_ADV_NAME_LENGTH];
va_list args;
va_start(args, fmt);
vsnprintf(name, sizeof(name), fmt, args);
va_end(args);
furi_hal_bt_set_profile_adv_name(get_hal_bt_profile(bt->profile), name);
bt_restart(bt);
}
const char* bt_get_profile_adv_name(Bt* bt) {
furi_assert(bt);
return furi_hal_bt_get_profile_adv_name(get_hal_bt_profile(bt->profile));
}
void bt_set_profile_mac_address(Bt* bt, const uint8_t mac[6]) {
furi_assert(bt);
furi_assert(mac);
furi_hal_bt_set_profile_mac_addr(get_hal_bt_profile(bt->profile), mac);
bt_restart(bt);
}
const uint8_t* bt_get_profile_mac_address(Bt* bt) {
furi_assert(bt);
return furi_hal_bt_get_profile_mac_addr(get_hal_bt_profile(bt->profile));
}
bool bt_remote_rssi(Bt* bt, uint8_t* rssi) {
furi_assert(bt);
uint8_t rssi_val;
uint32_t since = furi_hal_bt_get_conn_rssi(&rssi_val);
if(since == 0) return false;
*rssi = rssi_val;
return true;
}
void bt_set_profile_pairing_method(Bt* bt, GapPairing pairing_method) {
furi_assert(bt);
furi_hal_bt_set_profile_pairing_method(get_hal_bt_profile(bt->profile), pairing_method);
bt_restart(bt);
}
GapPairing bt_get_profile_pairing_method(Bt* bt) {
furi_assert(bt);
return furi_hal_bt_get_profile_pairing_method(get_hal_bt_profile(bt->profile));
}
void bt_disable_peer_key_update(Bt* bt) {
UNUSED(bt);
furi_hal_bt_set_key_storage_change_callback(NULL, NULL);
}
void bt_enable_peer_key_update(Bt* bt) {
furi_assert(bt);
furi_hal_bt_set_key_storage_change_callback(bt_on_key_storage_change_callback, bt);
}
int32_t bt_srv(void* p) {
UNUSED(p);
Bt* bt = bt_alloc();

View File

@@ -3,6 +3,8 @@
#include <stdint.h>
#include <stdbool.h>
#include <furi_hal_bt.h>
#ifdef __cplusplus
extern "C" {
#endif
@@ -23,6 +25,11 @@ typedef enum {
BtProfileHidKeyboard,
} BtProfile;
typedef struct {
uint8_t rssi;
uint32_t since;
} BtRssi;
typedef void (*BtStatusChangedCallback)(BtStatus status, void* context);
/** Change BLE Profile
@@ -69,6 +76,32 @@ void bt_keys_storage_set_storage_path(Bt* bt, const char* keys_storage_path);
*/
void bt_keys_storage_set_default_path(Bt* bt);
// New methods
void bt_set_profile_adv_name(Bt* bt, const char* fmt, ...);
const char* bt_get_profile_adv_name(Bt* bt);
void bt_set_profile_mac_address(Bt* bt, const uint8_t mac[6]);
const uint8_t* bt_get_profile_mac_address(Bt* bt);
bool bt_remote_rssi(Bt* bt, uint8_t* rssi);
void bt_set_profile_pairing_method(Bt* bt, GapPairing pairing_method);
GapPairing bt_get_profile_pairing_method(Bt* bt);
/** Stop saving new peer key to flash (in .bt.keys file)
*
*/
void bt_disable_peer_key_update(Bt* bt);
/** Enable saving peer key to internal flash (enable by default)
*
* @note This function should be called if bt_disable_peer_key_update was called before
*/
void bt_enable_peer_key_update(Bt* bt);
#ifdef __cplusplus
}
#endif

View File

@@ -76,4 +76,6 @@ struct Bt {
FuriEventFlag* api_event;
BtStatusChangedCallback status_changed_cb;
void* status_changed_ctx;
uint32_t pin;
bool suppress_pin_screen;
};

View File

@@ -573,6 +573,64 @@ void elements_string_fit_width(Canvas* canvas, FuriString* string, uint8_t width
}
}
void elements_scrollable_text_line_str(
Canvas* canvas,
uint8_t x,
uint8_t y,
uint8_t width,
const char* string,
size_t scroll,
bool ellipsis,
bool centered) {
FuriString* line = furi_string_alloc_set_str(string);
size_t len_px = canvas_string_width(canvas, furi_string_get_cstr(line));
if(len_px > width) {
if(centered) {
centered = false;
x -= width / 2;
}
if(ellipsis) {
width -= canvas_string_width(canvas, "...");
}
// Calculate scroll size
size_t scroll_size = furi_string_size(line);
size_t right_width = 0;
for(size_t i = scroll_size - 1; i > 0; i--) {
right_width += canvas_glyph_width(canvas, furi_string_get_char(line, i));
if(right_width > width) break;
scroll_size--;
if(!scroll_size) break;
}
// Ensure that we have something to scroll
if(scroll_size) {
scroll_size += 3;
scroll = scroll % scroll_size;
furi_string_right(line, scroll);
}
len_px = canvas_string_width(canvas, furi_string_get_cstr(line));
while(len_px > width) {
furi_string_left(line, furi_string_size(line) - 1);
len_px = canvas_string_width(canvas, furi_string_get_cstr(line));
}
if(ellipsis) {
furi_string_cat(line, "...");
}
}
if(centered) {
canvas_draw_str_aligned(
canvas, x, y, AlignCenter, AlignBottom, furi_string_get_cstr(line));
} else {
canvas_draw_str(canvas, x, y, furi_string_get_cstr(line));
}
furi_string_free(line);
}
void elements_scrollable_text_line(
Canvas* canvas,
uint8_t x,

View File

@@ -228,6 +228,16 @@ void elements_scrollable_text_line(
size_t scroll,
bool ellipsis);
void elements_scrollable_text_line_str(
Canvas* canvas,
uint8_t x,
uint8_t y,
uint8_t width,
const char* string,
size_t scroll,
bool ellipsis,
bool centered);
/** Draw text box element
*
* @param canvas Canvas instance

View File

@@ -1,11 +1,14 @@
#include "submenu.h"
#include <gui/canvas_i.h>
#include <assets_icons.h>
#include <gui/elements.h>
#include <furi.h>
#include <m-array.h>
struct Submenu {
View* view;
FuriTimer* locked_timer;
};
typedef struct {
@@ -13,6 +16,8 @@ typedef struct {
uint32_t index;
SubmenuItemCallback callback;
void* callback_context;
bool locked;
FuriString* locked_message;
} SubmenuItem;
static void SubmenuItem_init(SubmenuItem* item) {
@@ -20,6 +25,8 @@ static void SubmenuItem_init(SubmenuItem* item) {
item->index = 0;
item->callback = NULL;
item->callback_context = NULL;
item->locked = false;
item->locked_message = furi_string_alloc();
}
static void SubmenuItem_init_set(SubmenuItem* item, const SubmenuItem* src) {
@@ -27,6 +34,8 @@ static void SubmenuItem_init_set(SubmenuItem* item, const SubmenuItem* src) {
item->index = src->index;
item->callback = src->callback;
item->callback_context = src->callback_context;
item->locked = src->locked;
item->locked_message = furi_string_alloc_set(src->locked_message);
}
static void SubmenuItem_set(SubmenuItem* item, const SubmenuItem* src) {
@@ -34,10 +43,13 @@ static void SubmenuItem_set(SubmenuItem* item, const SubmenuItem* src) {
item->index = src->index;
item->callback = src->callback;
item->callback_context = src->callback_context;
item->locked = src->locked;
furi_string_set(item->locked_message, src->locked_message);
}
static void SubmenuItem_clear(SubmenuItem* item) {
furi_string_free(item->label);
furi_string_free(item->locked_message);
}
ARRAY_DEF(
@@ -53,6 +65,7 @@ typedef struct {
FuriString* header;
size_t position;
size_t window_position;
bool locked_message_visible;
} SubmenuModel;
static void submenu_process_up(Submenu* submenu);
@@ -63,7 +76,12 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) {
SubmenuModel* model = _model;
const uint8_t item_height = 16;
const uint8_t item_width = 123;
uint8_t item_width = 123;
if(canvas->orientation == CanvasOrientationVertical ||
canvas->orientation == CanvasOrientationVerticalFlip) {
item_width = 60;
}
canvas_clear(canvas);
@@ -96,9 +114,18 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) {
canvas_set_color(canvas, ColorBlack);
}
if(SubmenuItemArray_cref(it)->locked) {
canvas_draw_icon(
canvas,
110,
y_offset + (item_position * item_height) + item_height - 12,
&I_Lock_7x8);
}
FuriString* disp_str;
disp_str = furi_string_alloc_set(SubmenuItemArray_cref(it)->label);
elements_string_fit_width(canvas, disp_str, item_width - 11);
elements_string_fit_width(
canvas, disp_str, item_width - (SubmenuItemArray_cref(it)->locked ? 25 : 11));
canvas_draw_str(
canvas,
@@ -113,6 +140,23 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) {
}
elements_scrollbar(canvas, model->position, SubmenuItemArray_size(model->items));
if(model->locked_message_visible) {
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, 8, 10, 110, 48);
canvas_set_color(canvas, ColorBlack);
canvas_draw_icon(canvas, 10, 14, &I_WarningDolphin_45x42);
canvas_draw_rframe(canvas, 8, 8, 112, 50, 3);
canvas_draw_rframe(canvas, 9, 9, 110, 48, 2);
elements_multiline_text_aligned(
canvas,
84,
32,
AlignCenter,
AlignCenter,
furi_string_get_cstr(
SubmenuItemArray_get(model->items, model->position)->locked_message));
}
}
static bool submenu_view_input_callback(InputEvent* event, void* context) {
@@ -120,7 +164,19 @@ static bool submenu_view_input_callback(InputEvent* event, void* context) {
furi_assert(submenu);
bool consumed = false;
if(event->type == InputTypeShort) {
bool locked_message_visible = false;
with_view_model(
submenu->view,
SubmenuModel * model,
{ locked_message_visible = model->locked_message_visible; },
false);
if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) &&
locked_message_visible) {
with_view_model(
submenu->view, SubmenuModel * model, { model->locked_message_visible = false; }, true);
consumed = true;
} else if(event->type == InputTypeShort) {
switch(event->key) {
case InputKeyUp:
consumed = true;
@@ -150,6 +206,14 @@ static bool submenu_view_input_callback(InputEvent* event, void* context) {
return consumed;
}
void submenu_timer_callback(void* context) {
furi_assert(context);
Submenu* submenu = context;
with_view_model(
submenu->view, SubmenuModel * model, { model->locked_message_visible = false; }, true);
}
Submenu* submenu_alloc() {
Submenu* submenu = malloc(sizeof(Submenu));
submenu->view = view_alloc();
@@ -158,6 +222,8 @@ Submenu* submenu_alloc() {
view_set_draw_callback(submenu->view, submenu_view_draw_callback);
view_set_input_callback(submenu->view, submenu_view_input_callback);
submenu->locked_timer = furi_timer_alloc(submenu_timer_callback, FuriTimerTypeOnce, submenu);
with_view_model(
submenu->view,
SubmenuModel * model,
@@ -183,6 +249,8 @@ void submenu_free(Submenu* submenu) {
SubmenuItemArray_clear(model->items);
},
true);
furi_timer_stop(submenu->locked_timer);
furi_timer_free(submenu->locked_timer);
view_free(submenu->view);
free(submenu);
}
@@ -198,9 +266,23 @@ void submenu_add_item(
uint32_t index,
SubmenuItemCallback callback,
void* callback_context) {
submenu_add_lockable_item(submenu, label, index, callback, callback_context, false, NULL);
}
void submenu_add_lockable_item(
Submenu* submenu,
const char* label,
uint32_t index,
SubmenuItemCallback callback,
void* callback_context,
bool locked,
const char* locked_message) {
SubmenuItem* item = NULL;
furi_assert(label);
furi_assert(submenu);
if(locked) {
furi_assert(locked_message);
}
with_view_model(
submenu->view,
@@ -211,6 +293,10 @@ void submenu_add_item(
item->index = index;
item->callback = callback;
item->callback_context = callback_context;
item->locked = locked;
if(locked) {
furi_string_set_str(item->locked_message, locked_message);
}
},
true);
}
@@ -328,10 +414,14 @@ void submenu_process_ok(Submenu* submenu) {
if(model->position < items_size) {
item = SubmenuItemArray_get(model->items, model->position);
}
if(item && item->locked) {
model->locked_message_visible = true;
furi_timer_start(submenu->locked_timer, furi_kernel_get_tick_frequency() * 3);
}
},
true);
if(item && item->callback) {
if(item && !item->locked && item->callback) {
item->callback(item->callback_context, item->index);
}
}

View File

@@ -53,6 +53,26 @@ void submenu_add_item(
SubmenuItemCallback callback,
void* callback_context);
/** Add lockable item to submenu
*
* @param submenu Submenu instance
* @param label menu item label
* @param index menu item index, used for callback, may be
* the same with other items
* @param callback menu item callback
* @param callback_context menu item callback context
* @param locked menu item locked status
* @param locked_message menu item locked message
*/
void submenu_add_lockable_item(
Submenu* submenu,
const char* label,
uint32_t index,
SubmenuItemCallback callback,
void* callback_context,
bool locked,
const char* locked_message);
/** Remove all items from submenu
*
* @param submenu Submenu instance

View File

@@ -2,6 +2,7 @@
#include <gui/elements.h>
#include <gui/canvas.h>
#include <furi.h>
#include <assets_icons.h>
#include <m-array.h>
#include <stdint.h>
@@ -11,6 +12,8 @@ struct VariableItem {
FuriString* current_value_text;
uint8_t values_count;
VariableItemChangeCallback change_callback;
bool locked;
FuriString* locked_message;
void* context;
};
@@ -20,12 +23,16 @@ struct VariableItemList {
View* view;
VariableItemListEnterCallback callback;
void* context;
FuriTimer* scroll_timer;
FuriTimer* locked_timer;
};
typedef struct {
VariableItemArray_t items;
uint8_t position;
uint8_t window_position;
size_t scroll_counter;
bool locked_message_visible;
} VariableItemListModel;
static void variable_item_list_process_up(VariableItemList* variable_item_list);
@@ -56,31 +63,50 @@ static void variable_item_list_draw_callback(Canvas* canvas, void* _model) {
const VariableItem* item = VariableItemArray_cref(it);
uint8_t item_y = y_offset + (item_position * item_height);
uint8_t item_text_y = item_y + item_height - 4;
size_t scroll_counter = 0;
if(position == model->position) {
canvas_set_color(canvas, ColorBlack);
elements_slightly_rounded_box(canvas, 0, item_y + 1, item_width, item_height - 2);
canvas_set_color(canvas, ColorWhite);
scroll_counter = model->scroll_counter;
if(scroll_counter < 1) {
scroll_counter = 0;
} else {
scroll_counter -= 1;
}
} else {
canvas_set_color(canvas, ColorBlack);
}
canvas_draw_str(canvas, 6, item_text_y, item->label);
if(item->current_value_index > 0) {
canvas_draw_str(canvas, 73, item_text_y, "<");
if(item->current_value_index == 0 && furi_string_empty(item->current_value_text)) {
// Only left text, no right text
canvas_draw_str(canvas, 6, item_text_y, item->label);
} else {
elements_scrollable_text_line_str(
canvas, 6, item_text_y, 66, item->label, scroll_counter, false, false);
}
canvas_draw_str_aligned(
canvas,
(115 + 73) / 2 + 1,
item_text_y,
AlignCenter,
AlignBottom,
furi_string_get_cstr(item->current_value_text));
if(item->locked) {
canvas_draw_icon(canvas, 110, item_text_y - 8, &I_Lock_7x8);
} else {
if(item->current_value_index > 0) {
canvas_draw_str(canvas, 73, item_text_y, "<");
}
if(item->current_value_index < (item->values_count - 1)) {
canvas_draw_str(canvas, 115, item_text_y, ">");
elements_scrollable_text_line_str(
canvas,
(115 + 73) / 2 + 1,
item_text_y,
37,
furi_string_get_cstr(item->current_value_text),
scroll_counter,
false,
true);
if(item->current_value_index < (item->values_count - 1)) {
canvas_draw_str(canvas, 115, item_text_y, ">");
}
}
}
@@ -88,6 +114,23 @@ static void variable_item_list_draw_callback(Canvas* canvas, void* _model) {
}
elements_scrollbar(canvas, model->position, VariableItemArray_size(model->items));
if(model->locked_message_visible) {
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, 8, 10, 110, 48);
canvas_set_color(canvas, ColorBlack);
canvas_draw_icon(canvas, 10, 14, &I_WarningDolphin_45x42);
canvas_draw_rframe(canvas, 8, 8, 112, 50, 3);
canvas_draw_rframe(canvas, 9, 9, 110, 48, 2);
elements_multiline_text_aligned(
canvas,
84,
32,
AlignCenter,
AlignCenter,
furi_string_get_cstr(
VariableItemArray_get(model->items, model->position)->locked_message));
}
}
void variable_item_list_set_selected_item(VariableItemList* variable_item_list, uint8_t index) {
@@ -130,7 +173,22 @@ static bool variable_item_list_input_callback(InputEvent* event, void* context)
furi_assert(variable_item_list);
bool consumed = false;
if(event->type == InputTypeShort) {
bool locked_message_visible = false;
with_view_model(
variable_item_list->view,
VariableItemListModel * model,
{ locked_message_visible = model->locked_message_visible; },
false);
if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) &&
locked_message_visible) {
with_view_model(
variable_item_list->view,
VariableItemListModel * model,
{ model->locked_message_visible = false; },
true);
consumed = true;
} else if(event->type == InputTypeShort) {
switch(event->key) {
case InputKeyUp:
consumed = true;
@@ -198,6 +256,7 @@ void variable_item_list_process_up(VariableItemList* variable_item_list) {
model->window_position = model->position - (items_on_screen - 1);
}
}
model->scroll_counter = 0;
},
true);
}
@@ -219,6 +278,7 @@ void variable_item_list_process_down(VariableItemList* variable_item_list) {
model->position = 0;
model->window_position = 0;
}
model->scroll_counter = 0;
},
true);
}
@@ -248,8 +308,13 @@ void variable_item_list_process_left(VariableItemList* variable_item_list) {
VariableItemListModel * model,
{
VariableItem* item = variable_item_list_get_selected_item(model);
if(item->current_value_index > 0) {
if(item->locked) {
model->locked_message_visible = true;
furi_timer_start(
variable_item_list->locked_timer, furi_kernel_get_tick_frequency() * 3);
} else if(item->current_value_index > 0) {
item->current_value_index--;
model->scroll_counter = 0;
if(item->change_callback) {
item->change_callback(item);
}
@@ -264,8 +329,13 @@ void variable_item_list_process_right(VariableItemList* variable_item_list) {
VariableItemListModel * model,
{
VariableItem* item = variable_item_list_get_selected_item(model);
if(item->current_value_index < (item->values_count - 1)) {
if(item->locked) {
model->locked_message_visible = true;
furi_timer_start(
variable_item_list->locked_timer, furi_kernel_get_tick_frequency() * 3);
} else if(item->current_value_index < (item->values_count - 1)) {
item->current_value_index++;
model->scroll_counter = 0;
if(item->change_callback) {
item->change_callback(item);
}
@@ -279,11 +349,36 @@ void variable_item_list_process_ok(VariableItemList* variable_item_list) {
variable_item_list->view,
VariableItemListModel * model,
{
if(variable_item_list->callback) {
VariableItem* item = variable_item_list_get_selected_item(model);
if(item->locked) {
model->locked_message_visible = true;
furi_timer_start(
variable_item_list->locked_timer, furi_kernel_get_tick_frequency() * 3);
} else if(variable_item_list->callback) {
variable_item_list->callback(variable_item_list->context, model->position);
}
},
false);
true);
}
static void variable_item_list_scroll_timer_callback(void* context) {
VariableItemList* variable_item_list = context;
with_view_model(
variable_item_list->view,
VariableItemListModel * model,
{ model->scroll_counter++; },
true);
}
void variable_item_list_locked_timer_callback(void* context) {
furi_assert(context);
VariableItemList* variable_item_list = context;
with_view_model(
variable_item_list->view,
VariableItemListModel * model,
{ model->locked_message_visible = false; },
true);
}
VariableItemList* variable_item_list_alloc() {
@@ -295,6 +390,9 @@ VariableItemList* variable_item_list_alloc() {
view_set_draw_callback(variable_item_list->view, variable_item_list_draw_callback);
view_set_input_callback(variable_item_list->view, variable_item_list_input_callback);
variable_item_list->locked_timer = furi_timer_alloc(
variable_item_list_locked_timer_callback, FuriTimerTypeOnce, variable_item_list);
with_view_model(
variable_item_list->view,
VariableItemListModel * model,
@@ -302,8 +400,12 @@ VariableItemList* variable_item_list_alloc() {
VariableItemArray_init(model->items);
model->position = 0;
model->window_position = 0;
model->scroll_counter = 0;
},
true);
variable_item_list->scroll_timer = furi_timer_alloc(
variable_item_list_scroll_timer_callback, FuriTimerTypePeriodic, variable_item_list);
furi_timer_start(variable_item_list->scroll_timer, 333);
return variable_item_list;
}
@@ -319,10 +421,15 @@ void variable_item_list_free(VariableItemList* variable_item_list) {
for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);
VariableItemArray_next(it)) {
furi_string_free(VariableItemArray_ref(it)->current_value_text);
furi_string_free(VariableItemArray_ref(it)->locked_message);
}
VariableItemArray_clear(model->items);
},
false);
furi_timer_stop(variable_item_list->scroll_timer);
furi_timer_free(variable_item_list->scroll_timer);
furi_timer_stop(variable_item_list->locked_timer);
furi_timer_free(variable_item_list->locked_timer);
view_free(variable_item_list->view);
free(variable_item_list);
}
@@ -338,6 +445,7 @@ void variable_item_list_reset(VariableItemList* variable_item_list) {
for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);
VariableItemArray_next(it)) {
furi_string_free(VariableItemArray_ref(it)->current_value_text);
furi_string_free(VariableItemArray_ref(it)->locked_message);
}
VariableItemArray_reset(model->items);
},
@@ -370,6 +478,8 @@ VariableItem* variable_item_list_add(
item->context = context;
item->current_value_index = 0;
item->current_value_text = furi_string_alloc();
item->locked = false;
item->locked_message = furi_string_alloc();
},
true);
@@ -404,6 +514,14 @@ void variable_item_set_current_value_text(VariableItem* item, const char* curren
furi_string_set(item->current_value_text, current_value_text);
}
void variable_item_set_locked(VariableItem* item, bool locked, const char* locked_message) {
item->locked = locked;
if(locked) {
furi_assert(locked_message);
furi_string_set(item->locked_message, locked_message);
}
}
uint8_t variable_item_get_current_value_index(VariableItem* item) {
return item->current_value_index;
}

View File

@@ -95,6 +95,14 @@ void variable_item_set_values_count(VariableItem* item, uint8_t values_count);
*/
void variable_item_set_current_value_text(VariableItem* item, const char* current_value_text);
/** Set item locked state and text
*
* @param item VariableItem* instance
* @param locked Is item locked boolean
* @param locked_message The locked message text
*/
void variable_item_set_locked(VariableItem* item, bool locked, const char* locked_message);
/** Get item current selected index
*
* @param item VariableItem* instance