Merge branch 'release-candidate' into release

This commit is contained in:
Aleksandr Kutuzov 2022-02-14 17:37:24 +03:00
commit f8c8e63adf
No known key found for this signature in database
GPG Key ID: 0D0011717914BBCD
243 changed files with 4690 additions and 3508 deletions

View File

@ -11,8 +11,8 @@ volatile int WIEGAND::_bitCount = 0;
int WIEGAND::_wiegandType = 0;
constexpr uint32_t clocks_in_ms = 64 * 1000;
const GpioPin* pinD0 = &gpio_ext_pa4;
const GpioPin* pinD1 = &gpio_ext_pa7;
const GpioPin* const pinD0 = &gpio_ext_pa4;
const GpioPin* const pinD1 = &gpio_ext_pa7;
WIEGAND::WIEGAND() {
}
@ -31,9 +31,9 @@ int WIEGAND::getWiegandType() {
bool WIEGAND::available() {
bool ret;
__disable_irq();
FURI_CRITICAL_ENTER();
ret = DoWiegandConversion();
__enable_irq();
FURI_CRITICAL_EXIT();
return ret;
}
@ -221,4 +221,4 @@ bool WIEGAND::DoWiegandConversion() {
}
} else
return false;
}
}

View File

@ -37,7 +37,7 @@ bool AccessorSceneStart::on_event(AccessorApp* app, AccessorEvent* event) {
data[i] = wiegand->getCodeHigh() >> ((i - 4) * 8);
}
} else {
__disable_irq();
FURI_CRITICAL_ENTER();
if(onewire->reset()) {
type = 255;
onewire->write(0x33);
@ -49,7 +49,7 @@ bool AccessorSceneStart::on_event(AccessorApp* app, AccessorEvent* event) {
data[i] = data[i + 1];
}
}
__enable_irq();
FURI_CRITICAL_EXIT();
}
if(type > 0) {
@ -85,4 +85,4 @@ void AccessorSceneStart::on_exit(AccessorApp* app) {
Popup* popup = app->get_view_manager()->get_popup();
popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
}
}

View File

@ -39,6 +39,10 @@ ArchiveApp* archive_alloc() {
view_dispatcher_add_view(
archive->view_dispatcher, ArchiveViewTextInput, text_input_get_view(archive->text_input));
archive->widget = widget_alloc();
view_dispatcher_add_view(
archive->view_dispatcher, ArchiveViewWidget, widget_get_view(archive->widget));
return archive;
}
@ -47,6 +51,8 @@ void archive_free(ArchiveApp* archive) {
view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewBrowser);
view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewTextInput);
view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewWidget);
widget_free(archive->widget);
view_dispatcher_free(archive->view_dispatcher);
scene_manager_free(archive->scene_manager);
browser_free(archive->browser);

View File

@ -7,6 +7,7 @@
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/text_input.h>
#include <gui/modules/widget.h>
#include <loader/loader.h>
#include "views/archive_browser_view.h"
@ -15,6 +16,7 @@
typedef enum {
ArchiveViewBrowser,
ArchiveViewTextInput,
ArchiveViewWidget,
ArchiveViewTotal,
} ArchiveViewEnum;
@ -24,6 +26,7 @@ struct ArchiveApp {
SceneManager* scene_manager;
ArchiveBrowserView* browser;
TextInput* text_input;
Widget* widget;
char text_store[MAX_NAME_LEN];
char file_extension[MAX_EXT_LEN + 1];
};

View File

@ -0,0 +1,74 @@
#include "archive_files.h"
#include "archive_apps.h"
#include "archive_browser.h"
static const char* known_apps[] = {
[ArchiveAppTypeU2f] = "u2f",
};
ArchiveAppTypeEnum archive_get_app_type(const char* path) {
for(size_t i = 0; i < SIZEOF_ARRAY(known_apps); i++) {
if(strncmp(path, known_apps[i], strlen(known_apps[i])) != STRING_FAILURE) {
return i;
}
}
return ArchiveAppTypeUnknown;
}
bool archive_app_is_available(void* context, const char* path) {
furi_assert(path);
ArchiveAppTypeEnum app = archive_get_app_type(path);
if(app == ArchiveAppTypeU2f) {
FileWorker* file_worker = file_worker_alloc(true);
bool file_exists = false;
file_worker_is_file_exist(file_worker, "/any/u2f/key.u2f", &file_exists);
if(file_exists) {
file_worker_is_file_exist(file_worker, "/any/u2f/cnt.u2f", &file_exists);
}
file_worker_free(file_worker);
return file_exists;
} else {
return false;
}
}
bool archive_app_read_dir(void* context, const char* path) {
furi_assert(context);
furi_assert(path);
ArchiveBrowserView* browser = context;
ArchiveAppTypeEnum app = archive_get_app_type(path);
if(app == ArchiveAppTypeU2f) {
archive_add_app_item(browser, "/app:u2f/U2F Token");
return true;
} else {
return false;
}
}
void archive_app_delete_file(void* context, const char* path) {
furi_assert(context);
furi_assert(path);
ArchiveBrowserView* browser = context;
ArchiveAppTypeEnum app = archive_get_app_type(path);
bool res = false;
if(app == ArchiveAppTypeU2f) {
FileWorker* file_worker = file_worker_alloc(true);
res = file_worker_remove(file_worker, "/any/u2f/key.u2f");
res |= file_worker_remove(file_worker, "/any/u2f/cnt.u2f");
file_worker_free(file_worker);
if(archive_is_favorite("/app:u2f/U2F Token")) {
archive_favorites_delete("/app:u2f/U2F Token");
}
}
if(res) {
archive_file_array_rm_selected(browser);
}
}

View File

@ -0,0 +1,21 @@
#pragma once
typedef enum {
ArchiveAppTypeU2f,
ArchiveAppTypeUnknown,
ArchiveAppsTotal,
} ArchiveAppTypeEnum;
static const ArchiveFileTypeEnum app_file_types[] = {
[ArchiveAppTypeU2f] = ArchiveFileTypeU2f,
[ArchiveAppTypeUnknown] = ArchiveFileTypeUnknown,
};
static inline const ArchiveFileTypeEnum archive_get_app_filetype(ArchiveAppTypeEnum app) {
return app_file_types[app];
}
ArchiveAppTypeEnum archive_get_app_type(const char* path);
bool archive_app_is_available(void* context, const char* path);
bool archive_app_read_dir(void* context, const char* path);
void archive_app_delete_file(void* context, const char* path);

View File

@ -1,5 +1,7 @@
#include "archive_files.h"
#include "archive_apps.h"
#include "archive_browser.h"
#include "math.h"
#include <math.h>
void archive_update_offset(ArchiveBrowserView* browser) {
furi_assert(browser);
@ -177,24 +179,53 @@ void archive_set_last_tab(ArchiveBrowserView* browser, ArchiveTabEnum tab) {
});
}
void archive_add_item(ArchiveBrowserView* browser, FileInfo* file_info, const char* name) {
void archive_add_app_item(ArchiveBrowserView* browser, const char* name) {
furi_assert(browser);
furi_assert(name);
ArchiveFile_t item;
string_t full_name;
string_init_set(full_name, browser->path);
string_cat_printf(full_name, "/%s", name);
char* app_name = strchr(string_get_cstr(full_name), ':');
if(app_name == NULL) {
string_clear(full_name);
return;
}
ArchiveFile_t_init(&item);
string_init_set_str(item.name, name);
set_file_type(&item, NULL, app_name + 1, true);
with_view_model(
browser->view, (ArchiveBrowserViewModel * model) {
files_array_push_back(model->files, item);
return false;
});
ArchiveFile_t_clear(&item);
string_clear(full_name);
}
void archive_add_file_item(ArchiveBrowserView* browser, FileInfo* file_info, const char* name) {
furi_assert(browser);
furi_assert(file_info);
furi_assert(name);
ArchiveFile_t item;
if(filter_by_extension(file_info, get_tab_ext(archive_get_tab(browser)), name)) {
if(filter_by_extension(file_info, archive_get_tab_ext(archive_get_tab(browser)), name)) {
ArchiveFile_t_init(&item);
string_init_set_str(item.name, name);
set_file_type(&item, file_info);
set_file_type(&item, file_info, archive_get_path(browser), false);
with_view_model(
browser->view, (ArchiveBrowserViewModel * model) {
files_array_push_back(model->files, item);
return false;
});
ArchiveFile_t_clear(&item);
}
}
@ -208,8 +239,7 @@ void archive_show_file_menu(ArchiveBrowserView* browser, bool show) {
if(show) {
ArchiveFile_t* selected = files_array_get(model->files, model->idx);
selected->fav = archive_is_favorite(
"%s/%s", string_get_cstr(browser->path), string_get_cstr(selected->name));
selected->fav = archive_is_favorite("%s", string_get_cstr(selected->name));
}
return true;
@ -245,12 +275,18 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) {
archive_set_tab(browser, tab);
if((tab != ArchiveTabFavorites &&
!archive_dir_empty(browser, archive_get_default_path(tab))) ||
(tab == ArchiveTabFavorites && !archive_favorites_count(browser))) {
if(tab != ArchiveTabBrowser) {
archive_switch_tab(browser, key);
}
const char* path = archive_get_default_path(tab);
bool tab_empty = true;
if(tab == ArchiveTabFavorites) {
if(archive_favorites_count(browser) > 0) tab_empty = false;
} else if(strncmp(path, "/app:", 5) == 0) {
if(archive_app_is_available(browser, path)) tab_empty = false;
} else {
if(archive_dir_not_empty(browser, archive_get_default_path(tab))) tab_empty = false;
}
if((tab_empty) && (tab != ArchiveTabBrowser)) {
archive_switch_tab(browser, key);
} else {
with_view_model(
browser->view, (ArchiveBrowserViewModel * model) {
@ -277,8 +313,7 @@ void archive_enter_dir(ArchiveBrowserView* browser, string_t name) {
return false;
});
string_cat(browser->path, "/");
string_cat(browser->path, name);
string_set(browser->path, name);
archive_switch_dir(browser, string_get_cstr(browser->path));
}

View File

@ -11,6 +11,8 @@ static const char* tab_default_paths[] = {
[ArchiveTabSubGhz] = "/any/subghz",
[ArchiveTabLFRFID] = "/any/lfrfid",
[ArchiveTabIrda] = "/any/irda",
[ArchiveTabBadUsb] = "/any/badusb",
[ArchiveTabU2f] = "/app:u2f",
[ArchiveTabBrowser] = "/any",
};
@ -20,30 +22,37 @@ static const char* known_ext[] = {
[ArchiveFileTypeSubGhz] = ".sub",
[ArchiveFileTypeLFRFID] = ".rfid",
[ArchiveFileTypeIrda] = ".ir",
[ArchiveFileTypeBadUsb] = ".txt",
[ArchiveFileTypeU2f] = "?",
[ArchiveFileTypeFolder] = "?",
[ArchiveFileTypeUnknown] = "*",
};
static inline const char* get_tab_ext(ArchiveTabEnum tab) {
switch(tab) {
case ArchiveTabIButton:
return known_ext[ArchiveFileTypeIButton];
case ArchiveTabNFC:
return known_ext[ArchiveFileTypeNFC];
case ArchiveTabSubGhz:
return known_ext[ArchiveFileTypeSubGhz];
case ArchiveTabLFRFID:
return known_ext[ArchiveFileTypeLFRFID];
case ArchiveTabIrda:
return known_ext[ArchiveFileTypeIrda];
default:
return "*";
}
static const ArchiveFileTypeEnum known_type[] = {
[ArchiveTabFavorites] = ArchiveFileTypeUnknown,
[ArchiveTabIButton] = ArchiveFileTypeIButton,
[ArchiveTabNFC] = ArchiveFileTypeNFC,
[ArchiveTabSubGhz] = ArchiveFileTypeSubGhz,
[ArchiveTabLFRFID] = ArchiveFileTypeLFRFID,
[ArchiveTabIrda] = ArchiveFileTypeIrda,
[ArchiveTabBadUsb] = ArchiveFileTypeBadUsb,
[ArchiveTabU2f] = ArchiveFileTypeU2f,
[ArchiveTabBrowser] = ArchiveFileTypeUnknown,
};
static inline const ArchiveFileTypeEnum archive_get_tab_filetype(ArchiveTabEnum tab) {
return known_type[tab];
}
static inline const char* archive_get_tab_ext(ArchiveTabEnum tab) {
return known_ext[archive_get_tab_filetype(tab)];
}
static inline const char* archive_get_default_path(ArchiveTabEnum tab) {
return tab_default_paths[tab];
}
inline bool is_known_app(ArchiveFileTypeEnum type) {
inline bool archive_is_known_app(ArchiveFileTypeEnum type) {
return (type != ArchiveFileTypeFolder && type != ArchiveFileTypeUnknown);
}
@ -62,7 +71,8 @@ uint8_t archive_get_depth(ArchiveBrowserView* browser);
const char* archive_get_path(ArchiveBrowserView* browser);
const char* archive_get_name(ArchiveBrowserView* browser);
void archive_add_item(ArchiveBrowserView* browser, FileInfo* file_info, const char* name);
void archive_add_app_item(ArchiveBrowserView* browser, const char* name);
void archive_add_file_item(ArchiveBrowserView* browser, FileInfo* file_info, const char* name);
void archive_show_file_menu(ArchiveBrowserView* browser, bool show);
void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active);

View File

@ -1,5 +1,7 @@
#include "archive_favorites.h"
#include "archive_files.h"
#include "archive_apps.h"
#include "archive_browser.h"
uint16_t archive_favorites_count(void* context) {
@ -46,10 +48,16 @@ static bool archive_favourites_rescan() {
break;
}
bool file_exists = false;
file_worker_is_file_exist(file_worker, string_get_cstr(buffer), &file_exists);
if(file_exists) {
archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer));
if(string_search(buffer, "/app:") == 0) {
if(archive_app_is_available(NULL, string_get_cstr(buffer))) {
archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer));
}
} else {
bool file_exists = false;
file_worker_is_file_exist(file_worker, string_get_cstr(buffer), &file_exists);
if(file_exists) {
archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer));
}
}
}
}
@ -88,13 +96,22 @@ bool archive_favorites_read(void* context) {
break;
}
bool file_exists = false;
file_worker_is_file_exist(file_worker, string_get_cstr(buffer), &file_exists);
if(string_search(buffer, "/app:") == 0) {
if(archive_app_is_available(browser, string_get_cstr(buffer))) {
archive_add_app_item(browser, string_get_cstr(buffer));
} else {
need_refresh = true;
}
} else {
bool file_exists = false;
file_worker_is_file_exist(file_worker, string_get_cstr(buffer), &file_exists);
if(file_exists)
archive_add_file_item(browser, &file_info, string_get_cstr(buffer));
else
need_refresh = true;
}
if(file_exists)
archive_add_item(browser, &file_info, string_get_cstr(buffer));
else
need_refresh = true;
string_reset(buffer);
}
}
@ -185,8 +202,7 @@ bool archive_is_favorite(const char* format, ...) {
return found;
}
bool archive_favorites_rename(const char* file_path, const char* src, const char* dst) {
furi_assert(file_path);
bool archive_favorites_rename(const char* src, const char* dst) {
furi_assert(src);
furi_assert(dst);
@ -198,7 +214,7 @@ bool archive_favorites_rename(const char* file_path, const char* src, const char
string_init(buffer);
string_init(path);
string_printf(path, "%s/%s", file_path, src);
string_printf(path, "%s", src);
bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
if(result) {

View File

@ -8,6 +8,6 @@ uint16_t archive_favorites_count(void* context);
bool archive_favorites_read(void* context);
bool archive_favorites_delete(const char* format, ...);
bool archive_is_favorite(const char* format, ...);
bool archive_favorites_rename(const char* file_path, const char* src, const char* dst);
bool archive_favorites_rename(const char* src, const char* dst);
void archive_add_to_favorites(const char* file_path);
void archive_favorites_save(void* context);

View File

@ -1,8 +1,11 @@
#include "archive_files.h"
#include "archive_apps.h"
#include "archive_browser.h"
#define TAG "Archive"
#define ASSETS_DIR "assets"
bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name) {
furi_assert(file_info);
furi_assert(tab_ext);
@ -15,7 +18,11 @@ bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* n
} else if(strstr(name, tab_ext) != NULL) {
result = true;
} else if(file_info->flags & FSF_DIRECTORY) {
result = true;
if(strstr(name, ASSETS_DIR) != NULL) {
result = false; // Skip assets folder in all tabs except browser
} else {
result = true;
}
}
return result;
@ -38,21 +45,36 @@ void archive_get_file_extension(char* name, char* ext) {
strncpy(ext, dot, MAX_EXT_LEN);
}
void set_file_type(ArchiveFile_t* file, FileInfo* file_info) {
void set_file_type(ArchiveFile_t* file, FileInfo* file_info, const char* path, bool is_app) {
furi_assert(file);
furi_assert(file_info);
for(size_t i = 0; i < SIZEOF_ARRAY(known_ext); i++) {
if(string_search_str(file->name, known_ext[i], 0) != STRING_FAILURE) {
file->type = i;
return;
}
}
if(file_info->flags & FSF_DIRECTORY) {
file->type = ArchiveFileTypeFolder;
file->is_app = is_app;
if(is_app) {
file->type = archive_get_app_filetype(archive_get_app_type(path));
} else {
file->type = ArchiveFileTypeUnknown;
furi_assert(file_info);
for(size_t i = 0; i < SIZEOF_ARRAY(known_ext); i++) {
if((known_ext[i][0] == '?') || (known_ext[i][0] == '*')) continue;
if(string_search_str(file->name, known_ext[i], 0) != STRING_FAILURE) {
if(i == ArchiveFileTypeBadUsb) {
if(string_search_str(file->name, archive_get_default_path(ArchiveTabBadUsb)) ==
0) {
file->type = i;
return; // *.txt file is a BadUSB script only if it is in BadUSB folder
}
} else {
file->type = i;
return;
}
}
}
if(file_info->flags & FSF_DIRECTORY) {
file->type = ArchiveFileTypeFolder;
} else {
file->type = ArchiveFileTypeUnknown;
}
}
}
@ -63,17 +85,21 @@ bool archive_get_filenames(void* context, const char* path) {
ArchiveBrowserView* browser = context;
archive_file_array_rm_all(browser);
if(archive_get_tab(browser) != ArchiveTabFavorites) {
res = archive_read_dir(browser, path);
} else {
if(archive_get_tab(browser) == ArchiveTabFavorites) {
res = archive_favorites_read(browser);
} else if(strncmp(path, "/app:", 5) == 0) {
res = archive_app_read_dir(browser, path);
} else {
res = archive_read_dir(browser, path);
}
return res;
}
bool archive_dir_empty(void* context, const char* path) { // can be simpler?
bool archive_dir_not_empty(void* context, const char* path) { // can be simpler?
furi_assert(context);
ArchiveBrowserView* browser = context;
FileInfo file_info;
Storage* fs_api = furi_record_open("storage");
File* directory = storage_file_alloc(fs_api);
@ -92,8 +118,11 @@ bool archive_dir_empty(void* context, const char* path) { // can be simpler?
}
if(files_found) {
break;
} else if(storage_file_get_error(directory) == FSE_OK) {
files_found = name[0];
} else if((storage_file_get_error(directory) == FSE_OK) && (name[0])) {
if(filter_by_extension(
&file_info, archive_get_tab_ext(archive_get_tab(browser)), name)) {
files_found = true;
}
} else {
return false;
}
@ -114,6 +143,8 @@ bool archive_read_dir(void* context, const char* path) {
Storage* fs_api = furi_record_open("storage");
File* directory = storage_file_alloc(fs_api);
char name[MAX_NAME_LEN];
snprintf(name, MAX_NAME_LEN, "%s/", path);
size_t path_len = strlen(name);
size_t files_cnt = 0;
if(!storage_dir_open(directory, path)) {
@ -123,13 +154,14 @@ bool archive_read_dir(void* context, const char* path) {
}
while(1) {
if(!storage_dir_read(directory, &file_info, name, MAX_NAME_LEN)) {
if(!storage_dir_read(directory, &file_info, &name[path_len], MAX_NAME_LEN - path_len)) {
break;
}
if(files_cnt > MAX_FILES) {
break;
} else if(storage_file_get_error(directory) == FSE_OK) {
archive_add_item(browser, &file_info, name);
archive_add_file_item(browser, &file_info, name);
++files_cnt;
} else {
storage_dir_close(directory);

View File

@ -9,29 +9,38 @@ typedef enum {
ArchiveFileTypeSubGhz,
ArchiveFileTypeLFRFID,
ArchiveFileTypeIrda,
ArchiveFileTypeBadUsb,
ArchiveFileTypeU2f,
ArchiveFileTypeFolder,
ArchiveFileTypeUnknown,
AppIdTotal,
ArchiveFileTypesTotal,
} ArchiveFileTypeEnum;
typedef struct {
string_t name;
ArchiveFileTypeEnum type;
bool fav;
bool is_app;
} ArchiveFile_t;
static void ArchiveFile_t_init(ArchiveFile_t* obj) {
obj->type = ArchiveFileTypeUnknown;
obj->is_app = false;
obj->fav = false;
string_init(obj->name);
}
static void ArchiveFile_t_init_set(ArchiveFile_t* obj, const ArchiveFile_t* src) {
obj->type = src->type;
obj->is_app = src->is_app;
obj->fav = src->fav;
string_init_set(obj->name, src->name);
}
static void ArchiveFile_t_set(ArchiveFile_t* obj, const ArchiveFile_t* src) {
obj->type = src->type;
obj->is_app = src->is_app;
obj->fav = src->fav;
string_set(obj->name, src->name);
}
@ -48,11 +57,11 @@ ARRAY_DEF(
CLEAR(API_2(ArchiveFile_t_clear))))
bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name);
void set_file_type(ArchiveFile_t* file, FileInfo* file_info);
void set_file_type(ArchiveFile_t* file, FileInfo* file_info, const char* path, bool is_app);
void archive_trim_file_path(char* name, bool ext);
void archive_get_file_extension(char* name, char* ext);
bool archive_get_filenames(void* context, const char* path);
bool archive_dir_empty(void* context, const char* path);
bool archive_dir_not_empty(void* context, const char* path);
bool archive_read_dir(void* context, const char* path);
void archive_file_append(const char* path, const char* format, ...);
void archive_delete_file(void* context, const char* format, ...);

View File

@ -1,33 +1,41 @@
#include "../archive_i.h"
#include "../helpers/archive_files.h"
#include "../helpers/archive_apps.h"
#include "../helpers/archive_favorites.h"
#include "../helpers/archive_browser.h"
#include "../views/archive_browser_view.h"
#define TAG "ArchiveSceneBrowser"
static const char* flipper_app_name[] = {
[ArchiveFileTypeIButton] = "iButton",
[ArchiveFileTypeNFC] = "NFC",
[ArchiveFileTypeSubGhz] = "Sub-GHz",
[ArchiveFileTypeLFRFID] = "125 kHz RFID",
[ArchiveFileTypeIrda] = "Infrared",
[ArchiveFileTypeBadUsb] = "Bad USB",
[ArchiveFileTypeU2f] = "U2F",
};
static void archive_run_in_app(
ArchiveBrowserView* browser,
ArchiveFile_t* selected,
bool full_path_provided) {
static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selected) {
Loader* loader = furi_record_open("loader");
string_t full_path;
if(!full_path_provided) {
string_init_printf(
full_path, "%s/%s", string_get_cstr(browser->path), string_get_cstr(selected->name));
LoaderStatus status;
if(selected->is_app) {
char* param = strrchr(string_get_cstr(selected->name), '/');
if(param != NULL) {
param++;
}
status = loader_start(loader, flipper_app_name[selected->type], param);
} else {
string_init_set(full_path, selected->name);
status = loader_start(
loader, flipper_app_name[selected->type], string_get_cstr(selected->name));
}
if(status != LoaderStatusOk) {
FURI_LOG_E(TAG, "loader_start failed: %d", status);
}
loader_start(loader, flipper_app_name[selected->type], string_get_cstr(full_path));
string_clear(full_path);
furi_record_close("loader");
}
@ -50,9 +58,8 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
ArchiveBrowserView* browser = archive->browser;
ArchiveFile_t* selected = archive_get_current_file(browser);
const char* path = archive_get_path(browser);
const char* name = archive_get_name(browser);
bool known_app = is_known_app(selected->type);
bool known_app = archive_is_known_app(selected->type);
bool favorites = archive_get_tab(browser) == ArchiveTabFavorites;
bool consumed = false;
@ -68,7 +75,7 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
break;
case ArchiveBrowserEventFileMenuRun:
if(known_app) {
archive_run_in_app(browser, selected, favorites);
archive_run_in_app(browser, selected);
}
consumed = true;
break;
@ -78,10 +85,10 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
archive_file_array_rm_selected(browser);
archive_show_file_menu(browser, false);
} else if(known_app) {
if(archive_is_favorite("%s/%s", path, name)) {
archive_favorites_delete("%s/%s", path, name);
if(archive_is_favorite("%s", name)) {
archive_favorites_delete("%s", name);
} else {
archive_file_append(ARCHIVE_FAV_PATH, "%s/%s\n", path, name);
archive_file_append(ARCHIVE_FAV_PATH, "%s\n", name);
}
archive_show_file_menu(browser, false);
}
@ -91,18 +98,13 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
case ArchiveBrowserEventFileMenuAction:
if(favorites) {
browser->callback(ArchiveBrowserEventEnterFavMove, browser->context);
} else if(known_app) {
} else if((known_app) && (selected->is_app == false)) {
scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneRename);
}
consumed = true;
break;
case ArchiveBrowserEventFileMenuDelete:
if(favorites) {
archive_delete_file(browser, "%s", name);
} else {
archive_delete_file(browser, "%s/%s", path, name);
}
archive_show_file_menu(browser, false);
scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneDelete);
consumed = true;
break;
case ArchiveBrowserEventEnterDir:

View File

@ -1,2 +1,3 @@
ADD_SCENE(archive, browser, Browser)
ADD_SCENE(archive, rename, Rename)
ADD_SCENE(archive, delete, Delete)

View File

@ -0,0 +1,70 @@
#include "../archive_i.h"
#include "../helpers/archive_favorites.h"
#include "../helpers/archive_files.h"
#include "../helpers/archive_apps.h"
#include "../helpers/archive_browser.h"
#define SCENE_DELETE_CUSTOM_EVENT (0UL)
#define MAX_TEXT_INPUT_LEN 22
void archive_scene_delete_widget_callback(GuiButtonType result, InputType type, void* context) {
furi_assert(context);
ArchiveApp* app = (ArchiveApp*)context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
}
void archive_scene_delete_on_enter(void* context) {
furi_assert(context);
ArchiveApp* app = (ArchiveApp*)context;
widget_add_button_element(
app->widget, GuiButtonTypeLeft, "Back", archive_scene_delete_widget_callback, app);
widget_add_button_element(
app->widget, GuiButtonTypeRight, "Delete", archive_scene_delete_widget_callback, app);
ArchiveFile_t* current = archive_get_current_file(app->browser);
strlcpy(app->text_store, string_get_cstr(current->name), MAX_NAME_LEN);
char* name = strrchr(app->text_store, '/');
if(name != NULL) {
name++;
}
char delete_str[64];
snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", name);
widget_add_text_box_element(app->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str);
view_dispatcher_switch_to_view(app->view_dispatcher, ArchiveViewWidget);
}
bool archive_scene_delete_on_event(void* context, SceneManagerEvent event) {
furi_assert(context);
ArchiveApp* app = (ArchiveApp*)context;
ArchiveBrowserView* browser = app->browser;
ArchiveFile_t* selected = archive_get_current_file(browser);
const char* name = archive_get_name(browser);
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeRight) {
if(selected->is_app) {
archive_app_delete_file(browser, name);
} else {
archive_delete_file(browser, "%s", name);
}
archive_show_file_menu(browser, false);
return scene_manager_previous_scene(app->scene_manager);
} else if(event.event == GuiButtonTypeLeft) {
return scene_manager_previous_scene(app->scene_manager);
}
}
return false;
}
void archive_scene_delete_on_exit(void* context) {
furi_assert(context);
ArchiveApp* app = (ArchiveApp*)context;
widget_reset(app->widget);
}

View File

@ -52,7 +52,8 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) {
const char* path = archive_get_path(archive->browser);
const char* name = archive_get_name(archive->browser);
string_init_printf(buffer_src, "%s/%s", path, name);
string_init_printf(buffer_src, "%s", name);
//TODO: take path from src name
string_init_printf(buffer_dst, "%s/%s", path, archive->text_store);
// append extension
@ -64,7 +65,7 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) {
furi_record_close("storage");
if(file->fav) {
archive_favorites_rename(path, name, string_get_cstr(buffer_dst));
archive_favorites_rename(name, string_get_cstr(buffer_dst));
}
string_clear(buffer_src);

View File

@ -10,6 +10,8 @@ static const char* ArchiveTabNames[] = {
[ArchiveTabSubGhz] = "Sub-GHz",
[ArchiveTabLFRFID] = "RFID LF",
[ArchiveTabIrda] = "Infrared",
[ArchiveTabBadUsb] = "Bad USB",
[ArchiveTabU2f] = "U2F",
[ArchiveTabBrowser] = "Browser"};
static const Icon* ArchiveItemIcons[] = {
@ -18,6 +20,8 @@ static const Icon* ArchiveItemIcons[] = {
[ArchiveFileTypeSubGhz] = &I_sub1_10px,
[ArchiveFileTypeLFRFID] = &I_125_10px,
[ArchiveFileTypeIrda] = &I_ir_10px,
[ArchiveFileTypeBadUsb] = &I_badusb_10px,
[ArchiveFileTypeU2f] = &I_u2f_10px,
[ArchiveFileTypeFolder] = &I_dir_10px,
[ArchiveFileTypeUnknown] = &I_unknown_10px,
};
@ -47,15 +51,20 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
ArchiveFile_t* selected = files_array_get(model->files, model->idx);
if(!is_known_app(selected->type)) {
if(!archive_is_known_app(selected->type)) {
string_set_str(menu[0], "---");
string_set_str(menu[1], "---");
string_set_str(menu[2], "---");
} else if(selected->fav) {
} else {
if(model->tab_idx == ArchiveTabFavorites) {
string_set_str(menu[2], "Move");
} else if(selected->is_app) {
string_set_str(menu[2], "---");
}
}
if((selected->fav) || (model->tab_idx == ArchiveTabFavorites)) {
string_set_str(menu[1], "Unpin");
} else if(model->tab_idx == ArchiveTabFavorites) {
string_set_str(menu[1], "Unpin");
string_set_str(menu[2], "Move");
}
for(size_t i = 0; i < MENU_ITEMS; i++) {
@ -102,7 +111,7 @@ static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) {
ArchiveFile_t* file = files_array_get(model->files, CLAMP(idx, array_size - 1, 0));
strlcpy(cstr_buff, string_get_cstr(file->name), string_size(file->name) + 1);
archive_trim_file_path(cstr_buff, is_known_app(file->type));
archive_trim_file_path(cstr_buff, archive_is_known_app(file->type));
string_init_set_str(str_buff, cstr_buff);
elements_string_fit_width(
canvas, str_buff, (scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX) - x_offset);

View File

@ -24,6 +24,8 @@ typedef enum {
ArchiveTabNFC,
ArchiveTabIrda,
ArchiveTabIButton,
ArchiveTabBadUsb,
ArchiveTabU2f,
ArchiveTabBrowser,
ArchiveTabTotal,
} ArchiveTabEnum;

View File

@ -2,6 +2,7 @@
#include <furi.h>
#include <furi_hal.h>
#include <storage/storage.h>
#include <lib/toolbox/path.h>
static bool bad_usb_app_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
@ -39,27 +40,34 @@ static bool bad_usb_check_assets() {
return ret;
}
BadUsbApp* bad_usb_app_alloc() {
BadUsbApp* bad_usb_app_alloc(char* arg) {
BadUsbApp* app = furi_alloc(sizeof(BadUsbApp));
if(arg != NULL) {
string_t filename;
string_init(filename);
path_extract_filename_no_ext(arg, filename);
strncpy(app->file_name, string_get_cstr(filename), BAD_USB_FILE_NAME_LEN);
string_clear(filename);
}
app->gui = furi_record_open("gui");
app->notifications = furi_record_open("notification");
app->dialogs = furi_record_open("dialogs");
app->view_dispatcher = view_dispatcher_alloc();
app->scene_manager = scene_manager_alloc(&bad_usb_scene_handlers, app);
view_dispatcher_enable_queue(app->view_dispatcher);
app->scene_manager = scene_manager_alloc(&bad_usb_scene_handlers, app);
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
view_dispatcher_set_tick_event_callback(
app->view_dispatcher, bad_usb_app_tick_event_callback, 500);
view_dispatcher_set_custom_event_callback(
app->view_dispatcher, bad_usb_app_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, bad_usb_app_back_event_callback);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
// Custom Widget
app->widget = widget_alloc();
view_dispatcher_add_view(
@ -69,7 +77,11 @@ BadUsbApp* bad_usb_app_alloc() {
view_dispatcher_add_view(
app->view_dispatcher, BadUsbAppViewWork, bad_usb_get_view(app->bad_usb_view));
if(bad_usb_check_assets()) {
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
if(*app->file_name != '\0') {
scene_manager_next_scene(app->scene_manager, BadUsbSceneWork);
} else if(bad_usb_check_assets()) {
scene_manager_next_scene(app->scene_manager, BadUsbSceneFileSelect);
} else {
scene_manager_next_scene(app->scene_manager, BadUsbSceneError);
@ -106,7 +118,7 @@ int32_t bad_usb_app(void* p) {
FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config();
furi_hal_usb_set_config(&usb_hid);
BadUsbApp* bad_usb_app = bad_usb_app_alloc();
BadUsbApp* bad_usb_app = bad_usb_app_alloc((char*)p);
view_dispatcher_run(bad_usb_app->view_dispatcher);

View File

@ -144,7 +144,7 @@ void bad_usb_set_ok_callback(BadUsb* bad_usb, BadUsbOkCallback callback, void* c
bad_usb->view, (BadUsbModel * model) {
bad_usb->callback = callback;
bad_usb->context = context;
return false;
return true;
});
}
@ -153,7 +153,7 @@ void bad_usb_set_file_name(BadUsb* bad_usb, char* name) {
with_view_model(
bad_usb->view, (BadUsbModel * model) {
model->file_name = name;
return false;
return true;
});
}
@ -163,6 +163,6 @@ void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st) {
bad_usb->view, (BadUsbModel * model) {
memcpy(&(model->state), st, sizeof(BadUsbState));
model->anim_frame ^= 1;
return false;
return true;
});
}

View File

@ -16,9 +16,9 @@ static void bt_draw_statusbar_callback(Canvas* canvas, void* context) {
Bt* bt = context;
if(bt->status == BtStatusAdvertising) {
canvas_draw_icon(canvas, 0, 0, &I_Bluetooth_5x8);
canvas_draw_icon(canvas, 0, 0, &I_Bluetooth_Idle_5x8);
} else if(bt->status == BtStatusConnected) {
canvas_draw_icon(canvas, 0, 0, &I_BT_Pair_9x8);
canvas_draw_icon(canvas, 0, 0, &I_Bluetooth_Connected_16x8);
}
}
@ -199,13 +199,18 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) {
furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK);
if(bt->profile == BtProfileSerial) {
// Open RPC session
FURI_LOG_I(TAG, "Open RPC connection");
bt->rpc_session = rpc_session_open(bt->rpc);
rpc_session_set_send_bytes_callback(bt->rpc_session, bt_rpc_send_bytes_callback);
rpc_session_set_buffer_is_empty_callback(
bt->rpc_session, furi_hal_bt_serial_notify_buffer_is_empty);
rpc_session_set_context(bt->rpc_session, bt);
furi_hal_bt_serial_set_event_callback(RPC_BUFFER_SIZE, bt_serial_event_callback, bt);
if(bt->rpc_session) {
FURI_LOG_I(TAG, "Open RPC connection");
rpc_session_set_send_bytes_callback(bt->rpc_session, bt_rpc_send_bytes_callback);
rpc_session_set_buffer_is_empty_callback(
bt->rpc_session, furi_hal_bt_serial_notify_buffer_is_empty);
rpc_session_set_context(bt->rpc_session, bt);
furi_hal_bt_serial_set_event_callback(
RPC_BUFFER_SIZE, bt_serial_event_callback, bt);
} else {
FURI_LOG_W(TAG, "RPC is busy, failed to open new session");
}
}
// Update battery level
PowerInfo info;
@ -257,10 +262,10 @@ static void bt_on_key_storage_change_callback(uint8_t* addr, uint16_t size, void
static void bt_statusbar_update(Bt* bt) {
if(bt->status == BtStatusAdvertising) {
view_port_set_width(bt->statusbar_view_port, icon_get_width(&I_Bluetooth_5x8));
view_port_set_width(bt->statusbar_view_port, icon_get_width(&I_Bluetooth_Idle_5x8));
view_port_enabled_set(bt->statusbar_view_port, true);
} else if(bt->status == BtStatusConnected) {
view_port_set_width(bt->statusbar_view_port, icon_get_width(&I_BT_Pair_9x8));
view_port_set_width(bt->statusbar_view_port, icon_get_width(&I_Bluetooth_Connected_16x8));
view_port_enabled_set(bt->statusbar_view_port, true);
} else {
view_port_enabled_set(bt->statusbar_view_port, false);

View File

@ -43,10 +43,11 @@ bool bt_save_key_storage(Bt* bt) {
bool bt_delete_key_storage(Bt* bt) {
furi_assert(bt);
bool delete_succeed = false;
bool bt_is_active = furi_hal_bt_is_active();
furi_hal_bt_stop_advertising();
delete_succeed = furi_hal_bt_clear_white_list();
if(bt->bt_settings.enabled) {
if(bt_is_active) {
furi_hal_bt_start_advertising();
}

View File

@ -89,7 +89,7 @@ static void view_display_test_draw_callback_move(Canvas* canvas, void* _model) {
canvas_draw_box(canvas, x, y, block, block);
}
ViewDrawCallback view_display_test_tests[] = {
const ViewDrawCallback view_display_test_tests[] = {
view_display_test_draw_callback_intro,
view_display_test_draw_callback_fill,
view_display_test_draw_callback_hstripe,

View File

@ -408,6 +408,11 @@ static StorageAnimation*
return selected;
}
bool animation_manager_is_animation_loaded(AnimationManager* animation_manager) {
furi_assert(animation_manager);
return animation_manager->current_animation;
}
void animation_manager_unload_and_stall_animation(AnimationManager* animation_manager) {
furi_assert(animation_manager);
furi_assert(animation_manager->current_animation);

View File

@ -133,6 +133,12 @@ void animation_manager_set_interact_callback(
*/
void animation_manager_interact_process(AnimationManager* animation_manager);
/** Check if animation loaded
*
* @animation_manager instance
*/
bool animation_manager_is_animation_loaded(AnimationManager* animation_manager);
/**
* Unload and Stall animation actions. Draw callback in view
* paints first frame of current animation until

View File

@ -261,7 +261,7 @@ static void animation_storage_free_frames(BubbleAnimation* animation) {
}
}
free(icon->frames);
free((void*)icon->frames);
}
static bool animation_storage_load_frames(
@ -317,7 +317,7 @@ static bool animation_storage_load_frames(
break;
}
icon->frames[i] = furi_alloc(file_info.size);
FURI_CONST_ASSIGN_PTR(icon->frames[i], furi_alloc(file_info.size));
if(storage_file_read(file, (void*)icon->frames[i], file_info.size) != file_info.size) {
FURI_LOG_E(TAG, "Read failed: \'%s\'", string_get_cstr(filename));
break;

View File

@ -266,7 +266,7 @@ static Icon* bubble_animation_clone_first_frame(const Icon* icon_orig) {
* for compressed header
*/
size_t max_bitmap_size = ROUND_UP_TO(icon_orig->width, 8) * icon_orig->height + 1;
icon_clone->frames[0] = furi_alloc(max_bitmap_size);
FURI_CONST_ASSIGN_PTR(icon_clone->frames[0], furi_alloc(max_bitmap_size));
memcpy((void*)icon_clone->frames[0], icon_orig->frames[0], max_bitmap_size);
FURI_CONST_ASSIGN(icon_clone->frame_count, 1);
@ -278,7 +278,7 @@ static void bubble_animation_release_frame(Icon** icon) {
furi_assert(*icon);
free((void*)(*icon)->frames[0]);
free((*icon)->frames);
free((void*)(*icon)->frames);
free(*icon);
*icon = NULL;
}

View File

@ -81,7 +81,7 @@ OneShotView* one_shot_view_alloc(void) {
OneShotView* view = furi_alloc(sizeof(OneShotView));
view->view = view_alloc();
view->update_timer =
xTimerCreate("Update timer", 1000, pdTRUE, view, one_shot_view_update_timer_callback);
xTimerCreate(NULL, 1000, pdTRUE, view, one_shot_view_update_timer_callback);
view_allocate_model(view->view, ViewModelTypeLocking, sizeof(OneShotViewModel));
view_set_context(view->view, view);

View File

@ -1,34 +1,43 @@
#include "animations/animation_manager.h"
#include "desktop/scenes/desktop_scene.h"
#include "desktop/scenes/desktop_scene_i.h"
#include "desktop/views/desktop_locked.h"
#include "desktop_i.h"
#include <storage/storage.h>
#include <assets_icons.h>
#include <gui/view_stack.h>
#include <furi.h>
#include <furi_hal.h>
#include <portmacro.h>
#include <stdint.h>
#include <loader/loader.h>
#include "animations/animation_manager.h"
#include "desktop/scenes/desktop_scene.h"
#include "desktop/scenes/desktop_scene_i.h"
#include "desktop/views/desktop_view_locked.h"
#include "desktop/views/desktop_view_pin_input.h"
#include "desktop/views/desktop_view_pin_timeout.h"
#include "desktop_i.h"
#include "desktop_helpers.h"
static void desktop_lock_icon_callback(Canvas* canvas, void* context) {
furi_assert(canvas);
canvas_draw_icon(canvas, 0, 0, &I_Lock_8x8);
}
bool desktop_custom_event_callback(void* context, uint32_t event) {
static bool desktop_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
Desktop* desktop = (Desktop*)context;
return scene_manager_handle_custom_event(desktop->scene_manager, event);
}
bool desktop_back_event_callback(void* context) {
static bool desktop_back_event_callback(void* context) {
furi_assert(context);
Desktop* desktop = (Desktop*)context;
return scene_manager_handle_back_event(desktop->scene_manager);
}
static void desktop_tick_event_callback(void* context) {
furi_assert(context);
Desktop* app = context;
scene_manager_handle_tick_event(app->scene_manager);
}
Desktop* desktop_alloc() {
Desktop* desktop = furi_alloc(sizeof(Desktop));
@ -42,6 +51,8 @@ Desktop* desktop_alloc() {
view_dispatcher_enable_queue(desktop->view_dispatcher);
view_dispatcher_attach_to_gui(
desktop->view_dispatcher, desktop->gui, ViewDispatcherTypeDesktop);
view_dispatcher_set_tick_event_callback(
desktop->view_dispatcher, desktop_tick_event_callback, 500);
view_dispatcher_set_event_callback_context(desktop->view_dispatcher, desktop);
view_dispatcher_set_custom_event_callback(
@ -49,37 +60,60 @@ Desktop* desktop_alloc() {
view_dispatcher_set_navigation_event_callback(
desktop->view_dispatcher, desktop_back_event_callback);
desktop->locked_view = desktop_locked_alloc();
desktop->lock_menu = desktop_lock_menu_alloc();
desktop->debug_view = desktop_debug_alloc();
desktop->first_start_view = desktop_first_start_alloc();
desktop->hw_mismatch_popup = popup_alloc();
desktop->code_input = code_input_alloc();
desktop->locked_view = desktop_view_locked_alloc();
desktop->pin_input_view = desktop_view_pin_input_alloc();
desktop->pin_timeout_view = desktop_view_pin_timeout_alloc();
desktop->main_view_stack = view_stack_alloc();
desktop->main_view = desktop_main_alloc();
View* dolphin_view = animation_manager_get_animation_view(desktop->animation_manager);
view_stack_add_view(desktop->main_view_stack, desktop_main_get_view(desktop->main_view));
view_stack_add_view(desktop->main_view_stack, dolphin_view);
view_stack_add_view(desktop->main_view_stack, desktop_locked_get_view(desktop->locked_view));
view_stack_add_view(
desktop->main_view_stack, desktop_view_locked_get_view(desktop->locked_view));
/* locked view (as animation view) attends in 2 scenes: main & locked,
* because it has to draw "Unlocked" label on main scene */
desktop->locked_view_stack = view_stack_alloc();
view_stack_add_view(desktop->locked_view_stack, dolphin_view);
view_stack_add_view(
desktop->locked_view_stack, desktop_view_locked_get_view(desktop->locked_view));
view_dispatcher_add_view(
desktop->view_dispatcher, DesktopViewMain, view_stack_get_view(desktop->main_view_stack));
desktop->view_dispatcher,
DesktopViewIdMain,
view_stack_get_view(desktop->main_view_stack));
view_dispatcher_add_view(
desktop->view_dispatcher,
DesktopViewLockMenu,
DesktopViewIdLocked,
view_stack_get_view(desktop->locked_view_stack));
view_dispatcher_add_view(
desktop->view_dispatcher,
DesktopViewIdLockMenu,
desktop_lock_menu_get_view(desktop->lock_menu));
view_dispatcher_add_view(
desktop->view_dispatcher, DesktopViewDebug, desktop_debug_get_view(desktop->debug_view));
desktop->view_dispatcher, DesktopViewIdDebug, desktop_debug_get_view(desktop->debug_view));
view_dispatcher_add_view(
desktop->view_dispatcher,
DesktopViewFirstStart,
DesktopViewIdFirstStart,
desktop_first_start_get_view(desktop->first_start_view));
view_dispatcher_add_view(
desktop->view_dispatcher,
DesktopViewHwMismatch,
DesktopViewIdHwMismatch,
popup_get_view(desktop->hw_mismatch_popup));
view_dispatcher_add_view(
desktop->view_dispatcher, DesktopViewPinSetup, code_input_get_view(desktop->code_input));
desktop->view_dispatcher,
DesktopViewIdPinTimeout,
desktop_view_pin_timeout_get_view(desktop->pin_timeout_view));
view_dispatcher_add_view(
desktop->view_dispatcher,
DesktopViewIdPinInput,
desktop_view_pin_input_get_view(desktop->pin_input_view));
// Lock icon
desktop->lock_viewport = view_port_alloc();
view_port_set_width(desktop->lock_viewport, icon_get_width(&I_Lock_8x8));
@ -87,33 +121,43 @@ Desktop* desktop_alloc() {
view_port_enabled_set(desktop->lock_viewport, false);
gui_add_view_port(desktop->gui, desktop->lock_viewport, GuiLayerStatusBarLeft);
// Special case: autostart application is already running
Loader* loader = furi_record_open("loader");
if(loader_is_locked(loader) &&
animation_manager_is_animation_loaded(desktop->animation_manager)) {
animation_manager_unload_and_stall_animation(desktop->animation_manager);
}
furi_record_close("loader");
return desktop;
}
void desktop_free(Desktop* desktop) {
furi_assert(desktop);
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewMain);
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewLockMenu);
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewLocked);
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewDebug);
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewFirstStart);
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewHwMismatch);
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewPinSetup);
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdMain);
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLockMenu);
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLocked);
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdDebug);
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdFirstStart);
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdHwMismatch);
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdPinInput);
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdPinTimeout);
view_dispatcher_free(desktop->view_dispatcher);
scene_manager_free(desktop->scene_manager);
animation_manager_free(desktop->animation_manager);
view_stack_free(desktop->main_view_stack);
view_stack_free(desktop->locked_view_stack);
desktop_main_free(desktop->main_view);
view_stack_free(desktop->locked_view_stack);
desktop_view_locked_free(desktop->locked_view);
desktop_lock_menu_free(desktop->lock_menu);
desktop_locked_free(desktop->locked_view);
desktop_view_locked_free(desktop->locked_view);
desktop_debug_free(desktop->debug_view);
desktop_first_start_free(desktop->first_start_view);
popup_free(desktop->hw_mismatch_popup);
code_input_free(desktop->code_input);
desktop_view_pin_timeout_free(desktop->pin_timeout_view);
osSemaphoreDelete(desktop->unload_animation_semaphore);
@ -145,14 +189,18 @@ int32_t desktop_srv(void* p) {
SAVE_DESKTOP_SETTINGS(&desktop->settings);
}
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) {
furi_hal_usb_disable();
scene_manager_set_scene_state(
desktop->scene_manager, DesktopSceneMain, DesktopMainSceneStateLockedWithPin);
}
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) {
if(desktop->settings.pin_code.length > 0) {
scene_manager_set_scene_state(
desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER);
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked);
} else {
furi_hal_rtc_reset_flag(FuriHalRtcFlagLock);
}
}
if(desktop_is_first_start()) {
scene_manager_next_scene(desktop->scene_manager, DesktopSceneFirstStart);
}

View File

@ -0,0 +1,82 @@
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include <stddef.h>
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include "desktop_helpers.h"
#include "desktop_i.h"
static const NotificationSequence sequence_pin_fail = {
&message_display_on,
&message_red_255,
&message_vibro_on,
&message_delay_100,
&message_vibro_off,
&message_red_0,
&message_delay_250,
&message_red_255,
&message_vibro_on,
&message_delay_100,
&message_vibro_off,
&message_red_0,
NULL,
};
static const uint8_t desktop_helpers_fails_timeout[] = {
0,
0,
0,
0,
30,
60,
90,
120,
150,
180,
/* +60 for every next fail */
};
void desktop_helpers_emit_error_notification() {
NotificationApp* notification = furi_record_open("notification");
notification_message(notification, &sequence_pin_fail);
furi_record_close("notification");
}
void desktop_helpers_lock_system(Desktop* desktop, bool hard_lock) {
view_port_enabled_set(desktop->lock_viewport, true);
if(hard_lock) {
furi_hal_rtc_set_flag(FuriHalRtcFlagLock);
furi_hal_usb_disable();
}
Gui* gui = furi_record_open("gui");
gui_set_lockdown(gui, true);
furi_record_close("gui");
}
void desktop_helpers_unlock_system(Desktop* desktop) {
furi_hal_rtc_reset_flag(FuriHalRtcFlagLock);
furi_hal_usb_enable();
view_port_enabled_set(desktop->lock_viewport, false);
Gui* gui = furi_record_open("gui");
gui_set_lockdown(gui, false);
furi_record_close("gui");
}
uint32_t desktop_helpers_get_pin_fail_timeout(uint32_t pin_fails) {
uint32_t pin_timeout = 0;
uint32_t max_index = COUNT_OF(desktop_helpers_fails_timeout) - 1;
if(pin_fails <= max_index) {
pin_timeout = desktop_helpers_fails_timeout[pin_fails];
} else {
pin_timeout = desktop_helpers_fails_timeout[max_index] + (pin_fails - max_index) * 60;
}
return pin_timeout;
}

View File

@ -0,0 +1,9 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "desktop.h"
void desktop_helpers_emit_error_notification();
void desktop_helpers_lock_system(Desktop* desktop, bool hard_lock);
void desktop_helpers_unlock_system(Desktop* desktop);
uint32_t desktop_helpers_get_pin_fail_timeout(uint32_t pin_fails);

View File

@ -2,11 +2,13 @@
#include "desktop.h"
#include "animations/animation_manager.h"
#include "views/desktop_main.h"
#include "views/desktop_first_start.h"
#include "views/desktop_lock_menu.h"
#include "views/desktop_locked.h"
#include "views/desktop_debug.h"
#include "views/desktop_view_pin_timeout.h"
#include "views/desktop_view_pin_input.h"
#include "views/desktop_view_locked.h"
#include "views/desktop_view_main.h"
#include "views/desktop_view_first_start.h"
#include "views/desktop_view_lock_menu.h"
#include "views/desktop_view_debug.h"
#include "desktop/desktop_settings/desktop_settings.h"
#include <furi.h>
@ -14,21 +16,21 @@
#include <gui/view_stack.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/popup.h>
#include <gui/modules/code_input.h>
#include <gui/scene_manager.h>
#define STATUS_BAR_Y_SHIFT 13
typedef enum {
DesktopViewMain,
DesktopViewLockMenu,
DesktopViewLocked,
DesktopViewDebug,
DesktopViewFirstStart,
DesktopViewHwMismatch,
DesktopViewPinSetup,
DesktopViewTotal,
} DesktopViewEnum;
DesktopViewIdMain,
DesktopViewIdLockMenu,
DesktopViewIdLocked,
DesktopViewIdDebug,
DesktopViewIdFirstStart,
DesktopViewIdHwMismatch,
DesktopViewIdPinInput,
DesktopViewIdPinTimeout,
DesktopViewIdTotal,
} DesktopViewId;
struct Desktop {
// Scene
@ -42,16 +44,15 @@ struct Desktop {
Popup* hw_mismatch_popup;
DesktopLockMenuView* lock_menu;
DesktopDebugView* debug_view;
CodeInput* code_input;
DesktopViewLocked* locked_view;
DesktopMainView* main_view;
DesktopLockedView* locked_view;
DesktopViewPinTimeout* pin_timeout_view;
ViewStack* main_view_stack;
ViewStack* locked_view_stack;
DesktopSettings settings;
PinCode pincode_buffer;
DesktopViewPinInput* pin_input_view;
ViewPort* lock_viewport;

View File

@ -1,5 +1,6 @@
#pragma once
#include <furi_hal.h>
#include <stdint.h>
#include <stdbool.h>
#include <toolbox/saved_struct.h>
@ -9,6 +10,8 @@
#define DESKTOP_SETTINGS_MAGIC (0x17)
#define PIN_MAX_LENGTH 12
#define DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG "run_pin_setup"
#define SAVE_DESKTOP_SETTINGS(x) \
saved_struct_save( \
DESKTOP_SETTINGS_PATH, \
@ -25,12 +28,27 @@
DESKTOP_SETTINGS_MAGIC, \
DESKTOP_SETTINGS_VER)
#define MAX_PIN_SIZE 10
#define MIN_PIN_SIZE 4
typedef struct {
InputKey data[MAX_PIN_SIZE];
uint8_t length;
uint8_t data[PIN_MAX_LENGTH];
} PinCode;
typedef struct {
uint16_t favorite;
PinCode pincode;
PinCode pin_code;
} DesktopSettings;
static inline bool pins_are_equal(const PinCode* pin_code1, const PinCode* pin_code2) {
furi_assert(pin_code1);
furi_assert(pin_code2);
bool result = false;
if(pin_code1->length == pin_code2->length) {
result = !memcmp(pin_code1->data, pin_code2->data, pin_code1->length);
}
return result;
}

View File

@ -1,6 +1,10 @@
#include "desktop_settings_app.h"
#include <furi.h>
#include <gui/modules/popup.h>
#include <gui/scene_manager.h>
#include "desktop_settings_app.h"
#include "scenes/desktop_settings_scene.h"
#include "../views/desktop_view_pin_input.h"
static bool desktop_settings_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
@ -30,17 +34,28 @@ DesktopSettingsApp* desktop_settings_app_alloc() {
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
app->popup = popup_alloc();
app->submenu = submenu_alloc();
app->pin_input_view = desktop_view_pin_input_alloc();
app->pin_setup_howto_view = desktop_settings_view_pin_setup_howto_alloc();
app->pin_setup_howto2_view = desktop_settings_view_pin_setup_howto2_alloc();
view_dispatcher_add_view(
app->view_dispatcher, DesktopSettingsAppViewMenu, submenu_get_view(app->submenu));
app->code_input = code_input_alloc();
view_dispatcher_add_view(
app->view_dispatcher, DesktopSettingsAppViewIdPopup, popup_get_view(app->popup));
view_dispatcher_add_view(
app->view_dispatcher,
DesktopSettingsAppViewPincodeInput,
code_input_get_view(app->code_input));
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneStart);
DesktopSettingsAppViewIdPinInput,
desktop_view_pin_input_get_view(app->pin_input_view));
view_dispatcher_add_view(
app->view_dispatcher,
DesktopSettingsAppViewIdPinSetupHowto,
desktop_settings_view_pin_setup_howto_get_view(app->pin_setup_howto_view));
view_dispatcher_add_view(
app->view_dispatcher,
DesktopSettingsAppViewIdPinSetupHowto2,
desktop_settings_view_pin_setup_howto2_get_view(app->pin_setup_howto2_view));
return app;
}
@ -48,9 +63,15 @@ void desktop_settings_app_free(DesktopSettingsApp* app) {
furi_assert(app);
// Variable item list
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewMenu);
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPopup);
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput);
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto);
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto2);
submenu_free(app->submenu);
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewPincodeInput);
code_input_free(app->code_input);
popup_free(app->popup);
desktop_view_pin_input_free(app->pin_input_view);
desktop_settings_view_pin_setup_howto_free(app->pin_setup_howto_view);
desktop_settings_view_pin_setup_howto2_free(app->pin_setup_howto2_view);
// View dispatcher
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);
@ -62,6 +83,12 @@ void desktop_settings_app_free(DesktopSettingsApp* app) {
extern int32_t desktop_settings_app(void* p) {
DesktopSettingsApp* app = desktop_settings_app_alloc();
LOAD_DESKTOP_SETTINGS(&app->settings);
if(!strcmp(p, DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG)) {
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto);
} else {
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneStart);
}
view_dispatcher_run(app->view_dispatcher);
desktop_settings_app_free(app);
return 0;

View File

@ -1,22 +1,22 @@
#pragma once
#include <gui/gui.h>
#include <gui/modules/popup.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/submenu.h>
#include <gui/modules/code_input.h>
#include "desktop_settings.h"
typedef enum {
CodeEventsSetPin,
CodeEventsChangePin,
CodeEventsDisablePin,
} CodeEventsEnum;
#include "desktop/views/desktop_view_pin_input.h"
#include "views/desktop_settings_view_pin_setup_howto.h"
#include "views/desktop_settings_view_pin_setup_howto2.h"
typedef enum {
DesktopSettingsAppViewMenu,
DesktopSettingsAppViewPincodeInput,
DesktopSettingsAppViewIdPopup,
DesktopSettingsAppViewIdPinInput,
DesktopSettingsAppViewIdPinSetupHowto,
DesktopSettingsAppViewIdPinSetupHowto2,
} DesktopSettingsAppView;
typedef struct {
@ -26,7 +26,13 @@ typedef struct {
SceneManager* scene_manager;
ViewDispatcher* view_dispatcher;
Submenu* submenu;
CodeInput* code_input;
Popup* popup;
DesktopViewPinInput* pin_input_view;
DesktopSettingsViewPinSetupHowto* pin_setup_howto_view;
DesktopSettingsViewPinSetupHowto2* pin_setup_howto2_view;
PinCode pincode_buffer;
bool pincode_buffer_filled;
uint8_t menu_idx;

View File

@ -1,4 +1,11 @@
ADD_SCENE(desktop_settings, start, Start)
ADD_SCENE(desktop_settings, favorite, Favorite)
ADD_SCENE(desktop_settings, pincode_menu, PinCodeMenu)
ADD_SCENE(desktop_settings, pincode_input, PinCodeInput)
ADD_SCENE(desktop_settings, pin_menu, PinMenu)
ADD_SCENE(desktop_settings, pin_auth, PinAuth)
ADD_SCENE(desktop_settings, pin_error, PinError)
ADD_SCENE(desktop_settings, pin_disable, PinDisable)
ADD_SCENE(desktop_settings, pin_setup, PinSetup)
ADD_SCENE(desktop_settings, pin_setup_howto, PinSetupHowto)
ADD_SCENE(desktop_settings, pin_setup_howto2, PinSetupHowto2)
ADD_SCENE(desktop_settings, pin_setup_done, PinSetupDone)

View File

@ -0,0 +1,7 @@
#pragma once
#define SCENE_STATE_PIN_AUTH_DISABLE (0)
#define SCENE_STATE_PIN_AUTH_CHANGE_PIN (1)
#define SCENE_STATE_PIN_ERROR_MISMATCH (0)
#define SCENE_STATE_PIN_ERROR_WRONG (1)

View File

@ -0,0 +1,95 @@
#include <stdint.h>
#include <furi/check.h>
#include <gui/scene_manager.h>
#include "../desktop_settings_app.h"
#include "desktop/desktop_settings/desktop_settings.h"
#include "desktop/views/desktop_view_pin_input.h"
#include "desktop_settings_scene.h"
#include "desktop_settings_scene_i.h"
#define SCENE_EVENT_EXIT (0U)
#define SCENE_EVENT_PINS_EQUAL (1U)
#define SCENE_EVENT_PINS_DIFFERENT (2U)
static void pin_auth_done_callback(const PinCode* pin_code, void* context) {
furi_assert(pin_code);
furi_assert(context);
DesktopSettingsApp* app = context;
app->pincode_buffer = *pin_code;
if(pins_are_equal(&app->settings.pin_code, pin_code)) {
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL);
} else {
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT);
}
}
static void pin_auth_back_callback(void* context) {
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_EXIT);
}
void desktop_settings_scene_pin_auth_on_enter(void* context) {
DesktopSettingsApp* app = context;
LOAD_DESKTOP_SETTINGS(&app->settings);
furi_assert(app->settings.pin_code.length > 0);
desktop_view_pin_input_set_context(app->pin_input_view, app);
desktop_view_pin_input_set_back_callback(app->pin_input_view, pin_auth_back_callback);
desktop_view_pin_input_set_done_callback(app->pin_input_view, pin_auth_done_callback);
desktop_view_pin_input_set_label_button(app->pin_input_view, "OK");
desktop_view_pin_input_set_label_primary(app->pin_input_view, 0, 0, NULL);
desktop_view_pin_input_set_label_secondary(
app->pin_input_view, 0, 8, "Enter your current PIN:");
desktop_view_pin_input_reset_pin(app->pin_input_view);
desktop_view_pin_input_unlock_input(app->pin_input_view);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput);
}
bool desktop_settings_scene_pin_auth_on_event(void* context, SceneManagerEvent event) {
DesktopSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case SCENE_EVENT_PINS_DIFFERENT:
scene_manager_set_scene_state(
app->scene_manager, DesktopSettingsAppScenePinError, SCENE_STATE_PIN_ERROR_WRONG);
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinError);
consumed = true;
break;
case SCENE_EVENT_PINS_EQUAL: {
uint32_t state =
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppScenePinAuth);
if(state == SCENE_STATE_PIN_AUTH_CHANGE_PIN) {
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto);
} else if(state == SCENE_STATE_PIN_AUTH_DISABLE) {
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinDisable);
} else {
furi_assert(0);
}
consumed = true;
break;
}
case SCENE_EVENT_EXIT:
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, DesktopSettingsAppScenePinMenu);
consumed = true;
break;
default:
consumed = true;
break;
}
}
return consumed;
}
void desktop_settings_scene_pin_auth_on_exit(void* context) {
furi_assert(context);
DesktopSettingsApp* app = context;
desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL);
desktop_view_pin_input_set_done_callback(app->pin_input_view, NULL);
}

View File

@ -0,0 +1,56 @@
#include <stdint.h>
#include <furi/check.h>
#include <gui/scene_manager.h>
#include <gui/modules/popup.h>
#include "../desktop_settings_app.h"
#include "../desktop_settings.h"
#include "desktop/desktop_settings/desktop_settings.h"
#include "desktop_settings_scene.h"
#define SCENE_EVENT_EXIT (0U)
static void pin_disable_back_callback(void* context) {
furi_assert(context);
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_EXIT);
}
void desktop_settings_scene_pin_disable_on_enter(void* context) {
furi_assert(context);
DesktopSettingsApp* app = context;
app->settings.pin_code.length = 0;
memset(app->settings.pin_code.data, '0', sizeof(app->settings.pin_code.data));
SAVE_DESKTOP_SETTINGS(&app->settings);
popup_set_context(app->popup, app);
popup_set_callback(app->popup, pin_disable_back_callback);
popup_set_icon(app->popup, 0, 2, &I_DolphinMafia_115x62);
popup_set_header(app->popup, "PIN\ndeleted!", 95, 9, AlignCenter, AlignCenter);
popup_set_timeout(app->popup, 1500);
popup_enable_timeout(app->popup);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPopup);
}
bool desktop_settings_scene_pin_disable_on_event(void* context, SceneManagerEvent event) {
DesktopSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case SCENE_EVENT_EXIT:
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, DesktopSettingsAppScenePinMenu);
consumed = true;
break;
default:
consumed = true;
break;
}
}
return consumed;
}
void desktop_settings_scene_pin_disable_on_exit(void* context) {
}

View File

@ -0,0 +1,76 @@
#include <stdint.h>
#include <furi/check.h>
#include <gui/scene_manager.h>
#include "desktop/desktop_settings/desktop_settings.h"
#include "desktop/views/desktop_view_pin_input.h"
#include "desktop_settings_scene.h"
#include "desktop_settings_scene_i.h"
#include "../../desktop_helpers.h"
#include "../desktop_settings_app.h"
#define SCENE_EVENT_EXIT (0U)
static void pin_error_back_callback(void* context) {
furi_assert(context);
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_EXIT);
}
static void pin_error_done_callback(const PinCode* pin_code, void* context) {
furi_assert(context);
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_EXIT);
}
void desktop_settings_scene_pin_error_on_enter(void* context) {
DesktopSettingsApp* app = context;
desktop_helpers_emit_error_notification();
desktop_view_pin_input_set_context(app->pin_input_view, app);
desktop_view_pin_input_set_back_callback(app->pin_input_view, pin_error_back_callback);
desktop_view_pin_input_set_done_callback(app->pin_input_view, pin_error_done_callback);
uint32_t state =
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppScenePinError);
if(state == SCENE_STATE_PIN_ERROR_MISMATCH) {
desktop_view_pin_input_set_label_primary(app->pin_input_view, 29, 8, "PIN mismatch!");
} else if(state == SCENE_STATE_PIN_ERROR_WRONG) {
desktop_view_pin_input_set_label_primary(app->pin_input_view, 35, 8, "Wrong PIN!");
} else {
furi_assert(0);
}
desktop_view_pin_input_set_label_secondary(app->pin_input_view, 0, 8, NULL);
desktop_view_pin_input_set_label_button(app->pin_input_view, "Retry");
desktop_view_pin_input_lock_input(app->pin_input_view);
desktop_view_pin_input_set_pin(app->pin_input_view, &app->pincode_buffer);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput);
}
bool desktop_settings_scene_pin_error_on_event(void* context, SceneManagerEvent event) {
DesktopSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case SCENE_EVENT_EXIT:
scene_manager_previous_scene(app->scene_manager);
consumed = true;
break;
default:
consumed = true;
break;
}
}
return consumed;
}
void desktop_settings_scene_pin_error_on_exit(void* context) {
furi_assert(context);
DesktopSettingsApp* app = context;
desktop_view_pin_input_unlock_input(app->pin_input_view);
desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL);
desktop_view_pin_input_set_done_callback(app->pin_input_view, NULL);
}

View File

@ -1,38 +1,45 @@
#include "../desktop_settings_app.h"
#include "applications.h"
#include "desktop_settings_scene.h"
#include <gui/scene_manager.h>
#include <applications.h>
static void desktop_settings_scene_pincode_menu_submenu_callback(void* context, uint32_t index) {
#include "../desktop_settings_app.h"
#include "desktop_settings_scene.h"
#include "desktop_settings_scene_i.h"
#define SCENE_EVENT_SET_PIN 0
#define SCENE_EVENT_CHANGE_PIN 1
#define SCENE_EVENT_DISABLE_PIN 2
static void desktop_settings_scene_pin_menu_submenu_callback(void* context, uint32_t index) {
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void desktop_settings_scene_pincode_menu_on_enter(void* context) {
void desktop_settings_scene_pin_menu_on_enter(void* context) {
DesktopSettingsApp* app = context;
Submenu* submenu = app->submenu;
submenu_reset(submenu);
if(!app->settings.pincode.length) {
if(!app->settings.pin_code.length) {
submenu_add_item(
submenu,
"Set Pin",
CodeEventsSetPin,
desktop_settings_scene_pincode_menu_submenu_callback,
SCENE_EVENT_SET_PIN,
desktop_settings_scene_pin_menu_submenu_callback,
app);
} else {
submenu_add_item(
submenu,
"Change Pin",
CodeEventsChangePin,
desktop_settings_scene_pincode_menu_submenu_callback,
SCENE_EVENT_CHANGE_PIN,
desktop_settings_scene_pin_menu_submenu_callback,
app);
submenu_add_item(
submenu,
"Disable",
CodeEventsDisablePin,
desktop_settings_scene_pincode_menu_submenu_callback,
SCENE_EVENT_DISABLE_PIN,
desktop_settings_scene_pin_menu_submenu_callback,
app);
}
@ -41,28 +48,28 @@ void desktop_settings_scene_pincode_menu_on_enter(void* context) {
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu);
}
bool desktop_settings_scene_pincode_menu_on_event(void* context, SceneManagerEvent event) {
bool desktop_settings_scene_pin_menu_on_event(void* context, SceneManagerEvent event) {
DesktopSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case CodeEventsSetPin:
scene_manager_set_scene_state(
app->scene_manager, DesktopSettingsAppScenePinCodeInput, event.event);
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeInput);
case SCENE_EVENT_SET_PIN:
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto);
consumed = true;
break;
case CodeEventsChangePin:
case SCENE_EVENT_CHANGE_PIN:
scene_manager_set_scene_state(
app->scene_manager, DesktopSettingsAppScenePinCodeInput, event.event);
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeInput);
app->scene_manager,
DesktopSettingsAppScenePinAuth,
SCENE_STATE_PIN_AUTH_CHANGE_PIN);
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinAuth);
consumed = true;
break;
case CodeEventsDisablePin:
case SCENE_EVENT_DISABLE_PIN:
scene_manager_set_scene_state(
app->scene_manager, DesktopSettingsAppScenePinCodeInput, event.event);
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeInput);
app->scene_manager, DesktopSettingsAppScenePinAuth, SCENE_STATE_PIN_AUTH_DISABLE);
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinAuth);
consumed = true;
break;
default:
@ -73,7 +80,7 @@ bool desktop_settings_scene_pincode_menu_on_event(void* context, SceneManagerEve
return consumed;
}
void desktop_settings_scene_pincode_menu_on_exit(void* context) {
void desktop_settings_scene_pin_menu_on_exit(void* context) {
DesktopSettingsApp* app = context;
submenu_reset(app->submenu);
}

View File

@ -0,0 +1,107 @@
#include <stdint.h>
#include <furi/check.h>
#include <gui/scene_manager.h>
#include "../desktop_settings_app.h"
#include "desktop/desktop_settings/desktop_settings.h"
#include "desktop/views/desktop_view_pin_input.h"
#include "desktop_settings_scene.h"
#include "desktop_settings_scene_i.h"
#define SCENE_EVENT_EXIT (0U)
#define SCENE_EVENT_1ST_PIN_ENTERED (1U)
#define SCENE_EVENT_PINS_EQUAL (2U)
#define SCENE_EVENT_PINS_DIFFERENT (3U)
static void pin_setup_done_callback(const PinCode* pin_code, void* context) {
furi_assert(pin_code);
furi_assert(context);
DesktopSettingsApp* app = context;
if(!app->pincode_buffer_filled) {
app->pincode_buffer = *pin_code;
app->pincode_buffer_filled = true;
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_1ST_PIN_ENTERED);
} else {
app->pincode_buffer_filled = false;
if(pins_are_equal(&app->pincode_buffer, pin_code)) {
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL);
} else {
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT);
}
}
}
static void pin_setup_back_callback(void* context) {
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_EXIT);
}
void desktop_settings_scene_pin_setup_on_enter(void* context) {
DesktopSettingsApp* app = context;
app->pincode_buffer_filled = false;
desktop_view_pin_input_set_context(app->pin_input_view, app);
desktop_view_pin_input_set_back_callback(app->pin_input_view, pin_setup_back_callback);
desktop_view_pin_input_set_done_callback(app->pin_input_view, pin_setup_done_callback);
desktop_view_pin_input_set_label_button(app->pin_input_view, "OK");
desktop_view_pin_input_set_label_primary(app->pin_input_view, 0, 0, NULL);
desktop_view_pin_input_set_label_secondary(
app->pin_input_view, 0, 8, "Enter from 4 to 10 arrows:");
desktop_view_pin_input_reset_pin(app->pin_input_view);
desktop_view_pin_input_unlock_input(app->pin_input_view);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput);
}
bool desktop_settings_scene_pin_setup_on_event(void* context, SceneManagerEvent event) {
DesktopSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case SCENE_EVENT_1ST_PIN_ENTERED:
desktop_view_pin_input_set_label_button(app->pin_input_view, "OK");
desktop_view_pin_input_set_label_primary(app->pin_input_view, 0, 0, NULL);
desktop_view_pin_input_set_label_secondary(
app->pin_input_view, 0, 8, "Confirm your PIN:");
desktop_view_pin_input_reset_pin(app->pin_input_view);
desktop_view_pin_input_unlock_input(app->pin_input_view);
consumed = true;
break;
case SCENE_EVENT_PINS_DIFFERENT:
scene_manager_set_scene_state(
app->scene_manager,
DesktopSettingsAppScenePinError,
SCENE_STATE_PIN_ERROR_MISMATCH);
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinError);
consumed = true;
break;
case SCENE_EVENT_PINS_EQUAL:
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto2);
consumed = true;
break;
case SCENE_EVENT_EXIT: {
uint32_t scene_found;
scene_found = scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, DesktopSettingsAppScenePinMenu);
if(!scene_found) {
view_dispatcher_stop(app->view_dispatcher);
}
consumed = true;
break;
}
default:
consumed = true;
break;
}
}
return consumed;
}
void desktop_settings_scene_pin_setup_on_exit(void* context) {
furi_assert(context);
DesktopSettingsApp* app = context;
desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL);
desktop_view_pin_input_set_done_callback(app->pin_input_view, NULL);
}

View File

@ -0,0 +1,77 @@
#include <furi.h>
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include <stdint.h>
#include <gui/scene_manager.h>
#include <gui/view_dispatcher.h>
#include "../desktop_settings_app.h"
#include "desktop/desktop_settings/desktop_settings.h"
#include "desktop/views/desktop_view_pin_input.h"
#include "desktop_settings_scene.h"
#define SCENE_EVENT_DONE (0U)
static void pin_setup_done_callback(const PinCode* pin_code, void* context) {
furi_assert(pin_code);
furi_assert(context);
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_DONE);
}
void desktop_settings_scene_pin_setup_done_on_enter(void* context) {
DesktopSettingsApp* app = context;
app->settings.pin_code = app->pincode_buffer;
SAVE_DESKTOP_SETTINGS(&app->settings);
NotificationApp* notification = furi_record_open("notification");
notification_message(notification, &sequence_single_vibro);
furi_record_close("notification");
desktop_view_pin_input_set_context(app->pin_input_view, app);
desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL);
desktop_view_pin_input_set_done_callback(app->pin_input_view, pin_setup_done_callback);
desktop_view_pin_input_set_pin(app->pin_input_view, &app->settings.pin_code);
desktop_view_pin_input_set_label_button(app->pin_input_view, "Done");
desktop_view_pin_input_set_label_primary(app->pin_input_view, 29, 8, "PIN activated!");
desktop_view_pin_input_set_label_secondary(
app->pin_input_view, 7, 45, "Remember or write it down");
desktop_view_pin_input_lock_input(app->pin_input_view);
desktop_view_pin_input_set_pin_position(app->pin_input_view, 64, 24);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput);
}
bool desktop_settings_scene_pin_setup_done_on_event(void* context, SceneManagerEvent event) {
DesktopSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case SCENE_EVENT_DONE: {
bool scene_found = false;
scene_found = scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, DesktopSettingsAppScenePinMenu);
if(!scene_found) {
view_dispatcher_stop(app->view_dispatcher);
}
consumed = true;
break;
}
default:
consumed = true;
break;
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = true;
}
return consumed;
}
void desktop_settings_scene_pin_setup_done_on_exit(void* context) {
furi_assert(context);
DesktopSettingsApp* app = context;
desktop_view_pin_input_set_pin_position(app->pin_input_view, 64, 32);
desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL);
desktop_view_pin_input_set_done_callback(app->pin_input_view, NULL);
}

View File

@ -0,0 +1,44 @@
#include <furi.h>
#include <gui/scene_manager.h>
#include <gui/view_dispatcher.h>
#include "desktop_settings_scene.h"
#include "../desktop_settings_app.h"
#include "../views/desktop_settings_view_pin_setup_howto.h"
#define SCENE_EXIT_EVENT (0U)
static void desktop_settings_scene_pin_lock_done_callback(void* context) {
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EXIT_EVENT);
}
void desktop_settings_scene_pin_setup_howto_on_enter(void* context) {
DesktopSettingsApp* app = context;
desktop_settings_view_pin_setup_howto_set_callback(
app->pin_setup_howto_view, desktop_settings_scene_pin_lock_done_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto);
}
bool desktop_settings_scene_pin_setup_howto_on_event(void* context, SceneManagerEvent event) {
DesktopSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case SCENE_EXIT_EVENT:
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetup);
consumed = true;
break;
default:
furi_assert(0);
consumed = true;
break;
}
}
return consumed;
}
void desktop_settings_scene_pin_setup_howto_on_exit(void* context) {
}

View File

@ -0,0 +1,67 @@
#include <furi.h>
#include <gui/scene_manager.h>
#include <stdint.h>
#include "desktop_settings_scene.h"
#include "../desktop_settings_app.h"
#include "../views/desktop_settings_view_pin_setup_howto2.h"
#define SCENE_EXIT_EVENT (0U)
#define SCENE_DONE_EVENT (1U)
static void desktop_settings_scene_pin_setup_howto2_done_callback(void* context) {
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_DONE_EVENT);
}
static void desktop_settings_scene_pin_setup_howto2_exit_callback(void* context) {
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EXIT_EVENT);
}
void desktop_settings_scene_pin_setup_howto2_on_enter(void* context) {
DesktopSettingsApp* app = context;
desktop_settings_view_pin_setup_howto2_set_context(app->pin_setup_howto2_view, app);
desktop_settings_view_pin_setup_howto2_set_ok_callback(
app->pin_setup_howto2_view, desktop_settings_scene_pin_setup_howto2_done_callback);
desktop_settings_view_pin_setup_howto2_set_cancel_callback(
app->pin_setup_howto2_view, desktop_settings_scene_pin_setup_howto2_exit_callback);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto2);
}
bool desktop_settings_scene_pin_setup_howto2_on_event(void* context, SceneManagerEvent event) {
DesktopSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case SCENE_DONE_EVENT: {
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupDone);
consumed = true;
break;
}
case SCENE_EXIT_EVENT: {
bool scene_found = false;
scene_found = scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, DesktopSettingsAppScenePinMenu);
if(!scene_found) {
view_dispatcher_stop(app->view_dispatcher);
}
consumed = true;
break;
}
default:
furi_assert(0);
consumed = true;
break;
}
}
return consumed;
}
void desktop_settings_scene_pin_setup_howto2_on_exit(void* context) {
DesktopSettingsApp* app = context;
desktop_settings_view_pin_setup_howto2_set_ok_callback(app->pin_setup_howto2_view, NULL);
desktop_settings_view_pin_setup_howto2_set_cancel_callback(app->pin_setup_howto2_view, NULL);
}

View File

@ -1,64 +0,0 @@
#include "../desktop_settings_app.h"
#include "desktop_settings_scene.h"
#define SCENE_EXIT_EVENT (0U)
void desktop_settings_scene_ok_callback(void* context) {
DesktopSettingsApp* app = context;
uint32_t state =
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppScenePinCodeInput);
if(state == CodeEventsDisablePin) {
memset(app->settings.pincode.data, 0, app->settings.pincode.length * sizeof(uint8_t));
app->settings.pincode.length = 0;
}
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EXIT_EVENT);
}
void desktop_settings_scene_pincode_input_on_enter(void* context) {
DesktopSettingsApp* app = context;
CodeInput* code_input = app->code_input;
uint32_t state =
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppScenePinCodeInput);
bool update = state != CodeEventsDisablePin;
code_input_set_header_text(code_input, "PIN Code Setup");
code_input_set_result_callback(
code_input,
desktop_settings_scene_ok_callback,
NULL,
app,
app->settings.pincode.data,
&app->settings.pincode.length,
update);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewPincodeInput);
}
bool desktop_settings_scene_pincode_input_on_event(void* context, SceneManagerEvent event) {
DesktopSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case SCENE_EXIT_EVENT:
scene_manager_previous_scene(app->scene_manager);
consumed = true;
break;
default:
consumed = true;
break;
}
}
return consumed;
}
void desktop_settings_scene_pincode_input_on_exit(void* context) {
DesktopSettingsApp* app = context;
SAVE_DESKTOP_SETTINGS(&app->settings);
code_input_set_result_callback(app->code_input, NULL, NULL, NULL, NULL, NULL, 0);
code_input_set_header_text(app->code_input, "");
}

View File

@ -1,11 +1,10 @@
#include <applications.h>
#include "../desktop_settings_app.h"
#include "applications.h"
#include "desktop_settings_scene.h"
enum DesktopSettingsStartSubmenuIndex {
DesktopSettingsStartSubmenuIndexFavorite,
DesktopSettingsStartSubmenuIndexPinSetup,
};
#define SCENE_EVENT_SELECT_FAVORITE 0
#define SCENE_EVENT_SELECT_PIN_SETUP 1
static void desktop_settings_scene_start_submenu_callback(void* context, uint32_t index) {
DesktopSettingsApp* app = context;
@ -19,14 +18,14 @@ void desktop_settings_scene_start_on_enter(void* context) {
submenu_add_item(
submenu,
"Favorite App",
DesktopSettingsStartSubmenuIndexFavorite,
SCENE_EVENT_SELECT_FAVORITE,
desktop_settings_scene_start_submenu_callback,
app);
submenu_add_item(
submenu,
"PIN Setup",
DesktopSettingsStartSubmenuIndexPinSetup,
SCENE_EVENT_SELECT_PIN_SETUP,
desktop_settings_scene_start_submenu_callback,
app);
@ -39,12 +38,12 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DesktopSettingsStartSubmenuIndexFavorite:
case SCENE_EVENT_SELECT_FAVORITE:
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);
consumed = true;
break;
case DesktopSettingsStartSubmenuIndexPinSetup:
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeMenu);
case SCENE_EVENT_SELECT_PIN_SETUP:
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinMenu);
consumed = true;
break;
}

View File

@ -0,0 +1,78 @@
#include <furi.h>
#include <furi_hal.h>
#include <gui/elements.h>
#include <gui/canvas.h>
#include <toolbox/version.h>
#include <assets_icons.h>
#include <dolphin/helpers/dolphin_state.h>
#include <dolphin/dolphin.h>
#include "desktop_settings_view_pin_setup_howto.h"
struct DesktopSettingsViewPinSetupHowto {
View* view;
DesktopSettingsViewPinSetupHowtoDoneCallback callback;
void* context;
};
static void desktop_settings_view_pin_setup_howto_draw(Canvas* canvas, void* model) {
furi_assert(canvas);
furi_assert(model);
canvas_draw_icon(canvas, 16, 18, &I_Pin_attention_dpad_29x29);
elements_button_right(canvas, "Next");
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 64, 0, AlignCenter, AlignTop, "Setting up PIN");
canvas_set_font(canvas, FontSecondary);
elements_multiline_text(canvas, 58, 24, "Prepare to use\narrows as\nPIN symbols");
}
static bool desktop_settings_view_pin_setup_howto_input(InputEvent* event, void* context) {
furi_assert(event);
furi_assert(context);
DesktopSettingsViewPinSetupHowto* instance = context;
bool consumed = false;
if((event->key == InputKeyRight) && (event->type == InputTypeShort)) {
instance->callback(instance->context);
consumed = true;
}
return consumed;
}
void desktop_settings_view_pin_setup_howto_set_callback(
DesktopSettingsViewPinSetupHowto* instance,
DesktopSettingsViewPinSetupHowtoDoneCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
DesktopSettingsViewPinSetupHowto* desktop_settings_view_pin_setup_howto_alloc() {
DesktopSettingsViewPinSetupHowto* view = furi_alloc(sizeof(DesktopSettingsViewPinSetupHowto));
view->view = view_alloc();
view_allocate_model(view->view, ViewModelTypeLockFree, 1);
view_set_context(view->view, view);
view_set_draw_callback(view->view, desktop_settings_view_pin_setup_howto_draw);
view_set_input_callback(view->view, desktop_settings_view_pin_setup_howto_input);
return view;
}
void desktop_settings_view_pin_setup_howto_free(DesktopSettingsViewPinSetupHowto* instance) {
furi_assert(instance);
view_free(instance->view);
free(instance);
}
View* desktop_settings_view_pin_setup_howto_get_view(DesktopSettingsViewPinSetupHowto* instance) {
furi_assert(instance);
return instance->view;
}

View File

@ -0,0 +1,15 @@
#pragma once
#include <gui/view.h>
typedef struct DesktopSettingsViewPinSetupHowto DesktopSettingsViewPinSetupHowto;
typedef void (*DesktopSettingsViewPinSetupHowtoDoneCallback)(void*);
void desktop_settings_view_pin_setup_howto_set_callback(
DesktopSettingsViewPinSetupHowto* instance,
DesktopSettingsViewPinSetupHowtoDoneCallback callback,
void* context);
DesktopSettingsViewPinSetupHowto* desktop_settings_view_pin_setup_howto_alloc();
void desktop_settings_view_pin_setup_howto_free(DesktopSettingsViewPinSetupHowto* instance);
View* desktop_settings_view_pin_setup_howto_get_view(DesktopSettingsViewPinSetupHowto* instance);

View File

@ -0,0 +1,101 @@
#include <furi.h>
#include <furi_hal.h>
#include <gui/elements.h>
#include <gui/canvas.h>
#include <toolbox/version.h>
#include <assets_icons.h>
#include <dolphin/helpers/dolphin_state.h>
#include <dolphin/dolphin.h>
#include "desktop_settings_view_pin_setup_howto2.h"
struct DesktopSettingsViewPinSetupHowto2 {
View* view;
DesktopSettingsViewPinSetupHowto2Callback cancel_callback;
DesktopSettingsViewPinSetupHowto2Callback ok_callback;
void* context;
};
static void desktop_settings_view_pin_setup_howto2_draw(Canvas* canvas, void* model) {
furi_assert(canvas);
furi_assert(model);
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(
canvas,
64,
24,
AlignCenter,
AlignCenter,
"Forgotten PIN can only be\n"
"reset with entire device.\n"
"Read docs How to reset PIN.");
elements_button_right(canvas, "OK");
elements_button_left(canvas, "Cancel");
}
static bool desktop_settings_view_pin_setup_howto2_input(InputEvent* event, void* context) {
furi_assert(event);
furi_assert(context);
DesktopSettingsViewPinSetupHowto2* instance = context;
bool consumed = false;
if(event->type == InputTypeShort) {
if(event->key == InputKeyRight) {
instance->ok_callback(instance->context);
consumed = true;
} else if(event->key == InputKeyLeft) {
instance->cancel_callback(instance->context);
consumed = true;
}
}
return consumed;
}
void desktop_settings_view_pin_setup_howto2_set_context(
DesktopSettingsViewPinSetupHowto2* instance,
void* context) {
furi_assert(instance);
instance->context = context;
}
void desktop_settings_view_pin_setup_howto2_set_cancel_callback(
DesktopSettingsViewPinSetupHowto2* instance,
DesktopSettingsViewPinSetupHowto2Callback callback) {
furi_assert(instance);
instance->cancel_callback = callback;
}
void desktop_settings_view_pin_setup_howto2_set_ok_callback(
DesktopSettingsViewPinSetupHowto2* instance,
DesktopSettingsViewPinSetupHowto2Callback callback) {
furi_assert(instance);
instance->ok_callback = callback;
}
DesktopSettingsViewPinSetupHowto2* desktop_settings_view_pin_setup_howto2_alloc() {
DesktopSettingsViewPinSetupHowto2* view =
furi_alloc(sizeof(DesktopSettingsViewPinSetupHowto2));
view->view = view_alloc();
view_allocate_model(view->view, ViewModelTypeLockFree, 1);
view_set_context(view->view, view);
view_set_draw_callback(view->view, desktop_settings_view_pin_setup_howto2_draw);
view_set_input_callback(view->view, desktop_settings_view_pin_setup_howto2_input);
return view;
}
void desktop_settings_view_pin_setup_howto2_free(DesktopSettingsViewPinSetupHowto2* instance) {
furi_assert(instance);
view_free(instance->view);
free(instance);
}
View* desktop_settings_view_pin_setup_howto2_get_view(DesktopSettingsViewPinSetupHowto2* instance) {
furi_assert(instance);
return instance->view;
}

View File

@ -0,0 +1,20 @@
#pragma once
#include <gui/view.h>
typedef struct DesktopSettingsViewPinSetupHowto2 DesktopSettingsViewPinSetupHowto2;
typedef void (*DesktopSettingsViewPinSetupHowto2Callback)(void*);
DesktopSettingsViewPinSetupHowto2* desktop_settings_view_pin_setup_howto2_alloc();
void desktop_settings_view_pin_setup_howto2_free(DesktopSettingsViewPinSetupHowto2* instance);
View* desktop_settings_view_pin_setup_howto2_get_view(DesktopSettingsViewPinSetupHowto2* instance);
void desktop_settings_view_pin_setup_howto2_set_context(
DesktopSettingsViewPinSetupHowto2* instance,
void* context);
void desktop_settings_view_pin_setup_howto2_set_cancel_callback(
DesktopSettingsViewPinSetupHowto2* instance,
DesktopSettingsViewPinSetupHowto2Callback callback);
void desktop_settings_view_pin_setup_howto2_set_ok_callback(
DesktopSettingsViewPinSetupHowto2* instance,
DesktopSettingsViewPinSetupHowto2Callback callback);

View File

@ -3,5 +3,7 @@ ADD_SCENE(desktop, lock_menu, LockMenu)
ADD_SCENE(desktop, debug, Debug)
ADD_SCENE(desktop, first_start, FirstStart)
ADD_SCENE(desktop, hw_mismatch, HwMismatch)
ADD_SCENE(desktop, pinsetup, PinSetup)
ADD_SCENE(desktop, fault, Fault)
ADD_SCENE(desktop, locked, Locked)
ADD_SCENE(desktop, pin_input, PinInput)
ADD_SCENE(desktop, pin_timeout, PinTimeout)

View File

@ -3,7 +3,7 @@
#include <dolphin/helpers/dolphin_deed.h>
#include "../desktop_i.h"
#include "../views/desktop_debug.h"
#include "../views/desktop_view_debug.h"
#include "desktop_scene.h"
void desktop_scene_debug_callback(DesktopEvent event, void* context) {
@ -17,7 +17,7 @@ void desktop_scene_debug_on_enter(void* context) {
desktop_debug_get_dolphin_data(desktop->debug_view);
desktop_debug_set_callback(desktop->debug_view, desktop_scene_debug_callback, desktop);
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewDebug);
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdDebug);
}
bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) {

View File

@ -25,7 +25,7 @@ void desktop_scene_fault_on_enter(void* context) {
char* message = (char*)furi_hal_rtc_get_fault_data();
popup_set_text(popup, message, 60, 37 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter);
popup_set_callback(popup, desktop_scene_fault_callback);
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewHwMismatch);
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdHwMismatch);
}
bool desktop_scene_fault_on_event(void* context, SceneManagerEvent event) {

View File

@ -2,7 +2,7 @@
#include <storage/storage.h>
#include "../desktop_i.h"
#include "../views/desktop_first_start.h"
#include "../views/desktop_view_first_start.h"
#include "../views/desktop_events.h"
void desktop_scene_first_start_callback(DesktopEvent event, void* context) {
@ -17,7 +17,7 @@ void desktop_scene_first_start_on_enter(void* context) {
desktop_first_start_set_callback(
first_start_view, desktop_scene_first_start_callback, desktop);
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewFirstStart);
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdFirstStart);
}
bool desktop_scene_first_start_on_event(void* context, SceneManagerEvent event) {

View File

@ -1,5 +1,5 @@
#include <gui/scene_manager.h>
#include <furi_hal_version.h>
#include <furi_hal.h>
#include "desktop_scene.h"
#include "../desktop_i.h"
@ -31,7 +31,7 @@ void desktop_scene_hw_mismatch_on_enter(void* context) {
popup, "!!!! HW Mismatch !!!!", 60, 14 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter);
popup_set_text(popup, text_buffer, 60, 37 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter);
popup_set_callback(popup, desktop_scene_hw_mismatch_callback);
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewHwMismatch);
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdHwMismatch);
}
bool desktop_scene_hw_mismatch_on_event(void* context, SceneManagerEvent event) {

View File

@ -1,7 +1,4 @@
#pragma once
typedef enum {
DesktopMainSceneStateUnlocked,
DesktopMainSceneStateLockedWithPin,
DesktopMainSceneStateLockedNoPin,
} DesktopMainSceneState;
#define SCENE_LOCKED_FIRST_ENTER 0
#define SCENE_LOCKED_REPEAT_ENTER 1

View File

@ -1,11 +1,18 @@
#include <gui/scene_manager.h>
#include <applications.h>
#include <furi_hal.h>
#include <toolbox/saved_struct.h>
#include <stdbool.h>
#include <loader/loader.h>
#include "../desktop_i.h"
#include "../views/desktop_lock_menu.h"
#include "../desktop_settings/desktop_settings.h"
#include "../views/desktop_view_lock_menu.h"
#include "desktop_scene_i.h"
#include "desktop_scene.h"
#define TAG "DesktopSceneLock"
void desktop_scene_lock_menu_callback(DesktopEvent event, void* context) {
Desktop* desktop = (Desktop*)context;
view_dispatcher_send_custom_event(desktop->view_dispatcher, event);
@ -15,36 +22,53 @@ void desktop_scene_lock_menu_on_enter(void* context) {
Desktop* desktop = (Desktop*)context;
LOAD_DESKTOP_SETTINGS(&desktop->settings);
scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0);
desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop);
desktop_lock_menu_pin_set(desktop->lock_menu, desktop->settings.pincode.length > 0);
desktop_lock_menu_pin_set(desktop->lock_menu, desktop->settings.pin_code.length > 0);
desktop_lock_menu_set_idx(desktop->lock_menu, 0);
uint8_t idx = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLockMenu);
desktop_lock_menu_set_idx(desktop->lock_menu, idx);
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLockMenu);
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdLockMenu);
}
bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) {
Desktop* desktop = (Desktop*)context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.type == SceneManagerEventTypeTick) {
bool check_pin_changed =
scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLockMenu);
if(check_pin_changed) {
LOAD_DESKTOP_SETTINGS(&desktop->settings);
if(desktop->settings.pin_code.length > 0) {
desktop_lock_menu_pin_set(desktop->lock_menu, 1);
scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0);
}
}
} else if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DesktopLockMenuEventLock:
scene_manager_set_scene_state(
desktop->scene_manager, DesktopSceneMain, DesktopMainSceneStateLockedNoPin);
scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0);
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
scene_manager_set_scene_state(
desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER);
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked);
consumed = true;
break;
case DesktopLockMenuEventPinLock:
if(desktop->settings.pincode.length > 0) {
if(desktop->settings.pin_code.length > 0) {
furi_hal_rtc_set_flag(FuriHalRtcFlagLock);
scene_manager_set_scene_state(
desktop->scene_manager, DesktopSceneMain, DesktopMainSceneStateLockedWithPin);
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER);
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked);
} else {
scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 1);
scene_manager_next_scene(desktop->scene_manager, DesktopScenePinSetup);
Loader* loader = furi_record_open("loader");
LoaderStatus status =
loader_start(loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG);
furi_record_close("loader");
if(status == LoaderStatusOk) {
scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 1);
} else {
FURI_LOG_E(TAG, "Unable to start desktop settings");
}
}
consumed = true;

View File

@ -0,0 +1,109 @@
#include <furi.h>
#include <furi_hal.h>
#include <gui/scene_manager.h>
#include <gui/view_stack.h>
#include <stdint.h>
#include <portmacro.h>
#include "../desktop.h"
#include "../desktop_i.h"
#include "../desktop_helpers.h"
#include "../animations/animation_manager.h"
#include "../views/desktop_events.h"
#include "../views/desktop_view_pin_input.h"
#include "../views/desktop_view_locked.h"
#include "desktop_scene.h"
#include "desktop_scene_i.h"
#define WRONG_PIN_HEADER_TIMEOUT 3000
#define INPUT_PIN_VIEW_TIMEOUT 15000
static void desktop_scene_locked_callback(DesktopEvent event, void* context) {
Desktop* desktop = (Desktop*)context;
view_dispatcher_send_custom_event(desktop->view_dispatcher, event);
}
static void desktop_scene_locked_new_idle_animation_callback(void* context) {
furi_assert(context);
Desktop* desktop = context;
view_dispatcher_send_custom_event(
desktop->view_dispatcher, DesktopAnimationEventNewIdleAnimation);
}
void desktop_scene_locked_on_enter(void* context) {
Desktop* desktop = (Desktop*)context;
// callbacks for 1-st layer
animation_manager_set_new_idle_callback(
desktop->animation_manager, desktop_scene_locked_new_idle_animation_callback);
animation_manager_set_check_callback(desktop->animation_manager, NULL);
animation_manager_set_interact_callback(desktop->animation_manager, NULL);
// callbacks for 2-nd layer
desktop_view_locked_set_callback(desktop->locked_view, desktop_scene_locked_callback, desktop);
bool switch_to_timeout_scene = false;
uint32_t state = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLocked);
if(state == SCENE_LOCKED_FIRST_ENTER) {
bool pin_locked = furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock);
desktop_helpers_lock_system(desktop, pin_locked);
if(pin_locked) {
LOAD_DESKTOP_SETTINGS(&desktop->settings);
desktop_view_locked_lock(desktop->locked_view, true);
uint32_t pin_fails = furi_hal_rtc_get_pin_fails();
uint32_t pin_timeout = desktop_helpers_get_pin_fail_timeout(pin_fails);
if(pin_timeout) {
scene_manager_set_scene_state(
desktop->scene_manager, DesktopScenePinTimeout, pin_timeout);
switch_to_timeout_scene = true;
} else {
desktop_view_locked_close_doors(desktop->locked_view);
}
} else {
desktop_view_locked_lock(desktop->locked_view, false);
desktop_view_locked_close_doors(desktop->locked_view);
}
scene_manager_set_scene_state(
desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_REPEAT_ENTER);
}
if(switch_to_timeout_scene) {
scene_manager_next_scene(desktop->scene_manager, DesktopScenePinTimeout);
} else {
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdLocked);
}
}
bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) {
Desktop* desktop = (Desktop*)context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DesktopLockedEventUnlocked:
furi_hal_rtc_set_pin_fails(0);
desktop_helpers_unlock_system(desktop);
scene_manager_search_and_switch_to_previous_scene(
desktop->scene_manager, DesktopSceneMain);
consumed = true;
break;
case DesktopLockedEventUpdate:
desktop_view_locked_update(desktop->locked_view);
consumed = true;
break;
case DesktopLockedEventShowPinInput:
scene_manager_next_scene(desktop->scene_manager, DesktopScenePinInput);
consumed = true;
break;
case DesktopAnimationEventNewIdleAnimation:
animation_manager_new_idle_process(desktop->animation_manager);
consumed = true;
break;
}
}
return consumed;
}
void desktop_scene_locked_on_exit(void* context) {
}

View File

@ -4,12 +4,13 @@
#include <assets_icons.h>
#include <loader/loader.h>
#include "desktop/desktop_i.h"
#include "desktop/views/desktop_main.h"
#include "../desktop_i.h"
#include "../views/desktop_events.h"
#include "../views/desktop_view_main.h"
#include "desktop_scene.h"
#include "desktop_scene_i.h"
#define MAIN_VIEW_DEFAULT (0UL)
#define TAG "DesktopSrv"
static void desktop_scene_main_app_started_callback(const void* message, void* context) {
furi_assert(context);
@ -29,19 +30,22 @@ static void desktop_scene_main_app_started_callback(const void* message, void* c
static void desktop_scene_main_new_idle_animation_callback(void* context) {
furi_assert(context);
Desktop* desktop = context;
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopMainEventNewIdleAnimation);
view_dispatcher_send_custom_event(
desktop->view_dispatcher, DesktopAnimationEventNewIdleAnimation);
}
static void desktop_scene_main_check_animation_callback(void* context) {
furi_assert(context);
Desktop* desktop = context;
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopMainEventCheckAnimation);
view_dispatcher_send_custom_event(
desktop->view_dispatcher, DesktopAnimationEventCheckAnimation);
}
static void desktop_scene_main_interact_animation_callback(void* context) {
furi_assert(context);
Desktop* desktop = context;
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopMainEventInteractAnimation);
view_dispatcher_send_custom_event(
desktop->view_dispatcher, DesktopAnimationEventInteractAnimation);
}
static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* flipper_app) {
@ -78,7 +82,6 @@ void desktop_scene_main_on_enter(void* context) {
desktop->animation_manager, desktop_scene_main_check_animation_callback);
animation_manager_set_interact_callback(
desktop->animation_manager, desktop_scene_main_interact_animation_callback);
desktop_locked_set_callback(desktop->locked_view, desktop_scene_main_callback, desktop);
furi_assert(osSemaphoreGetCount(desktop->unload_animation_semaphore) == 0);
Loader* loader = furi_record_open("loader");
@ -88,24 +91,7 @@ void desktop_scene_main_on_enter(void* context) {
desktop_main_set_callback(main_view, desktop_scene_main_callback, desktop);
DesktopMainSceneState state =
scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneMain);
if(state == DesktopMainSceneStateLockedNoPin) {
desktop_locked_lock(desktop->locked_view);
view_port_enabled_set(desktop->lock_viewport, true);
} else if(state == DesktopMainSceneStateLockedWithPin) {
LOAD_DESKTOP_SETTINGS(&desktop->settings);
furi_assert(desktop->settings.pincode.length > 0);
desktop_locked_lock_pincode(desktop->locked_view, desktop->settings.pincode);
view_port_enabled_set(desktop->lock_viewport, true);
furi_hal_rtc_set_flag(FuriHalRtcFlagLock);
furi_hal_usb_disable();
} else {
furi_assert(state == DesktopMainSceneStateUnlocked);
view_port_enabled_set(desktop->lock_viewport, false);
}
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewMain);
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdMain);
}
bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
@ -142,23 +128,25 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
Loader* loader = furi_record_open("loader");
LoaderStatus status =
loader_start(loader, FLIPPER_APPS[desktop->settings.favorite].name, NULL);
furi_check(status == LoaderStatusOk);
if(status != LoaderStatusOk) {
FURI_LOG_E(TAG, "loader_start failed: %d", status);
}
furi_record_close("loader");
} else {
FURI_LOG_E("DesktopSrv", "Can't find favorite application");
FURI_LOG_E(TAG, "Can't find favorite application");
}
consumed = true;
break;
case DesktopMainEventCheckAnimation:
case DesktopAnimationEventCheckAnimation:
animation_manager_check_blocking_process(desktop->animation_manager);
consumed = true;
break;
case DesktopMainEventNewIdleAnimation:
case DesktopAnimationEventNewIdleAnimation:
animation_manager_new_idle_process(desktop->animation_manager);
consumed = true;
break;
case DesktopMainEventInteractAnimation:
case DesktopAnimationEventInteractAnimation:
animation_manager_interact_process(desktop->animation_manager);
consumed = true;
break;
@ -171,16 +159,8 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
animation_manager_load_and_continue_animation(desktop->animation_manager);
consumed = true;
break;
case DesktopMainEventUnlocked:
consumed = true;
furi_hal_rtc_reset_flag(FuriHalRtcFlagLock);
furi_hal_usb_enable();
view_port_enabled_set(desktop->lock_viewport, false);
scene_manager_set_scene_state(
desktop->scene_manager, DesktopSceneMain, DesktopMainSceneStateUnlocked);
break;
case DesktopMainEventUpdate:
desktop_locked_update(desktop->locked_view);
case DesktopLockedEventUpdate:
desktop_view_locked_update(desktop->locked_view);
consumed = true;
break;
@ -209,5 +189,4 @@ void desktop_scene_main_on_exit(void* context) {
animation_manager_set_check_callback(desktop->animation_manager, NULL);
animation_manager_set_interact_callback(desktop->animation_manager, NULL);
animation_manager_set_context(desktop->animation_manager, desktop);
scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneMain, MAIN_VIEW_DEFAULT);
}

View File

@ -0,0 +1,162 @@
#include <furi.h>
#include <furi_hal.h>
#include <gui/scene_manager.h>
#include <gui/view_stack.h>
#include <stdint.h>
#include <portmacro.h>
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include "../desktop.h"
#include "../desktop_i.h"
#include "../animations/animation_manager.h"
#include "../views/desktop_events.h"
#include "../views/desktop_view_pin_input.h"
#include "../desktop_helpers.h"
#include "desktop_scene.h"
#include "desktop_scene_i.h"
#define WRONG_PIN_HEADER_TIMEOUT 3000
#define INPUT_PIN_VIEW_TIMEOUT 15000
typedef struct {
TimerHandle_t timer;
} DesktopScenePinInputState;
static void desktop_scene_locked_light_red(bool value) {
NotificationApp* app = furi_record_open("notification");
if(value) {
notification_message(app, &sequence_set_only_red_255);
} else {
notification_message(app, &sequence_reset_red);
}
furi_record_close("notification");
}
static void
desktop_scene_pin_input_set_timer(Desktop* desktop, bool enable, TickType_t new_period) {
furi_assert(desktop);
DesktopScenePinInputState* state = (DesktopScenePinInputState*)scene_manager_get_scene_state(
desktop->scene_manager, DesktopScenePinInput);
furi_assert(state);
if(enable) {
xTimerChangePeriod(state->timer, new_period, portMAX_DELAY);
} else {
xTimerStop(state->timer, portMAX_DELAY);
}
}
static void desktop_scene_pin_input_back_callback(void* context) {
Desktop* desktop = (Desktop*)context;
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinInputEventBack);
}
static void desktop_scene_pin_input_done_callback(const PinCode* pin_code, void* context) {
Desktop* desktop = (Desktop*)context;
if(pins_are_equal(&desktop->settings.pin_code, pin_code)) {
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinInputEventUnlocked);
} else {
view_dispatcher_send_custom_event(
desktop->view_dispatcher, DesktopPinInputEventUnlockFailed);
}
}
static void desktop_scene_pin_input_timer_callback(TimerHandle_t timer) {
Desktop* desktop = pvTimerGetTimerID(timer);
view_dispatcher_send_custom_event(
desktop->view_dispatcher, DesktopPinInputEventResetWrongPinLabel);
}
void desktop_scene_pin_input_on_enter(void* context) {
Desktop* desktop = (Desktop*)context;
desktop_view_pin_input_set_context(desktop->pin_input_view, desktop);
desktop_view_pin_input_set_back_callback(
desktop->pin_input_view, desktop_scene_pin_input_back_callback);
desktop_view_pin_input_set_timeout_callback(
desktop->pin_input_view, desktop_scene_pin_input_back_callback);
desktop_view_pin_input_set_done_callback(
desktop->pin_input_view, desktop_scene_pin_input_done_callback);
DesktopScenePinInputState* state = furi_alloc(sizeof(DesktopScenePinInputState));
state->timer =
xTimerCreate(NULL, 10000, pdFALSE, desktop, desktop_scene_pin_input_timer_callback);
scene_manager_set_scene_state(desktop->scene_manager, DesktopScenePinInput, (uint32_t)state);
desktop_view_pin_input_hide_pin(desktop->pin_input_view, true);
desktop_view_pin_input_set_label_button(desktop->pin_input_view, "OK");
desktop_view_pin_input_set_label_secondary(desktop->pin_input_view, 44, 25, "Enter PIN:");
desktop_view_pin_input_set_pin_position(desktop->pin_input_view, 64, 37);
desktop_view_pin_input_reset_pin(desktop->pin_input_view);
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdPinInput);
}
bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) {
Desktop* desktop = (Desktop*)context;
bool consumed = false;
uint32_t pin_fails = 0;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DesktopPinInputEventUnlockFailed:
pin_fails = furi_hal_rtc_get_pin_fails();
pin_fails++;
furi_hal_rtc_set_pin_fails(pin_fails);
uint32_t pin_timeout = desktop_helpers_get_pin_fail_timeout(pin_fails);
if(pin_timeout > 0) {
desktop_helpers_emit_error_notification();
scene_manager_set_scene_state(
desktop->scene_manager, DesktopScenePinTimeout, pin_timeout);
scene_manager_next_scene(desktop->scene_manager, DesktopScenePinTimeout);
} else {
desktop_scene_locked_light_red(true);
desktop_view_pin_input_set_label_primary(desktop->pin_input_view, 0, 0, NULL);
desktop_view_pin_input_set_label_secondary(
desktop->pin_input_view, 25, 25, "Wrong PIN try again:");
desktop_scene_pin_input_set_timer(desktop, true, WRONG_PIN_HEADER_TIMEOUT);
desktop_view_pin_input_reset_pin(desktop->pin_input_view);
}
consumed = true;
break;
case DesktopPinInputEventResetWrongPinLabel:
desktop_scene_locked_light_red(false);
desktop_view_pin_input_set_label_primary(desktop->pin_input_view, 0, 0, NULL);
desktop_view_pin_input_set_label_secondary(
desktop->pin_input_view, 44, 25, "Enter PIN:");
consumed = true;
break;
case DesktopPinInputEventUnlocked:
desktop_view_locked_unlock(desktop->locked_view);
furi_hal_rtc_set_pin_fails(0);
desktop_helpers_unlock_system(desktop);
scene_manager_search_and_switch_to_previous_scene(
desktop->scene_manager, DesktopSceneMain);
consumed = true;
break;
case DesktopPinInputEventBack:
scene_manager_search_and_switch_to_previous_scene(
desktop->scene_manager, DesktopSceneLocked);
consumed = true;
break;
}
}
return consumed;
}
void desktop_scene_pin_input_on_exit(void* context) {
Desktop* desktop = (Desktop*)context;
desktop_scene_locked_light_red(false);
DesktopScenePinInputState* state = (DesktopScenePinInputState*)scene_manager_get_scene_state(
desktop->scene_manager, DesktopScenePinInput);
xTimerStop(state->timer, portMAX_DELAY);
while(xTimerIsTimerActive(state->timer)) {
delay(1);
}
xTimerDelete(state->timer, portMAX_DELAY);
free(state);
}

View File

@ -0,0 +1,46 @@
#include <furi.h>
#include <FreeRTOS.h>
#include <portmacro.h>
#include <timer.h>
#include <gui/scene_manager.h>
#include "../desktop_i.h"
#include "../views/desktop_view_pin_timeout.h"
#include "desktop_scene.h"
#include "desktop_scene_i.h"
static void desktop_scene_pin_timeout_callback(void* context) {
Desktop* desktop = (Desktop*)context;
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinTimeoutExit);
}
void desktop_scene_pin_timeout_on_enter(void* context) {
Desktop* desktop = (Desktop*)context;
uint32_t timeout =
scene_manager_get_scene_state(desktop->scene_manager, DesktopScenePinTimeout);
desktop_view_pin_timeout_start(desktop->pin_timeout_view, timeout);
desktop_view_pin_timeout_set_callback(
desktop->pin_timeout_view, desktop_scene_pin_timeout_callback, desktop);
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdPinTimeout);
}
bool desktop_scene_pin_timeout_on_event(void* context, SceneManagerEvent event) {
Desktop* desktop = (Desktop*)context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DesktopPinTimeoutExit:
scene_manager_previous_scene(desktop->scene_manager);
consumed = true;
break;
}
}
return consumed;
}
void desktop_scene_pin_timeout_on_exit(void* context) {
}

View File

@ -1,50 +0,0 @@
#include "../desktop_i.h"
#define SCENE_EXIT_EVENT (0U)
void desktop_scene_ok_callback(void* context) {
Desktop* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EXIT_EVENT);
}
void desktop_scene_pinsetup_on_enter(void* context) {
Desktop* app = context;
CodeInput* code_input = app->code_input;
code_input_set_result_callback(
code_input,
desktop_scene_ok_callback,
NULL,
app,
app->settings.pincode.data,
&app->settings.pincode.length,
true);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopViewPinSetup);
}
bool desktop_scene_pinsetup_on_event(void* context, SceneManagerEvent event) {
Desktop* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case SCENE_EXIT_EVENT:
scene_manager_previous_scene(app->scene_manager);
consumed = true;
break;
default:
consumed = true;
break;
}
}
return consumed;
}
void desktop_scene_pinsetup_on_exit(void* context) {
Desktop* app = context;
SAVE_DESKTOP_SETTINGS(&app->settings);
code_input_set_result_callback(app->code_input, NULL, NULL, NULL, NULL, NULL, 0);
code_input_set_header_text(app->code_input, "");
}

View File

@ -6,24 +6,35 @@ typedef enum {
DesktopMainEventOpenFavorite,
DesktopMainEventOpenMenu,
DesktopMainEventOpenDebug,
DesktopMainEventUpdate,
DesktopMainEventUnlocked,
DesktopMainEventRightShort,
DesktopMainEventCheckAnimation,
DesktopMainEventNewIdleAnimation,
DesktopMainEventInteractAnimation,
DesktopMainEventBeforeAppStarted,
DesktopMainEventAfterAppFinished,
DesktopLockedEventUnlock,
DesktopLockedEventCheckAnimation,
DesktopLockedEventMax,
DesktopLockedEventUnlocked,
DesktopLockedEventUpdate,
DesktopLockedEventShowPinInput,
DesktopPinInputEventResetWrongPinLabel,
DesktopPinInputEventUnlocked,
DesktopPinInputEventUnlockFailed,
DesktopPinInputEventBack,
DesktopPinTimeoutExit,
DesktopDebugEventDeed,
DesktopDebugEventWrongDeed,
DesktopDebugEventSaveState,
DesktopDebugEventExit,
DesktopFirstStartCompleted,
DesktopFirstStartPoweroff,
DesktopLockMenuEventLock,
DesktopLockMenuEventPinLock,
DesktopLockMenuEventExit,
DesktopAnimationEventCheckAnimation,
DesktopAnimationEventNewIdleAnimation,
DesktopAnimationEventInteractAnimation,
} DesktopEvent;

View File

@ -1,247 +0,0 @@
#include "desktop/desktop_settings/desktop_settings.h"
#include "furi/check.h"
#include "gui/view.h"
#include "portmacro.h"
#include <furi.h>
#include <gui/gui_i.h>
#include <gui/elements.h>
#include "../desktop_i.h"
#include "desktop_locked.h"
#include <stdint.h>
#define DOOR_MOVING_INTERVAL_MS (1000 / 16)
#define UNLOCKED_HINT_TIMEOUT_MS (2000)
struct DesktopLockedView {
View* view;
DesktopLockedViewCallback callback;
void* context;
TimerHandle_t timer;
uint8_t lock_count;
uint32_t lock_lastpress;
PinCode pincode;
PinCode pincode_input;
};
typedef struct {
uint32_t hint_icon_expire_at;
bool unlocked_hint;
bool locked;
bool pin_locked;
int8_t door_left_x;
int8_t door_right_x;
bool animation_seq_end;
} DesktopLockedViewModel;
static void desktop_locked_unlock(DesktopLockedView* locked_view);
void desktop_locked_set_callback(
DesktopLockedView* locked_view,
DesktopLockedViewCallback callback,
void* context) {
furi_assert(locked_view);
furi_assert(callback);
locked_view->callback = callback;
locked_view->context = context;
}
void locked_view_timer_callback(TimerHandle_t timer) {
DesktopLockedView* locked_view = pvTimerGetTimerID(timer);
locked_view->callback(DesktopMainEventUpdate, locked_view->context);
}
static void desktop_locked_update_hint_icon_timeout(DesktopLockedView* locked_view) {
DesktopLockedViewModel* model = view_get_model(locked_view->view);
model->hint_icon_expire_at = osKernelGetTickCount() + osKernelGetTickFreq();
view_commit_model(locked_view->view, true);
}
static void desktop_locked_reset_door_pos(DesktopLockedView* locked_view) {
DesktopLockedViewModel* model = view_get_model(locked_view->view);
model->animation_seq_end = false;
model->door_left_x = DOOR_L_POS;
model->door_right_x = DOOR_R_POS;
view_commit_model(locked_view->view, true);
}
void desktop_locked_update(DesktopLockedView* locked_view) {
bool stop_timer = false;
DesktopLockedViewModel* model = view_get_model(locked_view->view);
if(model->locked) {
if(model->door_left_x != DOOR_L_POS_MAX) {
model->door_left_x = CLAMP(model->door_left_x + 5, DOOR_L_POS_MAX, DOOR_L_POS);
model->door_right_x = CLAMP(model->door_right_x - 5, DOOR_R_POS, DOOR_R_POS_MIN);
} else {
model->animation_seq_end = true;
}
stop_timer = model->animation_seq_end;
} else {
model->unlocked_hint = false;
stop_timer = true;
}
view_commit_model(locked_view->view, true);
if(stop_timer) {
xTimerStop(locked_view->timer, portMAX_DELAY);
}
}
void desktop_locked_draw(Canvas* canvas, void* model) {
DesktopLockedViewModel* m = model;
uint32_t now = osKernelGetTickCount();
canvas_set_color(canvas, ColorBlack);
if(m->locked) {
if(!m->animation_seq_end) {
canvas_draw_icon(canvas, m->door_left_x, 0 + STATUS_BAR_Y_SHIFT, &I_DoorLeft_70x55);
canvas_draw_icon(canvas, m->door_right_x, 0 + STATUS_BAR_Y_SHIFT, &I_DoorRight_70x55);
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Locked");
} else if((now < m->hint_icon_expire_at) && !m->pin_locked) {
canvas_set_font(canvas, FontSecondary);
canvas_draw_icon(canvas, 13, 2 + STATUS_BAR_Y_SHIFT, &I_LockPopup_100x49);
elements_multiline_text(canvas, 65, 20 + STATUS_BAR_Y_SHIFT, "To unlock\npress:");
}
} else {
if(m->unlocked_hint) {
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Unlocked");
}
}
}
View* desktop_locked_get_view(DesktopLockedView* locked_view) {
furi_assert(locked_view);
return locked_view->view;
}
bool desktop_locked_input(InputEvent* event, void* context) {
furi_assert(event);
furi_assert(context);
DesktopLockedView* locked_view = context;
bool locked = false;
bool locked_with_pin = false;
uint32_t press_time = xTaskGetTickCount();
{
DesktopLockedViewModel* model = view_get_model(locked_view->view);
bool changed = false;
locked = model->locked;
locked_with_pin = model->pin_locked;
if(!locked && model->unlocked_hint && event->type == InputTypePress) {
model->unlocked_hint = false;
changed = true;
}
view_commit_model(locked_view->view, changed);
}
if(!locked || (event->type != InputTypeShort)) {
return locked;
}
if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT) {
locked_view->lock_lastpress = press_time;
locked_view->lock_count = 0;
locked_view->pincode_input.length = 0;
}
if(locked_with_pin) {
locked_view->pincode_input.length = code_input_push(
locked_view->pincode_input.data, locked_view->pincode_input.length, event->key);
bool match = code_input_compare(
locked_view->pincode_input.data,
locked_view->pincode_input.length,
locked_view->pincode.data,
locked_view->pincode.length);
if(match) {
desktop_locked_unlock(locked_view);
}
} else {
if(event->key == InputKeyBack) {
locked_view->lock_lastpress = press_time;
locked_view->lock_count++;
if(locked_view->lock_count == UNLOCK_CNT) {
desktop_locked_unlock(locked_view);
}
} else {
desktop_locked_update_hint_icon_timeout(locked_view);
locked_view->lock_count = 0;
}
}
locked_view->lock_lastpress = press_time;
return locked;
}
DesktopLockedView* desktop_locked_alloc() {
DesktopLockedView* locked_view = furi_alloc(sizeof(DesktopLockedView));
locked_view->view = view_alloc();
locked_view->timer =
xTimerCreate("Locked view", 1000 / 16, pdTRUE, locked_view, locked_view_timer_callback);
view_allocate_model(locked_view->view, ViewModelTypeLocking, sizeof(DesktopLockedViewModel));
view_set_context(locked_view->view, locked_view);
view_set_draw_callback(locked_view->view, (ViewDrawCallback)desktop_locked_draw);
view_set_input_callback(locked_view->view, desktop_locked_input);
return locked_view;
}
void desktop_locked_free(DesktopLockedView* locked_view) {
furi_assert(locked_view);
osTimerDelete(locked_view->timer);
view_free(locked_view->view);
free(locked_view);
}
void desktop_locked_lock(DesktopLockedView* locked_view) {
locked_view->pincode.length = 0;
DesktopLockedViewModel* model = view_get_model(locked_view->view);
model->locked = true;
model->pin_locked = false;
view_commit_model(locked_view->view, true);
desktop_locked_reset_door_pos(locked_view);
xTimerChangePeriod(locked_view->timer, DOOR_MOVING_INTERVAL_MS, portMAX_DELAY);
Gui* gui = furi_record_open("gui");
gui_set_lockdown(gui, true);
furi_record_close("gui");
}
void desktop_locked_lock_pincode(DesktopLockedView* locked_view, PinCode pincode) {
locked_view->pincode = pincode;
locked_view->pincode_input.length = 0;
DesktopLockedViewModel* model = view_get_model(locked_view->view);
model->locked = true;
model->pin_locked = true;
view_commit_model(locked_view->view, true);
desktop_locked_reset_door_pos(locked_view);
xTimerChangePeriod(locked_view->timer, DOOR_MOVING_INTERVAL_MS, portMAX_DELAY);
Gui* gui = furi_record_open("gui");
gui_set_lockdown(gui, true);
furi_record_close("gui");
}
static void desktop_locked_unlock(DesktopLockedView* locked_view) {
furi_assert(locked_view);
locked_view->lock_count = 0;
DesktopLockedViewModel* model = view_get_model(locked_view->view);
model->locked = false;
model->pin_locked = false;
model->unlocked_hint = true;
view_commit_model(locked_view->view, true);
locked_view->callback(DesktopMainEventUnlocked, locked_view->context);
xTimerChangePeriod(locked_view->timer, UNLOCKED_HINT_TIMEOUT_MS, portMAX_DELAY);
Gui* gui = furi_record_open("gui");
gui_set_lockdown(gui, false);
furi_record_close("gui");
}

View File

@ -1,36 +0,0 @@
#pragma once
#include <desktop/desktop_settings/desktop_settings.h>
#include <gui/view.h>
#include "desktop_events.h"
#define UNLOCK_RST_TIMEOUT 300
#define UNLOCK_CNT 3
#define DOOR_L_POS -57
#define DOOR_L_POS_MAX 0
#define DOOR_R_POS 115
#define DOOR_R_POS_MIN 60
typedef enum {
DesktopLockedWithPin,
DesktopLockedNoPin,
} DesktopLockedSceneState;
typedef struct DesktopLockedView DesktopLockedView;
typedef void (*DesktopLockedViewCallback)(DesktopEvent event, void* context);
void desktop_locked_set_callback(
DesktopLockedView* locked_view,
DesktopLockedViewCallback callback,
void* context);
void desktop_locked_update(DesktopLockedView* locked_view);
View* desktop_locked_get_view(DesktopLockedView* locked_view);
DesktopLockedView* desktop_locked_alloc();
void desktop_locked_free(DesktopLockedView* locked_view);
void desktop_locked_lock_pincode(DesktopLockedView* locked_view, PinCode pincode);
void desktop_locked_lock(DesktopLockedView* locked_view);

View File

@ -1,11 +1,11 @@
#include <toolbox/version.h>
#include <furi.h>
#include <furi_hal.h>
#include <dolphin/helpers/dolphin_state.h>
#include <dolphin/dolphin.h>
#include "../desktop_i.h"
#include "desktop_debug.h"
#include "dolphin/helpers/dolphin_state.h"
#include "dolphin/dolphin.h"
#include "desktop_view_debug.h"
void desktop_debug_set_callback(
DesktopDebugView* debug_view,

View File

@ -1,8 +1,9 @@
#include <furi.h>
#include <furi_hal.h>
#include <gui/elements.h>
#include "../desktop_i.h"
#include "desktop_first_start.h"
#include "desktop_view_first_start.h"
#define DESKTOP_FIRST_START_POWEROFF_SHORT 5000
#define DESKTOP_FIRST_START_POWEROFF_LONG (60 * 60 * 1000)

View File

@ -2,7 +2,7 @@
#include <gui/elements.h>
#include "../desktop_i.h"
#include "desktop_lock_menu.h"
#include "desktop_view_lock_menu.h"
#define LOCK_MENU_ITEMS_NB 3

View File

@ -0,0 +1,233 @@
#include <projdefs.h>
#include <stdint.h>
#include <furi.h>
#include <gui/elements.h>
#include <gui/icon.h>
#include <gui/view.h>
#include <portmacro.h>
#include "../desktop_settings/desktop_settings.h"
#include "../desktop_i.h"
#include "desktop_view_locked.h"
#define DOOR_MOVING_INTERVAL_MS (1000 / 16)
#define UNLOCKED_HINT_TIMEOUT_MS (2000)
#define DOOR_OFFSET_START -55
#define DOOR_OFFSET_END 0
#define DOOR_L_FINAL_POS 0
#define DOOR_R_FINAL_POS 60
#define UNLOCK_CNT 3
#define UNLOCK_RST_TIMEOUT 600
struct DesktopViewLocked {
View* view;
DesktopViewLockedCallback callback;
void* context;
TimerHandle_t timer;
uint8_t lock_count;
uint32_t lock_lastpress;
};
typedef struct {
uint32_t hint_icon_expire_at;
bool unlocked_hint;
bool locked;
bool pin_locked;
int8_t door_offset;
bool doors_closing;
} DesktopViewLockedModel;
void desktop_view_locked_set_callback(
DesktopViewLocked* locked_view,
DesktopViewLockedCallback callback,
void* context) {
furi_assert(locked_view);
furi_assert(callback);
locked_view->callback = callback;
locked_view->context = context;
}
static void locked_view_timer_callback(TimerHandle_t timer) {
DesktopViewLocked* locked_view = pvTimerGetTimerID(timer);
locked_view->callback(DesktopLockedEventUpdate, locked_view->context);
}
static void desktop_view_locked_doors_draw(Canvas* canvas, DesktopViewLockedModel* model) {
int8_t offset = model->door_offset;
uint8_t door_left_x = DOOR_L_FINAL_POS + offset;
uint8_t door_right_x = DOOR_R_FINAL_POS - offset;
uint8_t height = icon_get_height(&I_DoorLeft_70x55);
canvas_draw_icon(canvas, door_left_x, canvas_height(canvas) - height, &I_DoorLeft_70x55);
canvas_draw_icon(canvas, door_right_x, canvas_height(canvas) - height, &I_DoorRight_70x55);
}
static bool desktop_view_locked_doors_move(DesktopViewLockedModel* model) {
bool stop = false;
if(model->door_offset < DOOR_OFFSET_END) {
model->door_offset = CLAMP(model->door_offset + 5, DOOR_OFFSET_END, DOOR_OFFSET_START);
stop = true;
}
return stop;
}
static void desktop_view_locked_update_hint_icon_timeout(DesktopViewLocked* locked_view) {
DesktopViewLockedModel* model = view_get_model(locked_view->view);
model->hint_icon_expire_at = osKernelGetTickCount() + osKernelGetTickFreq();
view_commit_model(locked_view->view, true);
}
void desktop_view_locked_update(DesktopViewLocked* locked_view) {
bool stop_timer = false;
DesktopViewLockedModel* model = view_get_model(locked_view->view);
if(model->locked) {
model->doors_closing = desktop_view_locked_doors_move(model);
stop_timer = !model->doors_closing;
} else {
model->unlocked_hint = false;
stop_timer = true;
}
view_commit_model(locked_view->view, true);
if(stop_timer) {
xTimerStop(locked_view->timer, portMAX_DELAY);
}
}
static void desktop_view_locked_draw(Canvas* canvas, void* model) {
DesktopViewLockedModel* m = model;
uint32_t now = osKernelGetTickCount();
canvas_set_color(canvas, ColorBlack);
if(m->locked) {
if(m->doors_closing) {
desktop_view_locked_doors_draw(canvas, m);
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Locked");
} else if((now < m->hint_icon_expire_at) && !m->pin_locked) {
canvas_set_font(canvas, FontSecondary);
elements_bold_rounded_frame(canvas, 14, 2 + STATUS_BAR_Y_SHIFT, 99, 48);
elements_multiline_text(canvas, 65, 20 + STATUS_BAR_Y_SHIFT, "To unlock\npress:");
canvas_draw_icon(canvas, 65, 36 + STATUS_BAR_Y_SHIFT, &I_Back3_45x8);
canvas_draw_icon(canvas, 16, 7 + STATUS_BAR_Y_SHIFT, &I_WarningDolphin_45x42);
canvas_draw_dot(canvas, 17, 61);
}
} else {
if(m->unlocked_hint) {
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Unlocked");
}
}
}
View* desktop_view_locked_get_view(DesktopViewLocked* locked_view) {
furi_assert(locked_view);
return locked_view->view;
}
static bool desktop_view_locked_input(InputEvent* event, void* context) {
furi_assert(event);
furi_assert(context);
DesktopViewLocked* locked_view = context;
bool locked = false;
bool locked_with_pin = false;
bool doors_closing = false;
uint32_t press_time = xTaskGetTickCount();
{
DesktopViewLockedModel* model = view_get_model(locked_view->view);
bool changed = false;
locked = model->locked;
locked_with_pin = model->pin_locked;
doors_closing = model->doors_closing;
if(!locked && model->unlocked_hint && event->type == InputTypePress) {
model->unlocked_hint = false;
changed = true;
}
view_commit_model(locked_view->view, changed);
}
if(!locked || doors_closing || (event->type != InputTypeShort)) {
return locked;
}
if(locked_with_pin) {
locked_view->callback(DesktopLockedEventShowPinInput, locked_view->context);
} else {
if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT) {
locked_view->lock_lastpress = press_time;
locked_view->lock_count = 0;
}
desktop_view_locked_update_hint_icon_timeout(locked_view);
if(event->key == InputKeyBack) {
locked_view->lock_lastpress = press_time;
locked_view->lock_count++;
if(locked_view->lock_count == UNLOCK_CNT) {
desktop_view_locked_unlock(locked_view);
locked_view->callback(DesktopLockedEventUnlocked, locked_view->context);
}
} else {
locked_view->lock_count = 0;
}
locked_view->lock_lastpress = press_time;
}
return locked;
}
DesktopViewLocked* desktop_view_locked_alloc() {
DesktopViewLocked* locked_view = furi_alloc(sizeof(DesktopViewLocked));
locked_view->view = view_alloc();
locked_view->timer =
xTimerCreate(NULL, 1000 / 16, pdTRUE, locked_view, locked_view_timer_callback);
locked_view->view = view_alloc();
view_allocate_model(locked_view->view, ViewModelTypeLocking, sizeof(DesktopViewLockedModel));
view_set_context(locked_view->view, locked_view);
view_set_draw_callback(locked_view->view, desktop_view_locked_draw);
view_set_input_callback(locked_view->view, desktop_view_locked_input);
return locked_view;
}
void desktop_view_locked_free(DesktopViewLocked* locked_view) {
furi_assert(locked_view);
osTimerDelete(locked_view->timer);
view_free(locked_view->view);
free(locked_view);
}
void desktop_view_locked_close_doors(DesktopViewLocked* locked_view) {
DesktopViewLockedModel* model = view_get_model(locked_view->view);
model->doors_closing = true;
model->door_offset = DOOR_OFFSET_START;
view_commit_model(locked_view->view, true);
xTimerChangePeriod(locked_view->timer, pdMS_TO_TICKS(DOOR_MOVING_INTERVAL_MS), portMAX_DELAY);
}
void desktop_view_locked_lock(DesktopViewLocked* locked_view, bool pin_locked) {
DesktopViewLockedModel* model = view_get_model(locked_view->view);
model->locked = true;
model->pin_locked = pin_locked;
view_commit_model(locked_view->view, true);
}
void desktop_view_locked_unlock(DesktopViewLocked* locked_view) {
furi_assert(locked_view);
locked_view->lock_count = 0;
DesktopViewLockedModel* model = view_get_model(locked_view->view);
model->locked = false;
model->pin_locked = false;
model->unlocked_hint = true;
view_commit_model(locked_view->view, true);
xTimerChangePeriod(locked_view->timer, pdMS_TO_TICKS(UNLOCKED_HINT_TIMEOUT_MS), portMAX_DELAY);
}

View File

@ -0,0 +1,21 @@
#pragma once
#include "../desktop_settings/desktop_settings.h"
#include "../views/desktop_events.h"
#include <gui/view.h>
typedef struct DesktopViewLocked DesktopViewLocked;
typedef void (*DesktopViewLockedCallback)(DesktopEvent event, void* context);
void desktop_view_locked_set_callback(
DesktopViewLocked* locked_view,
DesktopViewLockedCallback callback,
void* context);
void desktop_view_locked_update(DesktopViewLocked* locked_view);
View* desktop_view_locked_get_view(DesktopViewLocked* locked_view);
DesktopViewLocked* desktop_view_locked_alloc();
void desktop_view_locked_free(DesktopViewLocked* locked_view);
void desktop_view_locked_lock(DesktopViewLocked* locked_view, bool pin_locked);
void desktop_view_locked_unlock(DesktopViewLocked* locked_view);
void desktop_view_locked_close_doors(DesktopViewLocked* locked_view);

View File

@ -7,7 +7,7 @@
#include <dolphin/dolphin.h>
#include "../desktop_i.h"
#include "desktop_main.h"
#include "desktop_view_main.h"
struct DesktopMainView {
View* view;

View File

@ -0,0 +1,340 @@
#include <gui/canvas.h>
#include <furi.h>
#include <gui/view.h>
#include <gui/elements.h>
#include <stdint.h>
#include <portmacro.h>
#include "desktop_view_pin_input.h"
#include "../desktop_settings/desktop_settings.h"
#define NO_ACTIVITY_TIMEOUT 15000
#define PIN_CELL_WIDTH 13
#define DEFAULT_PIN_X 64
#define DEFAULT_PIN_Y 32
struct DesktopViewPinInput {
View* view;
DesktopViewPinInputCallback back_callback;
DesktopViewPinInputCallback timeout_callback;
DesktopViewPinInputDoneCallback done_callback;
void* context;
TimerHandle_t timer;
};
typedef struct {
PinCode pin;
bool pin_hidden;
bool locked_input;
uint8_t pin_x;
uint8_t pin_y;
const char* primary_str;
uint8_t primary_str_x;
uint8_t primary_str_y;
const char* secondary_str;
uint8_t secondary_str_x;
uint8_t secondary_str_y;
const char* button_label;
} DesktopViewPinInputModel;
static bool desktop_view_pin_input_input(InputEvent* event, void* context) {
furi_assert(event);
furi_assert(context);
DesktopViewPinInput* pin_input = context;
DesktopViewPinInputModel* model = view_get_model(pin_input->view);
bool call_back_callback = false;
bool call_done_callback = false;
PinCode pin_code = {0};
if(event->type == InputTypeShort) {
switch(event->key) {
case InputKeyRight:
case InputKeyLeft:
case InputKeyDown:
case InputKeyUp:
if(!model->locked_input) {
if(model->pin.length < MAX_PIN_SIZE) {
model->pin.data[model->pin.length++] = event->key;
}
}
break;
case InputKeyOk:
if(model->pin.length >= MIN_PIN_SIZE) {
call_done_callback = true;
pin_code = model->pin;
}
break;
case InputKeyBack:
if(!model->locked_input) {
if(model->pin.length > 0) {
model->pin.length = 0;
} else {
call_back_callback = true;
}
}
break;
default:
furi_assert(0);
break;
}
}
view_commit_model(pin_input->view, true);
if(call_done_callback && pin_input->done_callback) {
pin_input->done_callback(&pin_code, pin_input->context);
} else if(call_back_callback && pin_input->back_callback) {
pin_input->back_callback(pin_input->context);
}
xTimerStart(pin_input->timer, 0);
return true;
}
static void desktop_view_pin_input_draw_cells(Canvas* canvas, DesktopViewPinInputModel* model) {
furi_assert(canvas);
furi_assert(model);
uint8_t draw_pin_size = MAX(4, model->pin.length + 1);
if(model->locked_input || (model->pin.length == MAX_PIN_SIZE)) {
draw_pin_size = model->pin.length;
}
uint8_t x = model->pin_x - (draw_pin_size * (PIN_CELL_WIDTH - 1)) / 2;
uint8_t y = model->pin_y - (PIN_CELL_WIDTH / 2);
for(int i = 0; i < draw_pin_size; ++i) {
canvas_draw_frame(canvas, x, y, PIN_CELL_WIDTH, PIN_CELL_WIDTH);
if(i < model->pin.length) {
if(model->pin_hidden) {
canvas_draw_icon(canvas, x + 3, y + 3, &I_Pin_star_7x7);
} else {
switch(model->pin.data[i]) {
case InputKeyDown:
canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_down_7x9);
break;
case InputKeyUp:
canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_up7x9);
break;
case InputKeyLeft:
canvas_draw_icon(canvas, x + 2, y + 3, &I_Pin_arrow_left_9x7);
break;
case InputKeyRight:
canvas_draw_icon(canvas, x + 2, y + 3, &I_Pin_arrow_right_9x7);
break;
default:
furi_assert(0);
break;
}
}
} else if(i == model->pin.length) {
canvas_draw_icon(canvas, x + 4, y + PIN_CELL_WIDTH + 1, &I_Pin_pointer_5x3);
}
x += PIN_CELL_WIDTH - 1;
}
}
static void desktop_view_pin_input_draw(Canvas* canvas, void* context) {
furi_assert(canvas);
furi_assert(context);
canvas_set_font(canvas, FontSecondary);
DesktopViewPinInputModel* model = context;
desktop_view_pin_input_draw_cells(canvas, model);
if((model->pin.length > 0) && !model->locked_input) {
canvas_draw_icon(canvas, 4, 53, &I_Pin_back_full_40x8);
}
if(model->button_label && ((model->pin.length >= MIN_PIN_SIZE) || model->locked_input)) {
elements_button_center(canvas, model->button_label);
}
if(model->primary_str) {
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, model->primary_str_x, model->primary_str_y, model->primary_str);
canvas_set_font(canvas, FontSecondary);
}
if(model->secondary_str) {
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(
canvas, model->secondary_str_x, model->secondary_str_y, model->secondary_str);
}
}
void desktop_view_pin_input_timer_callback(TimerHandle_t timer) {
DesktopViewPinInput* pin_input = pvTimerGetTimerID(timer);
if(pin_input->timeout_callback) {
pin_input->timeout_callback(pin_input->context);
}
}
static void desktop_view_pin_input_enter(void* context) {
DesktopViewPinInput* pin_input = context;
xTimerStart(pin_input->timer, portMAX_DELAY);
}
static void desktop_view_pin_input_exit(void* context) {
DesktopViewPinInput* pin_input = context;
xTimerStop(pin_input->timer, portMAX_DELAY);
}
DesktopViewPinInput* desktop_view_pin_input_alloc(void) {
DesktopViewPinInput* pin_input = furi_alloc(sizeof(DesktopViewPinInput));
pin_input->view = view_alloc();
view_allocate_model(pin_input->view, ViewModelTypeLocking, sizeof(DesktopViewPinInputModel));
view_set_context(pin_input->view, pin_input);
view_set_draw_callback(pin_input->view, desktop_view_pin_input_draw);
view_set_input_callback(pin_input->view, desktop_view_pin_input_input);
pin_input->timer = xTimerCreate(
NULL,
pdMS_TO_TICKS(NO_ACTIVITY_TIMEOUT),
pdFALSE,
pin_input,
desktop_view_pin_input_timer_callback);
view_set_enter_callback(pin_input->view, desktop_view_pin_input_enter);
view_set_exit_callback(pin_input->view, desktop_view_pin_input_exit);
DesktopViewPinInputModel* model = view_get_model(pin_input->view);
model->pin_x = DEFAULT_PIN_X;
model->pin_y = DEFAULT_PIN_Y;
model->pin.length = 0;
view_commit_model(pin_input->view, false);
return pin_input;
}
void desktop_view_pin_input_free(DesktopViewPinInput* pin_input) {
furi_assert(pin_input);
xTimerStop(pin_input->timer, portMAX_DELAY);
while(xTimerIsTimerActive(pin_input->timer)) {
delay(1);
}
xTimerDelete(pin_input->timer, portMAX_DELAY);
view_free(pin_input->view);
free(pin_input);
}
void desktop_view_pin_input_lock_input(DesktopViewPinInput* pin_input) {
furi_assert(pin_input);
DesktopViewPinInputModel* model = view_get_model(pin_input->view);
model->locked_input = true;
view_commit_model(pin_input->view, true);
}
void desktop_view_pin_input_unlock_input(DesktopViewPinInput* pin_input) {
furi_assert(pin_input);
DesktopViewPinInputModel* model = view_get_model(pin_input->view);
model->locked_input = false;
view_commit_model(pin_input->view, true);
}
void desktop_view_pin_input_set_pin(DesktopViewPinInput* pin_input, const PinCode* pin) {
furi_assert(pin_input);
furi_assert(pin);
DesktopViewPinInputModel* model = view_get_model(pin_input->view);
model->pin = *pin;
view_commit_model(pin_input->view, true);
}
void desktop_view_pin_input_reset_pin(DesktopViewPinInput* pin_input) {
furi_assert(pin_input);
DesktopViewPinInputModel* model = view_get_model(pin_input->view);
model->pin.length = 0;
view_commit_model(pin_input->view, true);
}
void desktop_view_pin_input_hide_pin(DesktopViewPinInput* pin_input, bool pin_hidden) {
furi_assert(pin_input);
DesktopViewPinInputModel* model = view_get_model(pin_input->view);
model->pin_hidden = pin_hidden;
view_commit_model(pin_input->view, true);
}
void desktop_view_pin_input_set_label_button(DesktopViewPinInput* pin_input, const char* label) {
furi_assert(pin_input);
DesktopViewPinInputModel* model = view_get_model(pin_input->view);
model->button_label = label;
view_commit_model(pin_input->view, true);
}
void desktop_view_pin_input_set_label_primary(
DesktopViewPinInput* pin_input,
uint8_t x,
uint8_t y,
const char* label) {
furi_assert(pin_input);
DesktopViewPinInputModel* model = view_get_model(pin_input->view);
model->primary_str = label;
model->primary_str_x = x;
model->primary_str_y = y;
view_commit_model(pin_input->view, true);
}
void desktop_view_pin_input_set_label_secondary(
DesktopViewPinInput* pin_input,
uint8_t x,
uint8_t y,
const char* label) {
furi_assert(pin_input);
DesktopViewPinInputModel* model = view_get_model(pin_input->view);
model->secondary_str = label;
model->secondary_str_x = x;
model->secondary_str_y = y;
view_commit_model(pin_input->view, true);
}
void desktop_view_pin_input_set_pin_position(DesktopViewPinInput* pin_input, uint8_t x, uint8_t y) {
furi_assert(pin_input);
DesktopViewPinInputModel* model = view_get_model(pin_input->view);
model->pin_x = x;
model->pin_y = y;
view_commit_model(pin_input->view, true);
}
void desktop_view_pin_input_set_context(DesktopViewPinInput* pin_input, void* context) {
furi_assert(pin_input);
pin_input->context = context;
}
void desktop_view_pin_input_set_timeout_callback(
DesktopViewPinInput* pin_input,
DesktopViewPinInputCallback callback) {
furi_assert(pin_input);
pin_input->timeout_callback = callback;
}
void desktop_view_pin_input_set_back_callback(
DesktopViewPinInput* pin_input,
DesktopViewPinInputCallback callback) {
furi_assert(pin_input);
pin_input->back_callback = callback;
}
void desktop_view_pin_input_set_done_callback(
DesktopViewPinInput* pin_input,
DesktopViewPinInputDoneCallback callback) {
furi_assert(pin_input);
pin_input->done_callback = callback;
}
View* desktop_view_pin_input_get_view(DesktopViewPinInput* pin_input) {
furi_assert(pin_input);
return pin_input->view;
}

View File

@ -0,0 +1,40 @@
#pragma once
#include <gui/view.h>
#include "desktop/desktop_settings/desktop_settings.h"
typedef void (*DesktopViewPinInputCallback)(void*);
typedef void (*DesktopViewPinInputDoneCallback)(const PinCode* pin_code, void*);
typedef struct DesktopViewPinInput DesktopViewPinInput;
DesktopViewPinInput* desktop_view_pin_input_alloc(void);
void desktop_view_pin_input_free(DesktopViewPinInput*);
void desktop_view_pin_input_set_pin(DesktopViewPinInput* pin_input, const PinCode* pin);
void desktop_view_pin_input_reset_pin(DesktopViewPinInput* pin_input);
void desktop_view_pin_input_hide_pin(DesktopViewPinInput* pin_input, bool pin_hidden);
void desktop_view_pin_input_set_label_button(DesktopViewPinInput* pin_input, const char* label);
void desktop_view_pin_input_set_label_primary(
DesktopViewPinInput* pin_input,
uint8_t x,
uint8_t y,
const char* label);
void desktop_view_pin_input_set_label_secondary(
DesktopViewPinInput* pin_input,
uint8_t x,
uint8_t y,
const char* label);
void desktop_view_pin_input_set_pin_position(DesktopViewPinInput* pin_input, uint8_t x, uint8_t y);
View* desktop_view_pin_input_get_view(DesktopViewPinInput*);
void desktop_view_pin_input_set_done_callback(
DesktopViewPinInput* pin_input,
DesktopViewPinInputDoneCallback callback);
void desktop_view_pin_input_set_back_callback(
DesktopViewPinInput* pin_input,
DesktopViewPinInputCallback callback);
void desktop_view_pin_input_set_timeout_callback(
DesktopViewPinInput* pin_input,
DesktopViewPinInputCallback callback);
void desktop_view_pin_input_set_context(DesktopViewPinInput* pin_input, void* context);
void desktop_view_pin_input_lock_input(DesktopViewPinInput* pin_input);
void desktop_view_pin_input_unlock_input(DesktopViewPinInput* pin_input);

View File

@ -0,0 +1,80 @@
#include <furi.h>
#include <furi_hal.h>
#include <gui/elements.h>
#include <gui/canvas.h>
#include <toolbox/version.h>
#include <assets_icons.h>
#include <dolphin/helpers/dolphin_state.h>
#include <dolphin/dolphin.h>
#include "../desktop_i.h"
#include "desktop_view_pin_setup_done.h"
struct DesktopViewPinSetupDone {
View* view;
DesktopViewPinSetupDoneDoneCallback callback;
void* context;
};
static void desktop_view_pin_done_draw(Canvas* canvas, void* model) {
furi_assert(canvas);
furi_assert(model);
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(
canvas, 64, 0, AlignCenter, AlignTop, "Prepare to use\narrows as\nPIN symbols");
canvas_set_font(canvas, FontSecondary);
elements_multiline_text(canvas, 58, 24, "Prepare to use\narrows as\nPIN symbols");
canvas_draw_icon(canvas, 16, 18, &I_Pin_attention_dpad_29x29);
elements_button_right(canvas, "Next");
}
static bool desktop_view_pin_done_input(InputEvent* event, void* context) {
furi_assert(event);
furi_assert(context);
DesktopViewPinSetupDone* instance = context;
bool consumed = false;
if((event->key == InputKeyRight) && (event->type == InputTypeShort)) {
instance->callback(instance->context);
consumed = true;
}
return consumed;
}
void desktop_view_pin_done_set_callback(
DesktopViewPinSetupDone* instance,
DesktopViewPinSetupDoneDoneCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
DesktopViewPinSetupDone* desktop_view_pin_done_alloc() {
DesktopViewPinSetupDone* view = furi_alloc(sizeof(DesktopViewPinSetupDone));
view->view = view_alloc();
view_allocate_model(view->view, ViewModelTypeLockFree, 1);
view_set_context(view->view, view);
view_set_draw_callback(view->view, desktop_view_pin_done_draw);
view_set_input_callback(view->view, desktop_view_pin_done_input);
return view;
}
void desktop_view_pin_done_free(DesktopViewPinSetupDone* instance) {
furi_assert(instance);
view_free(instance->view);
free(instance);
}
View* desktop_view_pin_done_get_view(DesktopViewPinSetupDone* instance) {
furi_assert(instance);
return instance->view;
}

View File

@ -0,0 +1,15 @@
#pragma once
#include <gui/view.h>
typedef struct DesktopViewPinSetupDone DesktopViewPinSetupDone;
typedef void (*DesktopViewPinSetupDoneDoneCallback)(void*);
void desktop_view_pin_done_set_callback(
DesktopViewPinSetupDone* instance,
DesktopViewPinSetupDoneDoneCallback callback,
void* context);
DesktopViewPinSetupDone* desktop_view_pin_done_alloc();
void desktop_view_pin_done_free(DesktopViewPinSetupDone* instance);
View* desktop_view_pin_done_get_view(DesktopViewPinSetupDone* instance);

View File

@ -0,0 +1,109 @@
#include <furi.h>
#include <stdint.h>
#include <stdio.h>
#include <FreeRTOS.h>
#include <portmacro.h>
#include <projdefs.h>
#include <input/input.h>
#include <gui/canvas.h>
#include <gui/view.h>
#include "desktop_view_pin_timeout.h"
struct DesktopViewPinTimeout {
View* view;
TimerHandle_t timer;
DesktopViewPinTimeoutDoneCallback callback;
void* context;
};
typedef struct {
uint32_t time_left;
} DesktopViewPinTimeoutModel;
void desktop_view_pin_timeout_set_callback(
DesktopViewPinTimeout* instance,
DesktopViewPinTimeoutDoneCallback callback,
void* context) {
furi_assert(instance);
instance->callback = callback;
instance->context = context;
}
static void desktop_view_pin_timeout_timer_callback(TimerHandle_t timer) {
DesktopViewPinTimeout* instance = pvTimerGetTimerID(timer);
bool stop = false;
DesktopViewPinTimeoutModel* model = view_get_model(instance->view);
if(model->time_left > 0) {
--model->time_left;
} else {
stop = true;
}
view_commit_model(instance->view, true);
if(stop) {
xTimerStop(instance->timer, portMAX_DELAY);
instance->callback(instance->context);
}
}
static bool desktop_view_pin_timeout_input(InputEvent* event, void* context) {
return true;
}
static void desktop_view_pin_timeout_draw(Canvas* canvas, void* _model) {
furi_assert(canvas);
furi_assert(_model);
DesktopViewPinTimeoutModel* model = _model;
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 36, 31, "Wrong PIN!");
canvas_set_font(canvas, FontSecondary);
char str[30] = {0};
snprintf(str, sizeof(str), "Timeout: %lds", model->time_left);
canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignCenter, str);
}
void desktop_view_pin_timeout_free(DesktopViewPinTimeout* instance) {
view_free(instance->view);
xTimerDelete(instance->timer, portMAX_DELAY);
free(instance);
}
DesktopViewPinTimeout* desktop_view_pin_timeout_alloc(void) {
DesktopViewPinTimeout* instance = furi_alloc(sizeof(DesktopViewPinTimeout));
instance->timer = xTimerCreate(
NULL, pdMS_TO_TICKS(1000), pdTRUE, instance, desktop_view_pin_timeout_timer_callback);
instance->view = view_alloc();
view_allocate_model(instance->view, ViewModelTypeLockFree, sizeof(DesktopViewPinTimeoutModel));
view_set_context(instance->view, instance);
view_set_draw_callback(instance->view, desktop_view_pin_timeout_draw);
view_set_input_callback(instance->view, desktop_view_pin_timeout_input);
return instance;
}
void desktop_view_pin_timeout_start(DesktopViewPinTimeout* instance, uint32_t time_left) {
furi_assert(instance);
DesktopViewPinTimeoutModel* model = view_get_model(instance->view);
// no race - always called when timer is stopped
model->time_left = time_left;
view_commit_model(instance->view, true);
xTimerStart(instance->timer, portMAX_DELAY);
}
View* desktop_view_pin_timeout_get_view(DesktopViewPinTimeout* instance) {
furi_assert(instance);
return instance->view;
}

View File

@ -0,0 +1,16 @@
#pragma once
#include <stdint.h>
#include <gui/view.h>
typedef void (*DesktopViewPinTimeoutDoneCallback)(void*);
typedef struct DesktopViewPinTimeout DesktopViewPinTimeout;
void desktop_view_pin_timeout_set_callback(
DesktopViewPinTimeout* instance,
DesktopViewPinTimeoutDoneCallback callback,
void* context);
DesktopViewPinTimeout* desktop_view_pin_timeout_alloc(void);
void desktop_view_pin_timeout_free(DesktopViewPinTimeout*);
void desktop_view_pin_timeout_start(DesktopViewPinTimeout* instance, uint32_t time_left);
View* desktop_view_pin_timeout_get_view(DesktopViewPinTimeout* instance);

View File

@ -80,15 +80,11 @@ Dolphin* dolphin_alloc() {
dolphin->event_queue = osMessageQueueNew(8, sizeof(DolphinEvent), NULL);
dolphin->pubsub = furi_pubsub_alloc();
dolphin->butthurt_timer = xTimerCreate(
"Butthurt timer", HOURS_IN_TICKS(2 * 24), pdTRUE, dolphin, dolphin_butthurt_timer_callback);
NULL, HOURS_IN_TICKS(2 * 24), pdTRUE, dolphin, dolphin_butthurt_timer_callback);
dolphin->flush_timer =
xTimerCreate("Flush timer", 30 * 1000, pdFALSE, dolphin, dolphin_flush_timer_callback);
xTimerCreate(NULL, 30 * 1000, pdFALSE, dolphin, dolphin_flush_timer_callback);
dolphin->clear_limits_timer = xTimerCreate(
"Clear limits timer",
HOURS_IN_TICKS(24),
pdTRUE,
dolphin,
dolphin_clear_limits_timer_callback);
NULL, HOURS_IN_TICKS(24), pdTRUE, dolphin, dolphin_clear_limits_timer_callback);
return dolphin;
}

View File

@ -12,19 +12,20 @@
#define MOODS_TOTAL 3
#define BUTTHURT_MAX 3
static const Icon* portrait_happy[BUTTHURT_MAX] = {
static const Icon* const portrait_happy[BUTTHURT_MAX] = {
&I_passport_happy1_46x49,
&I_passport_happy2_46x49,
&I_passport_happy3_46x49};
static const Icon* portrait_ok[BUTTHURT_MAX] = {
static const Icon* const portrait_ok[BUTTHURT_MAX] = {
&I_passport_okay1_46x49,
&I_passport_okay2_46x49,
&I_passport_okay3_46x49};
static const Icon* portrait_bad[BUTTHURT_MAX] = {
static const Icon* const portrait_bad[BUTTHURT_MAX] = {
&I_passport_bad1_46x49,
&I_passport_bad2_46x49,
&I_passport_bad3_46x49};
static const Icon** portraits[MOODS_TOTAL] = {portrait_happy, portrait_ok, portrait_bad};
static const Icon* const* portraits[MOODS_TOTAL] = {portrait_happy, portrait_ok, portrait_bad};
static void input_callback(InputEvent* input, void* ctx) {
osSemaphoreId_t semaphore = ctx;

View File

@ -151,6 +151,6 @@ void gpio_usb_uart_update_state(GpioUsbUart* instance, UsbUartConfig* cfg, UsbUa
model->rx_active = (model->rx_cnt != st->rx_cnt);
model->tx_cnt = st->tx_cnt;
model->rx_cnt = st->rx_cnt;
return false;
return true;
});
}

View File

@ -1,4 +1,7 @@
#include "elements.h"
#include <assets_icons.h>
#include "furi_hal_resources.h"
#include <furi_hal.h>
#include "gui/canvas.h"
#include <gui/icon_i.h>
@ -337,6 +340,47 @@ void elements_slightly_rounded_box(
canvas_draw_rbox(canvas, x, y, width, height, 1);
}
void elements_bold_rounded_frame(
Canvas* canvas,
uint8_t x,
uint8_t y,
uint8_t width,
uint8_t height) {
furi_assert(canvas);
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, x + 2, y + 2, width - 3, height - 3);
canvas_set_color(canvas, ColorBlack);
canvas_draw_line(canvas, x + 3, y, x + width - 3, y);
canvas_draw_line(canvas, x + 2, y + 1, x + width - 2, y + 1);
canvas_draw_line(canvas, x, y + 3, x, y + height - 3);
canvas_draw_line(canvas, x + 1, y + 2, x + 1, y + height - 2);
canvas_draw_line(canvas, x + width, y + 3, x + width, y + height - 3);
canvas_draw_line(canvas, x + width - 1, y + 2, x + width - 1, y + height - 2);
canvas_draw_line(canvas, x + 3, y + height, x + width - 3, y + height);
canvas_draw_line(canvas, x + 2, y + height - 1, x + width - 2, y + height - 1);
canvas_draw_dot(canvas, x + 2, y + 2);
canvas_draw_dot(canvas, x + 3, y + 2);
canvas_draw_dot(canvas, x + 2, y + 3);
canvas_draw_dot(canvas, x + width - 2, y + 2);
canvas_draw_dot(canvas, x + width - 3, y + 2);
canvas_draw_dot(canvas, x + width - 2, y + 3);
canvas_draw_dot(canvas, x + 2, y + height - 2);
canvas_draw_dot(canvas, x + 3, y + height - 2);
canvas_draw_dot(canvas, x + 2, y + height - 3);
canvas_draw_dot(canvas, x + width - 2, y + height - 2);
canvas_draw_dot(canvas, x + width - 3, y + height - 2);
canvas_draw_dot(canvas, x + width - 2, y + height - 3);
}
void elements_bubble(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height) {
furi_assert(canvas);
canvas_draw_rframe(canvas, x + 4, y, width, height, 3);

View File

@ -150,6 +150,19 @@ void elements_slightly_rounded_box(
uint8_t width,
uint8_t height);
/** Draw bold rounded frame
*
* @param canvas Canvas instance
* @param x, y top left corner coordinates
* @param width, height size of frame
*/
void elements_bold_rounded_frame(
Canvas* canvas,
uint8_t x,
uint8_t y,
uint8_t width,
uint8_t height);
/** Draw bubble frame for text
*
* @param canvas Canvas instance

View File

@ -11,5 +11,5 @@ struct Icon {
const uint8_t height;
const uint8_t frame_count;
const uint8_t frame_rate;
const uint8_t** frames;
const uint8_t* const* frames;
};

View File

@ -1,478 +0,0 @@
#include "code_input.h"
#include <gui/elements.h>
#include <furi.h>
#define MAX_CODE_LEN 10
struct CodeInput {
View* view;
};
typedef enum {
CodeInputStateVerify,
CodeInputStateUpdate,
CodeInputStateTotal,
} CodeInputStateEnum;
typedef enum {
CodeInputFirst,
CodeInputSecond,
CodeInputTotal,
} CodeInputsEnum;
typedef struct {
uint8_t state;
uint8_t current;
bool ext_update;
uint8_t input_length[CodeInputTotal];
uint8_t local_buffer[CodeInputTotal][MAX_CODE_LEN];
CodeInputOkCallback ok_callback;
CodeInputFailCallback fail_callback;
void* callback_context;
const char* header;
uint8_t* ext_buffer;
uint8_t* ext_buffer_length;
} CodeInputModel;
static const Icon* keys_assets[] = {
[InputKeyUp] = &I_ButtonUp_7x4,
[InputKeyDown] = &I_ButtonDown_7x4,
[InputKeyRight] = &I_ButtonRight_4x7,
[InputKeyLeft] = &I_ButtonLeft_4x7,
};
/**
* @brief Compare buffers
*
* @param in Input buffer pointer
* @param len_in Input array length
* @param src Source buffer pointer
* @param len_src Source array length
*/
bool code_input_compare(uint8_t* in, size_t len_in, uint8_t* src, size_t len_src) {
bool result = false;
do {
result = (len_in && len_src);
if(!result) {
break;
}
result = (len_in == len_src);
if(!result) {
break;
}
for(size_t i = 0; i < len_in; i++) {
result = (in[i] == src[i]);
if(!result) {
break;
}
}
} while(false);
return result;
}
/**
* @brief Compare local buffers
*
* @param model
*/
static bool code_input_compare_local(CodeInputModel* model) {
uint8_t* source = model->local_buffer[CodeInputFirst];
size_t source_length = model->input_length[CodeInputFirst];
uint8_t* input = model->local_buffer[CodeInputSecond];
size_t input_length = model->input_length[CodeInputSecond];
return code_input_compare(input, input_length, source, source_length);
}
/**
* @brief Compare ext with local
*
* @param model
*/
static bool code_input_compare_ext(CodeInputModel* model) {
uint8_t* input = model->local_buffer[CodeInputFirst];
size_t input_length = model->input_length[CodeInputFirst];
uint8_t* source = model->ext_buffer;
size_t source_length = *model->ext_buffer_length;
return code_input_compare(input, input_length, source, source_length);
}
/**
* @brief Set ext buffer
*
* @param model
*/
static void code_input_set_ext(CodeInputModel* model) {
*model->ext_buffer_length = model->input_length[CodeInputFirst];
for(size_t i = 0; i <= model->input_length[CodeInputFirst]; i++) {
model->ext_buffer[i] = model->local_buffer[CodeInputFirst][i];
}
}
/**
* @brief Draw input sequence
*
* @param canvas
* @param buffer
* @param length
* @param x
* @param y
* @param active
*/
static void code_input_draw_sequence(
Canvas* canvas,
uint8_t* buffer,
uint8_t length,
uint8_t x,
uint8_t y,
bool active) {
uint8_t pos_x = x + 6;
uint8_t pos_y = y + 3;
if(active) canvas_draw_icon(canvas, x - 4, y + 5, &I_ButtonRightSmall_3x5);
elements_slightly_rounded_frame(canvas, x, y, 116, 15);
for(size_t i = 0; i < length; i++) {
// maybe symmetrical assets? :-/
uint8_t offset_y = buffer[i] < 2 ? 2 + (buffer[i] * 2) : 1;
canvas_draw_icon(canvas, pos_x, pos_y + offset_y, keys_assets[buffer[i]]);
pos_x += buffer[i] > 1 ? 9 : 11;
}
}
/**
* @brief Reset input count
*
* @param model
*/
static void code_input_reset_count(CodeInputModel* model) {
model->input_length[model->current] = 0;
}
/**
* @brief Call input callback
*
* @param model
*/
static void code_input_call_ok_callback(CodeInputModel* model) {
if(model->ok_callback != NULL) {
model->ok_callback(model->callback_context);
}
}
/**
* @brief Call changed callback
*
* @param model
*/
static void code_input_call_fail_callback(CodeInputModel* model) {
if(model->fail_callback != NULL) {
model->fail_callback(model->callback_context);
}
}
/**
* @brief Handle Back button
*
* @param model
*/
static bool code_input_handle_back(CodeInputModel* model) {
if(model->current && !model->input_length[model->current]) {
--model->current;
return true;
}
if(model->input_length[model->current]) {
code_input_reset_count(model);
return true;
}
code_input_call_fail_callback(model);
return false;
}
/**
* @brief Handle OK button
*
* @param model
*/
static void code_input_handle_ok(CodeInputModel* model) {
switch(model->state) {
case CodeInputStateVerify:
if(code_input_compare_ext(model)) {
if(model->ext_update) {
model->state = CodeInputStateUpdate;
} else {
code_input_call_ok_callback(model);
}
}
code_input_reset_count(model);
break;
case CodeInputStateUpdate:
if(!model->current && model->input_length[model->current]) {
model->current++;
} else {
if(code_input_compare_local(model)) {
if(model->ext_update) {
code_input_set_ext(model);
}
code_input_call_ok_callback(model);
} else {
code_input_reset_count(model);
}
}
break;
default:
break;
}
}
/**
* @brief Handle input
*
* @param model
* @param key
*/
size_t code_input_push(uint8_t* buffer, size_t length, InputKey key) {
buffer[length] = key;
length = CLAMP(length + 1, MAX_CODE_LEN, 0);
return length;
}
/**
* @brief Handle D-pad keys
*
* @param model
* @param key
*/
static void code_input_handle_dpad(CodeInputModel* model, InputKey key) {
uint8_t at = model->current;
size_t new_length = code_input_push(model->local_buffer[at], model->input_length[at], key);
model->input_length[at] = new_length;
}
/**
* @brief Draw callback
*
* @param canvas
* @param _model
*/
static void code_input_view_draw_callback(Canvas* canvas, void* _model) {
CodeInputModel* model = _model;
uint8_t y_offset = 0;
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
if(model->header && strlen(model->header)) {
canvas_draw_str(canvas, 2, 9, model->header);
} else {
y_offset = 4;
}
canvas_set_font(canvas, FontSecondary);
switch(model->state) {
case CodeInputStateVerify:
code_input_draw_sequence(
canvas,
model->local_buffer[CodeInputFirst],
model->input_length[CodeInputFirst],
6,
30 + y_offset,
true);
break;
case CodeInputStateUpdate:
code_input_draw_sequence(
canvas,
model->local_buffer[CodeInputFirst],
model->input_length[CodeInputFirst],
6,
14 + y_offset,
!model->current);
code_input_draw_sequence(
canvas,
model->local_buffer[CodeInputSecond],
model->input_length[CodeInputSecond],
6,
44 + y_offset,
model->current);
if(model->current) canvas_draw_str(canvas, 2, 39 + y_offset, "Repeat code");
break;
default:
break;
}
}
/**
* @brief Input callback
*
* @param event
* @param context
* @return true
* @return false
*/
static bool code_input_view_input_callback(InputEvent* event, void* context) {
CodeInput* code_input = context;
furi_assert(code_input);
bool consumed = false;
if(event->type == InputTypeShort || event->type == InputTypeRepeat) {
switch(event->key) {
case InputKeyBack:
with_view_model(
code_input->view, (CodeInputModel * model) {
consumed = code_input_handle_back(model);
return true;
});
break;
case InputKeyOk:
with_view_model(
code_input->view, (CodeInputModel * model) {
code_input_handle_ok(model);
return true;
});
consumed = true;
break;
default:
with_view_model(
code_input->view, (CodeInputModel * model) {
code_input_handle_dpad(model, event->key);
return true;
});
consumed = true;
break;
}
}
return consumed;
}
/**
* @brief Reset all input-related data in model
*
* @param model CodeInputModel
*/
static void code_input_reset_model_input_data(CodeInputModel* model) {
model->current = 0;
model->input_length[CodeInputFirst] = 0;
model->input_length[CodeInputSecond] = 0;
model->ext_buffer = NULL;
model->ext_update = false;
model->state = 0;
}
/**
* @brief Allocate and initialize code input. This code input is used to enter codes.
*
* @return CodeInput instance pointer
*/
CodeInput* code_input_alloc() {
CodeInput* code_input = furi_alloc(sizeof(CodeInput));
code_input->view = view_alloc();
view_set_context(code_input->view, code_input);
view_allocate_model(code_input->view, ViewModelTypeLocking, sizeof(CodeInputModel));
view_set_draw_callback(code_input->view, code_input_view_draw_callback);
view_set_input_callback(code_input->view, code_input_view_input_callback);
with_view_model(
code_input->view, (CodeInputModel * model) {
model->header = "";
model->ok_callback = NULL;
model->fail_callback = NULL;
model->callback_context = NULL;
code_input_reset_model_input_data(model);
return true;
});
return code_input;
}
/**
* @brief Deinitialize and free code input
*
* @param code_input Code input instance
*/
void code_input_free(CodeInput* code_input) {
furi_assert(code_input);
view_free(code_input->view);
free(code_input);
}
/**
* @brief Get code input view
*
* @param code_input code input instance
* @return View instance that can be used for embedding
*/
View* code_input_get_view(CodeInput* code_input) {
furi_assert(code_input);
return code_input->view;
}
/**
* @brief Set code input callbacks
*
* @param code_input code input instance
* @param ok_callback input callback fn
* @param fail_callback code match callback fn
* @param callback_context callback context
* @param buffer buffer
* @param buffer_length ptr to buffer length uint
* @param ext_update true to update buffer
*/
void code_input_set_result_callback(
CodeInput* code_input,
CodeInputOkCallback ok_callback,
CodeInputFailCallback fail_callback,
void* callback_context,
uint8_t* buffer,
uint8_t* buffer_length,
bool ext_update) {
with_view_model(
code_input->view, (CodeInputModel * model) {
code_input_reset_model_input_data(model);
model->ok_callback = ok_callback;
model->fail_callback = fail_callback;
model->callback_context = callback_context;
model->ext_buffer = buffer;
model->ext_buffer_length = buffer_length;
model->state = (*buffer_length == 0) ? 1 : 0;
model->ext_update = ext_update;
return true;
});
}
/**
* @brief Set code input header text
*
* @param code_input code input instance
* @param text text to be shown
*/
void code_input_set_header_text(CodeInput* code_input, const char* text) {
with_view_model(
code_input->view, (CodeInputModel * model) {
model->header = text;
return true;
});
}

View File

@ -1,91 +0,0 @@
/**
* @file code_input.h
* GUI: CodeInput keyboard view module API
*/
#pragma once
#include <gui/view.h>
#ifdef __cplusplus
extern "C" {
#endif
/** Code input anonymous structure */
typedef struct CodeInput CodeInput;
/** callback that is executed when entered code matches ext buffer */
typedef void (*CodeInputOkCallback)(void* context);
/** callback that is executed when entered code does not matches ext buffer */
typedef void (*CodeInputFailCallback)(void* context);
/** Allocate and initialize code input. This code input is used to enter codes.
*
* @return CodeInput instance pointer
*/
CodeInput* code_input_alloc();
/** Deinitialize and free code input
*
* @param code_input Code input instance
*/
void code_input_free(CodeInput* code_input);
/** Get code input view
*
* @param code_input code input instance
*
* @return View instance that can be used for embedding
*/
View* code_input_get_view(CodeInput* code_input);
/** Set code input result callback
*
* @param code_input code input instance
* @param ok_callback ok callback fn
* @param fail_callback fail callback fn
* @param callback_context callback context
* @param buffer buffer to use
* @param buffer_length buffer length
* @param update set true to update buffer
*/
void code_input_set_result_callback(
CodeInput* code_input,
CodeInputOkCallback ok_callback,
CodeInputFailCallback fail_callback,
void* callback_context,
uint8_t* buffer,
uint8_t* buffer_length,
bool update);
/** Set code input header text
*
* @param code_input code input instance
* @param text text to be shown
*/
void code_input_set_header_text(CodeInput* code_input, const char* text);
/** Compare two buffers
*
* @param in buffer to compare to source
* @param len_in length of input buffer
* @param src source buffer
* @param len_src length of insourceput buffer
* @return true if buffers match
*/
bool code_input_compare(uint8_t* in, size_t len_in, uint8_t* src, size_t len_src);
/** Push input into the end of array
*
* @param buffer buffer
* @param length length of buffer
* @param key input key
* @return new length of input buffer
*/
size_t code_input_push(uint8_t* buffer, size_t length, InputKey key);
#ifdef __cplusplus
}
#endif

View File

@ -253,27 +253,26 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e
}
// Deliver event
if(view_dispatcher->ongoing_input_view == view_dispatcher->current_view) {
bool is_consumed = false;
if(view_dispatcher->current_view) {
is_consumed = view_input(view_dispatcher->current_view, event);
}
if(!is_consumed && (event->type == InputTypeShort || event->type == InputTypeLong)) {
// TODO remove view navigation handlers
uint32_t view_id = VIEW_IGNORE;
if(event->key == InputKeyBack) {
view_id = view_previous(view_dispatcher->current_view);
if((view_id == VIEW_IGNORE) && (view_dispatcher->navigation_event_callback)) {
is_consumed =
view_dispatcher->navigation_event_callback(view_dispatcher->event_context);
if(!is_consumed) {
view_dispatcher_stop(view_dispatcher);
return;
}
}
}
if(!is_consumed) {
if(view_dispatcher->current_view &&
view_dispatcher->ongoing_input_view == view_dispatcher->current_view) {
// Dispatch input to current view
bool is_consumed = view_input(view_dispatcher->current_view, event);
// Navigate if input is not consumed
if(!is_consumed && (event->key == InputKeyBack) &&
(event->type == InputTypeShort || event->type == InputTypeLong)) {
// Navigate to previous
uint32_t view_id = view_previous(view_dispatcher->current_view);
if(view_id != VIEW_IGNORE) {
// Switch to returned view
view_dispatcher_switch_to_view(view_dispatcher, view_id);
} else if(view_dispatcher->navigation_event_callback) {
// Dispatch navigation event
if(!view_dispatcher->navigation_event_callback(view_dispatcher->event_context)) {
// TODO: should we allow view_dispatcher to stop without navigation_event_callback?
view_dispatcher_stop(view_dispatcher);
return;
}
}
}
} else if(view_dispatcher->ongoing_input_view && event->type == InputTypeRelease) {

View File

@ -59,9 +59,8 @@ KeyReader::~KeyReader() {
bool KeyReader::read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_size) {
bool readed = false;
switch(read_mode) {
case ReadMode::DALLAS:
__disable_irq();
if(read_mode == ReadMode::DALLAS) {
FURI_CRITICAL_ENTER();
if(onewire_master->search(data)) {
onewire_master->reset_search();
readed = true;
@ -69,9 +68,8 @@ bool KeyReader::read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_s
} else {
onewire_master->reset_search();
}
__enable_irq();
break;
case ReadMode::CYFRAL_METAKOM:
FURI_CRITICAL_EXIT();
} else if(read_mode == ReadMode::CYFRAL_METAKOM) {
if(cyfral_decoder.read(data, 2)) {
readed = true;
*key_type = iButtonKeyType::KeyCyfral;
@ -79,7 +77,6 @@ bool KeyReader::read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_s
readed = true;
*key_type = iButtonKeyType::KeyMetakom;
}
break;
}
return readed;
@ -88,10 +85,10 @@ bool KeyReader::read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_s
bool KeyReader::verify_key(iButtonKeyType key_type, const uint8_t* const data, uint8_t data_size) {
bool result = true;
switch(key_type) {
case iButtonKeyType::KeyDallas:
if(key_type == iButtonKeyType::KeyDallas) {
switch_to(ReadMode::DALLAS);
__disable_irq();
FURI_CRITICAL_ENTER();
if(onewire_master->reset()) {
onewire_master->write(DS1990::CMD_READ_ROM);
for(uint8_t i = 0; i < data_size; i++) {
@ -101,14 +98,11 @@ bool KeyReader::verify_key(iButtonKeyType key_type, const uint8_t* const data, u
}
} else {
result = false;
break;
}
__enable_irq();
break;
FURI_CRITICAL_EXIT();
default:
} else {
result = false;
break;
}
return result;

View File

@ -74,7 +74,7 @@ bool KeyWriter::compare_key_ds1990(iButtonKey* key) {
bool result = false;
if(key->get_key_type() == iButtonKeyType::KeyDallas) {
__disable_irq();
FURI_CRITICAL_ENTER();
bool presence = onewire_master->reset();
if(presence) {
@ -89,7 +89,7 @@ bool KeyWriter::compare_key_ds1990(iButtonKey* key) {
}
}
__enable_irq();
FURI_CRITICAL_EXIT();
}
return result;
@ -99,7 +99,7 @@ bool KeyWriter::write_1990_1(iButtonKey* key) {
bool result = false;
if(key->get_key_type() == iButtonKeyType::KeyDallas) {
__disable_irq();
FURI_CRITICAL_ENTER();
// unlock
onewire_master->reset();
@ -120,7 +120,7 @@ bool KeyWriter::write_1990_1(iButtonKey* key) {
onewire_master->write(RW1990_1::CMD_WRITE_RECORD_FLAG);
onewire_write_one_bit(1);
__enable_irq();
FURI_CRITICAL_EXIT();
if(compare_key_ds1990(key)) {
result = true;
@ -134,7 +134,7 @@ bool KeyWriter::write_1990_2(iButtonKey* key) {
bool result = false;
if(key->get_key_type() == iButtonKeyType::KeyDallas) {
__disable_irq();
FURI_CRITICAL_ENTER();
// unlock
onewire_master->reset();
@ -154,7 +154,7 @@ bool KeyWriter::write_1990_2(iButtonKey* key) {
onewire_master->write(RW1990_2::CMD_WRITE_RECORD_FLAG);
onewire_write_one_bit(0);
__enable_irq();
FURI_CRITICAL_EXIT();
if(compare_key_ds1990(key)) {
result = true;
@ -169,7 +169,7 @@ bool KeyWriter::write_TM2004(iButtonKey* key) {
bool result = true;
if(key->get_key_type() == iButtonKeyType::KeyDallas) {
__disable_irq();
FURI_CRITICAL_ENTER();
// write rom, addr is 0x0000
onewire_master->reset();
@ -204,7 +204,7 @@ bool KeyWriter::write_TM2004(iButtonKey* key) {
onewire_master->reset();
__enable_irq();
FURI_CRITICAL_EXIT();
} else {
result = false;
}
@ -216,7 +216,7 @@ bool KeyWriter::write_TM01(iButtonKey* key) {
/*bool result = true;
// TODO test and encoding
__disable_irq();
FURI_CRITICAL_ENTER();
// unlock
onewire_master->reset();
@ -240,13 +240,13 @@ bool KeyWriter::write_TM01(iButtonKey* key) {
onewire_master->write(TM01::CMD_WRITE_RECORD_FLAG);
onewire_write_one_bit(0, 10000);
__enable_irq();
FURI_CRITICAL_EXIT();
if(!compare_key_ds1990(key)) {
result = false;
}
__disable_irq();
FURI_CRITICAL_ENTER();
if(key->get_key_type() == iButtonKeyType::KeyMetakom ||
key->get_key_type() == iButtonKeyType::KeyCyfral) {
@ -258,7 +258,7 @@ bool KeyWriter::write_TM01(iButtonKey* key) {
onewire_write_one_bit(1);
}
__enable_irq();
FURI_CRITICAL_EXIT();
return result;*/
return false;
@ -275,4 +275,4 @@ void KeyWriter::write_byte_ds1990(uint8_t data) {
delay_us(5000);
data = data >> 1;
}
}
}

View File

@ -121,12 +121,12 @@ void RfidWriter::write_em(const uint8_t em_data[5]) {
em_card.encode(em_data, 5, reinterpret_cast<uint8_t*>(&em_encoded_data), sizeof(uint64_t));
const uint32_t em_config_block_data = 0b00000000000101001000000001000000;
__disable_irq();
FURI_CRITICAL_ENTER();
write_block(0, 0, false, em_config_block_data);
write_block(0, 1, false, em_encoded_data);
write_block(0, 2, false, em_encoded_data >> 32);
write_reset();
__enable_irq();
FURI_CRITICAL_EXIT();
}
void RfidWriter::write_hid(const uint8_t hid_data[3]) {
@ -136,13 +136,13 @@ void RfidWriter::write_hid(const uint8_t hid_data[3]) {
const uint32_t hid_config_block_data = 0b00000000000100000111000001100000;
__disable_irq();
FURI_CRITICAL_ENTER();
write_block(0, 0, false, hid_config_block_data);
write_block(0, 1, false, card_data[0]);
write_block(0, 2, false, card_data[1]);
write_block(0, 3, false, card_data[2]);
write_reset();
__enable_irq();
FURI_CRITICAL_EXIT();
}
void RfidWriter::write_indala(const uint8_t indala_data[3]) {
@ -153,10 +153,10 @@ void RfidWriter::write_indala(const uint8_t indala_data[3]) {
const uint32_t indala_config_block_data = 0b00000000000010000001000001000000;
__disable_irq();
FURI_CRITICAL_ENTER();
write_block(0, 0, false, indala_config_block_data);
write_block(0, 1, false, card_data[0]);
write_block(0, 2, false, card_data[1]);
write_reset();
__enable_irq();
FURI_CRITICAL_EXIT();
}

View File

@ -1,3 +1,4 @@
#include "applications.h"
#include <furi.h>
#include "loader/loader.h"
#include "loader_i.h"
@ -9,28 +10,46 @@
static Loader* loader_instance = NULL;
static bool
loader_start_application(const FlipperApplication* application, const char* arguments) {
loader_instance->application = application;
furi_assert(loader_instance->application_arguments == NULL);
if(arguments && strlen(arguments) > 0) {
loader_instance->application_arguments = strdup(arguments);
}
FURI_LOG_I(TAG, "Starting: %s", loader_instance->application->name);
furi_thread_set_name(loader_instance->application_thread, loader_instance->application->name);
furi_thread_set_stack_size(
loader_instance->application_thread, loader_instance->application->stack_size);
furi_thread_set_context(
loader_instance->application_thread, loader_instance->application_arguments);
furi_thread_set_callback(
loader_instance->application_thread, loader_instance->application->app);
bool result = furi_thread_start(loader_instance->application_thread);
if(!result) {
loader_instance->application = NULL;
}
return result;
}
static void loader_menu_callback(void* _ctx, uint32_t index) {
const FlipperApplication* flipper_app = _ctx;
const FlipperApplication* application = _ctx;
furi_assert(flipper_app->app);
furi_assert(flipper_app->name);
furi_assert(application->app);
furi_assert(application->name);
if(!loader_lock(loader_instance)) return;
if(furi_thread_get_state(loader_instance->thread) != FuriThreadStateStopped) {
FURI_LOG_E(TAG, "Can't start app. %s is running", loader_instance->current_app->name);
if(!loader_lock(loader_instance)) {
FURI_LOG_E(TAG, "Loader is locked");
return;
}
furi_hal_power_insomnia_enter();
loader_instance->current_app = flipper_app;
FURI_LOG_I(TAG, "Starting: %s", loader_instance->current_app->name);
furi_thread_set_name(loader_instance->thread, flipper_app->name);
furi_thread_set_stack_size(loader_instance->thread, flipper_app->stack_size);
furi_thread_set_context(loader_instance->thread, NULL);
furi_thread_set_callback(loader_instance->thread, flipper_app->app);
furi_thread_start(loader_instance->thread);
loader_start_application(application, NULL);
}
static void loader_submenu_callback(void* context, uint32_t index) {
@ -61,6 +80,12 @@ const FlipperApplication* loader_find_application_by_name(const char* name) {
}
}
for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) {
if(strcmp(name, FLIPPER_SETTINGS_APPS[i].name) == 0) {
application = &FLIPPER_SETTINGS_APPS[i];
}
}
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
for(size_t i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) {
if(strcmp(name, FLIPPER_DEBUG_APPS[i].name) == 0) {
@ -73,32 +98,36 @@ const FlipperApplication* loader_find_application_by_name(const char* name) {
}
void loader_cli_open(Cli* cli, string_t args, Loader* instance) {
string_strim(args);
string_t application_name;
string_init(application_name);
if(string_size(args) == 0) {
printf("No application provided\r\n");
return;
}
do {
if(!args_read_probably_quoted_string_and_trim(args, application_name)) {
printf("No application provided\r\n");
break;
}
const FlipperApplication* application = loader_find_application_by_name(string_get_cstr(args));
if(!application) {
printf("%s doesn't exists\r\n", string_get_cstr(args));
return;
}
const FlipperApplication* application =
loader_find_application_by_name(string_get_cstr(application_name));
if(!application) {
printf("%s doesn't exists\r\n", string_get_cstr(application_name));
break;
}
if(furi_thread_get_state(instance->thread) != FuriThreadStateStopped) {
printf("Can't start, furi application is running");
return;
}
string_strim(args);
if(!loader_start_application(application, string_get_cstr(args))) {
printf("Can't start, furi application is running");
return;
} else {
// We must to increment lock counter to keep balance
// TODO: rewrite whole thing, it's complex as hell
FURI_CRITICAL_ENTER();
instance->lock_count++;
FURI_CRITICAL_EXIT();
}
} while(false);
instance->lock_semaphore++;
furi_hal_power_insomnia_enter();
instance->current_app = application;
printf("Starting: %s\r\n", instance->current_app->name);
furi_thread_set_name(instance->thread, application->name);
furi_thread_set_stack_size(instance->thread, application->stack_size);
furi_thread_set_callback(instance->thread, application->app);
furi_thread_start(instance->thread);
string_clear(application_name);
}
void loader_cli_list(Cli* cli, string_t args, Loader* instance) {
@ -152,62 +181,44 @@ void loader_cli(Cli* cli, string_t args, void* _ctx) {
LoaderStatus loader_start(Loader* instance, const char* name, const char* args) {
furi_assert(name);
const FlipperApplication* flipper_app = loader_find_application_by_name(name);
const FlipperApplication* application = loader_find_application_by_name(name);
if(!flipper_app) {
if(!application) {
FURI_LOG_E(TAG, "Can't find application with name %s", name);
return LoaderStatusErrorUnknownApp;
}
bool locked = loader_lock(instance);
if(!locked || (furi_thread_get_state(instance->thread) != FuriThreadStateStopped)) {
FURI_LOG_E(TAG, "Can't start app. %s is running", instance->current_app->name);
/* no need to call loader_unlock() - it is called as soon as application stops */
if(!loader_lock(loader_instance)) {
FURI_LOG_E(TAG, "Loader is locked");
return LoaderStatusErrorAppStarted;
}
instance->current_app = flipper_app;
void* thread_args = NULL;
if(args) {
string_set_str(instance->args, args);
string_strim(instance->args);
thread_args = (void*)string_get_cstr(instance->args);
FURI_LOG_I(TAG, "Start %s app with args: %s", name, args);
} else {
string_reset(instance->args);
FURI_LOG_I(TAG, "Start %s app with no args", name);
if(!loader_start_application(application, args)) {
return LoaderStatusErrorInternal;
}
furi_thread_set_name(instance->thread, flipper_app->name);
furi_thread_set_stack_size(instance->thread, flipper_app->stack_size);
furi_thread_set_context(instance->thread, thread_args);
furi_thread_set_callback(instance->thread, flipper_app->app);
bool thread_started = furi_thread_start(instance->thread);
return thread_started ? LoaderStatusOk : LoaderStatusErrorInternal;
return LoaderStatusOk;
}
bool loader_lock(Loader* instance) {
bool ret = false;
furi_check(osMutexAcquire(instance->mutex, osWaitForever) == osOK);
if(instance->lock_semaphore == 0) {
instance->lock_semaphore++;
ret = true;
FURI_CRITICAL_ENTER();
bool result = false;
if(instance->lock_count == 0) {
instance->lock_count++;
result = true;
}
furi_check(osMutexRelease(instance->mutex) == osOK);
return ret;
FURI_CRITICAL_EXIT();
return result;
}
void loader_unlock(Loader* instance) {
furi_check(osMutexAcquire(instance->mutex, osWaitForever) == osOK);
furi_check(instance->lock_semaphore > 0);
instance->lock_semaphore--;
furi_check(osMutexRelease(instance->mutex) == osOK);
FURI_CRITICAL_ENTER();
if(instance->lock_count > 0) instance->lock_count--;
FURI_CRITICAL_EXIT();
}
bool loader_is_locked(Loader* instance) {
return (instance->lock_semaphore > 0);
return instance->lock_count > 0;
}
static void loader_thread_state_callback(FuriThreadState thread_state, void* context) {
@ -219,6 +230,7 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con
if(thread_state == FuriThreadStateRunning) {
event.type = LoaderEventTypeApplicationStarted;
furi_pubsub_publish(loader_instance->pubsub, &event);
furi_hal_power_insomnia_enter();
// Snapshot current memory usage
instance->free_heap_size = memmgr_get_free_heap();
@ -239,7 +251,13 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con
TAG,
"Application thread stopped. Heap allocation balance: %d. Thread allocation balance: %d.",
heap_diff,
furi_thread_get_heap_size(instance->thread));
furi_thread_get_heap_size(instance->application_thread));
if(loader_instance->application_arguments) {
free(loader_instance->application_arguments);
loader_instance->application_arguments = NULL;
}
furi_hal_power_insomnia_exit();
loader_unlock(instance);
@ -262,15 +280,12 @@ static uint32_t loader_back_to_primary_menu(void* context) {
static Loader* loader_alloc() {
Loader* instance = furi_alloc(sizeof(Loader));
instance->thread = furi_thread_alloc();
furi_thread_enable_heap_trace(instance->thread);
furi_thread_set_state_context(instance->thread, instance);
furi_thread_set_state_callback(instance->thread, loader_thread_state_callback);
string_init(instance->args);
instance->application_thread = furi_thread_alloc();
furi_thread_enable_heap_trace(instance->application_thread);
furi_thread_set_state_context(instance->application_thread, instance);
furi_thread_set_state_callback(instance->application_thread, loader_thread_state_callback);
instance->pubsub = furi_pubsub_alloc();
instance->mutex = osMutexNew(NULL);
#ifdef SRV_CLI
instance->cli = furi_record_open("cli");
@ -327,13 +342,9 @@ static void loader_free(Loader* instance) {
furi_record_close("cli");
}
osMutexDelete(instance->mutex);
furi_pubsub_free(instance->pubsub);
string_clear(instance->args);
furi_thread_free(instance->thread);
furi_thread_free(instance->application_thread);
menu_free(loader_instance->primary_menu);
view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewPrimary);

View File

@ -16,9 +16,11 @@
struct Loader {
osThreadId_t loader_thread;
FuriThread* thread;
const FlipperApplication* current_app;
string_t args;
const FlipperApplication* application;
FuriThread* application_thread;
char* application_arguments;
Cli* cli;
Gui* gui;
@ -29,8 +31,7 @@ struct Loader {
Submenu* settings_menu;
size_t free_heap_size;
osMutexId_t mutex;
volatile uint8_t lock_semaphore;
volatile uint8_t lock_count;
FuriPubSub* pubsub;
};

View File

@ -102,7 +102,7 @@ typedef struct {
uint8_t volume_id_max;
} State;
float volumes[] = {0, 0.02, 0.05, 0.1, 0.5};
const float volumes[] = {0, 0.02, 0.05, 0.1, 0.5};
bool is_white_note(const MelodyEventRecord* note_record, uint8_t id) {
if(note_record == NULL) return false;

View File

@ -1,28 +1,30 @@
#include "power_cli.h"
#include <power/power_service/power.h>
#include <cli/cli.h>
#include <furi_hal.h>
#include <cli/cli.h>
#include <lib/toolbox/args.h>
#include <power/power_service/power.h>
void power_cli_poweroff(Cli* cli, string_t args, void* context) {
void power_cli_off(Cli* cli, string_t args) {
Power* power = furi_record_open("power");
printf("It's now safe to disconnect USB from your flipper\r\n");
osDelay(666);
power_off(power);
}
void power_cli_reboot(Cli* cli, string_t args, void* context) {
void power_cli_reboot(Cli* cli, string_t args) {
power_reboot(PowerBootModeNormal);
}
void power_cli_dfu(Cli* cli, string_t args, void* context) {
void power_cli_reboot2dfu(Cli* cli, string_t args) {
power_reboot(PowerBootModeDfu);
}
void power_cli_info(Cli* cli, string_t args, void* context) {
void power_cli_debug(Cli* cli, string_t args) {
furi_hal_power_dump_state();
}
void power_cli_otg(Cli* cli, string_t args, void* context) {
void power_cli_5v(Cli* cli, string_t args) {
if(!string_cmp(args, "0")) {
furi_hal_power_disable_otg();
} else if(!string_cmp(args, "1")) {
@ -32,7 +34,7 @@ void power_cli_otg(Cli* cli, string_t args, void* context) {
}
}
void power_cli_ext(Cli* cli, string_t args, void* context) {
void power_cli_3v3(Cli* cli, string_t args) {
if(!string_cmp(args, "0")) {
furi_hal_power_disable_external_3_3v();
} else if(!string_cmp(args, "1")) {
@ -42,16 +44,70 @@ void power_cli_ext(Cli* cli, string_t args, void* context) {
}
}
static void power_cli_command_print_usage() {
printf("Usage:\r\n");
printf("power <cmd> <args>\r\n");
printf("Cmd list:\r\n");
printf("\toff\t - shutdown power\r\n");
printf("\treboot\t - reboot\r\n");
printf("\treboot2dfu\t - reboot to dfu bootloader\r\n");
printf("\tdebug\t - show debug information\r\n");
printf("\t5v <0 or 1>\t - enable or disable 5v ext\r\n");
printf("\t3v3 <0 or 1>\t - enable or disable 3v3 ext\r\n");
}
void power_cli(Cli* cli, string_t args, void* context) {
string_t cmd;
string_init(cmd);
do {
if(!args_read_string_and_trim(args, cmd)) {
power_cli_command_print_usage();
break;
}
if(string_cmp_str(cmd, "off") == 0) {
power_cli_off(cli, args);
break;
}
if(string_cmp_str(cmd, "reboot") == 0) {
power_cli_reboot(cli, args);
break;
}
if(string_cmp_str(cmd, "reboot2dfu") == 0) {
power_cli_reboot2dfu(cli, args);
break;
}
if(string_cmp_str(cmd, "debug") == 0) {
power_cli_debug(cli, args);
break;
}
if(string_cmp_str(cmd, "5v") == 0) {
power_cli_5v(cli, args);
break;
}
if(string_cmp_str(cmd, "3v3") == 0) {
power_cli_3v3(cli, args);
break;
}
power_cli_command_print_usage();
} while(false);
string_clear(cmd);
}
void power_on_system_start() {
#ifdef SRV_CLI
Cli* cli = furi_record_open("cli");
cli_add_command(cli, "poweroff", CliCommandFlagParallelSafe, power_cli_poweroff, NULL);
cli_add_command(cli, "reboot", CliCommandFlagParallelSafe, power_cli_reboot, NULL);
cli_add_command(cli, "dfu", CliCommandFlagParallelSafe, power_cli_dfu, NULL);
cli_add_command(cli, "power_info", CliCommandFlagParallelSafe, power_cli_info, NULL);
cli_add_command(cli, "power_otg", CliCommandFlagParallelSafe, power_cli_otg, NULL);
cli_add_command(cli, "power_ext", CliCommandFlagParallelSafe, power_cli_ext, NULL);
cli_add_command(cli, "power", CliCommandFlagParallelSafe, power_cli, NULL);
furi_record_close("cli");
#endif

View File

@ -10,7 +10,7 @@ void power_off(Power* power) {
view_dispatcher_send_to_front(power->view_dispatcher);
view_dispatcher_switch_to_view(power->view_dispatcher, PowerViewPopup);
osDelay(10);
furi_crash("Disconnect USB for safe shutdown");
furi_halt("Disconnect USB for safe shutdown");
}
void power_reboot(PowerBootMode mode) {

View File

@ -3,6 +3,7 @@
typedef enum {
SubghzCustomEventManagerNoSet = 0,
SubghzCustomEventManagerSet,
SubghzCustomEventManagerSetRAW,
SubghzCustomEventSceneDeleteSuccess = 100,
SubghzCustomEventSceneDelete,

Some files were not shown because too many files have changed in this diff Show More