Merge branch 'dev' into release-candidate
This commit is contained in:
		
						commit
						661b4b181a
					
				| @ -31,9 +31,9 @@ int WIEGAND::getWiegandType() { | |||||||
| 
 | 
 | ||||||
| bool WIEGAND::available() { | bool WIEGAND::available() { | ||||||
|     bool ret; |     bool ret; | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|     ret = DoWiegandConversion(); |     ret = DoWiegandConversion(); | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -37,7 +37,7 @@ bool AccessorSceneStart::on_event(AccessorApp* app, AccessorEvent* event) { | |||||||
|                 data[i] = wiegand->getCodeHigh() >> ((i - 4) * 8); |                 data[i] = wiegand->getCodeHigh() >> ((i - 4) * 8); | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             __disable_irq(); |             FURI_CRITICAL_ENTER(); | ||||||
|             if(onewire->reset()) { |             if(onewire->reset()) { | ||||||
|                 type = 255; |                 type = 255; | ||||||
|                 onewire->write(0x33); |                 onewire->write(0x33); | ||||||
| @ -49,7 +49,7 @@ bool AccessorSceneStart::on_event(AccessorApp* app, AccessorEvent* event) { | |||||||
|                     data[i] = data[i + 1]; |                     data[i] = data[i + 1]; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             __enable_irq(); |             FURI_CRITICAL_EXIT(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if(type > 0) { |         if(type > 0) { | ||||||
|  | |||||||
| @ -39,6 +39,10 @@ ArchiveApp* archive_alloc() { | |||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         archive->view_dispatcher, ArchiveViewTextInput, text_input_get_view(archive->text_input)); |         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; |     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, ArchiveViewBrowser); | ||||||
|     view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewTextInput); |     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); |     view_dispatcher_free(archive->view_dispatcher); | ||||||
|     scene_manager_free(archive->scene_manager); |     scene_manager_free(archive->scene_manager); | ||||||
|     browser_free(archive->browser); |     browser_free(archive->browser); | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ | |||||||
| #include <gui/view_dispatcher.h> | #include <gui/view_dispatcher.h> | ||||||
| #include <gui/scene_manager.h> | #include <gui/scene_manager.h> | ||||||
| #include <gui/modules/text_input.h> | #include <gui/modules/text_input.h> | ||||||
|  | #include <gui/modules/widget.h> | ||||||
| #include <loader/loader.h> | #include <loader/loader.h> | ||||||
| 
 | 
 | ||||||
| #include "views/archive_browser_view.h" | #include "views/archive_browser_view.h" | ||||||
| @ -15,6 +16,7 @@ | |||||||
| typedef enum { | typedef enum { | ||||||
|     ArchiveViewBrowser, |     ArchiveViewBrowser, | ||||||
|     ArchiveViewTextInput, |     ArchiveViewTextInput, | ||||||
|  |     ArchiveViewWidget, | ||||||
|     ArchiveViewTotal, |     ArchiveViewTotal, | ||||||
| } ArchiveViewEnum; | } ArchiveViewEnum; | ||||||
| 
 | 
 | ||||||
| @ -24,6 +26,7 @@ struct ArchiveApp { | |||||||
|     SceneManager* scene_manager; |     SceneManager* scene_manager; | ||||||
|     ArchiveBrowserView* browser; |     ArchiveBrowserView* browser; | ||||||
|     TextInput* text_input; |     TextInput* text_input; | ||||||
|  |     Widget* widget; | ||||||
|     char text_store[MAX_NAME_LEN]; |     char text_store[MAX_NAME_LEN]; | ||||||
|     char file_extension[MAX_EXT_LEN + 1]; |     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 "archive_browser.h" | ||||||
| #include "math.h" | #include <math.h> | ||||||
| 
 | 
 | ||||||
| void archive_update_offset(ArchiveBrowserView* browser) { | void archive_update_offset(ArchiveBrowserView* browser) { | ||||||
|     furi_assert(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(browser); | ||||||
|     furi_assert(file_info); |  | ||||||
|     furi_assert(name); |     furi_assert(name); | ||||||
| 
 | 
 | ||||||
|     ArchiveFile_t item; |     ArchiveFile_t item; | ||||||
| 
 | 
 | ||||||
|     if(filter_by_extension(file_info, get_tab_ext(archive_get_tab(browser)), name)) { |     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); |     ArchiveFile_t_init(&item); | ||||||
|     string_init_set_str(item.name, name); |     string_init_set_str(item.name, name); | ||||||
|         set_file_type(&item, file_info); |     set_file_type(&item, NULL, app_name + 1, true); | ||||||
| 
 | 
 | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         browser->view, (ArchiveBrowserViewModel * model) { |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|             files_array_push_back(model->files, item); |             files_array_push_back(model->files, item); | ||||||
|             return false; |             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, 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, 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); |         ArchiveFile_t_clear(&item); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -208,8 +239,7 @@ void archive_show_file_menu(ArchiveBrowserView* browser, bool show) { | |||||||
| 
 | 
 | ||||||
|             if(show) { |             if(show) { | ||||||
|                 ArchiveFile_t* selected = files_array_get(model->files, model->idx); |                 ArchiveFile_t* selected = files_array_get(model->files, model->idx); | ||||||
|                 selected->fav = archive_is_favorite( |                 selected->fav = archive_is_favorite("%s", string_get_cstr(selected->name)); | ||||||
|                     "%s/%s", string_get_cstr(browser->path), string_get_cstr(selected->name)); |  | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             return true; |             return true; | ||||||
| @ -245,12 +275,18 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { | |||||||
| 
 | 
 | ||||||
|     archive_set_tab(browser, tab); |     archive_set_tab(browser, tab); | ||||||
| 
 | 
 | ||||||
|     if((tab != ArchiveTabFavorites && |     const char* path = archive_get_default_path(tab); | ||||||
|         !archive_dir_empty(browser, archive_get_default_path(tab))) || |     bool tab_empty = true; | ||||||
|        (tab == ArchiveTabFavorites && !archive_favorites_count(browser))) { |     if(tab == ArchiveTabFavorites) { | ||||||
|         if(tab != ArchiveTabBrowser) { |         if(archive_favorites_count(browser) > 0) tab_empty = false; | ||||||
|             archive_switch_tab(browser, key); |     } 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 { |     } else { | ||||||
|         with_view_model( |         with_view_model( | ||||||
|             browser->view, (ArchiveBrowserViewModel * model) { |             browser->view, (ArchiveBrowserViewModel * model) { | ||||||
| @ -277,8 +313,7 @@ void archive_enter_dir(ArchiveBrowserView* browser, string_t name) { | |||||||
|             return false; |             return false; | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|     string_cat(browser->path, "/"); |     string_set(browser->path, name); | ||||||
|     string_cat(browser->path, name); |  | ||||||
| 
 | 
 | ||||||
|     archive_switch_dir(browser, string_get_cstr(browser->path)); |     archive_switch_dir(browser, string_get_cstr(browser->path)); | ||||||
| } | } | ||||||
|  | |||||||
| @ -11,6 +11,8 @@ static const char* tab_default_paths[] = { | |||||||
|     [ArchiveTabSubGhz] = "/any/subghz", |     [ArchiveTabSubGhz] = "/any/subghz", | ||||||
|     [ArchiveTabLFRFID] = "/any/lfrfid", |     [ArchiveTabLFRFID] = "/any/lfrfid", | ||||||
|     [ArchiveTabIrda] = "/any/irda", |     [ArchiveTabIrda] = "/any/irda", | ||||||
|  |     [ArchiveTabBadUsb] = "/any/badusb", | ||||||
|  |     [ArchiveTabU2f] = "/app:u2f", | ||||||
|     [ArchiveTabBrowser] = "/any", |     [ArchiveTabBrowser] = "/any", | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -20,30 +22,37 @@ static const char* known_ext[] = { | |||||||
|     [ArchiveFileTypeSubGhz] = ".sub", |     [ArchiveFileTypeSubGhz] = ".sub", | ||||||
|     [ArchiveFileTypeLFRFID] = ".rfid", |     [ArchiveFileTypeLFRFID] = ".rfid", | ||||||
|     [ArchiveFileTypeIrda] = ".ir", |     [ArchiveFileTypeIrda] = ".ir", | ||||||
|  |     [ArchiveFileTypeBadUsb] = ".txt", | ||||||
|  |     [ArchiveFileTypeU2f] = "?", | ||||||
|  |     [ArchiveFileTypeFolder] = "?", | ||||||
|  |     [ArchiveFileTypeUnknown] = "*", | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static inline const char* get_tab_ext(ArchiveTabEnum tab) { | static const ArchiveFileTypeEnum known_type[] = { | ||||||
|     switch(tab) { |     [ArchiveTabFavorites] = ArchiveFileTypeUnknown, | ||||||
|     case ArchiveTabIButton: |     [ArchiveTabIButton] = ArchiveFileTypeIButton, | ||||||
|         return known_ext[ArchiveFileTypeIButton]; |     [ArchiveTabNFC] = ArchiveFileTypeNFC, | ||||||
|     case ArchiveTabNFC: |     [ArchiveTabSubGhz] = ArchiveFileTypeSubGhz, | ||||||
|         return known_ext[ArchiveFileTypeNFC]; |     [ArchiveTabLFRFID] = ArchiveFileTypeLFRFID, | ||||||
|     case ArchiveTabSubGhz: |     [ArchiveTabIrda] = ArchiveFileTypeIrda, | ||||||
|         return known_ext[ArchiveFileTypeSubGhz]; |     [ArchiveTabBadUsb] = ArchiveFileTypeBadUsb, | ||||||
|     case ArchiveTabLFRFID: |     [ArchiveTabU2f] = ArchiveFileTypeU2f, | ||||||
|         return known_ext[ArchiveFileTypeLFRFID]; |     [ArchiveTabBrowser] = ArchiveFileTypeUnknown, | ||||||
|     case ArchiveTabIrda: | }; | ||||||
|         return known_ext[ArchiveFileTypeIrda]; | 
 | ||||||
|     default: | static inline const ArchiveFileTypeEnum archive_get_tab_filetype(ArchiveTabEnum tab) { | ||||||
|         return "*"; |     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) { | static inline const char* archive_get_default_path(ArchiveTabEnum tab) { | ||||||
|     return tab_default_paths[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); |     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_path(ArchiveBrowserView* browser); | ||||||
| const char* archive_get_name(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_show_file_menu(ArchiveBrowserView* browser, bool show); | ||||||
| void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active); | void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| 
 | 
 | ||||||
| #include "archive_favorites.h" | #include "archive_favorites.h" | ||||||
|  | #include "archive_files.h" | ||||||
|  | #include "archive_apps.h" | ||||||
| #include "archive_browser.h" | #include "archive_browser.h" | ||||||
| 
 | 
 | ||||||
| uint16_t archive_favorites_count(void* context) { | uint16_t archive_favorites_count(void* context) { | ||||||
| @ -46,6 +48,11 @@ static bool archive_favourites_rescan() { | |||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             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; |                 bool file_exists = false; | ||||||
|                 file_worker_is_file_exist(file_worker, string_get_cstr(buffer), &file_exists); |                 file_worker_is_file_exist(file_worker, string_get_cstr(buffer), &file_exists); | ||||||
|                 if(file_exists) { |                 if(file_exists) { | ||||||
| @ -53,6 +60,7 @@ static bool archive_favourites_rescan() { | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     string_clear(buffer); |     string_clear(buffer); | ||||||
| 
 | 
 | ||||||
| @ -88,13 +96,22 @@ bool archive_favorites_read(void* context) { | |||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             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; |                 bool file_exists = false; | ||||||
|                 file_worker_is_file_exist(file_worker, string_get_cstr(buffer), &file_exists); |                 file_worker_is_file_exist(file_worker, string_get_cstr(buffer), &file_exists); | ||||||
| 
 | 
 | ||||||
|                 if(file_exists) |                 if(file_exists) | ||||||
|                 archive_add_item(browser, &file_info, string_get_cstr(buffer)); |                     archive_add_file_item(browser, &file_info, string_get_cstr(buffer)); | ||||||
|                 else |                 else | ||||||
|                     need_refresh = true; |                     need_refresh = true; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             string_reset(buffer); |             string_reset(buffer); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -185,8 +202,7 @@ bool archive_is_favorite(const char* format, ...) { | |||||||
|     return found; |     return found; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool archive_favorites_rename(const char* file_path, const char* src, const char* dst) { | bool archive_favorites_rename(const char* src, const char* dst) { | ||||||
|     furi_assert(file_path); |  | ||||||
|     furi_assert(src); |     furi_assert(src); | ||||||
|     furi_assert(dst); |     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(buffer); | ||||||
|     string_init(path); |     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); |     bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); | ||||||
| 
 | 
 | ||||||
|     if(result) { |     if(result) { | ||||||
|  | |||||||
| @ -8,6 +8,6 @@ uint16_t archive_favorites_count(void* context); | |||||||
| bool archive_favorites_read(void* context); | bool archive_favorites_read(void* context); | ||||||
| bool archive_favorites_delete(const char* format, ...); | bool archive_favorites_delete(const char* format, ...); | ||||||
| bool archive_is_favorite(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_add_to_favorites(const char* file_path); | ||||||
| void archive_favorites_save(void* context); | void archive_favorites_save(void* context); | ||||||
|  | |||||||
| @ -1,8 +1,11 @@ | |||||||
| #include "archive_files.h" | #include "archive_files.h" | ||||||
|  | #include "archive_apps.h" | ||||||
| #include "archive_browser.h" | #include "archive_browser.h" | ||||||
| 
 | 
 | ||||||
| #define TAG "Archive" | #define TAG "Archive" | ||||||
| 
 | 
 | ||||||
|  | #define ASSETS_DIR "assets" | ||||||
|  | 
 | ||||||
| bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name) { | bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name) { | ||||||
|     furi_assert(file_info); |     furi_assert(file_info); | ||||||
|     furi_assert(tab_ext); |     furi_assert(tab_ext); | ||||||
| @ -15,8 +18,12 @@ bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* n | |||||||
|     } else if(strstr(name, tab_ext) != NULL) { |     } else if(strstr(name, tab_ext) != NULL) { | ||||||
|         result = true; |         result = true; | ||||||
|     } else if(file_info->flags & FSF_DIRECTORY) { |     } else if(file_info->flags & FSF_DIRECTORY) { | ||||||
|  |         if(strstr(name, ASSETS_DIR) != NULL) { | ||||||
|  |             result = false; // Skip assets folder in all tabs except browser
 | ||||||
|  |         } else { | ||||||
|             result = true; |             result = true; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| @ -38,22 +45,37 @@ void archive_get_file_extension(char* name, char* ext) { | |||||||
|         strncpy(ext, dot, MAX_EXT_LEN); |         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); | ||||||
|  | 
 | ||||||
|  |     file->is_app = is_app; | ||||||
|  |     if(is_app) { | ||||||
|  |         file->type = archive_get_app_filetype(archive_get_app_type(path)); | ||||||
|  |     } else { | ||||||
|         furi_assert(file_info); |         furi_assert(file_info); | ||||||
| 
 | 
 | ||||||
|         for(size_t i = 0; i < SIZEOF_ARRAY(known_ext); i++) { |         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(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; |                     file->type = i; | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         if(file_info->flags & FSF_DIRECTORY) { |         if(file_info->flags & FSF_DIRECTORY) { | ||||||
|             file->type = ArchiveFileTypeFolder; |             file->type = ArchiveFileTypeFolder; | ||||||
|         } else { |         } else { | ||||||
|             file->type = ArchiveFileTypeUnknown; |             file->type = ArchiveFileTypeUnknown; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool archive_get_filenames(void* context, const char* path) { | bool archive_get_filenames(void* context, const char* path) { | ||||||
| @ -63,17 +85,21 @@ bool archive_get_filenames(void* context, const char* path) { | |||||||
|     ArchiveBrowserView* browser = context; |     ArchiveBrowserView* browser = context; | ||||||
|     archive_file_array_rm_all(browser); |     archive_file_array_rm_all(browser); | ||||||
| 
 | 
 | ||||||
|     if(archive_get_tab(browser) != ArchiveTabFavorites) { |     if(archive_get_tab(browser) == ArchiveTabFavorites) { | ||||||
|         res = archive_read_dir(browser, path); |  | ||||||
|     } else { |  | ||||||
|         res = archive_favorites_read(browser); |         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; |     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); |     furi_assert(context); | ||||||
| 
 | 
 | ||||||
|  |     ArchiveBrowserView* browser = context; | ||||||
|  | 
 | ||||||
|     FileInfo file_info; |     FileInfo file_info; | ||||||
|     Storage* fs_api = furi_record_open("storage"); |     Storage* fs_api = furi_record_open("storage"); | ||||||
|     File* directory = storage_file_alloc(fs_api); |     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) { |         if(files_found) { | ||||||
|             break; |             break; | ||||||
|         } else if(storage_file_get_error(directory) == FSE_OK) { |         } else if((storage_file_get_error(directory) == FSE_OK) && (name[0])) { | ||||||
|             files_found = name[0]; |             if(filter_by_extension( | ||||||
|  |                    &file_info, archive_get_tab_ext(archive_get_tab(browser)), name)) { | ||||||
|  |                 files_found = true; | ||||||
|  |             } | ||||||
|         } else { |         } else { | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
| @ -114,6 +143,8 @@ bool archive_read_dir(void* context, const char* path) { | |||||||
|     Storage* fs_api = furi_record_open("storage"); |     Storage* fs_api = furi_record_open("storage"); | ||||||
|     File* directory = storage_file_alloc(fs_api); |     File* directory = storage_file_alloc(fs_api); | ||||||
|     char name[MAX_NAME_LEN]; |     char name[MAX_NAME_LEN]; | ||||||
|  |     snprintf(name, MAX_NAME_LEN, "%s/", path); | ||||||
|  |     size_t path_len = strlen(name); | ||||||
|     size_t files_cnt = 0; |     size_t files_cnt = 0; | ||||||
| 
 | 
 | ||||||
|     if(!storage_dir_open(directory, path)) { |     if(!storage_dir_open(directory, path)) { | ||||||
| @ -123,13 +154,14 @@ bool archive_read_dir(void* context, const char* path) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     while(1) { |     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; |             break; | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|         if(files_cnt > MAX_FILES) { |         if(files_cnt > MAX_FILES) { | ||||||
|             break; |             break; | ||||||
|         } else if(storage_file_get_error(directory) == FSE_OK) { |         } 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; |             ++files_cnt; | ||||||
|         } else { |         } else { | ||||||
|             storage_dir_close(directory); |             storage_dir_close(directory); | ||||||
|  | |||||||
| @ -9,29 +9,38 @@ typedef enum { | |||||||
|     ArchiveFileTypeSubGhz, |     ArchiveFileTypeSubGhz, | ||||||
|     ArchiveFileTypeLFRFID, |     ArchiveFileTypeLFRFID, | ||||||
|     ArchiveFileTypeIrda, |     ArchiveFileTypeIrda, | ||||||
|  |     ArchiveFileTypeBadUsb, | ||||||
|  |     ArchiveFileTypeU2f, | ||||||
|     ArchiveFileTypeFolder, |     ArchiveFileTypeFolder, | ||||||
|     ArchiveFileTypeUnknown, |     ArchiveFileTypeUnknown, | ||||||
|     AppIdTotal, |     ArchiveFileTypesTotal, | ||||||
| } ArchiveFileTypeEnum; | } ArchiveFileTypeEnum; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     string_t name; |     string_t name; | ||||||
|     ArchiveFileTypeEnum type; |     ArchiveFileTypeEnum type; | ||||||
|     bool fav; |     bool fav; | ||||||
|  |     bool is_app; | ||||||
| } ArchiveFile_t; | } ArchiveFile_t; | ||||||
| 
 | 
 | ||||||
| static void ArchiveFile_t_init(ArchiveFile_t* obj) { | static void ArchiveFile_t_init(ArchiveFile_t* obj) { | ||||||
|     obj->type = ArchiveFileTypeUnknown; |     obj->type = ArchiveFileTypeUnknown; | ||||||
|  |     obj->is_app = false; | ||||||
|  |     obj->fav = false; | ||||||
|     string_init(obj->name); |     string_init(obj->name); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void ArchiveFile_t_init_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { | static void ArchiveFile_t_init_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { | ||||||
|     obj->type = src->type; |     obj->type = src->type; | ||||||
|  |     obj->is_app = src->is_app; | ||||||
|  |     obj->fav = src->fav; | ||||||
|     string_init_set(obj->name, src->name); |     string_init_set(obj->name, src->name); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void ArchiveFile_t_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { | static void ArchiveFile_t_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { | ||||||
|     obj->type = src->type; |     obj->type = src->type; | ||||||
|  |     obj->is_app = src->is_app; | ||||||
|  |     obj->fav = src->fav; | ||||||
|     string_set(obj->name, src->name); |     string_set(obj->name, src->name); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -48,11 +57,11 @@ ARRAY_DEF( | |||||||
|      CLEAR(API_2(ArchiveFile_t_clear)))) |      CLEAR(API_2(ArchiveFile_t_clear)))) | ||||||
| 
 | 
 | ||||||
| bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name); | 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_trim_file_path(char* name, bool ext); | ||||||
| void archive_get_file_extension(char* name, char* ext); | void archive_get_file_extension(char* name, char* ext); | ||||||
| bool archive_get_filenames(void* context, const char* path); | 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); | bool archive_read_dir(void* context, const char* path); | ||||||
| void archive_file_append(const char* path, const char* format, ...); | void archive_file_append(const char* path, const char* format, ...); | ||||||
| void archive_delete_file(void* context, const char* format, ...); | void archive_delete_file(void* context, const char* format, ...); | ||||||
| @ -1,33 +1,41 @@ | |||||||
| #include "../archive_i.h" | #include "../archive_i.h" | ||||||
| #include "../helpers/archive_files.h" | #include "../helpers/archive_files.h" | ||||||
|  | #include "../helpers/archive_apps.h" | ||||||
| #include "../helpers/archive_favorites.h" | #include "../helpers/archive_favorites.h" | ||||||
| #include "../helpers/archive_browser.h" | #include "../helpers/archive_browser.h" | ||||||
| #include "../views/archive_browser_view.h" | #include "../views/archive_browser_view.h" | ||||||
| 
 | 
 | ||||||
|  | #define TAG "ArchiveSceneBrowser" | ||||||
|  | 
 | ||||||
| static const char* flipper_app_name[] = { | static const char* flipper_app_name[] = { | ||||||
|     [ArchiveFileTypeIButton] = "iButton", |     [ArchiveFileTypeIButton] = "iButton", | ||||||
|     [ArchiveFileTypeNFC] = "NFC", |     [ArchiveFileTypeNFC] = "NFC", | ||||||
|     [ArchiveFileTypeSubGhz] = "Sub-GHz", |     [ArchiveFileTypeSubGhz] = "Sub-GHz", | ||||||
|     [ArchiveFileTypeLFRFID] = "125 kHz RFID", |     [ArchiveFileTypeLFRFID] = "125 kHz RFID", | ||||||
|     [ArchiveFileTypeIrda] = "Infrared", |     [ArchiveFileTypeIrda] = "Infrared", | ||||||
|  |     [ArchiveFileTypeBadUsb] = "Bad USB", | ||||||
|  |     [ArchiveFileTypeU2f] = "U2F", | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void archive_run_in_app( | static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selected) { | ||||||
|     ArchiveBrowserView* browser, |  | ||||||
|     ArchiveFile_t* selected, |  | ||||||
|     bool full_path_provided) { |  | ||||||
|     Loader* loader = furi_record_open("loader"); |     Loader* loader = furi_record_open("loader"); | ||||||
| 
 | 
 | ||||||
|     string_t full_path; |     LoaderStatus status; | ||||||
|     if(!full_path_provided) { |     if(selected->is_app) { | ||||||
|         string_init_printf( |         char* param = strrchr(string_get_cstr(selected->name), '/'); | ||||||
|             full_path, "%s/%s", string_get_cstr(browser->path), string_get_cstr(selected->name)); |         if(param != NULL) { | ||||||
|     } else { |             param++; | ||||||
|         string_init_set(full_path, selected->name); |         } | ||||||
|  |         status = loader_start(loader, flipper_app_name[selected->type], param); | ||||||
|  |     } else { | ||||||
|  |         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"); |     furi_record_close("loader"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -50,9 +58,8 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { | |||||||
|     ArchiveBrowserView* browser = archive->browser; |     ArchiveBrowserView* browser = archive->browser; | ||||||
|     ArchiveFile_t* selected = archive_get_current_file(browser); |     ArchiveFile_t* selected = archive_get_current_file(browser); | ||||||
| 
 | 
 | ||||||
|     const char* path = archive_get_path(browser); |  | ||||||
|     const char* name = archive_get_name(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 favorites = archive_get_tab(browser) == ArchiveTabFavorites; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
| @ -68,7 +75,7 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { | |||||||
|             break; |             break; | ||||||
|         case ArchiveBrowserEventFileMenuRun: |         case ArchiveBrowserEventFileMenuRun: | ||||||
|             if(known_app) { |             if(known_app) { | ||||||
|                 archive_run_in_app(browser, selected, favorites); |                 archive_run_in_app(browser, selected); | ||||||
|             } |             } | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
| @ -78,10 +85,10 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { | |||||||
|                 archive_file_array_rm_selected(browser); |                 archive_file_array_rm_selected(browser); | ||||||
|                 archive_show_file_menu(browser, false); |                 archive_show_file_menu(browser, false); | ||||||
|             } else if(known_app) { |             } else if(known_app) { | ||||||
|                 if(archive_is_favorite("%s/%s", path, name)) { |                 if(archive_is_favorite("%s", name)) { | ||||||
|                     archive_favorites_delete("%s/%s", path, name); |                     archive_favorites_delete("%s", name); | ||||||
|                 } else { |                 } 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); |                 archive_show_file_menu(browser, false); | ||||||
|             } |             } | ||||||
| @ -91,18 +98,13 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { | |||||||
|         case ArchiveBrowserEventFileMenuAction: |         case ArchiveBrowserEventFileMenuAction: | ||||||
|             if(favorites) { |             if(favorites) { | ||||||
|                 browser->callback(ArchiveBrowserEventEnterFavMove, browser->context); |                 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); |                 scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneRename); | ||||||
|             } |             } | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case ArchiveBrowserEventFileMenuDelete: |         case ArchiveBrowserEventFileMenuDelete: | ||||||
|             if(favorites) { |             scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneDelete); | ||||||
|                 archive_delete_file(browser, "%s", name); |  | ||||||
|             } else { |  | ||||||
|                 archive_delete_file(browser, "%s/%s", path, name); |  | ||||||
|             } |  | ||||||
|             archive_show_file_menu(browser, false); |  | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case ArchiveBrowserEventEnterDir: |         case ArchiveBrowserEventEnterDir: | ||||||
|  | |||||||
| @ -1,2 +1,3 @@ | |||||||
| ADD_SCENE(archive, browser, Browser) | ADD_SCENE(archive, browser, Browser) | ||||||
| ADD_SCENE(archive, rename, Rename) | 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* path = archive_get_path(archive->browser); | ||||||
|             const char* name = archive_get_name(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); |             string_init_printf(buffer_dst, "%s/%s", path, archive->text_store); | ||||||
| 
 | 
 | ||||||
|             // append extension
 |             // append extension
 | ||||||
| @ -64,7 +65,7 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) { | |||||||
|             furi_record_close("storage"); |             furi_record_close("storage"); | ||||||
| 
 | 
 | ||||||
|             if(file->fav) { |             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); |             string_clear(buffer_src); | ||||||
|  | |||||||
| @ -10,6 +10,8 @@ static const char* ArchiveTabNames[] = { | |||||||
|     [ArchiveTabSubGhz] = "Sub-GHz", |     [ArchiveTabSubGhz] = "Sub-GHz", | ||||||
|     [ArchiveTabLFRFID] = "RFID LF", |     [ArchiveTabLFRFID] = "RFID LF", | ||||||
|     [ArchiveTabIrda] = "Infrared", |     [ArchiveTabIrda] = "Infrared", | ||||||
|  |     [ArchiveTabBadUsb] = "Bad USB", | ||||||
|  |     [ArchiveTabU2f] = "U2F", | ||||||
|     [ArchiveTabBrowser] = "Browser"}; |     [ArchiveTabBrowser] = "Browser"}; | ||||||
| 
 | 
 | ||||||
| static const Icon* ArchiveItemIcons[] = { | static const Icon* ArchiveItemIcons[] = { | ||||||
| @ -18,6 +20,8 @@ static const Icon* ArchiveItemIcons[] = { | |||||||
|     [ArchiveFileTypeSubGhz] = &I_sub1_10px, |     [ArchiveFileTypeSubGhz] = &I_sub1_10px, | ||||||
|     [ArchiveFileTypeLFRFID] = &I_125_10px, |     [ArchiveFileTypeLFRFID] = &I_125_10px, | ||||||
|     [ArchiveFileTypeIrda] = &I_ir_10px, |     [ArchiveFileTypeIrda] = &I_ir_10px, | ||||||
|  |     [ArchiveFileTypeBadUsb] = &I_badusb_10px, | ||||||
|  |     [ArchiveFileTypeU2f] = &I_u2f_10px, | ||||||
|     [ArchiveFileTypeFolder] = &I_dir_10px, |     [ArchiveFileTypeFolder] = &I_dir_10px, | ||||||
|     [ArchiveFileTypeUnknown] = &I_unknown_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); |     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[0], "---"); | ||||||
|         string_set_str(menu[1], "---"); |         string_set_str(menu[1], "---"); | ||||||
|         string_set_str(menu[2], "---"); |         string_set_str(menu[2], "---"); | ||||||
|     } else if(selected->fav) { |     } else { | ||||||
|         string_set_str(menu[1], "Unpin"); |         if(model->tab_idx == ArchiveTabFavorites) { | ||||||
|     } else if(model->tab_idx == ArchiveTabFavorites) { |  | ||||||
|         string_set_str(menu[1], "Unpin"); |  | ||||||
|             string_set_str(menu[2], "Move"); |             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"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     for(size_t i = 0; i < MENU_ITEMS; i++) { |     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)); |         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); |         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); |         string_init_set_str(str_buff, cstr_buff); | ||||||
|         elements_string_fit_width( |         elements_string_fit_width( | ||||||
|             canvas, str_buff, (scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX) - x_offset); |             canvas, str_buff, (scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX) - x_offset); | ||||||
|  | |||||||
| @ -24,6 +24,8 @@ typedef enum { | |||||||
|     ArchiveTabNFC, |     ArchiveTabNFC, | ||||||
|     ArchiveTabIrda, |     ArchiveTabIrda, | ||||||
|     ArchiveTabIButton, |     ArchiveTabIButton, | ||||||
|  |     ArchiveTabBadUsb, | ||||||
|  |     ArchiveTabU2f, | ||||||
|     ArchiveTabBrowser, |     ArchiveTabBrowser, | ||||||
|     ArchiveTabTotal, |     ArchiveTabTotal, | ||||||
| } ArchiveTabEnum; | } ArchiveTabEnum; | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
| #include <storage/storage.h> | #include <storage/storage.h> | ||||||
|  | #include <lib/toolbox/path.h> | ||||||
| 
 | 
 | ||||||
| static bool bad_usb_app_custom_event_callback(void* context, uint32_t event) { | static bool bad_usb_app_custom_event_callback(void* context, uint32_t event) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
| @ -39,27 +40,34 @@ static bool bad_usb_check_assets() { | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| BadUsbApp* bad_usb_app_alloc() { | BadUsbApp* bad_usb_app_alloc(char* arg) { | ||||||
|     BadUsbApp* app = furi_alloc(sizeof(BadUsbApp)); |     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->gui = furi_record_open("gui"); | ||||||
|     app->notifications = furi_record_open("notification"); |     app->notifications = furi_record_open("notification"); | ||||||
|     app->dialogs = furi_record_open("dialogs"); |     app->dialogs = furi_record_open("dialogs"); | ||||||
| 
 | 
 | ||||||
|     app->view_dispatcher = view_dispatcher_alloc(); |     app->view_dispatcher = view_dispatcher_alloc(); | ||||||
|     app->scene_manager = scene_manager_alloc(&bad_usb_scene_handlers, app); |  | ||||||
|     view_dispatcher_enable_queue(app->view_dispatcher); |     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_event_callback_context(app->view_dispatcher, app); | ||||||
|     view_dispatcher_set_tick_event_callback( |     view_dispatcher_set_tick_event_callback( | ||||||
|         app->view_dispatcher, bad_usb_app_tick_event_callback, 500); |         app->view_dispatcher, bad_usb_app_tick_event_callback, 500); | ||||||
| 
 |  | ||||||
|     view_dispatcher_set_custom_event_callback( |     view_dispatcher_set_custom_event_callback( | ||||||
|         app->view_dispatcher, bad_usb_app_custom_event_callback); |         app->view_dispatcher, bad_usb_app_custom_event_callback); | ||||||
|     view_dispatcher_set_navigation_event_callback( |     view_dispatcher_set_navigation_event_callback( | ||||||
|         app->view_dispatcher, bad_usb_app_back_event_callback); |         app->view_dispatcher, bad_usb_app_back_event_callback); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); |  | ||||||
| 
 |  | ||||||
|     // Custom Widget
 |     // Custom Widget
 | ||||||
|     app->widget = widget_alloc(); |     app->widget = widget_alloc(); | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
| @ -69,7 +77,11 @@ BadUsbApp* bad_usb_app_alloc() { | |||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         app->view_dispatcher, BadUsbAppViewWork, bad_usb_get_view(app->bad_usb_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); |         scene_manager_next_scene(app->scene_manager, BadUsbSceneFileSelect); | ||||||
|     } else { |     } else { | ||||||
|         scene_manager_next_scene(app->scene_manager, BadUsbSceneError); |         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(); |     FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); | ||||||
|     furi_hal_usb_set_config(&usb_hid); |     furi_hal_usb_set_config(&usb_hid); | ||||||
| 
 | 
 | ||||||
|     BadUsbApp* bad_usb_app = bad_usb_app_alloc(); |     BadUsbApp* bad_usb_app = bad_usb_app_alloc((char*)p); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_run(bad_usb_app->view_dispatcher); |     view_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->view, (BadUsbModel * model) { | ||||||
|             bad_usb->callback = callback; |             bad_usb->callback = callback; | ||||||
|             bad_usb->context = context; |             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( |     with_view_model( | ||||||
|         bad_usb->view, (BadUsbModel * model) { |         bad_usb->view, (BadUsbModel * model) { | ||||||
|             model->file_name = name; |             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) { |         bad_usb->view, (BadUsbModel * model) { | ||||||
|             memcpy(&(model->state), st, sizeof(BadUsbState)); |             memcpy(&(model->state), st, sizeof(BadUsbState)); | ||||||
|             model->anim_frame ^= 1; |             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; |     Bt* bt = context; | ||||||
|     if(bt->status == BtStatusAdvertising) { |     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) { |     } 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); |         furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); | ||||||
|         if(bt->profile == BtProfileSerial) { |         if(bt->profile == BtProfileSerial) { | ||||||
|             // Open RPC session
 |             // Open RPC session
 | ||||||
|             FURI_LOG_I(TAG, "Open RPC connection"); |  | ||||||
|             bt->rpc_session = rpc_session_open(bt->rpc); |             bt->rpc_session = rpc_session_open(bt->rpc); | ||||||
|  |             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_send_bytes_callback(bt->rpc_session, bt_rpc_send_bytes_callback); | ||||||
|                 rpc_session_set_buffer_is_empty_callback( |                 rpc_session_set_buffer_is_empty_callback( | ||||||
|                     bt->rpc_session, furi_hal_bt_serial_notify_buffer_is_empty); |                     bt->rpc_session, furi_hal_bt_serial_notify_buffer_is_empty); | ||||||
|                 rpc_session_set_context(bt->rpc_session, bt); |                 rpc_session_set_context(bt->rpc_session, bt); | ||||||
|             furi_hal_bt_serial_set_event_callback(RPC_BUFFER_SIZE, bt_serial_event_callback, 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
 |         // Update battery level
 | ||||||
|         PowerInfo info; |         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) { | static void bt_statusbar_update(Bt* bt) { | ||||||
|     if(bt->status == BtStatusAdvertising) { |     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); |         view_port_enabled_set(bt->statusbar_view_port, true); | ||||||
|     } else if(bt->status == BtStatusConnected) { |     } 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); |         view_port_enabled_set(bt->statusbar_view_port, true); | ||||||
|     } else { |     } else { | ||||||
|         view_port_enabled_set(bt->statusbar_view_port, false); |         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) { | bool bt_delete_key_storage(Bt* bt) { | ||||||
|     furi_assert(bt); |     furi_assert(bt); | ||||||
|     bool delete_succeed = false; |     bool delete_succeed = false; | ||||||
|  |     bool bt_is_active = furi_hal_bt_is_active(); | ||||||
| 
 | 
 | ||||||
|     furi_hal_bt_stop_advertising(); |     furi_hal_bt_stop_advertising(); | ||||||
|     delete_succeed = furi_hal_bt_clear_white_list(); |     delete_succeed = furi_hal_bt_clear_white_list(); | ||||||
|     if(bt->bt_settings.enabled) { |     if(bt_is_active) { | ||||||
|         furi_hal_bt_start_advertising(); |         furi_hal_bt_start_advertising(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -81,7 +81,7 @@ OneShotView* one_shot_view_alloc(void) { | |||||||
|     OneShotView* view = furi_alloc(sizeof(OneShotView)); |     OneShotView* view = furi_alloc(sizeof(OneShotView)); | ||||||
|     view->view = view_alloc(); |     view->view = view_alloc(); | ||||||
|     view->update_timer = |     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_allocate_model(view->view, ViewModelTypeLocking, sizeof(OneShotViewModel)); | ||||||
|     view_set_context(view->view, view); |     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 <storage/storage.h> | ||||||
| #include <assets_icons.h> | #include <assets_icons.h> | ||||||
| #include <gui/view_stack.h> | #include <gui/view_stack.h> | ||||||
| @ -12,23 +6,38 @@ | |||||||
| #include <portmacro.h> | #include <portmacro.h> | ||||||
| #include <stdint.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) { | static void desktop_lock_icon_callback(Canvas* canvas, void* context) { | ||||||
|     furi_assert(canvas); |     furi_assert(canvas); | ||||||
|     canvas_draw_icon(canvas, 0, 0, &I_Lock_8x8); |     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); |     furi_assert(context); | ||||||
|     Desktop* desktop = (Desktop*)context; |     Desktop* desktop = (Desktop*)context; | ||||||
|     return scene_manager_handle_custom_event(desktop->scene_manager, event); |     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); |     furi_assert(context); | ||||||
|     Desktop* desktop = (Desktop*)context; |     Desktop* desktop = (Desktop*)context; | ||||||
|     return scene_manager_handle_back_event(desktop->scene_manager); |     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_alloc() { | ||||||
|     Desktop* desktop = furi_alloc(sizeof(Desktop)); |     Desktop* desktop = furi_alloc(sizeof(Desktop)); | ||||||
| 
 | 
 | ||||||
| @ -42,6 +51,8 @@ Desktop* desktop_alloc() { | |||||||
|     view_dispatcher_enable_queue(desktop->view_dispatcher); |     view_dispatcher_enable_queue(desktop->view_dispatcher); | ||||||
|     view_dispatcher_attach_to_gui( |     view_dispatcher_attach_to_gui( | ||||||
|         desktop->view_dispatcher, desktop->gui, ViewDispatcherTypeDesktop); |         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_event_callback_context(desktop->view_dispatcher, desktop); | ||||||
|     view_dispatcher_set_custom_event_callback( |     view_dispatcher_set_custom_event_callback( | ||||||
| @ -49,37 +60,60 @@ Desktop* desktop_alloc() { | |||||||
|     view_dispatcher_set_navigation_event_callback( |     view_dispatcher_set_navigation_event_callback( | ||||||
|         desktop->view_dispatcher, desktop_back_event_callback); |         desktop->view_dispatcher, desktop_back_event_callback); | ||||||
| 
 | 
 | ||||||
|     desktop->locked_view = desktop_locked_alloc(); |  | ||||||
|     desktop->lock_menu = desktop_lock_menu_alloc(); |     desktop->lock_menu = desktop_lock_menu_alloc(); | ||||||
|     desktop->debug_view = desktop_debug_alloc(); |     desktop->debug_view = desktop_debug_alloc(); | ||||||
|     desktop->first_start_view = desktop_first_start_alloc(); |     desktop->first_start_view = desktop_first_start_alloc(); | ||||||
|     desktop->hw_mismatch_popup = popup_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_stack = view_stack_alloc(); | ||||||
|     desktop->main_view = desktop_main_alloc(); |     desktop->main_view = desktop_main_alloc(); | ||||||
|     View* dolphin_view = animation_manager_get_animation_view(desktop->animation_manager); |     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, 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, 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( |     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( |     view_dispatcher_add_view( | ||||||
|         desktop->view_dispatcher, |         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)); |         desktop_lock_menu_get_view(desktop->lock_menu)); | ||||||
|     view_dispatcher_add_view( |     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( |     view_dispatcher_add_view( | ||||||
|         desktop->view_dispatcher, |         desktop->view_dispatcher, | ||||||
|         DesktopViewFirstStart, |         DesktopViewIdFirstStart, | ||||||
|         desktop_first_start_get_view(desktop->first_start_view)); |         desktop_first_start_get_view(desktop->first_start_view)); | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         desktop->view_dispatcher, |         desktop->view_dispatcher, | ||||||
|         DesktopViewHwMismatch, |         DesktopViewIdHwMismatch, | ||||||
|         popup_get_view(desktop->hw_mismatch_popup)); |         popup_get_view(desktop->hw_mismatch_popup)); | ||||||
|     view_dispatcher_add_view( |     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
 |     // Lock icon
 | ||||||
|     desktop->lock_viewport = view_port_alloc(); |     desktop->lock_viewport = view_port_alloc(); | ||||||
|     view_port_set_width(desktop->lock_viewport, icon_get_width(&I_Lock_8x8)); |     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) { | void desktop_free(Desktop* desktop) { | ||||||
|     furi_assert(desktop); |     furi_assert(desktop); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewMain); |     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdMain); | ||||||
|     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewLockMenu); |     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLockMenu); | ||||||
|     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewLocked); |     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLocked); | ||||||
|     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewDebug); |     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdDebug); | ||||||
|     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewFirstStart); |     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdFirstStart); | ||||||
|     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewHwMismatch); |     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdHwMismatch); | ||||||
|     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewPinSetup); |     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdPinInput); | ||||||
|  |     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdPinTimeout); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_free(desktop->view_dispatcher); |     view_dispatcher_free(desktop->view_dispatcher); | ||||||
|     scene_manager_free(desktop->scene_manager); |     scene_manager_free(desktop->scene_manager); | ||||||
| 
 | 
 | ||||||
|     animation_manager_free(desktop->animation_manager); |     animation_manager_free(desktop->animation_manager); | ||||||
|     view_stack_free(desktop->main_view_stack); |     view_stack_free(desktop->main_view_stack); | ||||||
|     view_stack_free(desktop->locked_view_stack); |  | ||||||
|     desktop_main_free(desktop->main_view); |     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_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_debug_free(desktop->debug_view); | ||||||
|     desktop_first_start_free(desktop->first_start_view); |     desktop_first_start_free(desktop->first_start_view); | ||||||
|     popup_free(desktop->hw_mismatch_popup); |     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); |     osSemaphoreDelete(desktop->unload_animation_semaphore); | ||||||
| 
 | 
 | ||||||
| @ -145,14 +181,18 @@ int32_t desktop_srv(void* p) { | |||||||
|         SAVE_DESKTOP_SETTINGS(&desktop->settings); |         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); |     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()) { |     if(desktop_is_first_start()) { | ||||||
|         scene_manager_next_scene(desktop->scene_manager, DesktopSceneFirstStart); |         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 "desktop.h" | ||||||
| #include "animations/animation_manager.h" | #include "animations/animation_manager.h" | ||||||
| #include "views/desktop_main.h" | #include "views/desktop_view_pin_timeout.h" | ||||||
| #include "views/desktop_first_start.h" | #include "views/desktop_view_pin_input.h" | ||||||
| #include "views/desktop_lock_menu.h" | #include "views/desktop_view_locked.h" | ||||||
| #include "views/desktop_locked.h" | #include "views/desktop_view_main.h" | ||||||
| #include "views/desktop_debug.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 "desktop/desktop_settings/desktop_settings.h" | ||||||
| 
 | 
 | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| @ -14,21 +16,21 @@ | |||||||
| #include <gui/view_stack.h> | #include <gui/view_stack.h> | ||||||
| #include <gui/view_dispatcher.h> | #include <gui/view_dispatcher.h> | ||||||
| #include <gui/modules/popup.h> | #include <gui/modules/popup.h> | ||||||
| #include <gui/modules/code_input.h> |  | ||||||
| #include <gui/scene_manager.h> | #include <gui/scene_manager.h> | ||||||
| 
 | 
 | ||||||
| #define STATUS_BAR_Y_SHIFT 13 | #define STATUS_BAR_Y_SHIFT 13 | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     DesktopViewMain, |     DesktopViewIdMain, | ||||||
|     DesktopViewLockMenu, |     DesktopViewIdLockMenu, | ||||||
|     DesktopViewLocked, |     DesktopViewIdLocked, | ||||||
|     DesktopViewDebug, |     DesktopViewIdDebug, | ||||||
|     DesktopViewFirstStart, |     DesktopViewIdFirstStart, | ||||||
|     DesktopViewHwMismatch, |     DesktopViewIdHwMismatch, | ||||||
|     DesktopViewPinSetup, |     DesktopViewIdPinInput, | ||||||
|     DesktopViewTotal, |     DesktopViewIdPinTimeout, | ||||||
| } DesktopViewEnum; |     DesktopViewIdTotal, | ||||||
|  | } DesktopViewId; | ||||||
| 
 | 
 | ||||||
| struct Desktop { | struct Desktop { | ||||||
|     // Scene
 |     // Scene
 | ||||||
| @ -42,16 +44,15 @@ struct Desktop { | |||||||
|     Popup* hw_mismatch_popup; |     Popup* hw_mismatch_popup; | ||||||
|     DesktopLockMenuView* lock_menu; |     DesktopLockMenuView* lock_menu; | ||||||
|     DesktopDebugView* debug_view; |     DesktopDebugView* debug_view; | ||||||
|     CodeInput* code_input; |     DesktopViewLocked* locked_view; | ||||||
| 
 |  | ||||||
|     DesktopMainView* main_view; |     DesktopMainView* main_view; | ||||||
|     DesktopLockedView* locked_view; |     DesktopViewPinTimeout* pin_timeout_view; | ||||||
| 
 | 
 | ||||||
|     ViewStack* main_view_stack; |     ViewStack* main_view_stack; | ||||||
|     ViewStack* locked_view_stack; |     ViewStack* locked_view_stack; | ||||||
| 
 | 
 | ||||||
|     DesktopSettings settings; |     DesktopSettings settings; | ||||||
|     PinCode pincode_buffer; |     DesktopViewPinInput* pin_input_view; | ||||||
| 
 | 
 | ||||||
|     ViewPort* lock_viewport; |     ViewPort* lock_viewport; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <furi_hal.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include <toolbox/saved_struct.h> | #include <toolbox/saved_struct.h> | ||||||
| @ -9,6 +10,8 @@ | |||||||
| #define DESKTOP_SETTINGS_MAGIC (0x17) | #define DESKTOP_SETTINGS_MAGIC (0x17) | ||||||
| #define PIN_MAX_LENGTH 12 | #define PIN_MAX_LENGTH 12 | ||||||
| 
 | 
 | ||||||
|  | #define DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG "run_pin_setup" | ||||||
|  | 
 | ||||||
| #define SAVE_DESKTOP_SETTINGS(x) \ | #define SAVE_DESKTOP_SETTINGS(x) \ | ||||||
|     saved_struct_save(           \ |     saved_struct_save(           \ | ||||||
|         DESKTOP_SETTINGS_PATH,   \ |         DESKTOP_SETTINGS_PATH,   \ | ||||||
| @ -25,12 +28,27 @@ | |||||||
|         DESKTOP_SETTINGS_MAGIC,  \ |         DESKTOP_SETTINGS_MAGIC,  \ | ||||||
|         DESKTOP_SETTINGS_VER) |         DESKTOP_SETTINGS_VER) | ||||||
| 
 | 
 | ||||||
|  | #define MAX_PIN_SIZE 10 | ||||||
|  | #define MIN_PIN_SIZE 4 | ||||||
|  | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|  |     InputKey data[MAX_PIN_SIZE]; | ||||||
|     uint8_t length; |     uint8_t length; | ||||||
|     uint8_t data[PIN_MAX_LENGTH]; |  | ||||||
| } PinCode; | } PinCode; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     uint16_t favorite; |     uint16_t favorite; | ||||||
|     PinCode pincode; |     PinCode pin_code; | ||||||
| } DesktopSettings; | } 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 <furi.h> | ||||||
|  | #include <gui/modules/popup.h> | ||||||
|  | #include <gui/scene_manager.h> | ||||||
|  | 
 | ||||||
|  | #include "desktop_settings_app.h" | ||||||
| #include "scenes/desktop_settings_scene.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) { | static bool desktop_settings_custom_event_callback(void* context, uint32_t event) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
| @ -30,17 +34,28 @@ DesktopSettingsApp* desktop_settings_app_alloc() { | |||||||
| 
 | 
 | ||||||
|     view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); |     view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); | ||||||
| 
 | 
 | ||||||
|  |     app->popup = popup_alloc(); | ||||||
|     app->submenu = submenu_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( |     view_dispatcher_add_view( | ||||||
|         app->view_dispatcher, DesktopSettingsAppViewMenu, submenu_get_view(app->submenu)); |         app->view_dispatcher, DesktopSettingsAppViewMenu, submenu_get_view(app->submenu)); | ||||||
| 
 |     view_dispatcher_add_view( | ||||||
|     app->code_input = code_input_alloc(); |         app->view_dispatcher, DesktopSettingsAppViewIdPopup, popup_get_view(app->popup)); | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         app->view_dispatcher, |         app->view_dispatcher, | ||||||
|         DesktopSettingsAppViewPincodeInput, |         DesktopSettingsAppViewIdPinInput, | ||||||
|         code_input_get_view(app->code_input)); |         desktop_view_pin_input_get_view(app->pin_input_view)); | ||||||
| 
 |     view_dispatcher_add_view( | ||||||
|     scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneStart); |         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; |     return app; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -48,9 +63,15 @@ void desktop_settings_app_free(DesktopSettingsApp* app) { | |||||||
|     furi_assert(app); |     furi_assert(app); | ||||||
|     // Variable item list
 |     // Variable item list
 | ||||||
|     view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewMenu); |     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); |     submenu_free(app->submenu); | ||||||
|     view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewPincodeInput); |     popup_free(app->popup); | ||||||
|     code_input_free(app->code_input); |     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
 | ||||||
|     view_dispatcher_free(app->view_dispatcher); |     view_dispatcher_free(app->view_dispatcher); | ||||||
|     scene_manager_free(app->scene_manager); |     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) { | extern int32_t desktop_settings_app(void* p) { | ||||||
|     DesktopSettingsApp* app = desktop_settings_app_alloc(); |     DesktopSettingsApp* app = desktop_settings_app_alloc(); | ||||||
|     LOAD_DESKTOP_SETTINGS(&app->settings); |     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); |     view_dispatcher_run(app->view_dispatcher); | ||||||
|     desktop_settings_app_free(app); |     desktop_settings_app_free(app); | ||||||
|     return 0; |     return 0; | ||||||
|  | |||||||
| @ -1,22 +1,22 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <gui/gui.h> | #include <gui/gui.h> | ||||||
|  | #include <gui/modules/popup.h> | ||||||
| #include <gui/view_dispatcher.h> | #include <gui/view_dispatcher.h> | ||||||
| #include <gui/scene_manager.h> | #include <gui/scene_manager.h> | ||||||
| #include <gui/modules/submenu.h> | #include <gui/modules/submenu.h> | ||||||
| #include <gui/modules/code_input.h> |  | ||||||
| 
 | 
 | ||||||
| #include "desktop_settings.h" | #include "desktop_settings.h" | ||||||
| 
 | #include "desktop/views/desktop_view_pin_input.h" | ||||||
| typedef enum { | #include "views/desktop_settings_view_pin_setup_howto.h" | ||||||
|     CodeEventsSetPin, | #include "views/desktop_settings_view_pin_setup_howto2.h" | ||||||
|     CodeEventsChangePin, |  | ||||||
|     CodeEventsDisablePin, |  | ||||||
| } CodeEventsEnum; |  | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     DesktopSettingsAppViewMenu, |     DesktopSettingsAppViewMenu, | ||||||
|     DesktopSettingsAppViewPincodeInput, |     DesktopSettingsAppViewIdPopup, | ||||||
|  |     DesktopSettingsAppViewIdPinInput, | ||||||
|  |     DesktopSettingsAppViewIdPinSetupHowto, | ||||||
|  |     DesktopSettingsAppViewIdPinSetupHowto2, | ||||||
| } DesktopSettingsAppView; | } DesktopSettingsAppView; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
| @ -26,7 +26,13 @@ typedef struct { | |||||||
|     SceneManager* scene_manager; |     SceneManager* scene_manager; | ||||||
|     ViewDispatcher* view_dispatcher; |     ViewDispatcher* view_dispatcher; | ||||||
|     Submenu* submenu; |     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; |     uint8_t menu_idx; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,4 +1,11 @@ | |||||||
| ADD_SCENE(desktop_settings, start, Start) | ADD_SCENE(desktop_settings, start, Start) | ||||||
| ADD_SCENE(desktop_settings, favorite, Favorite) | ADD_SCENE(desktop_settings, favorite, Favorite) | ||||||
| ADD_SCENE(desktop_settings, pincode_menu, PinCodeMenu) | ADD_SCENE(desktop_settings, pin_menu, PinMenu) | ||||||
| ADD_SCENE(desktop_settings, pincode_input, PinCodeInput) | 
 | ||||||
|  | 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 <gui/scene_manager.h> | ||||||
| #include "applications.h" | #include <applications.h> | ||||||
| #include "desktop_settings_scene.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; |     DesktopSettingsApp* app = context; | ||||||
|     view_dispatcher_send_custom_event(app->view_dispatcher, index); |     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; |     DesktopSettingsApp* app = context; | ||||||
|     Submenu* submenu = app->submenu; |     Submenu* submenu = app->submenu; | ||||||
|     submenu_reset(submenu); |     submenu_reset(submenu); | ||||||
| 
 | 
 | ||||||
|     if(!app->settings.pincode.length) { |     if(!app->settings.pin_code.length) { | ||||||
|         submenu_add_item( |         submenu_add_item( | ||||||
|             submenu, |             submenu, | ||||||
|             "Set Pin", |             "Set Pin", | ||||||
|             CodeEventsSetPin, |             SCENE_EVENT_SET_PIN, | ||||||
|             desktop_settings_scene_pincode_menu_submenu_callback, |             desktop_settings_scene_pin_menu_submenu_callback, | ||||||
|             app); |             app); | ||||||
| 
 | 
 | ||||||
|     } else { |     } else { | ||||||
|         submenu_add_item( |         submenu_add_item( | ||||||
|             submenu, |             submenu, | ||||||
|             "Change Pin", |             "Change Pin", | ||||||
|             CodeEventsChangePin, |             SCENE_EVENT_CHANGE_PIN, | ||||||
|             desktop_settings_scene_pincode_menu_submenu_callback, |             desktop_settings_scene_pin_menu_submenu_callback, | ||||||
|             app); |             app); | ||||||
| 
 | 
 | ||||||
|         submenu_add_item( |         submenu_add_item( | ||||||
|             submenu, |             submenu, | ||||||
|             "Disable", |             "Disable", | ||||||
|             CodeEventsDisablePin, |             SCENE_EVENT_DISABLE_PIN, | ||||||
|             desktop_settings_scene_pincode_menu_submenu_callback, |             desktop_settings_scene_pin_menu_submenu_callback, | ||||||
|             app); |             app); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -41,28 +48,28 @@ void desktop_settings_scene_pincode_menu_on_enter(void* context) { | |||||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); |     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; |     DesktopSettingsApp* app = context; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         switch(event.event) { |         switch(event.event) { | ||||||
|         case CodeEventsSetPin: |         case SCENE_EVENT_SET_PIN: | ||||||
|             scene_manager_set_scene_state( |             scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto); | ||||||
|                 app->scene_manager, DesktopSettingsAppScenePinCodeInput, event.event); |  | ||||||
|             scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeInput); |  | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case CodeEventsChangePin: |         case SCENE_EVENT_CHANGE_PIN: | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|                 app->scene_manager, DesktopSettingsAppScenePinCodeInput, event.event); |                 app->scene_manager, | ||||||
|             scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeInput); |                 DesktopSettingsAppScenePinAuth, | ||||||
|  |                 SCENE_STATE_PIN_AUTH_CHANGE_PIN); | ||||||
|  |             scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinAuth); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case CodeEventsDisablePin: |         case SCENE_EVENT_DISABLE_PIN: | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|                 app->scene_manager, DesktopSettingsAppScenePinCodeInput, event.event); |                 app->scene_manager, DesktopSettingsAppScenePinAuth, SCENE_STATE_PIN_AUTH_DISABLE); | ||||||
|             scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeInput); |             scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinAuth); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
| @ -73,7 +80,7 @@ bool desktop_settings_scene_pincode_menu_on_event(void* context, SceneManagerEve | |||||||
|     return consumed; |     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; |     DesktopSettingsApp* app = context; | ||||||
|     submenu_reset(app->submenu); |     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 "../desktop_settings_app.h" | ||||||
| #include "applications.h" |  | ||||||
| #include "desktop_settings_scene.h" | #include "desktop_settings_scene.h" | ||||||
| 
 | 
 | ||||||
| enum DesktopSettingsStartSubmenuIndex { | #define SCENE_EVENT_SELECT_FAVORITE 0 | ||||||
|     DesktopSettingsStartSubmenuIndexFavorite, | #define SCENE_EVENT_SELECT_PIN_SETUP 1 | ||||||
|     DesktopSettingsStartSubmenuIndexPinSetup, |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| static void desktop_settings_scene_start_submenu_callback(void* context, uint32_t index) { | static void desktop_settings_scene_start_submenu_callback(void* context, uint32_t index) { | ||||||
|     DesktopSettingsApp* app = context; |     DesktopSettingsApp* app = context; | ||||||
| @ -19,14 +18,14 @@ void desktop_settings_scene_start_on_enter(void* context) { | |||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         submenu, |         submenu, | ||||||
|         "Favorite App", |         "Favorite App", | ||||||
|         DesktopSettingsStartSubmenuIndexFavorite, |         SCENE_EVENT_SELECT_FAVORITE, | ||||||
|         desktop_settings_scene_start_submenu_callback, |         desktop_settings_scene_start_submenu_callback, | ||||||
|         app); |         app); | ||||||
| 
 | 
 | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         submenu, |         submenu, | ||||||
|         "PIN Setup", |         "PIN Setup", | ||||||
|         DesktopSettingsStartSubmenuIndexPinSetup, |         SCENE_EVENT_SELECT_PIN_SETUP, | ||||||
|         desktop_settings_scene_start_submenu_callback, |         desktop_settings_scene_start_submenu_callback, | ||||||
|         app); |         app); | ||||||
| 
 | 
 | ||||||
| @ -39,12 +38,12 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even | |||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         switch(event.event) { |         switch(event.event) { | ||||||
|         case DesktopSettingsStartSubmenuIndexFavorite: |         case SCENE_EVENT_SELECT_FAVORITE: | ||||||
|             scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); |             scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case DesktopSettingsStartSubmenuIndexPinSetup: |         case SCENE_EVENT_SELECT_PIN_SETUP: | ||||||
|             scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeMenu); |             scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinMenu); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             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, debug, Debug) | ||||||
| ADD_SCENE(desktop, first_start, FirstStart) | ADD_SCENE(desktop, first_start, FirstStart) | ||||||
| ADD_SCENE(desktop, hw_mismatch, HwMismatch) | ADD_SCENE(desktop, hw_mismatch, HwMismatch) | ||||||
| ADD_SCENE(desktop, pinsetup, PinSetup) |  | ||||||
| ADD_SCENE(desktop, fault, Fault) | 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 <dolphin/helpers/dolphin_deed.h> | ||||||
| 
 | 
 | ||||||
| #include "../desktop_i.h" | #include "../desktop_i.h" | ||||||
| #include "../views/desktop_debug.h" | #include "../views/desktop_view_debug.h" | ||||||
| #include "desktop_scene.h" | #include "desktop_scene.h" | ||||||
| 
 | 
 | ||||||
| void desktop_scene_debug_callback(DesktopEvent event, void* context) { | 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_get_dolphin_data(desktop->debug_view); | ||||||
| 
 | 
 | ||||||
|     desktop_debug_set_callback(desktop->debug_view, desktop_scene_debug_callback, desktop); |     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) { | 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(); |     char* message = (char*)furi_hal_rtc_get_fault_data(); | ||||||
|     popup_set_text(popup, message, 60, 37 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter); |     popup_set_text(popup, message, 60, 37 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter); | ||||||
|     popup_set_callback(popup, desktop_scene_fault_callback); |     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) { | bool desktop_scene_fault_on_event(void* context, SceneManagerEvent event) { | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ | |||||||
| #include <storage/storage.h> | #include <storage/storage.h> | ||||||
| 
 | 
 | ||||||
| #include "../desktop_i.h" | #include "../desktop_i.h" | ||||||
| #include "../views/desktop_first_start.h" | #include "../views/desktop_view_first_start.h" | ||||||
| #include "../views/desktop_events.h" | #include "../views/desktop_events.h" | ||||||
| 
 | 
 | ||||||
| void desktop_scene_first_start_callback(DesktopEvent event, void* context) { | 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( |     desktop_first_start_set_callback( | ||||||
|         first_start_view, desktop_scene_first_start_callback, desktop); |         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) { | bool desktop_scene_first_start_on_event(void* context, SceneManagerEvent event) { | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| #include <gui/scene_manager.h> | #include <gui/scene_manager.h> | ||||||
| #include <furi_hal_version.h> | #include <furi_hal.h> | ||||||
| 
 | 
 | ||||||
| #include "desktop_scene.h" | #include "desktop_scene.h" | ||||||
| #include "../desktop_i.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, "!!!! 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_text(popup, text_buffer, 60, 37 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter); | ||||||
|     popup_set_callback(popup, desktop_scene_hw_mismatch_callback); |     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) { | bool desktop_scene_hw_mismatch_on_event(void* context, SceneManagerEvent event) { | ||||||
|  | |||||||
| @ -1,7 +1,4 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| typedef enum { | #define SCENE_LOCKED_FIRST_ENTER 0 | ||||||
|     DesktopMainSceneStateUnlocked, | #define SCENE_LOCKED_REPEAT_ENTER 1 | ||||||
|     DesktopMainSceneStateLockedWithPin, |  | ||||||
|     DesktopMainSceneStateLockedNoPin, |  | ||||||
| } DesktopMainSceneState; |  | ||||||
|  | |||||||
| @ -1,8 +1,13 @@ | |||||||
|  | #include <gui/scene_manager.h> | ||||||
|  | #include <applications.h> | ||||||
|  | #include <furi_hal.h> | ||||||
| #include <toolbox/saved_struct.h> | #include <toolbox/saved_struct.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
|  | #include <loader/loader.h> | ||||||
| 
 | 
 | ||||||
| #include "../desktop_i.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_i.h" | ||||||
| #include "desktop_scene.h" | #include "desktop_scene.h" | ||||||
| 
 | 
 | ||||||
| @ -15,36 +20,50 @@ void desktop_scene_lock_menu_on_enter(void* context) { | |||||||
|     Desktop* desktop = (Desktop*)context; |     Desktop* desktop = (Desktop*)context; | ||||||
| 
 | 
 | ||||||
|     LOAD_DESKTOP_SETTINGS(&desktop->settings); |     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_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); |     view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdLockMenu); | ||||||
|     desktop_lock_menu_set_idx(desktop->lock_menu, idx); |  | ||||||
|     view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLockMenu); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { | bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { | ||||||
|     Desktop* desktop = (Desktop*)context; |     Desktop* desktop = (Desktop*)context; | ||||||
|     bool consumed = false; |     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) { |         switch(event.event) { | ||||||
|         case DesktopLockMenuEventLock: |         case DesktopLockMenuEventLock: | ||||||
|             scene_manager_set_scene_state( |  | ||||||
|                 desktop->scene_manager, DesktopSceneMain, DesktopMainSceneStateLockedNoPin); |  | ||||||
|             scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); |             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; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case DesktopLockMenuEventPinLock: |         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( |                 scene_manager_set_scene_state( | ||||||
|                     desktop->scene_manager, DesktopSceneMain, DesktopMainSceneStateLockedWithPin); |                     desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER); | ||||||
|                 scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); |                 scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); | ||||||
|             } else { |             } else { | ||||||
|                 scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 1); |                 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; |             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 <assets_icons.h> | ||||||
| #include <loader/loader.h> | #include <loader/loader.h> | ||||||
| 
 | 
 | ||||||
| #include "desktop/desktop_i.h" | #include "../desktop_i.h" | ||||||
| #include "desktop/views/desktop_main.h" | #include "../views/desktop_events.h" | ||||||
|  | #include "../views/desktop_view_main.h" | ||||||
| #include "desktop_scene.h" | #include "desktop_scene.h" | ||||||
| #include "desktop_scene_i.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) { | static void desktop_scene_main_app_started_callback(const void* message, void* context) { | ||||||
|     furi_assert(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) { | static void desktop_scene_main_new_idle_animation_callback(void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     Desktop* desktop = 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) { | static void desktop_scene_main_check_animation_callback(void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     Desktop* desktop = 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) { | static void desktop_scene_main_interact_animation_callback(void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     Desktop* desktop = 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) { | 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); |         desktop->animation_manager, desktop_scene_main_check_animation_callback); | ||||||
|     animation_manager_set_interact_callback( |     animation_manager_set_interact_callback( | ||||||
|         desktop->animation_manager, desktop_scene_main_interact_animation_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); |     furi_assert(osSemaphoreGetCount(desktop->unload_animation_semaphore) == 0); | ||||||
|     Loader* loader = furi_record_open("loader"); |     Loader* loader = furi_record_open("loader"); | ||||||
|     desktop->app_start_stop_subscription = furi_pubsub_subscribe( |     desktop->app_start_stop_subscription = furi_pubsub_subscribe( | ||||||
|         loader_get_pubsub(loader), desktop_scene_main_app_started_callback, desktop); |         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"); |     furi_record_close("loader"); | ||||||
| 
 | 
 | ||||||
|     desktop_main_set_callback(main_view, desktop_scene_main_callback, desktop); |     desktop_main_set_callback(main_view, desktop_scene_main_callback, desktop); | ||||||
| 
 | 
 | ||||||
|     DesktopMainSceneState state = |     view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdMain); | ||||||
|         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); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { | 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"); |                 Loader* loader = furi_record_open("loader"); | ||||||
|                 LoaderStatus status = |                 LoaderStatus status = | ||||||
|                     loader_start(loader, FLIPPER_APPS[desktop->settings.favorite].name, NULL); |                     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"); |                 furi_record_close("loader"); | ||||||
|             } else { |             } else { | ||||||
|                 FURI_LOG_E("DesktopSrv", "Can't find favorite application"); |                 FURI_LOG_E(TAG, "Can't find favorite application"); | ||||||
|             } |             } | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         case DesktopMainEventCheckAnimation: |         case DesktopAnimationEventCheckAnimation: | ||||||
|             animation_manager_check_blocking_process(desktop->animation_manager); |             animation_manager_check_blocking_process(desktop->animation_manager); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case DesktopMainEventNewIdleAnimation: |         case DesktopAnimationEventNewIdleAnimation: | ||||||
|             animation_manager_new_idle_process(desktop->animation_manager); |             animation_manager_new_idle_process(desktop->animation_manager); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case DesktopMainEventInteractAnimation: |         case DesktopAnimationEventInteractAnimation: | ||||||
|             animation_manager_interact_process(desktop->animation_manager); |             animation_manager_interact_process(desktop->animation_manager); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
| @ -171,16 +165,8 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { | |||||||
|             animation_manager_load_and_continue_animation(desktop->animation_manager); |             animation_manager_load_and_continue_animation(desktop->animation_manager); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case DesktopMainEventUnlocked: |         case DesktopLockedEventUpdate: | ||||||
|             consumed = true; |             desktop_view_locked_update(desktop->locked_view); | ||||||
|             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); |  | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             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_check_callback(desktop->animation_manager, NULL); | ||||||
|     animation_manager_set_interact_callback(desktop->animation_manager, NULL); |     animation_manager_set_interact_callback(desktop->animation_manager, NULL); | ||||||
|     animation_manager_set_context(desktop->animation_manager, desktop); |     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, |     DesktopMainEventOpenFavorite, | ||||||
|     DesktopMainEventOpenMenu, |     DesktopMainEventOpenMenu, | ||||||
|     DesktopMainEventOpenDebug, |     DesktopMainEventOpenDebug, | ||||||
|     DesktopMainEventUpdate, |  | ||||||
|     DesktopMainEventUnlocked, |  | ||||||
|     DesktopMainEventRightShort, |     DesktopMainEventRightShort, | ||||||
|     DesktopMainEventCheckAnimation, |  | ||||||
|     DesktopMainEventNewIdleAnimation, |  | ||||||
|     DesktopMainEventInteractAnimation, |  | ||||||
|     DesktopMainEventBeforeAppStarted, |     DesktopMainEventBeforeAppStarted, | ||||||
|     DesktopMainEventAfterAppFinished, |     DesktopMainEventAfterAppFinished, | ||||||
|     DesktopLockedEventUnlock, | 
 | ||||||
|     DesktopLockedEventCheckAnimation, |     DesktopLockedEventUnlocked, | ||||||
|     DesktopLockedEventMax, |     DesktopLockedEventUpdate, | ||||||
|  |     DesktopLockedEventShowPinInput, | ||||||
|  | 
 | ||||||
|  |     DesktopPinInputEventResetWrongPinLabel, | ||||||
|  |     DesktopPinInputEventUnlocked, | ||||||
|  |     DesktopPinInputEventUnlockFailed, | ||||||
|  |     DesktopPinInputEventBack, | ||||||
|  | 
 | ||||||
|  |     DesktopPinTimeoutExit, | ||||||
|  | 
 | ||||||
|     DesktopDebugEventDeed, |     DesktopDebugEventDeed, | ||||||
|     DesktopDebugEventWrongDeed, |     DesktopDebugEventWrongDeed, | ||||||
|     DesktopDebugEventSaveState, |     DesktopDebugEventSaveState, | ||||||
|     DesktopDebugEventExit, |     DesktopDebugEventExit, | ||||||
|  | 
 | ||||||
|     DesktopFirstStartCompleted, |     DesktopFirstStartCompleted, | ||||||
|     DesktopFirstStartPoweroff, |     DesktopFirstStartPoweroff, | ||||||
|  | 
 | ||||||
|     DesktopLockMenuEventLock, |     DesktopLockMenuEventLock, | ||||||
|     DesktopLockMenuEventPinLock, |     DesktopLockMenuEventPinLock, | ||||||
|     DesktopLockMenuEventExit, |     DesktopLockMenuEventExit, | ||||||
|  | 
 | ||||||
|  |     DesktopAnimationEventCheckAnimation, | ||||||
|  |     DesktopAnimationEventNewIdleAnimation, | ||||||
|  |     DesktopAnimationEventInteractAnimation, | ||||||
|  | 
 | ||||||
| } DesktopEvent; | } 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 <toolbox/version.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
|  | #include <dolphin/helpers/dolphin_state.h> | ||||||
|  | #include <dolphin/dolphin.h> | ||||||
| 
 | 
 | ||||||
| #include "../desktop_i.h" | #include "../desktop_i.h" | ||||||
| #include "desktop_debug.h" | #include "desktop_view_debug.h" | ||||||
| #include "dolphin/helpers/dolphin_state.h" |  | ||||||
| #include "dolphin/dolphin.h" |  | ||||||
| 
 | 
 | ||||||
| void desktop_debug_set_callback( | void desktop_debug_set_callback( | ||||||
|     DesktopDebugView* debug_view, |     DesktopDebugView* debug_view, | ||||||
| @ -1,8 +1,9 @@ | |||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
| #include <gui/elements.h> | #include <gui/elements.h> | ||||||
|  | 
 | ||||||
| #include "../desktop_i.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_SHORT 5000 | ||||||
| #define DESKTOP_FIRST_START_POWEROFF_LONG (60 * 60 * 1000) | #define DESKTOP_FIRST_START_POWEROFF_LONG (60 * 60 * 1000) | ||||||
| @ -2,7 +2,7 @@ | |||||||
| #include <gui/elements.h> | #include <gui/elements.h> | ||||||
| 
 | 
 | ||||||
| #include "../desktop_i.h" | #include "../desktop_i.h" | ||||||
| #include "desktop_lock_menu.h" | #include "desktop_view_lock_menu.h" | ||||||
| 
 | 
 | ||||||
| #define LOCK_MENU_ITEMS_NB 3 | #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 <dolphin/dolphin.h> | ||||||
| 
 | 
 | ||||||
| #include "../desktop_i.h" | #include "../desktop_i.h" | ||||||
| #include "desktop_main.h" | #include "desktop_view_main.h" | ||||||
| 
 | 
 | ||||||
| struct DesktopMainView { | struct DesktopMainView { | ||||||
|     View* view; |     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->event_queue = osMessageQueueNew(8, sizeof(DolphinEvent), NULL); | ||||||
|     dolphin->pubsub = furi_pubsub_alloc(); |     dolphin->pubsub = furi_pubsub_alloc(); | ||||||
|     dolphin->butthurt_timer = xTimerCreate( |     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 = |     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( |     dolphin->clear_limits_timer = xTimerCreate( | ||||||
|         "Clear limits timer", |         NULL, HOURS_IN_TICKS(24), pdTRUE, dolphin, dolphin_clear_limits_timer_callback); | ||||||
|         HOURS_IN_TICKS(24), |  | ||||||
|         pdTRUE, |  | ||||||
|         dolphin, |  | ||||||
|         dolphin_clear_limits_timer_callback); |  | ||||||
| 
 | 
 | ||||||
|     return dolphin; |     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->rx_active = (model->rx_cnt != st->rx_cnt); | ||||||
|             model->tx_cnt = st->tx_cnt; |             model->tx_cnt = st->tx_cnt; | ||||||
|             model->rx_cnt = st->rx_cnt; |             model->rx_cnt = st->rx_cnt; | ||||||
|             return false; |             return true; | ||||||
|         }); |         }); | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,4 +1,7 @@ | |||||||
| #include "elements.h" | #include "elements.h" | ||||||
|  | #include <assets_icons.h> | ||||||
|  | #include "furi_hal_resources.h" | ||||||
|  | #include <furi_hal.h> | ||||||
| #include "gui/canvas.h" | #include "gui/canvas.h" | ||||||
| 
 | 
 | ||||||
| #include <gui/icon_i.h> | #include <gui/icon_i.h> | ||||||
| @ -337,6 +340,47 @@ void elements_slightly_rounded_box( | |||||||
|     canvas_draw_rbox(canvas, x, y, width, height, 1); |     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) { | void elements_bubble(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height) { | ||||||
|     furi_assert(canvas); |     furi_assert(canvas); | ||||||
|     canvas_draw_rframe(canvas, x + 4, y, width, height, 3); |     canvas_draw_rframe(canvas, x + 4, y, width, height, 3); | ||||||
|  | |||||||
| @ -150,6 +150,19 @@ void elements_slightly_rounded_box( | |||||||
|     uint8_t width, |     uint8_t width, | ||||||
|     uint8_t height); |     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
 | /** Draw bubble frame for text
 | ||||||
|  * |  * | ||||||
|  * @param   canvas  Canvas instance |  * @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,29 +253,28 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Deliver event
 |     // Deliver event
 | ||||||
|     if(view_dispatcher->ongoing_input_view == view_dispatcher->current_view) { |     if(view_dispatcher->current_view && | ||||||
|         bool is_consumed = false; |        view_dispatcher->ongoing_input_view == view_dispatcher->current_view) { | ||||||
|         if(view_dispatcher->current_view) { |         // Dispatch input to current view
 | ||||||
|             is_consumed = view_input(view_dispatcher->current_view, event); |         bool is_consumed = view_input(view_dispatcher->current_view, event); | ||||||
|         } | 
 | ||||||
|         if(!is_consumed && (event->type == InputTypeShort || event->type == InputTypeLong)) { |         // Navigate if input is not consumed
 | ||||||
|             // TODO remove view navigation handlers
 |         if(!is_consumed && (event->key == InputKeyBack) && | ||||||
|             uint32_t view_id = VIEW_IGNORE; |            (event->type == InputTypeShort || event->type == InputTypeLong)) { | ||||||
|             if(event->key == InputKeyBack) { |             // Navigate to previous
 | ||||||
|                 view_id = view_previous(view_dispatcher->current_view); |             uint32_t view_id = view_previous(view_dispatcher->current_view); | ||||||
|                 if((view_id == VIEW_IGNORE) && (view_dispatcher->navigation_event_callback)) { |             if(view_id != VIEW_IGNORE) { | ||||||
|                     is_consumed = |                 // Switch to returned view
 | ||||||
|                         view_dispatcher->navigation_event_callback(view_dispatcher->event_context); |                 view_dispatcher_switch_to_view(view_dispatcher, view_id); | ||||||
|                     if(!is_consumed) { |             } 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); |                     view_dispatcher_stop(view_dispatcher); | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|             if(!is_consumed) { |  | ||||||
|                 view_dispatcher_switch_to_view(view_dispatcher, view_id); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } else if(view_dispatcher->ongoing_input_view && event->type == InputTypeRelease) { |     } else if(view_dispatcher->ongoing_input_view && event->type == InputTypeRelease) { | ||||||
|         FURI_LOG_D( |         FURI_LOG_D( | ||||||
|             TAG, |             TAG, | ||||||
|  | |||||||
| @ -59,9 +59,8 @@ KeyReader::~KeyReader() { | |||||||
| bool KeyReader::read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_size) { | bool KeyReader::read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_size) { | ||||||
|     bool readed = false; |     bool readed = false; | ||||||
| 
 | 
 | ||||||
|     switch(read_mode) { |     if(read_mode == ReadMode::DALLAS) { | ||||||
|     case ReadMode::DALLAS: |         FURI_CRITICAL_ENTER(); | ||||||
|         __disable_irq(); |  | ||||||
|         if(onewire_master->search(data)) { |         if(onewire_master->search(data)) { | ||||||
|             onewire_master->reset_search(); |             onewire_master->reset_search(); | ||||||
|             readed = true; |             readed = true; | ||||||
| @ -69,9 +68,8 @@ bool KeyReader::read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_s | |||||||
|         } else { |         } else { | ||||||
|             onewire_master->reset_search(); |             onewire_master->reset_search(); | ||||||
|         } |         } | ||||||
|         __enable_irq(); |         FURI_CRITICAL_EXIT(); | ||||||
|         break; |     } else if(read_mode == ReadMode::CYFRAL_METAKOM) { | ||||||
|     case ReadMode::CYFRAL_METAKOM: |  | ||||||
|         if(cyfral_decoder.read(data, 2)) { |         if(cyfral_decoder.read(data, 2)) { | ||||||
|             readed = true; |             readed = true; | ||||||
|             *key_type = iButtonKeyType::KeyCyfral; |             *key_type = iButtonKeyType::KeyCyfral; | ||||||
| @ -79,7 +77,6 @@ bool KeyReader::read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_s | |||||||
|             readed = true; |             readed = true; | ||||||
|             *key_type = iButtonKeyType::KeyMetakom; |             *key_type = iButtonKeyType::KeyMetakom; | ||||||
|         } |         } | ||||||
|         break; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return readed; |     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 KeyReader::verify_key(iButtonKeyType key_type, const uint8_t* const data, uint8_t data_size) { | ||||||
|     bool result = true; |     bool result = true; | ||||||
| 
 | 
 | ||||||
|     switch(key_type) { |     if(key_type == iButtonKeyType::KeyDallas) { | ||||||
|     case iButtonKeyType::KeyDallas: |  | ||||||
|         switch_to(ReadMode::DALLAS); |         switch_to(ReadMode::DALLAS); | ||||||
|         __disable_irq(); | 
 | ||||||
|  |         FURI_CRITICAL_ENTER(); | ||||||
|         if(onewire_master->reset()) { |         if(onewire_master->reset()) { | ||||||
|             onewire_master->write(DS1990::CMD_READ_ROM); |             onewire_master->write(DS1990::CMD_READ_ROM); | ||||||
|             for(uint8_t i = 0; i < data_size; i++) { |             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 { |         } else { | ||||||
|             result = false; |             result = false; | ||||||
|             break; |  | ||||||
|         } |         } | ||||||
|         __enable_irq(); |         FURI_CRITICAL_EXIT(); | ||||||
|         break; |  | ||||||
| 
 | 
 | ||||||
|     default: |     } else { | ||||||
|         result = false; |         result = false; | ||||||
|         break; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return result; |     return result; | ||||||
|  | |||||||
| @ -74,7 +74,7 @@ bool KeyWriter::compare_key_ds1990(iButtonKey* key) { | |||||||
|     bool result = false; |     bool result = false; | ||||||
| 
 | 
 | ||||||
|     if(key->get_key_type() == iButtonKeyType::KeyDallas) { |     if(key->get_key_type() == iButtonKeyType::KeyDallas) { | ||||||
|         __disable_irq(); |         FURI_CRITICAL_ENTER(); | ||||||
|         bool presence = onewire_master->reset(); |         bool presence = onewire_master->reset(); | ||||||
| 
 | 
 | ||||||
|         if(presence) { |         if(presence) { | ||||||
| @ -89,7 +89,7 @@ bool KeyWriter::compare_key_ds1990(iButtonKey* key) { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         __enable_irq(); |         FURI_CRITICAL_EXIT(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return result; |     return result; | ||||||
| @ -99,7 +99,7 @@ bool KeyWriter::write_1990_1(iButtonKey* key) { | |||||||
|     bool result = false; |     bool result = false; | ||||||
| 
 | 
 | ||||||
|     if(key->get_key_type() == iButtonKeyType::KeyDallas) { |     if(key->get_key_type() == iButtonKeyType::KeyDallas) { | ||||||
|         __disable_irq(); |         FURI_CRITICAL_ENTER(); | ||||||
| 
 | 
 | ||||||
|         // unlock
 |         // unlock
 | ||||||
|         onewire_master->reset(); |         onewire_master->reset(); | ||||||
| @ -120,7 +120,7 @@ bool KeyWriter::write_1990_1(iButtonKey* key) { | |||||||
|         onewire_master->write(RW1990_1::CMD_WRITE_RECORD_FLAG); |         onewire_master->write(RW1990_1::CMD_WRITE_RECORD_FLAG); | ||||||
|         onewire_write_one_bit(1); |         onewire_write_one_bit(1); | ||||||
| 
 | 
 | ||||||
|         __enable_irq(); |         FURI_CRITICAL_EXIT(); | ||||||
| 
 | 
 | ||||||
|         if(compare_key_ds1990(key)) { |         if(compare_key_ds1990(key)) { | ||||||
|             result = true; |             result = true; | ||||||
| @ -134,7 +134,7 @@ bool KeyWriter::write_1990_2(iButtonKey* key) { | |||||||
|     bool result = false; |     bool result = false; | ||||||
| 
 | 
 | ||||||
|     if(key->get_key_type() == iButtonKeyType::KeyDallas) { |     if(key->get_key_type() == iButtonKeyType::KeyDallas) { | ||||||
|         __disable_irq(); |         FURI_CRITICAL_ENTER(); | ||||||
| 
 | 
 | ||||||
|         // unlock
 |         // unlock
 | ||||||
|         onewire_master->reset(); |         onewire_master->reset(); | ||||||
| @ -154,7 +154,7 @@ bool KeyWriter::write_1990_2(iButtonKey* key) { | |||||||
|         onewire_master->write(RW1990_2::CMD_WRITE_RECORD_FLAG); |         onewire_master->write(RW1990_2::CMD_WRITE_RECORD_FLAG); | ||||||
|         onewire_write_one_bit(0); |         onewire_write_one_bit(0); | ||||||
| 
 | 
 | ||||||
|         __enable_irq(); |         FURI_CRITICAL_EXIT(); | ||||||
| 
 | 
 | ||||||
|         if(compare_key_ds1990(key)) { |         if(compare_key_ds1990(key)) { | ||||||
|             result = true; |             result = true; | ||||||
| @ -169,7 +169,7 @@ bool KeyWriter::write_TM2004(iButtonKey* key) { | |||||||
|     bool result = true; |     bool result = true; | ||||||
| 
 | 
 | ||||||
|     if(key->get_key_type() == iButtonKeyType::KeyDallas) { |     if(key->get_key_type() == iButtonKeyType::KeyDallas) { | ||||||
|         __disable_irq(); |         FURI_CRITICAL_ENTER(); | ||||||
| 
 | 
 | ||||||
|         // write rom, addr is 0x0000
 |         // write rom, addr is 0x0000
 | ||||||
|         onewire_master->reset(); |         onewire_master->reset(); | ||||||
| @ -204,7 +204,7 @@ bool KeyWriter::write_TM2004(iButtonKey* key) { | |||||||
| 
 | 
 | ||||||
|         onewire_master->reset(); |         onewire_master->reset(); | ||||||
| 
 | 
 | ||||||
|         __enable_irq(); |         FURI_CRITICAL_EXIT(); | ||||||
|     } else { |     } else { | ||||||
|         result = false; |         result = false; | ||||||
|     } |     } | ||||||
| @ -216,7 +216,7 @@ bool KeyWriter::write_TM01(iButtonKey* key) { | |||||||
|     /*bool result = true;
 |     /*bool result = true;
 | ||||||
| 
 | 
 | ||||||
|     // TODO test and encoding
 |     // TODO test and encoding
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
| 
 | 
 | ||||||
|     // unlock
 |     // unlock
 | ||||||
|     onewire_master->reset(); |     onewire_master->reset(); | ||||||
| @ -240,13 +240,13 @@ bool KeyWriter::write_TM01(iButtonKey* key) { | |||||||
|     onewire_master->write(TM01::CMD_WRITE_RECORD_FLAG); |     onewire_master->write(TM01::CMD_WRITE_RECORD_FLAG); | ||||||
|     onewire_write_one_bit(0, 10000); |     onewire_write_one_bit(0, 10000); | ||||||
| 
 | 
 | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| 
 | 
 | ||||||
|     if(!compare_key_ds1990(key)) { |     if(!compare_key_ds1990(key)) { | ||||||
|         result = false; |         result = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
| 
 | 
 | ||||||
|     if(key->get_key_type() == iButtonKeyType::KeyMetakom || |     if(key->get_key_type() == iButtonKeyType::KeyMetakom || | ||||||
|        key->get_key_type() == iButtonKeyType::KeyCyfral) { |        key->get_key_type() == iButtonKeyType::KeyCyfral) { | ||||||
| @ -258,7 +258,7 @@ bool KeyWriter::write_TM01(iButtonKey* key) { | |||||||
|         onewire_write_one_bit(1); |         onewire_write_one_bit(1); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| 
 | 
 | ||||||
|     return result;*/ |     return result;*/ | ||||||
|     return false; |     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)); |     em_card.encode(em_data, 5, reinterpret_cast<uint8_t*>(&em_encoded_data), sizeof(uint64_t)); | ||||||
|     const uint32_t em_config_block_data = 0b00000000000101001000000001000000; |     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, 0, false, em_config_block_data); | ||||||
|     write_block(0, 1, false, em_encoded_data); |     write_block(0, 1, false, em_encoded_data); | ||||||
|     write_block(0, 2, false, em_encoded_data >> 32); |     write_block(0, 2, false, em_encoded_data >> 32); | ||||||
|     write_reset(); |     write_reset(); | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RfidWriter::write_hid(const uint8_t hid_data[3]) { | 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; |     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, 0, false, hid_config_block_data); | ||||||
|     write_block(0, 1, false, card_data[0]); |     write_block(0, 1, false, card_data[0]); | ||||||
|     write_block(0, 2, false, card_data[1]); |     write_block(0, 2, false, card_data[1]); | ||||||
|     write_block(0, 3, false, card_data[2]); |     write_block(0, 3, false, card_data[2]); | ||||||
|     write_reset(); |     write_reset(); | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RfidWriter::write_indala(const uint8_t indala_data[3]) { | 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; |     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, 0, false, indala_config_block_data); | ||||||
|     write_block(0, 1, false, card_data[0]); |     write_block(0, 1, false, card_data[0]); | ||||||
|     write_block(0, 2, false, card_data[1]); |     write_block(0, 2, false, card_data[1]); | ||||||
|     write_reset(); |     write_reset(); | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,3 +1,4 @@ | |||||||
|  | #include "applications.h" | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include "loader/loader.h" | #include "loader/loader.h" | ||||||
| #include "loader_i.h" | #include "loader_i.h" | ||||||
| @ -9,28 +10,46 @@ | |||||||
| 
 | 
 | ||||||
| static Loader* loader_instance = NULL; | 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) { | 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(application->app); | ||||||
|     furi_assert(flipper_app->name); |     furi_assert(application->name); | ||||||
| 
 | 
 | ||||||
|     if(!loader_lock(loader_instance)) return; |     if(!loader_lock(loader_instance)) { | ||||||
| 
 |         FURI_LOG_E(TAG, "Loader is locked"); | ||||||
|     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); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     furi_hal_power_insomnia_enter(); |  | ||||||
| 
 | 
 | ||||||
|     loader_instance->current_app = flipper_app; |     loader_start_application(application, NULL); | ||||||
| 
 |  | ||||||
|     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); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void loader_submenu_callback(void* context, uint32_t index) { | 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)) { |     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||||
|         for(size_t i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) { |         for(size_t i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) { | ||||||
|             if(strcmp(name, FLIPPER_DEBUG_APPS[i].name) == 0) { |             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) { | 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) { |     do { | ||||||
|  |         if(!args_read_probably_quoted_string_and_trim(args, application_name)) { | ||||||
|             printf("No application provided\r\n"); |             printf("No application provided\r\n"); | ||||||
|         return; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     const FlipperApplication* application = loader_find_application_by_name(string_get_cstr(args)); |         const FlipperApplication* application = | ||||||
|  |             loader_find_application_by_name(string_get_cstr(application_name)); | ||||||
|         if(!application) { |         if(!application) { | ||||||
|         printf("%s doesn't exists\r\n", string_get_cstr(args)); |             printf("%s doesn't exists\r\n", string_get_cstr(application_name)); | ||||||
|         return; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     if(furi_thread_get_state(instance->thread) != FuriThreadStateStopped) { |         string_strim(args); | ||||||
|  |         if(!loader_start_application(application, string_get_cstr(args))) { | ||||||
|             printf("Can't start, furi application is running"); |             printf("Can't start, furi application is running"); | ||||||
|             return; |             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++; |     string_clear(application_name); | ||||||
|     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); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void loader_cli_list(Cli* cli, string_t args, Loader* instance) { | 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) { | LoaderStatus loader_start(Loader* instance, const char* name, const char* args) { | ||||||
|     furi_assert(name); |     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); |         FURI_LOG_E(TAG, "Can't find application with name %s", name); | ||||||
|         return LoaderStatusErrorUnknownApp; |         return LoaderStatusErrorUnknownApp; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool locked = loader_lock(instance); |     if(!loader_lock(loader_instance)) { | ||||||
| 
 |         FURI_LOG_E(TAG, "Loader is locked"); | ||||||
|     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 */ |  | ||||||
|         return LoaderStatusErrorAppStarted; |         return LoaderStatusErrorAppStarted; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     instance->current_app = flipper_app; |     if(!loader_start_application(application, args)) { | ||||||
|     void* thread_args = NULL; |         return LoaderStatusErrorInternal; | ||||||
|     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); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     furi_thread_set_name(instance->thread, flipper_app->name); |     return LoaderStatusOk; | ||||||
|     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; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool loader_lock(Loader* instance) { | bool loader_lock(Loader* instance) { | ||||||
|     bool ret = false; |     FURI_CRITICAL_ENTER(); | ||||||
|     furi_check(osMutexAcquire(instance->mutex, osWaitForever) == osOK); |     bool result = false; | ||||||
|     if(instance->lock_semaphore == 0) { |     if(instance->lock_count == 0) { | ||||||
|         instance->lock_semaphore++; |         instance->lock_count++; | ||||||
|         ret = true; |         result = true; | ||||||
|     } |     } | ||||||
|     furi_check(osMutexRelease(instance->mutex) == osOK); |     FURI_CRITICAL_EXIT(); | ||||||
|     return ret; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void loader_unlock(Loader* instance) { | void loader_unlock(Loader* instance) { | ||||||
|     furi_check(osMutexAcquire(instance->mutex, osWaitForever) == osOK); |     FURI_CRITICAL_ENTER(); | ||||||
|     furi_check(instance->lock_semaphore > 0); |     if(instance->lock_count > 0) instance->lock_count--; | ||||||
|     instance->lock_semaphore--; |     FURI_CRITICAL_EXIT(); | ||||||
|     furi_check(osMutexRelease(instance->mutex) == osOK); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool loader_is_locked(Loader* instance) { | 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) { | 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) { |     if(thread_state == FuriThreadStateRunning) { | ||||||
|         event.type = LoaderEventTypeApplicationStarted; |         event.type = LoaderEventTypeApplicationStarted; | ||||||
|         furi_pubsub_publish(loader_instance->pubsub, &event); |         furi_pubsub_publish(loader_instance->pubsub, &event); | ||||||
|  |         furi_hal_power_insomnia_enter(); | ||||||
| 
 | 
 | ||||||
|         // Snapshot current memory usage
 |         // Snapshot current memory usage
 | ||||||
|         instance->free_heap_size = memmgr_get_free_heap(); |         instance->free_heap_size = memmgr_get_free_heap(); | ||||||
| @ -239,7 +251,13 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con | |||||||
|             TAG, |             TAG, | ||||||
|             "Application thread stopped. Heap allocation balance: %d. Thread allocation balance: %d.", |             "Application thread stopped. Heap allocation balance: %d. Thread allocation balance: %d.", | ||||||
|             heap_diff, |             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(); |         furi_hal_power_insomnia_exit(); | ||||||
|         loader_unlock(instance); |         loader_unlock(instance); | ||||||
| 
 | 
 | ||||||
| @ -262,15 +280,12 @@ static uint32_t loader_back_to_primary_menu(void* context) { | |||||||
| static Loader* loader_alloc() { | static Loader* loader_alloc() { | ||||||
|     Loader* instance = furi_alloc(sizeof(Loader)); |     Loader* instance = furi_alloc(sizeof(Loader)); | ||||||
| 
 | 
 | ||||||
|     instance->thread = furi_thread_alloc(); |     instance->application_thread = furi_thread_alloc(); | ||||||
|     furi_thread_enable_heap_trace(instance->thread); |     furi_thread_enable_heap_trace(instance->application_thread); | ||||||
|     furi_thread_set_state_context(instance->thread, instance); |     furi_thread_set_state_context(instance->application_thread, instance); | ||||||
|     furi_thread_set_state_callback(instance->thread, loader_thread_state_callback); |     furi_thread_set_state_callback(instance->application_thread, loader_thread_state_callback); | ||||||
| 
 |  | ||||||
|     string_init(instance->args); |  | ||||||
| 
 | 
 | ||||||
|     instance->pubsub = furi_pubsub_alloc(); |     instance->pubsub = furi_pubsub_alloc(); | ||||||
|     instance->mutex = osMutexNew(NULL); |  | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_CLI | #ifdef SRV_CLI | ||||||
|     instance->cli = furi_record_open("cli"); |     instance->cli = furi_record_open("cli"); | ||||||
| @ -327,13 +342,9 @@ static void loader_free(Loader* instance) { | |||||||
|         furi_record_close("cli"); |         furi_record_close("cli"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     osMutexDelete(instance->mutex); |  | ||||||
| 
 |  | ||||||
|     furi_pubsub_free(instance->pubsub); |     furi_pubsub_free(instance->pubsub); | ||||||
| 
 | 
 | ||||||
|     string_clear(instance->args); |     furi_thread_free(instance->application_thread); | ||||||
| 
 |  | ||||||
|     furi_thread_free(instance->thread); |  | ||||||
| 
 | 
 | ||||||
|     menu_free(loader_instance->primary_menu); |     menu_free(loader_instance->primary_menu); | ||||||
|     view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewPrimary); |     view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewPrimary); | ||||||
|  | |||||||
| @ -16,9 +16,11 @@ | |||||||
| 
 | 
 | ||||||
| struct Loader { | struct Loader { | ||||||
|     osThreadId_t loader_thread; |     osThreadId_t loader_thread; | ||||||
|     FuriThread* thread; | 
 | ||||||
|     const FlipperApplication* current_app; |     const FlipperApplication* application; | ||||||
|     string_t args; |     FuriThread* application_thread; | ||||||
|  |     char* application_arguments; | ||||||
|  | 
 | ||||||
|     Cli* cli; |     Cli* cli; | ||||||
|     Gui* gui; |     Gui* gui; | ||||||
| 
 | 
 | ||||||
| @ -29,8 +31,7 @@ struct Loader { | |||||||
|     Submenu* settings_menu; |     Submenu* settings_menu; | ||||||
| 
 | 
 | ||||||
|     size_t free_heap_size; |     size_t free_heap_size; | ||||||
|     osMutexId_t mutex; |     volatile uint8_t lock_count; | ||||||
|     volatile uint8_t lock_semaphore; |  | ||||||
| 
 | 
 | ||||||
|     FuriPubSub* pubsub; |     FuriPubSub* pubsub; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -1,28 +1,29 @@ | |||||||
| #include "power_cli.h" | #include "power_cli.h" | ||||||
| 
 | 
 | ||||||
| #include <power/power_service/power.h> |  | ||||||
| #include <cli/cli.h> |  | ||||||
| #include <furi_hal.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"); |     Power* power = furi_record_open("power"); | ||||||
|     printf("It's now safe to disconnect USB from your flipper\r\n"); |     printf("It's now safe to disconnect USB from your flipper\r\n"); | ||||||
|     power_off(power); |     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); |     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); |     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(); |     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")) { |     if(!string_cmp(args, "0")) { | ||||||
|         furi_hal_power_disable_otg(); |         furi_hal_power_disable_otg(); | ||||||
|     } else if(!string_cmp(args, "1")) { |     } 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")) { |     if(!string_cmp(args, "0")) { | ||||||
|         furi_hal_power_disable_external_3_3v(); |         furi_hal_power_disable_external_3_3v(); | ||||||
|     } else if(!string_cmp(args, "1")) { |     } 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() { | void power_on_system_start() { | ||||||
| #ifdef SRV_CLI | #ifdef SRV_CLI | ||||||
|     Cli* cli = furi_record_open("cli"); |     Cli* cli = furi_record_open("cli"); | ||||||
| 
 | 
 | ||||||
|     cli_add_command(cli, "poweroff", CliCommandFlagParallelSafe, power_cli_poweroff, NULL); |     cli_add_command(cli, "power", CliCommandFlagParallelSafe, power_cli, 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); |  | ||||||
| 
 | 
 | ||||||
|     furi_record_close("cli"); |     furi_record_close("cli"); | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ void power_off(Power* power) { | |||||||
|     view_dispatcher_send_to_front(power->view_dispatcher); |     view_dispatcher_send_to_front(power->view_dispatcher); | ||||||
|     view_dispatcher_switch_to_view(power->view_dispatcher, PowerViewPopup); |     view_dispatcher_switch_to_view(power->view_dispatcher, PowerViewPopup); | ||||||
|     osDelay(10); |     osDelay(10); | ||||||
|     furi_crash("Disconnect USB for safe shutdown"); |     furi_halt("Disconnect USB for safe shutdown"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void power_reboot(PowerBootMode mode) { | void power_reboot(PowerBootMode mode) { | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ | |||||||
| typedef enum { | typedef enum { | ||||||
|     SubghzCustomEventManagerNoSet = 0, |     SubghzCustomEventManagerNoSet = 0, | ||||||
|     SubghzCustomEventManagerSet, |     SubghzCustomEventManagerSet, | ||||||
|  |     SubghzCustomEventManagerSetRAW, | ||||||
| 
 | 
 | ||||||
|     SubghzCustomEventSceneDeleteSuccess = 100, |     SubghzCustomEventSceneDeleteSuccess = 100, | ||||||
|     SubghzCustomEventSceneDelete, |     SubghzCustomEventSceneDelete, | ||||||
|  | |||||||
| @ -261,7 +261,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | |||||||
|         case SubghzCustomEventViewReadRAWSave: |         case SubghzCustomEventViewReadRAWSave: | ||||||
|             if(subghz_scene_read_raw_update_filename(subghz)) { |             if(subghz_scene_read_raw_update_filename(subghz)) { | ||||||
|                 scene_manager_set_scene_state( |                 scene_manager_set_scene_state( | ||||||
|                     subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerSet); |                     subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerSetRAW); | ||||||
|                 subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; |                 subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; | ||||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); |                 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; |         dev_name_empty = true; | ||||||
|     } else { |     } else { | ||||||
|         strcpy(subghz->file_name_tmp, subghz->file_name); |         strcpy(subghz->file_name_tmp, subghz->file_name); | ||||||
|         if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == |         if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != | ||||||
|            SubghzCustomEventManagerSet) { |            SubghzCustomEventManagerNoSet) { | ||||||
|             subghz_get_next_name_file(subghz); |             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); |                     subghz_save_protocol_to_file(subghz, subghz->file_name); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == |                 if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != | ||||||
|                    SubghzCustomEventManagerSet) { |                    SubghzCustomEventManagerNoSet) { | ||||||
|                     subghz_protocol_raw_set_last_file_name( |                     subghz_protocol_raw_set_last_file_name( | ||||||
|                         (SubGhzProtocolRAW*)subghz->txrx->protocol_result, subghz->file_name); |                         (SubGhzProtocolRAW*)subghz->txrx->protocol_result, subghz->file_name); | ||||||
|                     scene_manager_set_scene_state( |                     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); |         view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventConnect); | ||||||
|     else if(evt == U2fNotifyDisconnect) |     else if(evt == U2fNotifyDisconnect) | ||||||
|         view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventDisconnect); |         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) { | 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) { |             if(app->event_cur != U2fCustomEventNone) { | ||||||
|                 u2f_confirm_user_present(app->u2f_instance); |                 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; |         consumed = true; | ||||||
|     } else if(event.type == SceneManagerEventTypeTick) { |  | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -186,6 +186,13 @@ static uint16_t u2f_register(U2fData* U2F, uint8_t* buf) { | |||||||
|     uint8_t hash[32]; |     uint8_t hash[32]; | ||||||
|     uint8_t signature[64]; |     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->callback != NULL) U2F->callback(U2fNotifyRegister, U2F->context); | ||||||
|     if(U2F->user_present == false) { |     if(U2F->user_present == false) { | ||||||
|         memcpy(&buf[0], state_user_missing, 2); |         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 hash[32]; | ||||||
|     uint8_t signature[64]; |     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->callback != NULL) U2F->callback(U2fNotifyAuth, U2F->context); | ||||||
|     if(U2F->user_present == true) { |     if(U2F->user_present == true) { | ||||||
|         flags |= 1; |         flags |= 1; | ||||||
|  | |||||||
| @ -13,6 +13,7 @@ typedef enum { | |||||||
|     U2fNotifyWink, |     U2fNotifyWink, | ||||||
|     U2fNotifyConnect, |     U2fNotifyConnect, | ||||||
|     U2fNotifyDisconnect, |     U2fNotifyDisconnect, | ||||||
|  |     U2fNotifyError, | ||||||
| } U2fNotifyEvent; | } U2fNotifyEvent; | ||||||
| 
 | 
 | ||||||
| typedef struct U2fData U2fData; | typedef struct U2fData U2fData; | ||||||
|  | |||||||
| @ -48,7 +48,7 @@ U2fApp* u2f_app_alloc() { | |||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         app->view_dispatcher, U2fAppViewMain, u2f_view_get_view(app->u2f_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); |         scene_manager_next_scene(app->scene_manager, U2fSceneMain); | ||||||
|     } else { |     } else { | ||||||
|         scene_manager_next_scene(app->scene_manager, U2fSceneError); |         scene_manager_next_scene(app->scene_manager, U2fSceneError); | ||||||
|  | |||||||
| @ -20,6 +20,7 @@ typedef enum { | |||||||
| 
 | 
 | ||||||
|     U2fCustomEventConnect, |     U2fCustomEventConnect, | ||||||
|     U2fCustomEventDisconnect, |     U2fCustomEventDisconnect, | ||||||
|  |     U2fCustomEventDataError, | ||||||
| 
 | 
 | ||||||
|     U2fCustomEventRegister, |     U2fCustomEventRegister, | ||||||
|     U2fCustomEventAuth, |     U2fCustomEventAuth, | ||||||
|  | |||||||
| @ -38,17 +38,25 @@ typedef struct { | |||||||
|     uint32_t control; |     uint32_t control; | ||||||
| } __attribute__((packed)) U2fCounterData; | } __attribute__((packed)) U2fCounterData; | ||||||
| 
 | 
 | ||||||
| bool u2f_data_check() { | bool u2f_data_check(bool cert_only) { | ||||||
|     bool state = false; |     bool state = false; | ||||||
|     Storage* fs_api = furi_record_open("storage"); |     Storage* fs_api = furi_record_open("storage"); | ||||||
|     File* file = storage_file_alloc(fs_api); |     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); |         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; |             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_close(file); | ||||||
|     storage_file_free(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