diff --git a/lib/I18n/translations/belarusian.yaml b/lib/I18n/translations/belarusian.yaml index fe49dadb..aac46c1b 100644 --- a/lib/I18n/translations/belarusian.yaml +++ b/lib/I18n/translations/belarusian.yaml @@ -265,6 +265,7 @@ STR_RECENTS: "Нядаўнія" STR_PAGE_OVERLAY: "Накладанне на старонку" STR_READING: "Чытанне..." STR_INFO: "Інфо" +STR_OPTIONS: "Options" STR_AUTHOR: "Аўтар" STR_SERIES: "Серыя" STR_FILE_SIZE: "Памер" @@ -445,6 +446,8 @@ STR_LOADING_FONT_LIST: "Загрузка спісу шрыфтоў..." STR_LOWER_PROGRESS_BAR: "Ніжняя паласа прагрэсу" STR_LOWER_PROGRESS_BAR_THICKNESS: "Тоўшчыня ніжняй паласы прагрэсу" STR_MARK_AS_READ: "Пазначыць як прачытана" +STR_FLASH_FIRMWARE: "Flash Firmware" +STR_FETCH_AND_OPEN: "Fetch Progress & Open" STR_MAX_DARK: "Максімум" STR_MEM_COMBINED: "Свабодна / Мін / Макс" STR_MENU_BTN_ACTIONS: "Дзеянні кнопак" diff --git a/lib/I18n/translations/catalan.yaml b/lib/I18n/translations/catalan.yaml index ace37a4f..beceb746 100644 --- a/lib/I18n/translations/catalan.yaml +++ b/lib/I18n/translations/catalan.yaml @@ -249,6 +249,8 @@ STR_GO_HOME_BUTTON: "Ves a l'inici" STR_SYNC_PROGRESS: "Sincronitza el progrés" STR_DELETE_CACHE: "Esborra la memòria cau del llibre" STR_DELETE: "Esborra" +STR_FLASH_FIRMWARE: "Flash Firmware" +STR_FETCH_AND_OPEN: "Obtén el progrés i obre" STR_DISPLAY_QR: "Mostra la pàgina com a QR" STR_CHAPTER_PREFIX: "Capítol: " STR_PAGES_SEPARATOR: " pàgines | " diff --git a/lib/I18n/translations/czech.yaml b/lib/I18n/translations/czech.yaml index 71330b6b..03b0c046 100644 --- a/lib/I18n/translations/czech.yaml +++ b/lib/I18n/translations/czech.yaml @@ -226,6 +226,8 @@ STR_GO_HOME_BUTTON: "Přejít Domů" STR_SYNC_PROGRESS: "Průběh synchronizace" STR_DELETE_CACHE: "Smazat mezipaměť knihy" STR_DELETE: "Smazat" +STR_FLASH_FIRMWARE: "Flash Firmware" +STR_FETCH_AND_OPEN: "Fetch Progress & Open" STR_CHAPTER_PREFIX: "Kapitola: " STR_PAGES_SEPARATOR: " stránek | " STR_BOOK_PREFIX: "Kniha: " diff --git a/lib/I18n/translations/danish.yaml b/lib/I18n/translations/danish.yaml index 961fadb2..b3e11d48 100644 --- a/lib/I18n/translations/danish.yaml +++ b/lib/I18n/translations/danish.yaml @@ -249,6 +249,8 @@ STR_GO_HOME_BUTTON: "Gå til start" STR_SYNC_PROGRESS: "Synkroniser fremskridt" STR_DELETE_CACHE: "Slet bogcache" STR_DELETE: "Slet" +STR_FLASH_FIRMWARE: "Flash firmware" +STR_FETCH_AND_OPEN: "Hent fremskridt og åbn" STR_DISPLAY_QR: "Vis side som QR" STR_CHAPTER_PREFIX: "Kapitel: " STR_PAGES_SEPARATOR: " sider | " diff --git a/lib/I18n/translations/dutch.yaml b/lib/I18n/translations/dutch.yaml index deb758c2..f4c864ea 100644 --- a/lib/I18n/translations/dutch.yaml +++ b/lib/I18n/translations/dutch.yaml @@ -28,6 +28,8 @@ STR_FORGET_BOOK: "Boek vergeten" STR_FORGET_BOOK_DESC: "Verwijder boek uit Recente boeken" STR_FONT_MANAGER: "Lettertypebeheer" STR_MARK_AS_READ: "Markeren als gelezen" +STR_FLASH_FIRMWARE: "Firmware flashen" +STR_FETCH_AND_OPEN: "Voortgang ophalen & openen" STR_INDEXING: "Indexeren" STR_MEMORY_ERROR: "Geheugenfout" STR_PAGE_LOAD_ERROR: "Fout bij laden pagina" @@ -518,6 +520,7 @@ STR_WEATHER_DESC_THUNDERSTORM_HAIL: "Onweer met hagel" STR_WEATHER_DESC_THUNDERSTORM_HEAVY_HAIL: "Onweer met zware hagel" STR_WEATHER_DESC_UNKNOWN: "Onbekend" STR_INFO: "Info" +STR_OPTIONS: "Opties" STR_AUTHOR: "Auteur" STR_SERIES: "Serie" STR_FILE_SIZE: "Grootte" diff --git a/lib/I18n/translations/english.yaml b/lib/I18n/translations/english.yaml index 916f1235..ebd6deca 100644 --- a/lib/I18n/translations/english.yaml +++ b/lib/I18n/translations/english.yaml @@ -384,6 +384,8 @@ STR_DELETE: "Delete" STR_REMOVE: "Remove" STR_DISPLAY_QR: "Show page as QR" STR_MARK_AS_READ: "Mark as read" +STR_FLASH_FIRMWARE: "Flash Firmware" +STR_FETCH_AND_OPEN: "Fetch Progress & Open" STR_CHAPTER_PREFIX: "Chapter: " STR_PAGES_SEPARATOR: " pages | " STR_BOOK_PREFIX: "Book: " @@ -508,6 +510,7 @@ STR_WEATHER_DESC_THUNDERSTORM_HAIL: "Thunderstorm, hail" STR_WEATHER_DESC_THUNDERSTORM_HEAVY_HAIL: "Thunderstorm, heavy hail" STR_WEATHER_DESC_UNKNOWN: "Unknown" STR_INFO: "Info" +STR_OPTIONS: "Options" STR_AUTHOR: "Author" STR_SERIES: "Series" STR_FILE_SIZE: "Size" @@ -666,4 +669,5 @@ STR_FIRMWARE_UPDATE_DO_NOT_POWER_OFF: "Do not power off!" STR_RECOVERY_MODE: "Recovery Mode" STR_RECOVERY_MODE_HINT: "Place firmware.bin on SD card root and select it" STR_VIEW_LIST: "List" -STR_VIEW_GRID: "Grid" \ No newline at end of file +STR_VIEW_GRID: "Grid" +STR_TOOL_UTILITIES: "Utilities" diff --git a/lib/I18n/translations/finnish.yaml b/lib/I18n/translations/finnish.yaml index c355261c..0ab85f0e 100644 --- a/lib/I18n/translations/finnish.yaml +++ b/lib/I18n/translations/finnish.yaml @@ -225,6 +225,8 @@ STR_GO_TO_PERCENT: "Siirry kohtaan %" STR_GO_HOME_BUTTON: "Siirry kotiin" STR_SYNC_PROGRESS: "Synkronoi edistyminen" STR_DELETE_CACHE: "Poista kirjan välimuisti" +STR_FLASH_FIRMWARE: "Flash firmware" +STR_FETCH_AND_OPEN: "Hae edistyminen ja avaa" STR_CHAPTER_PREFIX: "Luku: " STR_PAGES_SEPARATOR: " sivua | " STR_BOOK_PREFIX: "Kirja: " diff --git a/lib/I18n/translations/french.yaml b/lib/I18n/translations/french.yaml index 2df760db..facd9e11 100644 --- a/lib/I18n/translations/french.yaml +++ b/lib/I18n/translations/french.yaml @@ -28,6 +28,8 @@ STR_FORGET_BOOK: "Oublier le livre" STR_FORGET_BOOK_DESC: "Retirer le livre des Livres récents" STR_FONT_MANAGER: "Gestionnaire de polices" STR_MARK_AS_READ: "Marquer comme lu" +STR_FLASH_FIRMWARE: "Flasher le firmware" +STR_FETCH_AND_OPEN: "Récupérer progression & ouvrir" STR_INDEXING: "Indexation en cours" STR_MEMORY_ERROR: "Erreur de mémoire" STR_PAGE_LOAD_ERROR: "Erreur de chargement" @@ -377,6 +379,7 @@ STR_RECENTS: "Récents" STR_PAGE_OVERLAY: "Superposition de page" STR_READING: "Lecture…" STR_INFO: "Info" +STR_OPTIONS: "Options" STR_AUTHOR: "Auteur" STR_SERIES: "Série" STR_FILE_SIZE: "Taille" diff --git a/lib/I18n/translations/german.yaml b/lib/I18n/translations/german.yaml index e1294346..3bcafc07 100644 --- a/lib/I18n/translations/german.yaml +++ b/lib/I18n/translations/german.yaml @@ -28,6 +28,8 @@ STR_FORGET_BOOK: "Buch vergessen" STR_FORGET_BOOK_DESC: "Buch aus den zuletzt verwendeten entfernen" STR_FONT_MANAGER: "Schriftverwaltung" STR_MARK_AS_READ: "Als gelesen markieren" +STR_FLASH_FIRMWARE: "Firmware flashen" +STR_FETCH_AND_OPEN: "Fortschritt holen & öffnen" STR_INDEXING: "Indexieren" STR_MEMORY_ERROR: "Speicherfehler" STR_PAGE_LOAD_ERROR: "Seitenladefehler" @@ -370,6 +372,7 @@ STR_RECENTS: "Zuletzt" STR_MAPPING_REMOTE: "Externe Position zuordnen..." STR_MAPPING_LOCAL: "Lokale Position berechnen..." STR_INFO: "Info" +STR_OPTIONS: "Optionen" STR_AUTHOR: "Autor" STR_SERIES: "Reihe" STR_FILE_SIZE: "Größe" diff --git a/lib/I18n/translations/hungarian.yaml b/lib/I18n/translations/hungarian.yaml index 7de4a4a9..5219fcb7 100644 --- a/lib/I18n/translations/hungarian.yaml +++ b/lib/I18n/translations/hungarian.yaml @@ -249,6 +249,8 @@ STR_GO_HOME_BUTTON: "Főoldalra" STR_SYNC_PROGRESS: "Haladás szinkronizálása" STR_DELETE_CACHE: "Könyv gyorsítótár törlése" STR_DELETE: "Törlés" +STR_FLASH_FIRMWARE: "Flash Firmware" +STR_FETCH_AND_OPEN: "Haladás lekérése és megnyitás" STR_DISPLAY_QR: "Oldal megjelenítése QR-ként" STR_CHAPTER_PREFIX: "Fejezet: " STR_PAGES_SEPARATOR: " oldal | " diff --git a/lib/I18n/translations/italian.yaml b/lib/I18n/translations/italian.yaml index e441db0c..08791003 100644 --- a/lib/I18n/translations/italian.yaml +++ b/lib/I18n/translations/italian.yaml @@ -28,6 +28,8 @@ STR_FORGET_BOOK: "Dimentica libro" STR_FORGET_BOOK_DESC: "Rimuovi il libro da Libri recenti" STR_FONT_MANAGER: "Gestore font" STR_MARK_AS_READ: "Segna come letto" +STR_FLASH_FIRMWARE: "Flash firmware" +STR_FETCH_AND_OPEN: "Recupera progresso e apri" STR_INDEXING: "Indicizzazione..." STR_MEMORY_ERROR: "Errore di memoria" STR_PAGE_LOAD_ERROR: "Errore caricamento pagina" @@ -507,6 +509,7 @@ STR_WEATHER_DESC_THUNDERSTORM_HAIL: "Temporale con grandine" STR_WEATHER_DESC_THUNDERSTORM_HEAVY_HAIL: "Temporale con grandine forte" STR_WEATHER_DESC_UNKNOWN: "Sconosciuto" STR_INFO: "Info" +STR_OPTIONS: "Opzioni" STR_AUTHOR: "Autore" STR_SERIES: "Serie" STR_FILE_SIZE: "Dimensione" diff --git a/lib/I18n/translations/kazakh.yaml b/lib/I18n/translations/kazakh.yaml index d074b15c..2d2d4721 100644 --- a/lib/I18n/translations/kazakh.yaml +++ b/lib/I18n/translations/kazakh.yaml @@ -224,6 +224,8 @@ STR_GO_TO_PERCENT: "%-ке өту" STR_GO_HOME_BUTTON: "Басты бетке өту" STR_SYNC_PROGRESS: "Үлгерімді синхрондау" STR_DELETE_CACHE: "Кітап кэшін жою" +STR_FLASH_FIRMWARE: "Flash Firmware" +STR_FETCH_AND_OPEN: "Fetch Progress & Open" STR_CHAPTER_PREFIX: "Тарау: " STR_PAGES_SEPARATOR: " бет | " STR_BOOK_PREFIX: "Кітап: " diff --git a/lib/I18n/translations/lithuanian.yaml b/lib/I18n/translations/lithuanian.yaml index b2952993..dbd1abca 100644 --- a/lib/I18n/translations/lithuanian.yaml +++ b/lib/I18n/translations/lithuanian.yaml @@ -249,6 +249,8 @@ STR_GO_HOME_BUTTON: "Pradžia" STR_SYNC_PROGRESS: "Sinchr. progresą" STR_DELETE_CACHE: "Trinti talpyklą" STR_DELETE: "Trinti" +STR_FLASH_FIRMWARE: "Flash Firmware" +STR_FETCH_AND_OPEN: "Fetch Progress & Open" STR_DISPLAY_QR: "QR kodas" STR_CHAPTER_PREFIX: "Sk: " STR_PAGES_SEPARATOR: " psl. | " diff --git a/lib/I18n/translations/polish.yaml b/lib/I18n/translations/polish.yaml index cad38342..09ab2e51 100644 --- a/lib/I18n/translations/polish.yaml +++ b/lib/I18n/translations/polish.yaml @@ -28,6 +28,8 @@ STR_FORGET_BOOK: "Zapomnij książkę" STR_FORGET_BOOK_DESC: "Usuń książkę z Ostatnich książek" STR_FONT_MANAGER: "Menedżer czcionek" STR_MARK_AS_READ: "Oznacz jako przeczytane" +STR_FLASH_FIRMWARE: "Wgraj firmware" +STR_FETCH_AND_OPEN: "Pobierz postęp i otwórz" STR_INDEXING: "Indeksowanie" STR_MEMORY_ERROR: "Błąd pamięci" STR_PAGE_LOAD_ERROR: "Błąd ładowania strony" @@ -529,6 +531,7 @@ STR_WEATHER_DESC_THUNDERSTORM_HAIL: "Burza z gradem" STR_WEATHER_DESC_THUNDERSTORM_HEAVY_HAIL: "Burza z dużym gradem" STR_WEATHER_DESC_UNKNOWN: "Nieznany" STR_INFO: "Info" +STR_OPTIONS: "Opcje" STR_AUTHOR: "Autor" STR_SERIES: "Seria" STR_FILE_SIZE: "Rozmiar" diff --git a/lib/I18n/translations/portuguese_br.yaml b/lib/I18n/translations/portuguese_br.yaml index af858a07..9117d7bb 100644 --- a/lib/I18n/translations/portuguese_br.yaml +++ b/lib/I18n/translations/portuguese_br.yaml @@ -28,6 +28,8 @@ STR_FORGET_BOOK: "Esquecer livro" STR_FORGET_BOOK_DESC: "Remover o livro de Livros Recentes" STR_FONT_MANAGER: "Gerenciador de fontes" STR_MARK_AS_READ: "Marcar como lido" +STR_FLASH_FIRMWARE: "Gravar firmware" +STR_FETCH_AND_OPEN: "Obter progresso e abrir" STR_INDEXING: "Indexando" STR_MEMORY_ERROR: "Erro de memória" STR_PAGE_LOAD_ERROR: "Erro ao carregar a página" @@ -382,6 +384,7 @@ STR_RECENTS: "Recentes" STR_PAGE_OVERLAY: "Sobreposição de página" STR_READING: "Lendo..." STR_INFO: "Info" +STR_OPTIONS: "Opções" STR_AUTHOR: "Autor" STR_SERIES: "Série" STR_FILE_SIZE: "Tamanho" diff --git a/lib/I18n/translations/portuguese_pt.yaml b/lib/I18n/translations/portuguese_pt.yaml index c1758f90..de1bfb03 100644 --- a/lib/I18n/translations/portuguese_pt.yaml +++ b/lib/I18n/translations/portuguese_pt.yaml @@ -28,6 +28,8 @@ STR_FORGET_BOOK: "Esquecer livro" STR_FORGET_BOOK_DESC: "Remover o livro de Livros Recentes" STR_FONT_MANAGER: "Gestor de fontes" STR_MARK_AS_READ: "Marcar como lido" +STR_FLASH_FIRMWARE: "Gravar firmware" +STR_FETCH_AND_OPEN: "Obter progresso e abrir" STR_INDEXING: "A indexar" STR_MEMORY_ERROR: "Erro de memória" STR_PAGE_LOAD_ERROR: "Erro ao carregar a página" @@ -504,6 +506,7 @@ STR_WEATHER_DESC_THUNDERSTORM_HAIL: "Trovoada com granizo" STR_WEATHER_DESC_THUNDERSTORM_HEAVY_HAIL: "Trovoada com granizo forte" STR_WEATHER_DESC_UNKNOWN: "Desconhecido" STR_INFO: "Informação" +STR_OPTIONS: "Opções" STR_AUTHOR: "Autor" STR_SERIES: "Série" STR_FILE_SIZE: "Tamanho" diff --git a/lib/I18n/translations/romanian.yaml b/lib/I18n/translations/romanian.yaml index 43915d1b..ba151f39 100644 --- a/lib/I18n/translations/romanian.yaml +++ b/lib/I18n/translations/romanian.yaml @@ -249,6 +249,8 @@ STR_GO_HOME_BUTTON: "Acasă" STR_SYNC_PROGRESS: "Progres sincronizare" STR_DELETE_CACHE: "Ştergere cache cărţi" STR_DELETE: "Ştergeți" +STR_FLASH_FIRMWARE: "Flash Firmware" +STR_FETCH_AND_OPEN: "Fetch Progress & Open" STR_DISPLAY_QR: "Afişați pagina ca cod QR" STR_CHAPTER_PREFIX: "Capitol: " STR_PAGES_SEPARATOR: " pagini | " diff --git a/lib/I18n/translations/russian.yaml b/lib/I18n/translations/russian.yaml index 6f5eb6f3..cdca3b73 100644 --- a/lib/I18n/translations/russian.yaml +++ b/lib/I18n/translations/russian.yaml @@ -28,6 +28,8 @@ STR_FORGET_BOOK: "Забыть книгу" STR_FORGET_BOOK_DESC: "Удалить книгу из последних" STR_FONT_MANAGER: "Менеджер шрифтов" STR_MARK_AS_READ: "Отметить как прочитанное" +STR_FLASH_FIRMWARE: "Прошить прошивку" +STR_FETCH_AND_OPEN: "Получить прогресс и открыть" STR_INDEXING: "Индексация" STR_MEMORY_ERROR: "Ошибка памяти" STR_PAGE_LOAD_ERROR: "Ошибка загрузки страницы" @@ -374,6 +376,7 @@ STR_RECENTS: "Недавние" STR_PAGE_OVERLAY: "Наложение на страницу" STR_READING: "Чтение..." STR_INFO: "Инфо" +STR_OPTIONS: "Действия" STR_AUTHOR: "Автор" STR_SERIES: "Серия" STR_FILE_SIZE: "Размер" diff --git a/lib/I18n/translations/slovenian.yaml b/lib/I18n/translations/slovenian.yaml index a23c22a5..c81861ea 100644 --- a/lib/I18n/translations/slovenian.yaml +++ b/lib/I18n/translations/slovenian.yaml @@ -28,6 +28,8 @@ STR_FORGET_BOOK: "Pozabi knjigo" STR_FORGET_BOOK_DESC: "Odstrani knjigo iz Nedavnih knjig" STR_FONT_MANAGER: "Upravitelj pisav" STR_MARK_AS_READ: "Označi kot prebrano" +STR_FLASH_FIRMWARE: "Flash firmware" +STR_FETCH_AND_OPEN: "Pridobi napredek in odpri" STR_TILT_PAGE_TURN: "Prelistaj z nagibanjem" STR_INDEXING: "Indeksiranje" STR_MEMORY_ERROR: "Napaka pomnilnika" @@ -518,6 +520,7 @@ STR_WEATHER_DESC_THUNDERSTORM_HAIL: "Nevihta s točo" STR_WEATHER_DESC_THUNDERSTORM_HEAVY_HAIL: "Nevihta z močno točo" STR_WEATHER_DESC_UNKNOWN: "Neznano" STR_INFO: "Informacije" +STR_OPTIONS: "Možnosti" STR_AUTHOR: "Avtor" STR_SERIES: "Zbirka" STR_FILE_SIZE: "Velikost" diff --git a/lib/I18n/translations/spanish.yaml b/lib/I18n/translations/spanish.yaml index d93df3ec..3434884b 100644 --- a/lib/I18n/translations/spanish.yaml +++ b/lib/I18n/translations/spanish.yaml @@ -28,6 +28,8 @@ STR_FORGET_BOOK: "Olvidar libro" STR_FORGET_BOOK_DESC: "Quitar el libro de Libros recientes" STR_FONT_MANAGER: "Administrador de fuentes" STR_MARK_AS_READ: "Marcar como leído" +STR_FLASH_FIRMWARE: "Flashear firmware" +STR_FETCH_AND_OPEN: "Obtener progreso y abrir" STR_INDEXING: "Indexando" STR_MEMORY_ERROR: "Error de memoria" STR_PAGE_LOAD_ERROR: "Error al cargar la página" @@ -371,6 +373,7 @@ STR_RECENTS: "Recientes" STR_PAGE_OVERLAY: "Superposición de página" STR_READING: "Leyendo..." STR_INFO: "Info" +STR_OPTIONS: "Opciones" STR_AUTHOR: "Autor" STR_SERIES: "Serie" STR_FILE_SIZE: "Tamaño" diff --git a/lib/I18n/translations/swedish.yaml b/lib/I18n/translations/swedish.yaml index fa73503b..70c7d92f 100644 --- a/lib/I18n/translations/swedish.yaml +++ b/lib/I18n/translations/swedish.yaml @@ -28,6 +28,8 @@ STR_FORGET_BOOK: "Glöm bok" STR_FORGET_BOOK_DESC: "Ta bort boken från Senaste böcker" STR_FONT_MANAGER: "Teckensnittshanterare" STR_MARK_AS_READ: "Markera som läst" +STR_FLASH_FIRMWARE: "Flasha firmware" +STR_FETCH_AND_OPEN: "Hämta framsteg & öppna" STR_INDEXING: "Indexerar" STR_MEMORY_ERROR: "Minnesfel" STR_PAGE_LOAD_ERROR: "Sidladdningsfel" @@ -518,6 +520,7 @@ STR_WEATHER_DESC_THUNDERSTORM_HAIL: "Åska och hagel" STR_WEATHER_DESC_THUNDERSTORM_HEAVY_HAIL: "Åska med kraftigt hagel" STR_WEATHER_DESC_UNKNOWN: "Okänt" STR_INFO: "Info" +STR_OPTIONS: "Alternativ" STR_AUTHOR: "Författare" STR_SERIES: "Serie" STR_FILE_SIZE: "Storlek" diff --git a/lib/I18n/translations/turkish.yaml b/lib/I18n/translations/turkish.yaml index 98183fa7..90836ab4 100644 --- a/lib/I18n/translations/turkish.yaml +++ b/lib/I18n/translations/turkish.yaml @@ -28,6 +28,8 @@ STR_FORGET_BOOK: "Kitabı unut" STR_FORGET_BOOK_DESC: "Kitabı Son Okunanlar listesinden kaldır" STR_FONT_MANAGER: "Yazı Tipi Yöneticisi" STR_MARK_AS_READ: "Okundu olarak işaretle" +STR_FLASH_FIRMWARE: "Firmware yükle" +STR_FETCH_AND_OPEN: "İlerleme al ve aç" STR_TILT_PAGE_TURN: "Sayfa çevirme için eğme" STR_INDEXING: "Endeksleniyor" STR_MEMORY_ERROR: "Bellek hatası" @@ -518,6 +520,7 @@ STR_WEATHER_DESC_THUNDERSTORM_HAIL: "Dolu ile fırtına" STR_WEATHER_DESC_THUNDERSTORM_HEAVY_HAIL: "Şiddetli dolu fırtınası" STR_WEATHER_DESC_UNKNOWN: "Bilinmiyor" STR_INFO: "Bilgi" +STR_OPTIONS: "Seçenekler" STR_AUTHOR: "Yazar" STR_SERIES: "Seri" STR_FILE_SIZE: "Boyut" diff --git a/lib/I18n/translations/ukrainian.yaml b/lib/I18n/translations/ukrainian.yaml index 17670ae5..66ff635a 100644 --- a/lib/I18n/translations/ukrainian.yaml +++ b/lib/I18n/translations/ukrainian.yaml @@ -28,6 +28,8 @@ STR_FORGET_BOOK: "Забути книгу" STR_FORGET_BOOK_DESC: "Видалити книгу з останніх" STR_FONT_MANAGER: "Менеджер шрифтів" STR_MARK_AS_READ: "Позначити як прочитане" +STR_FLASH_FIRMWARE: "Прошити прошивку" +STR_FETCH_AND_OPEN: "Отримати прогрес і відкрити" STR_INDEXING: "Індексація" STR_MEMORY_ERROR: "Помилка пам'яті" STR_PAGE_LOAD_ERROR: "Помилка завантаження сторінки" @@ -518,6 +520,7 @@ STR_WEATHER_DESC_THUNDERSTORM_HAIL: "Гроза з градом" STR_WEATHER_DESC_THUNDERSTORM_HEAVY_HAIL: "Гроза із сильним градом" STR_WEATHER_DESC_UNKNOWN: "Невідомо" STR_INFO: "Інфо" +STR_OPTIONS: "Дії" STR_AUTHOR: "Автор" STR_SERIES: "Серія" STR_FILE_SIZE: "Розмір" diff --git a/lib/I18n/translations/vietnamese.yaml b/lib/I18n/translations/vietnamese.yaml index 437f3857..656b1b07 100644 --- a/lib/I18n/translations/vietnamese.yaml +++ b/lib/I18n/translations/vietnamese.yaml @@ -28,6 +28,8 @@ STR_FORGET_BOOK: "Quên sách" STR_FORGET_BOOK_DESC: "Xóa sách khỏi Gần đây" STR_FONT_MANAGER: "Quản lý phông chữ" STR_MARK_AS_READ: "Đánh dấu đã đọc" +STR_FLASH_FIRMWARE: "Flash firmware" +STR_FETCH_AND_OPEN: "Lấy tiến trình và mở" STR_INDEXING: "Đang lập chỉ mục" STR_MEMORY_ERROR: "Lỗi bộ nhớ" STR_PAGE_LOAD_ERROR: "Lỗi tải trang" @@ -518,6 +520,7 @@ STR_WEATHER_DESC_THUNDERSTORM_HAIL: "Giông bão, mưa đá" STR_WEATHER_DESC_THUNDERSTORM_HEAVY_HAIL: "Giông bão, mưa đá lớn" STR_WEATHER_DESC_UNKNOWN: "Không xác định" STR_INFO: "Thông tin" +STR_OPTIONS: "Tùy chọn" STR_AUTHOR: "Tác giả" STR_SERIES: "Bộ sách" STR_FILE_SIZE: "Kích thước" diff --git a/src/activities/home/FileBrowserActivity.cpp b/src/activities/home/FileBrowserActivity.cpp index 51e143d1..39eec730 100644 --- a/src/activities/home/FileBrowserActivity.cpp +++ b/src/activities/home/FileBrowserActivity.cpp @@ -3,16 +3,24 @@ #include #include #include +#include #include #include +#include +#include #include #include "../ActivityManager.h" +#include "../ActivityResult.h" +#include "../reader/FinishedBookActivity.h" +#include "../settings/SdFirmwareUpdateActivity.h" +#include "../util/BmpViewerActivity.h" #include "../util/ConfirmationActivity.h" #include "BookInfoActivity.h" #include "CrossPointSettings.h" #include "CrossPointState.h" +#include "FileContextMenuActivity.h" #include "KOReaderCredentialStore.h" #include "MappedInputManager.h" #include "components/UITheme.h" @@ -133,10 +141,12 @@ void FileBrowserActivity::onExit() { } void FileBrowserActivity::clearFileMetadata(const std::string& fullPath) { - // Only clear cache for .epub files if (FsHelpers::hasEpubExtension(fullPath)) { Epub(fullPath, "/.crosspoint").clearCache(); LOG_DBG("FileBrowser", "Cleared metadata cache for: %s", fullPath.c_str()); + } else if (FsHelpers::hasXtcExtension(fullPath)) { + Xtc(fullPath, "/.crosspoint").clearCache(); + LOG_DBG("FileBrowser", "Cleared metadata cache for: %s", fullPath.c_str()); } } @@ -229,50 +239,21 @@ void FileBrowserActivity::loop() { const std::string& entry = files[selectorIndex]; const bool isDirectory = (entry.back() == '/'); std::string entryName = entry; - if (isDirectory) { - entryName.pop_back(); - } + if (isDirectory) entryName.pop_back(); std::string cleanBase = basepath; if (cleanBase.back() != '/') cleanBase += "/"; const std::string fullPath = cleanBase + entryName; - auto handler = [this, fullPath, isDirectory](const ActivityResult& res) { - if (!res.isCancelled) { - LOG_DBG("FileBrowser", "Attempting to delete: %s", fullPath.c_str()); - clearFileMetadata(fullPath); - const bool deleted = isDirectory ? Storage.removeDir(fullPath.c_str()) : Storage.remove(fullPath.c_str()); - if (deleted) { - LOG_DBG("FileBrowser", "Deleted successfully"); - loadFiles(); - if (files.empty()) { - selectorIndex = 0; - } else if (selectorIndex >= static_cast(files.size())) { - selectorIndex = static_cast(files.size()) - 1; - } - requestUpdate(true); - } else { - LOG_ERR("FileBrowser", "Failed to delete: %s", fullPath.c_str()); - } - } else { - LOG_DBG("FileBrowser", "Delete cancelled by user"); - } - }; - - startActivityForResult( - std::make_unique(renderer, mappedInput, tr(STR_DELETE) + std::string("? "), entry), - handler); + doRemove(fullPath, entry, isDirectory); return; } if (ev.button == MappedInputManager::Button::Right && ev.type == ButtonEventManager::PressType::Short) { if (files.empty()) return; const std::string& entry = files[selectorIndex]; - if (entry.back() != '/' && (FsHelpers::hasEpubExtension(entry) || FsHelpers::hasXtcExtension(entry))) { - std::string cleanBase = basepath; - if (cleanBase.back() != '/') cleanBase += "/"; - startActivityForResult(std::make_unique(renderer, mappedInput, cleanBase + entry), - [this](const ActivityResult&) { requestUpdate(); }); + if (entry.back() != '/') { + openContextMenu(); } return; } @@ -332,10 +313,8 @@ void FileBrowserActivity::render(RenderLock&&) { const char* backLabel = (basepath == "/") ? (mode == Mode::PickFirmware ? tr(STR_BACK) : tr(STR_HOME)) : tr(STR_BACK); const bool selectingFirmwareFile = mode == Mode::PickFirmware && !files.empty() && files[selectorIndex].back() != '/'; const char* confirmLabel = files.empty() ? "" : (selectingFirmwareFile ? tr(STR_SELECT) : tr(STR_OPEN)); - const bool hasInfo = - mode == Mode::Books && !files.empty() && files[selectorIndex].back() != '/' && - (FsHelpers::hasEpubExtension(files[selectorIndex]) || FsHelpers::hasXtcExtension(files[selectorIndex])); - const auto labels = mappedInput.mapLabels(backLabel, confirmLabel, "", hasInfo ? tr(STR_INFO) : ""); + const bool hasContextMenu = mode == Mode::Books && !files.empty() && files[selectorIndex].back() != '/'; + const auto labels = mappedInput.mapLabels(backLabel, confirmLabel, "", hasContextMenu ? tr(STR_OPTIONS) : ""); GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); renderer.displayBuffer(); @@ -345,4 +324,234 @@ size_t FileBrowserActivity::findEntry(const std::string& name) const { for (size_t i = 0; i < files.size(); i++) if (files[i] == name) return i; return files.size(); +} + +void FileBrowserActivity::openContextMenu() { + if (files.empty() || selectorIndex < 0 || selectorIndex >= static_cast(files.size())) return; + const std::string& entry = files[selectorIndex]; + if (entry.back() == '/') return; + + std::string cleanBase = basepath; + if (cleanBase.back() != '/') cleanBase += "/"; + const std::string fullPath = cleanBase + entry; + + startActivityForResult(std::make_unique(renderer, mappedInput, fullPath), + [this, fullPath, entry](const ActivityResult& res) { + if (res.isCancelled) { + requestUpdate(); + return; + } + const auto* menuRes = std::get_if(&res.data); + if (!menuRes) { + requestUpdate(); + return; + } + handleContextMenuAction(menuRes->action, fullPath, entry); + }); +} + +void FileBrowserActivity::handleContextMenuAction(int action, const std::string& fullPath, const std::string& entry) { + using Action = FileContextMenuActivity::Action; + switch (static_cast(action)) { + case Action::Open: { + ReturnHint hint; + hint.target = ReturnTo::FileBrowser; + hint.path = basepath; + hint.selectName = entry; + activityManager.replaceWithReader(std::string(fullPath), std::move(hint)); + return; + } + case Action::FetchAndOpen: { + if (KOREADER_STORE.hasCredentials() && FsHelpers::hasEpubExtension(fullPath)) { + auto& sync = APP_STATE.koReaderSyncSession; + sync.autoPullEpubPath = fullPath; + sync.exitToHomeAfterSync = false; + APP_STATE.saveToFile(); + } + ReturnHint hint; + hint.target = ReturnTo::FileBrowser; + hint.path = basepath; + hint.selectName = entry; + activityManager.replaceWithReader(std::string(fullPath), std::move(hint)); + return; + } + case Action::MarkAsRead: + doMarkAsRead(fullPath); + return; + case Action::Info: + startActivityForResult(std::make_unique(renderer, mappedInput, fullPath), + [this](const ActivityResult&) { requestUpdate(); }); + return; + case Action::DeleteCache: + doDeleteCache(fullPath, entry); + return; + case Action::SetAsSleepCover: + doSetAsSleepCover(fullPath); + return; + case Action::FlashFirmware: + doFlashFirmware(fullPath); + return; + case Action::Remove: + doRemove(fullPath, entry, false); + return; + default: + requestUpdate(); + return; + } +} + +void FileBrowserActivity::doMarkAsRead(const std::string& fullPath) { + std::string cachePath; + uint8_t data[7] = {0}; + size_t dataLen = 0; + + if (FsHelpers::hasEpubExtension(fullPath)) { + Epub epub(fullPath, "/.crosspoint"); + epub.setupCacheDir(); + cachePath = epub.getCachePath(); + // 7-byte EPUB progress: spine(2) + page(2) + pageCount(2) + percent(1) + data[6] = 100; + dataLen = 7; + } else if (FsHelpers::hasXtcExtension(fullPath)) { + Xtc xtc(fullPath, "/.crosspoint"); + xtc.setupCacheDir(); + cachePath = xtc.getCachePath(); + // 5-byte XTC progress: page(4) + percent(1) + data[4] = 100; + dataLen = 5; + } else if (FsHelpers::hasTxtExtension(fullPath) || FsHelpers::hasMarkdownExtension(fullPath)) { + Txt txt(fullPath, "/.crosspoint"); + txt.setupCacheDir(); + cachePath = txt.getCachePath(); + // 7-byte TXT progress: page(2) + offset(4) + percent(1) + data[6] = 100; + dataLen = 7; + } else { + return; + } + + FsFile f; + if (!Storage.openFileForWrite("FBR", cachePath + "/progress.bin", f)) { + LOG_ERR("FBR", "Failed to write progress for mark-as-read: %s", fullPath.c_str()); + return; + } + f.write(data, dataLen); + f.close(); + LOG_INF("FBR", "Marked as read: %s", fullPath.c_str()); + + // Series/index unknown without loading — findNextBook falls back to alphabetical order. + const std::string nextBookPath = BookFinished::findNextBookInDirectory(fullPath, {}, {}); + startActivityForResult(std::make_unique(renderer, mappedInput, fullPath, nextBookPath), + [this, fullPath, nextBookPath](const ActivityResult& result) { + if (result.isCancelled) { + requestUpdate(); + return; + } + const auto& menuResult = std::get(result.data); + if (menuResult.action == static_cast(BookFinished::FinishedBookAction::GoHome)) { + if (SETTINGS.moveFinishedBooksToCompleted) { + std::string movedPath; + BookFinished::moveFinishedBookToCompleted(fullPath, movedPath); + } + if (SETTINGS.removeFinishedBooksFromRecents) { + RECENT_BOOKS.removeBook(fullPath); + } + onGoHome(); + return; + } + if (menuResult.action == static_cast(BookFinished::FinishedBookAction::OpenNextBook) && + !nextBookPath.empty()) { + if (SETTINGS.moveFinishedBooksToCompleted) { + std::string movedPath; + BookFinished::moveFinishedBookToCompleted(fullPath, movedPath); + } + if (SETTINGS.removeFinishedBooksFromRecents) { + RECENT_BOOKS.removeBook(fullPath); + } + ReturnHint hint; + hint.target = ReturnTo::FileBrowser; + hint.path = basepath; + activityManager.replaceWithReader(nextBookPath, std::move(hint)); + return; + } + // Stay — apply side effects then reload the list (file may have moved to /COMPLETED). + if (SETTINGS.moveFinishedBooksToCompleted) { + std::string movedPath; + BookFinished::moveFinishedBookToCompleted(fullPath, movedPath); + } + if (SETTINGS.removeFinishedBooksFromRecents) { + RECENT_BOOKS.removeBook(fullPath); + } + loadFiles(); + if (selectorIndex >= static_cast(files.size())) { + selectorIndex = files.empty() ? 0 : static_cast(files.size()) - 1; + } + requestUpdate(true); + }); +} + +void FileBrowserActivity::doSetAsSleepCover(const std::string& fullPath) { + if (FsHelpers::hasBmpExtension(fullPath)) { + // BMP: use the shared helper that just does a file copy + settings update. + const bool success = BmpViewerActivity::setBmpFileAsSleepScreen(fullPath); + { + RenderLock lock(*this); + const char* msg = success ? tr(STR_SLEEP_SCREEN_SET) : tr(STR_FAILED_TO_SET_SLEEP_SCREEN); + GUI.drawPopup(renderer, msg); + renderer.displayBuffer(HalDisplay::HALF_REFRESH); + } + requestUpdate(); + } else { + // JPG/PNG: must render to framebuffer — open the image viewer so the user can use its Set Sleep button. + ReturnHint hint; + hint.target = ReturnTo::FileBrowser; + hint.path = basepath; + hint.selectName = files[selectorIndex]; + activityManager.replaceWithReader(std::string(fullPath), std::move(hint)); + } +} + +void FileBrowserActivity::doDeleteCache(const std::string& fullPath, const std::string& entry) { + startActivityForResult( + std::make_unique(renderer, mappedInput, tr(STR_DELETE_CACHE) + std::string("?"), entry), + [this, fullPath](const ActivityResult& res) { + if (!res.isCancelled) { + clearFileMetadata(fullPath); + LOG_INF("FBR", "Cache deleted for: %s", fullPath.c_str()); + } + requestUpdate(); + }); +} + +void FileBrowserActivity::doRemove(const std::string& fullPath, const std::string& entry, bool isDirectory) { + startActivityForResult( + std::make_unique(renderer, mappedInput, tr(STR_DELETE) + std::string("? "), entry), + [this, fullPath, isDirectory](const ActivityResult& res) { + if (!res.isCancelled) { + LOG_DBG("FBR", "Attempting to delete: %s", fullPath.c_str()); + clearFileMetadata(fullPath); + const bool deleted = isDirectory ? Storage.removeDir(fullPath.c_str()) : Storage.remove(fullPath.c_str()); + if (deleted) { + LOG_DBG("FBR", "Deleted successfully"); + loadFiles(); + if (files.empty()) { + selectorIndex = 0; + } else if (selectorIndex >= static_cast(files.size())) { + selectorIndex = static_cast(files.size()) - 1; + } + requestUpdate(true); + } else { + LOG_ERR("FBR", "Failed to delete: %s", fullPath.c_str()); + requestUpdate(); + } + } else { + requestUpdate(); + } + }); +} + +void FileBrowserActivity::doFlashFirmware(const std::string& fullPath) { + // Use the pre-selected-path constructor to skip the picker inside SdFirmwareUpdateActivity. + startActivityForResult(std::make_unique(renderer, mappedInput, fullPath), + [this](const ActivityResult&) { requestUpdate(); }); } \ No newline at end of file diff --git a/src/activities/home/FileBrowserActivity.h b/src/activities/home/FileBrowserActivity.h index e1d10ae0..3775d2aa 100644 --- a/src/activities/home/FileBrowserActivity.h +++ b/src/activities/home/FileBrowserActivity.h @@ -14,8 +14,14 @@ class FileBrowserActivity final : public Activity { enum class Mode { Books, PickFirmware }; private: - // Deletion void clearFileMetadata(const std::string& fullPath); + void openContextMenu(); + void handleContextMenuAction(int action, const std::string& fullPath, const std::string& entry); + void doMarkAsRead(const std::string& fullPath); + void doSetAsSleepCover(const std::string& fullPath); + void doDeleteCache(const std::string& fullPath, const std::string& entry); + void doRemove(const std::string& fullPath, const std::string& entry, bool isDirectory); + void doFlashFirmware(const std::string& fullPath); ButtonNavigator buttonNavigator; diff --git a/src/activities/home/FileContextMenuActivity.cpp b/src/activities/home/FileContextMenuActivity.cpp new file mode 100644 index 00000000..5475208d --- /dev/null +++ b/src/activities/home/FileContextMenuActivity.cpp @@ -0,0 +1,115 @@ +#include "FileContextMenuActivity.h" + +#include +#include + +#include "../ActivityResult.h" +#include "../settings/SettingInfo.h" +#include "CrossPointSettings.h" +#include "KOReaderCredentialStore.h" +#include "components/UITheme.h" + +FileContextMenuActivity::FileContextMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, + const std::string& filePath) + : MenuListActivity("FileContextMenu", renderer, mappedInput), filePath(filePath) { + const std::string_view name{filePath}; + const bool isBin = FsHelpers::checkFileExtension(name, ".bin"); + const bool isEpub = FsHelpers::hasEpubExtension(name); + const bool isXtc = FsHelpers::hasXtcExtension(name); + const bool isTxt = FsHelpers::hasTxtExtension(name) || FsHelpers::hasMarkdownExtension(name); + const bool isImage = + FsHelpers::hasBmpExtension(name) || FsHelpers::hasJpgExtension(name) || FsHelpers::hasPngExtension(name); + + if (isBin) { + menuItems.push_back(SettingInfo::Action(StrId::STR_FLASH_FIRMWARE, SettingAction::None)); + menuItems.push_back(SettingInfo::Action(StrId::STR_REMOVE, SettingAction::None)); + } else if (isImage) { + menuItems.push_back(SettingInfo::Action(StrId::STR_OPEN, SettingAction::None)); + menuItems.push_back(SettingInfo::Action(StrId::STR_SET_SLEEP_SCREEN, SettingAction::None)); + menuItems.push_back(SettingInfo::Separator(StrId::STR_TOOL_UTILITIES)); + menuItems.push_back(SettingInfo::Action(StrId::STR_REMOVE, SettingAction::None)); + } else if (isEpub) { + menuItems.push_back(SettingInfo::Action(StrId::STR_OPEN, SettingAction::None)); + if (KOREADER_STORE.hasCredentials()) { + menuItems.push_back(SettingInfo::Action(StrId::STR_FETCH_AND_OPEN, SettingAction::None)); + } + menuItems.push_back(SettingInfo::Action(StrId::STR_MARK_AS_READ, SettingAction::None)); + menuItems.push_back(SettingInfo::Action(StrId::STR_INFO, SettingAction::None)); + menuItems.push_back(SettingInfo::Separator(StrId::STR_TOOL_UTILITIES)); + menuItems.push_back(SettingInfo::Action(StrId::STR_DELETE_CACHE, SettingAction::None)); + menuItems.push_back(SettingInfo::Action(StrId::STR_REMOVE, SettingAction::None)); + } else if (isXtc) { + menuItems.push_back(SettingInfo::Action(StrId::STR_OPEN, SettingAction::None)); + menuItems.push_back(SettingInfo::Action(StrId::STR_MARK_AS_READ, SettingAction::None)); + menuItems.push_back(SettingInfo::Action(StrId::STR_INFO, SettingAction::None)); + menuItems.push_back(SettingInfo::Separator(StrId::STR_TOOL_UTILITIES)); + menuItems.push_back(SettingInfo::Action(StrId::STR_DELETE_CACHE, SettingAction::None)); + menuItems.push_back(SettingInfo::Action(StrId::STR_REMOVE, SettingAction::None)); + } else if (isTxt) { + menuItems.push_back(SettingInfo::Action(StrId::STR_OPEN, SettingAction::None)); + menuItems.push_back(SettingInfo::Action(StrId::STR_MARK_AS_READ, SettingAction::None)); + menuItems.push_back(SettingInfo::Separator(StrId::STR_TOOL_UTILITIES)); + menuItems.push_back(SettingInfo::Action(StrId::STR_REMOVE, SettingAction::None)); + } +} + +void FileContextMenuActivity::onActionSelected(int index) { + const StrId nameId = menuItems[index].nameId; + Action action = Action::None; + + if (nameId == StrId::STR_OPEN) { + action = Action::Open; + } else if (nameId == StrId::STR_FETCH_AND_OPEN) { + action = Action::FetchAndOpen; + } else if (nameId == StrId::STR_MARK_AS_READ) { + action = Action::MarkAsRead; + } else if (nameId == StrId::STR_INFO) { + action = Action::Info; + } else if (nameId == StrId::STR_DELETE_CACHE) { + action = Action::DeleteCache; + } else if (nameId == StrId::STR_SET_SLEEP_SCREEN) { + action = Action::SetAsSleepCover; + } else if (nameId == StrId::STR_FLASH_FIRMWARE) { + action = Action::FlashFirmware; + } else if (nameId == StrId::STR_REMOVE) { + action = Action::Remove; + } + + if (action == Action::None) return; + + MenuResult res; + res.action = static_cast(action); + ActivityResult result{std::move(res)}; + result.isCancelled = false; + setResult(std::move(result)); + finish(); +} + +void FileContextMenuActivity::onBackPressed() { + ActivityResult result; + result.isCancelled = true; + setResult(std::move(result)); + finish(); +} + +void FileContextMenuActivity::render(RenderLock&&) { + renderer.clearScreen(); + + const auto& metrics = UITheme::getInstance().getMetrics(); + const Rect contentRect = UITheme::getContentRect(renderer, true, true); + + // Show the bare filename (without path) as the header + const auto slashPos = filePath.rfind('/'); + const std::string fileName = (slashPos == std::string::npos) ? filePath : filePath.substr(slashPos + 1); + GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.headerHeight}, + fileName.c_str()); + + const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing; + const int contentHeight = contentRect.height - contentTop - metrics.verticalSpacing; + drawMenuList(Rect{contentRect.x, contentTop, contentRect.width, contentHeight}); + + const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), "", ""); + GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); + + renderer.displayBuffer(); +} diff --git a/src/activities/home/FileContextMenuActivity.h b/src/activities/home/FileContextMenuActivity.h new file mode 100644 index 00000000..a3a9c3e5 --- /dev/null +++ b/src/activities/home/FileContextMenuActivity.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include "../MenuListActivity.h" + +// Context menu shown when the user long-presses the info button on a file in the file browser. +// The available actions depend on file type. The selected action is returned via MenuResult::action. +class FileContextMenuActivity final : public MenuListActivity { + public: + enum class Action { + None = -1, + Open = 0, + FetchAndOpen, + MarkAsRead, + Info, + DeleteCache, + SetAsSleepCover, + FlashFirmware, + Remove, + }; + + explicit FileContextMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::string& filePath); + + void render(RenderLock&&) override; + + private: + std::string filePath; + + void onActionSelected(int index) override; + void onBackPressed() override; +}; diff --git a/src/activities/reader/EpubReaderMenuActivity.cpp b/src/activities/reader/EpubReaderMenuActivity.cpp index 676e07b7..8d763cb3 100644 --- a/src/activities/reader/EpubReaderMenuActivity.cpp +++ b/src/activities/reader/EpubReaderMenuActivity.cpp @@ -256,15 +256,17 @@ void EpubReaderMenuActivity::buildMenuItems(bool hasFootnotes, bool hasStarredPa SettingInfo::Action(StrId::STR_MARK_AS_READ, SettingAction::None).withSubmenu(StrId::STR_READER_TOOLS)); menuItems.push_back( SettingInfo::Action(StrId::STR_DELETE_CACHE, SettingAction::None).withSubmenu(StrId::STR_READER_TOOLS)); - menuItems.push_back(SettingInfo::Separator(StrId::STR_NONE_OPT).withSubmenu(StrId::STR_READER_TOOLS)); + menuItems.push_back(SettingInfo::Separator(StrId::STR_TOOL_UTILITIES).withSubmenu(StrId::STR_READER_TOOLS)); menuItems.push_back( SettingInfo::Action(StrId::STR_SCREENSHOT_BUTTON, SettingAction::None).withSubmenu(StrId::STR_READER_TOOLS)); menuItems.push_back( SettingInfo::Action(StrId::STR_DISPLAY_QR, SettingAction::None).withSubmenu(StrId::STR_READER_TOOLS)); menuItems.push_back(SettingInfo::Action(StrId::STR_GO_HOME_BUTTON, SettingAction::None)); +#ifdef ENABLE_BENCHMARKS menuItems.push_back(SettingInfo::Separator(StrId::STR_NONE_OPT).withSubmenu(StrId::STR_READER_TOOLS)); menuItems.push_back( SettingInfo::Action(StrId::STR_RENDER_BENCHMARK, SettingAction::None).withSubmenu(StrId::STR_READER_TOOLS)); +#endif } EpubReaderMenuActivity::MenuAction EpubReaderMenuActivity::actionForNameId(StrId nameId) { diff --git a/src/activities/settings/SdFirmwareUpdateActivity.cpp b/src/activities/settings/SdFirmwareUpdateActivity.cpp index 1ffb72af..7775476e 100644 --- a/src/activities/settings/SdFirmwareUpdateActivity.cpp +++ b/src/activities/settings/SdFirmwareUpdateActivity.cpp @@ -17,8 +17,23 @@ void SdFirmwareUpdateActivity::onEnter() { Activity::onEnter(); LOG_INF("FW", "SdFirmwareUpdateActivity build=%s %s recovery=%d", __DATE__, __TIME__, recoveryMode ? 1 : 0); - state = State::PICKING; - launchPicker(); + if (!firmwarePath.empty()) { + // Pre-selected path: skip picker and go straight to validation. + { + RenderLock lock(*this); + state = State::VALIDATING; + } + requestUpdateAndWait(); + if (!validateFirmware()) { + state = State::FAILED; + requestUpdate(); + return; + } + promptConfirmation(); + } else { + state = State::PICKING; + launchPicker(); + } } void SdFirmwareUpdateActivity::launchPicker() { diff --git a/src/activities/settings/SdFirmwareUpdateActivity.h b/src/activities/settings/SdFirmwareUpdateActivity.h index 4de520b5..2b5709af 100644 --- a/src/activities/settings/SdFirmwareUpdateActivity.h +++ b/src/activities/settings/SdFirmwareUpdateActivity.h @@ -31,6 +31,12 @@ class SdFirmwareUpdateActivity : public Activity { explicit SdFirmwareUpdateActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, bool recoveryMode = false) : Activity("SdFirmwareUpdate", renderer, mappedInput), recoveryMode(recoveryMode) {} + // Start with a pre-selected firmware path — skips the file picker. + explicit SdFirmwareUpdateActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::string preSelectedPath) + : Activity("SdFirmwareUpdate", renderer, mappedInput), + recoveryMode(false), + firmwarePath(std::move(preSelectedPath)) {} + void onEnter() override; void loop() override; void render(RenderLock&&) override; diff --git a/src/activities/settings/SettingInfo.cpp b/src/activities/settings/SettingInfo.cpp index 912f8556..086af934 100644 --- a/src/activities/settings/SettingInfo.cpp +++ b/src/activities/settings/SettingInfo.cpp @@ -6,8 +6,11 @@ #include "components/UITheme.h" std::string SettingInfo::getTitle() const { - const auto t = I18N.get(nameId); - return isSeparator ? UITheme::makeSeparatorTitle(t) : t; + if (isSeparator) { + if (nameId == StrId::STR_NONE_OPT) return UITheme::makeSeparatorTitle(std::string{}); + return UITheme::makeSeparatorTitle(std::string{I18N.get(nameId)}); + } + return std::string{I18N.get(nameId)}; } std::string SettingInfo::getDisplayValue() const { diff --git a/src/activities/util/BmpViewerActivity.cpp b/src/activities/util/BmpViewerActivity.cpp index c8592e35..01c3cacd 100644 --- a/src/activities/util/BmpViewerActivity.cpp +++ b/src/activities/util/BmpViewerActivity.cpp @@ -343,11 +343,23 @@ void BmpViewerActivity::renderError(const char* message) { renderer.displayBuffer(HalDisplay::HALF_REFRESH); } +bool BmpViewerActivity::setBmpFileAsSleepScreen(const std::string& filePath) { + const bool success = (filePath == SLEEP_BMP_PATH) ? true : Storage.copyFile("BMP", filePath, SLEEP_BMP_PATH); + if (success) { + SETTINGS.sleepScreen = CrossPointSettings::SLEEP_SCREEN_MODE::CUSTOM; + SETTINGS.saveToFile(); + LOG_INF("BMP", "Set %s as sleep screen", filePath.c_str()); + } else { + LOG_ERR("BMP", "Failed to copy %s as sleep screen", filePath.c_str()); + } + return success; +} + void BmpViewerActivity::setAsSleepScreen() { bool success = false; if (FsHelpers::hasBmpExtension(filePath)) { - success = (filePath == SLEEP_BMP_PATH) ? true : Storage.copyFile("BMP", filePath, SLEEP_BMP_PATH); + success = setBmpFileAsSleepScreen(filePath); } else { const bool renderedForCapture = renderDecodedImage(false); if (renderedForCapture) { @@ -361,27 +373,25 @@ void BmpViewerActivity::setAsSleepScreen() { if (!success && Storage.exists(SLEEP_BMP_TMP_PATH)) { Storage.remove(SLEEP_BMP_TMP_PATH); } + + if (success) { + SETTINGS.sleepScreen = CrossPointSettings::SLEEP_SCREEN_MODE::CUSTOM; + SETTINGS.saveToFile(); + LOG_INF("BMP", "Set %s as sleep screen", filePath.c_str()); + } } if (!success) { LOG_ERR("BMP", "Failed to set %s as sleep screen", filePath.c_str()); - { - RenderLock lock(*this); - GUI.drawPopup(renderer, tr(STR_FAILED_TO_SET_SLEEP_SCREEN)); - renderer.displayBuffer(HalDisplay::HALF_REFRESH); - } + RenderLock lock(*this); + GUI.drawPopup(renderer, tr(STR_FAILED_TO_SET_SLEEP_SCREEN)); + renderer.displayBuffer(HalDisplay::HALF_REFRESH); return; } - SETTINGS.sleepScreen = CrossPointSettings::SLEEP_SCREEN_MODE::CUSTOM; - SETTINGS.saveToFile(); - LOG_INF("BMP", "Set %s as sleep screen", filePath.c_str()); - - { - RenderLock lock(*this); - GUI.drawPopup(renderer, tr(STR_SLEEP_SCREEN_SET)); - renderer.displayBuffer(HalDisplay::HALF_REFRESH); - } + RenderLock lock(*this); + GUI.drawPopup(renderer, tr(STR_SLEEP_SCREEN_SET)); + renderer.displayBuffer(HalDisplay::HALF_REFRESH); } void BmpViewerActivity::loop() { diff --git a/src/activities/util/BmpViewerActivity.h b/src/activities/util/BmpViewerActivity.h index 7b238bdc..1eba1042 100644 --- a/src/activities/util/BmpViewerActivity.h +++ b/src/activities/util/BmpViewerActivity.h @@ -41,4 +41,9 @@ class BmpViewerActivity final : public Activity { #endif void renderError(const char* message); void setAsSleepScreen(); + + public: + // Sets a BMP file as the sleep screen without needing an open viewer instance. + // Returns true on success. Only works for .bmp files; for JPG/PNG open BmpViewerActivity. + static bool setBmpFileAsSleepScreen(const std::string& filePath); };