Merge branch 'dev' into release-candidate
This commit is contained in:
		
						commit
						661b4b181a
					
				| @ -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; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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]; | ||||
| }; | ||||
|  | ||||
							
								
								
									
										74
									
								
								applications/archive/helpers/archive_apps.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								applications/archive/helpers/archive_apps.c
									
									
									
									
									
										Normal 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); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										21
									
								
								applications/archive/helpers/archive_apps.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								applications/archive/helpers/archive_apps.h
									
									
									
									
									
										Normal 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); | ||||
| @ -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)); | ||||
| } | ||||
|  | ||||
| @ -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); | ||||
| 
 | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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, ...); | ||||
| @ -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: | ||||
|  | ||||
| @ -1,2 +1,3 @@ | ||||
| ADD_SCENE(archive, browser, Browser) | ||||
| ADD_SCENE(archive, rename, Rename) | ||||
| ADD_SCENE(archive, delete, Delete) | ||||
|  | ||||
							
								
								
									
										70
									
								
								applications/archive/scenes/archive_scene_delete.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								applications/archive/scenes/archive_scene_delete.c
									
									
									
									
									
										Normal 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); | ||||
| } | ||||
| @ -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); | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -24,6 +24,8 @@ typedef enum { | ||||
|     ArchiveTabNFC, | ||||
|     ArchiveTabIrda, | ||||
|     ArchiveTabIButton, | ||||
|     ArchiveTabBadUsb, | ||||
|     ArchiveTabU2f, | ||||
|     ArchiveTabBrowser, | ||||
|     ArchiveTabTotal, | ||||
| } ArchiveTabEnum; | ||||
|  | ||||
| @ -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); | ||||
| 
 | ||||
|  | ||||
| @ -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; | ||||
|         }); | ||||
| } | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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(); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -1,9 +1,3 @@ | ||||
| #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> | ||||
| @ -12,23 +6,38 @@ | ||||
| #include <portmacro.h> | ||||
| #include <stdint.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)); | ||||
| @ -93,27 +127,29 @@ Desktop* desktop_alloc() { | ||||
| 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 +181,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); | ||||
|     } | ||||
|  | ||||
							
								
								
									
										82
									
								
								applications/desktop/desktop_helpers.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								applications/desktop/desktop_helpers.c
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										9
									
								
								applications/desktop/desktop_helpers.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								applications/desktop/desktop_helpers.h
									
									
									
									
									
										Normal 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); | ||||
| @ -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; | ||||
| 
 | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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; | ||||
| 
 | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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) | ||||
| @ -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); | ||||
| } | ||||
| @ -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) { | ||||
| } | ||||
| @ -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); | ||||
| } | ||||
| @ -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); | ||||
| } | ||||
| @ -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); | ||||
| } | ||||
| @ -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); | ||||
| } | ||||
| @ -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) { | ||||
| } | ||||
| @ -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); | ||||
| } | ||||
| @ -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, ""); | ||||
| } | ||||
| @ -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; | ||||
|         } | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
| @ -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); | ||||
| @ -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; | ||||
| } | ||||
| @ -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); | ||||
| @ -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) | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -1,7 +1,4 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| typedef enum { | ||||
|     DesktopMainSceneStateUnlocked, | ||||
|     DesktopMainSceneStateLockedWithPin, | ||||
|     DesktopMainSceneStateLockedNoPin, | ||||
| } DesktopMainSceneState; | ||||
| #define SCENE_LOCKED_FIRST_ENTER 0 | ||||
| #define SCENE_LOCKED_REPEAT_ENTER 1 | ||||
|  | ||||
| @ -1,8 +1,13 @@ | ||||
| #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" | ||||
| 
 | ||||
| @ -15,36 +20,50 @@ 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_check(status == LoaderStatusOk); | ||||
|                 furi_record_close("loader"); | ||||
|             } | ||||
| 
 | ||||
|             consumed = true; | ||||
|  | ||||
							
								
								
									
										109
									
								
								applications/desktop/scenes/desktop_scene_locked.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								applications/desktop/scenes/desktop_scene_locked.c
									
									
									
									
									
										Normal 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) { | ||||
| } | ||||
| @ -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,34 +82,22 @@ 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"); | ||||
|     desktop->app_start_stop_subscription = furi_pubsub_subscribe( | ||||
|         loader_get_pubsub(loader), desktop_scene_main_app_started_callback, desktop); | ||||
| 
 | ||||
|     // Special case: application is already running (autostart application)
 | ||||
|     if(loader_is_locked(loader)) { | ||||
|         animation_manager_unload_and_stall_animation(desktop->animation_manager); | ||||
|     } | ||||
| 
 | ||||
|     furi_record_close("loader"); | ||||
| 
 | ||||
|     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 +134,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 +165,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 +195,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); | ||||
| } | ||||
|  | ||||
							
								
								
									
										162
									
								
								applications/desktop/scenes/desktop_scene_pin_input.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								applications/desktop/scenes/desktop_scene_pin_input.c
									
									
									
									
									
										Normal 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); | ||||
| } | ||||
							
								
								
									
										46
									
								
								applications/desktop/scenes/desktop_scene_pin_timeout.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								applications/desktop/scenes/desktop_scene_pin_timeout.c
									
									
									
									
									
										Normal 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) { | ||||
| } | ||||
| @ -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, ""); | ||||
| } | ||||
| @ -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; | ||||
|  | ||||
| @ -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"); | ||||
| } | ||||
| @ -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); | ||||
| @ -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, | ||||
| @ -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) | ||||
| @ -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 | ||||
| 
 | ||||
							
								
								
									
										233
									
								
								applications/desktop/views/desktop_view_locked.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								applications/desktop/views/desktop_view_locked.c
									
									
									
									
									
										Normal 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); | ||||
| } | ||||
							
								
								
									
										21
									
								
								applications/desktop/views/desktop_view_locked.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								applications/desktop/views/desktop_view_locked.h
									
									
									
									
									
										Normal 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); | ||||
| @ -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; | ||||
							
								
								
									
										340
									
								
								applications/desktop/views/desktop_view_pin_input.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										340
									
								
								applications/desktop/views/desktop_view_pin_input.c
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										40
									
								
								applications/desktop/views/desktop_view_pin_input.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								applications/desktop/views/desktop_view_pin_input.h
									
									
									
									
									
										Normal 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); | ||||
							
								
								
									
										80
									
								
								applications/desktop/views/desktop_view_pin_setup_done.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								applications/desktop/views/desktop_view_pin_setup_done.c
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										15
									
								
								applications/desktop/views/desktop_view_pin_setup_done.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								applications/desktop/views/desktop_view_pin_setup_done.h
									
									
									
									
									
										Normal 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); | ||||
							
								
								
									
										109
									
								
								applications/desktop/views/desktop_view_pin_timeout.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								applications/desktop/views/desktop_view_pin_timeout.c
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										16
									
								
								applications/desktop/views/desktop_view_pin_timeout.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								applications/desktop/views/desktop_view_pin_timeout.h
									
									
									
									
									
										Normal 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); | ||||
| @ -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; | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
|         }); | ||||
| } | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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; | ||||
|         }); | ||||
| } | ||||
| @ -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 | ||||
| @ -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) { | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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(); | ||||
| } | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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; | ||||
| }; | ||||
|  | ||||
| @ -1,28 +1,29 @@ | ||||
| #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"); | ||||
|     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 +33,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 +43,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 | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -3,6 +3,7 @@ | ||||
| typedef enum { | ||||
|     SubghzCustomEventManagerNoSet = 0, | ||||
|     SubghzCustomEventManagerSet, | ||||
|     SubghzCustomEventManagerSetRAW, | ||||
| 
 | ||||
|     SubghzCustomEventSceneDeleteSuccess = 100, | ||||
|     SubghzCustomEventSceneDelete, | ||||
|  | ||||
| @ -261,7 +261,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|         case SubghzCustomEventViewReadRAWSave: | ||||
|             if(subghz_scene_read_raw_update_filename(subghz)) { | ||||
|                 scene_manager_set_scene_state( | ||||
|                     subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerSet); | ||||
|                     subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerSetRAW); | ||||
|                 subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; | ||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); | ||||
|             } | ||||
|  | ||||
| @ -23,9 +23,13 @@ void subghz_scene_save_name_on_enter(void* context) { | ||||
|         dev_name_empty = true; | ||||
|     } else { | ||||
|         strcpy(subghz->file_name_tmp, subghz->file_name); | ||||
|         if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == | ||||
|            SubghzCustomEventManagerSet) { | ||||
|         if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != | ||||
|            SubghzCustomEventManagerNoSet) { | ||||
|             subghz_get_next_name_file(subghz); | ||||
|             if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == | ||||
|                SubghzCustomEventManagerSetRAW) { | ||||
|                 dev_name_empty = true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -62,8 +66,8 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { | ||||
|                     subghz_save_protocol_to_file(subghz, subghz->file_name); | ||||
|                 } | ||||
| 
 | ||||
|                 if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == | ||||
|                    SubghzCustomEventManagerSet) { | ||||
|                 if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != | ||||
|                    SubghzCustomEventManagerNoSet) { | ||||
|                     subghz_protocol_raw_set_last_file_name( | ||||
|                         (SubGhzProtocolRAW*)subghz->txrx->protocol_result, subghz->file_name); | ||||
|                     scene_manager_set_scene_state( | ||||
|  | ||||
| @ -28,6 +28,8 @@ static void u2f_scene_main_event_callback(U2fNotifyEvent evt, void* context) { | ||||
|         view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventConnect); | ||||
|     else if(evt == U2fNotifyDisconnect) | ||||
|         view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventDisconnect); | ||||
|     else if(evt == U2fNotifyError) | ||||
|         view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventDataError); | ||||
| } | ||||
| 
 | ||||
| static void u2f_scene_main_timer_callback(void* context) { | ||||
| @ -75,10 +77,13 @@ bool u2f_scene_main_on_event(void* context, SceneManagerEvent event) { | ||||
|             if(app->event_cur != U2fCustomEventNone) { | ||||
|                 u2f_confirm_user_present(app->u2f_instance); | ||||
|             } | ||||
|         } else if(event.event == U2fCustomEventDataError) { | ||||
|             osTimerStop(app->timer); | ||||
|             u2f_view_set_state(app->u2f_view, U2fMsgError); | ||||
|         } | ||||
|         consumed = true; | ||||
|     } else if(event.type == SceneManagerEventTypeTick) { | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -186,6 +186,13 @@ static uint16_t u2f_register(U2fData* U2F, uint8_t* buf) { | ||||
|     uint8_t hash[32]; | ||||
|     uint8_t signature[64]; | ||||
| 
 | ||||
|     if(u2f_data_check(false) == false) { | ||||
|         U2F->ready = false; | ||||
|         if(U2F->callback != NULL) U2F->callback(U2fNotifyError, U2F->context); | ||||
|         memcpy(&buf[0], state_not_supported, 2); | ||||
|         return 2; | ||||
|     } | ||||
| 
 | ||||
|     if(U2F->callback != NULL) U2F->callback(U2fNotifyRegister, U2F->context); | ||||
|     if(U2F->user_present == false) { | ||||
|         memcpy(&buf[0], state_user_missing, 2); | ||||
| @ -250,6 +257,13 @@ static uint16_t u2f_authenticate(U2fData* U2F, uint8_t* buf) { | ||||
|     uint8_t hash[32]; | ||||
|     uint8_t signature[64]; | ||||
| 
 | ||||
|     if(u2f_data_check(false) == false) { | ||||
|         U2F->ready = false; | ||||
|         if(U2F->callback != NULL) U2F->callback(U2fNotifyError, U2F->context); | ||||
|         memcpy(&buf[0], state_not_supported, 2); | ||||
|         return 2; | ||||
|     } | ||||
| 
 | ||||
|     if(U2F->callback != NULL) U2F->callback(U2fNotifyAuth, U2F->context); | ||||
|     if(U2F->user_present == true) { | ||||
|         flags |= 1; | ||||
|  | ||||
| @ -13,6 +13,7 @@ typedef enum { | ||||
|     U2fNotifyWink, | ||||
|     U2fNotifyConnect, | ||||
|     U2fNotifyDisconnect, | ||||
|     U2fNotifyError, | ||||
| } U2fNotifyEvent; | ||||
| 
 | ||||
| typedef struct U2fData U2fData; | ||||
|  | ||||
| @ -48,7 +48,7 @@ U2fApp* u2f_app_alloc() { | ||||
|     view_dispatcher_add_view( | ||||
|         app->view_dispatcher, U2fAppViewMain, u2f_view_get_view(app->u2f_view)); | ||||
| 
 | ||||
|     if(u2f_data_check()) { | ||||
|     if(u2f_data_check(true)) { | ||||
|         scene_manager_next_scene(app->scene_manager, U2fSceneMain); | ||||
|     } else { | ||||
|         scene_manager_next_scene(app->scene_manager, U2fSceneError); | ||||
|  | ||||
| @ -20,6 +20,7 @@ typedef enum { | ||||
| 
 | ||||
|     U2fCustomEventConnect, | ||||
|     U2fCustomEventDisconnect, | ||||
|     U2fCustomEventDataError, | ||||
| 
 | ||||
|     U2fCustomEventRegister, | ||||
|     U2fCustomEventAuth, | ||||
|  | ||||
| @ -38,17 +38,25 @@ typedef struct { | ||||
|     uint32_t control; | ||||
| } __attribute__((packed)) U2fCounterData; | ||||
| 
 | ||||
| bool u2f_data_check() { | ||||
| bool u2f_data_check(bool cert_only) { | ||||
|     bool state = false; | ||||
|     Storage* fs_api = furi_record_open("storage"); | ||||
|     File* file = storage_file_alloc(fs_api); | ||||
| 
 | ||||
|     if(storage_file_open(file, U2F_CERT_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) { | ||||
|     do { | ||||
|         if(!storage_file_open(file, U2F_CERT_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) break; | ||||
|         storage_file_close(file); | ||||
|         if(storage_file_open(file, U2F_CERT_KEY_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) { | ||||
|         if(!storage_file_open(file, U2F_CERT_KEY_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) break; | ||||
|         if(cert_only) { | ||||
|             state = true; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|         storage_file_close(file); | ||||
|         if(!storage_file_open(file, U2F_KEY_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) break; | ||||
|         storage_file_close(file); | ||||
|         if(!storage_file_open(file, U2F_CNT_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) break; | ||||
|         state = true; | ||||
|     } while(0); | ||||
| 
 | ||||
|     storage_file_close(file); | ||||
|     storage_file_free(file); | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Aleksandr Kutuzov
						Aleksandr Kutuzov