diff --git a/packages/app-expo/package.json b/packages/app-expo/package.json index 8117f1158..b2ff918b8 100644 --- a/packages/app-expo/package.json +++ b/packages/app-expo/package.json @@ -73,6 +73,7 @@ "expo-document-picker": "~14.0.8", "expo-file-system": "~19.0.21", "expo-font": "~14.0.11", + "expo-keep-awake": "~15.0.8", "expo-image-picker": "~17.0.11", "expo-linear-gradient": "~15.0.8", "expo-linking": "~8.0.11", diff --git a/packages/app-expo/src/screens/ReaderScreen.tsx b/packages/app-expo/src/screens/ReaderScreen.tsx index a87fe5edc..d5b76b936 100644 --- a/packages/app-expo/src/screens/ReaderScreen.tsx +++ b/packages/app-expo/src/screens/ReaderScreen.tsx @@ -73,6 +73,7 @@ import { import { SafeAreaView } from "react-native-safe-area-context"; import { WebView } from "react-native-webview"; +import { ReaderKeepAwake } from "./reader/ReaderKeepAwake"; // ── Extracted modules ── import { ReaderNoteViewModal } from "./reader/ReaderNoteViewModal"; @@ -1464,6 +1465,10 @@ export function ReaderScreen({ route, navigation }: Props) { return ( + ; +} diff --git a/packages/app-expo/src/screens/reader/ReaderSettingsPanel.tsx b/packages/app-expo/src/screens/reader/ReaderSettingsPanel.tsx index f3ae11e95..95a54af2b 100644 --- a/packages/app-expo/src/screens/reader/ReaderSettingsPanel.tsx +++ b/packages/app-expo/src/screens/reader/ReaderSettingsPanel.tsx @@ -41,6 +41,7 @@ export function ReaderSettingsPanel({ visible, readSettings, bookId, onClose, on viewMode: settingViewMode, volumeButtonsPageTurn, smoothReading, + keepScreenOnWhileReading, showTopTitleProgress, showBottomTimeBattery, followSystemFontScale, @@ -297,6 +298,36 @@ export function ReaderSettingsPanel({ visible, readSettings, bookId, onClose, on )} + {Platform.OS === "android" && ( + + + + {t("settings.keepScreenOnWhileReading", "Keep screen on while reading")} + + + {t( + "settings.keepScreenOnWhileReadingDesc", + "Prevent the screen from turning off while a book is open", + )} + + + + onUpdateSetting("keepScreenOnWhileReading", !keepScreenOnWhileReading) + } + > + + {keepScreenOnWhileReading ? t("settings.enabled") : t("settings.disabled")} + + + + )} {t("settings.showTopTitleProgress")} + readFileSync(resolve(repositoryRoot, relativePath), "utf8"); + +describe("Android reader keep-awake setting", () => { + it("persists as opt-in and is exposed only in Android reader settings", () => { + const bookTypes = readSource("packages/core/src/types/book.ts"); + const readerSettings = readSource( + "packages/app-expo/src/screens/reader/ReaderSettingsPanel.tsx", + ); + const generalSettings = readSource( + "packages/app-expo/src/screens/settings/AppearanceSettingsScreen.tsx", + ); + + expect(bookTypes).toContain("keepScreenOnWhileReading?: boolean"); + expect(readerSettings).toContain('Platform.OS === "android"'); + expect(readerSettings).toContain( + 'onUpdateSetting("keepScreenOnWhileReading", !keepScreenOnWhileReading)', + ); + expect(readerSettings.indexOf('t("settings.volumeButtonsPageTurn")')).toBeLessThan( + readerSettings.indexOf('t("settings.keepScreenOnWhileReading"'), + ); + expect(generalSettings).not.toContain("keepScreenOnWhileReading"); + }); + + it("uses the native keep-awake claim only while the reader is focused", () => { + const owner = readSource("packages/app-expo/src/screens/reader/ReaderKeepAwake.tsx"); + const reader = readSource("packages/app-expo/src/screens/ReaderScreen.tsx"); + + expect(owner).toContain('import { useKeepAwake } from "expo-keep-awake"'); + expect(owner).toContain('Platform.OS !== "android" || !enabled || !isFocused'); + expect(owner).toContain('useKeepAwake("readany-reader")'); + expect(reader).toContain("enabled={readSettings.keepScreenOnWhileReading === true}"); + expect(reader).toContain("isFocused={isFocused}"); + }); +}); diff --git a/packages/core/src/i18n/locales/en/settings.json b/packages/core/src/i18n/locales/en/settings.json index 7c62a68bc..096704f23 100644 --- a/packages/core/src/i18n/locales/en/settings.json +++ b/packages/core/src/i18n/locales/en/settings.json @@ -74,6 +74,8 @@ "volumeButtonsPageTurnDesc": "While reading, volume keys turn pages", "smoothReading": "Smooth Reading", "smoothReadingDesc": "Enable page-turn transition animation and scroll inertia. Disable for better performance on low-end devices", + "keepScreenOnWhileReading": "Keep screen on while reading", + "keepScreenOnWhileReadingDesc": "Prevent the screen from turning off while a book is open", "followSystemFontScale": "Follow System Font Scale", "followSystemFontScaleDesc": "Auto-scale by the OS accessibility font size", "fixedLayoutNotice": "PDF is a fixed layout format. Font and layout settings are not available.", diff --git a/packages/core/src/i18n/locales/es/settings.json b/packages/core/src/i18n/locales/es/settings.json index 1169f171d..5fa6d748b 100644 --- a/packages/core/src/i18n/locales/es/settings.json +++ b/packages/core/src/i18n/locales/es/settings.json @@ -74,6 +74,8 @@ "volumeButtonsPageTurnDesc": "Durante la lectura, los botones de volumen pasan página", "smoothReading": "Lectura fluida", "smoothReadingDesc": "Activa la animación de transición de página y la inercia de desplazamiento. Desactívelo para mejor rendimiento en dispositivos básicos", + "keepScreenOnWhileReading": "Mantener la pantalla encendida al leer", + "keepScreenOnWhileReadingDesc": "Evita que la pantalla se apague mientras hay un libro abierto", "followSystemFontScale": "Seguir escala de fuente del sistema", "followSystemFontScaleDesc": "Escalar automáticamente según el tamaño de fuente de accesibilidad del SO", "fixedLayoutNotice": "PDF es un formato de diseño fijo. Los ajustes de fuente y diseño no están disponibles.", diff --git a/packages/core/src/i18n/locales/fr/settings.json b/packages/core/src/i18n/locales/fr/settings.json index 64bcde715..7c6243b6f 100644 --- a/packages/core/src/i18n/locales/fr/settings.json +++ b/packages/core/src/i18n/locales/fr/settings.json @@ -74,6 +74,8 @@ "volumeButtonsPageTurnDesc": "En lecture, les boutons de volume tournent les pages", "smoothReading": "Lecture fluide", "smoothReadingDesc": "Active l'animation de transition de page et l'inertie de défilement. Désactivez pour de meilleures performances sur les appareils modestes", + "keepScreenOnWhileReading": "Garder l'écran allumé pendant la lecture", + "keepScreenOnWhileReadingDesc": "Empêcher l'écran de s'éteindre lorsqu'un livre est ouvert", "followSystemFontScale": "Suivre l'échelle de police système", "followSystemFontScaleDesc": "Mise à l'échelle auto selon la taille de police d'accessibilité du système", "fixedLayoutNotice": "Le PDF est un format à mise en page fixe. Les paramètres de police et mise en page ne sont pas disponibles.", diff --git a/packages/core/src/i18n/locales/ja/settings.json b/packages/core/src/i18n/locales/ja/settings.json index c89c5832a..a753c6f55 100644 --- a/packages/core/src/i18n/locales/ja/settings.json +++ b/packages/core/src/i18n/locales/ja/settings.json @@ -74,6 +74,8 @@ "volumeButtonsPageTurnDesc": "読書中は音量ボタンでページ送りします", "smoothReading": "スムーズリーディング", "smoothReadingDesc": "ページめくりアニメーションとスクロール慣性を有効化。低スペック端末ではオフにすると快適です", + "keepScreenOnWhileReading": "読書中は画面をオンに保つ", + "keepScreenOnWhileReadingDesc": "本を開いている間、画面が自動で消えないようにします", "followSystemFontScale": "システムフォントサイズに従う", "followSystemFontScaleDesc": "OSのアクセシビリティフォントサイズで自動調整", "fixedLayoutNotice": "PDFは固定レイアウト形式です。フォントとレイアウト設定は利用できません。", diff --git a/packages/core/src/i18n/locales/ko/settings.json b/packages/core/src/i18n/locales/ko/settings.json index 18b715456..40ca1a2b6 100644 --- a/packages/core/src/i18n/locales/ko/settings.json +++ b/packages/core/src/i18n/locales/ko/settings.json @@ -74,6 +74,8 @@ "volumeButtonsPageTurnDesc": "독서 중 볼륨 버튼으로 페이지를 넘깁니다", "smoothReading": "부드러운 읽기", "smoothReadingDesc": "페이지 넘김 전환 애니메이션과 스크롤 관성을 켭니다. 저사양 기기에서는 끄면 더 부드럽습니다", + "keepScreenOnWhileReading": "독서 중 화면 켜짐 유지", + "keepScreenOnWhileReadingDesc": "책을 열어 둔 동안 화면이 자동으로 꺼지지 않게 합니다", "followSystemFontScale": "시스템 글꼴 크기 따르기", "followSystemFontScaleDesc": "OS 접근성 글꼴 크기에 따라 자동 조정", "fixedLayoutNotice": "PDF는 고정 레이아웃 형식이에요. 글꼴 및 레이아웃 설정을 사용할 수 없어요.", diff --git a/packages/core/src/i18n/locales/zh-TW/settings.json b/packages/core/src/i18n/locales/zh-TW/settings.json index 1cb61a0ad..b4d9e212c 100644 --- a/packages/core/src/i18n/locales/zh-TW/settings.json +++ b/packages/core/src/i18n/locales/zh-TW/settings.json @@ -74,6 +74,8 @@ "volumeButtonsPageTurnDesc": "開啟後閱讀時音量鍵用於上下翻頁", "smoothReading": "平滑閱讀", "smoothReadingDesc": "開啟翻頁過渡動畫與滾動慣性,關閉可提升低效能裝置流暢度", + "keepScreenOnWhileReading": "閱讀時保持螢幕開啟", + "keepScreenOnWhileReadingDesc": "開啟書籍時防止螢幕自動關閉", "followSystemFontScale": "跟隨系統字型大小", "followSystemFontScaleDesc": "按系統輔助功能字型大小自動放大", "fixedLayoutNotice": "PDF 為固定排版格式,字型和排版設定不可用。", diff --git a/packages/core/src/i18n/locales/zh/settings.json b/packages/core/src/i18n/locales/zh/settings.json index 508744bc6..18b4643ed 100644 --- a/packages/core/src/i18n/locales/zh/settings.json +++ b/packages/core/src/i18n/locales/zh/settings.json @@ -74,6 +74,8 @@ "volumeButtonsPageTurnDesc": "开启后阅读时音量键用于上下翻页", "smoothReading": "平滑阅读", "smoothReadingDesc": "开启翻页过渡动画与滚动惯性,关闭可提升低性能设备流畅度", + "keepScreenOnWhileReading": "阅读时保持屏幕常亮", + "keepScreenOnWhileReadingDesc": "打开书籍时防止屏幕自动关闭", "followSystemFontScale": "跟随系统字号", "followSystemFontScaleDesc": "按系统辅助功能字号自动放大", "fixedLayoutNotice": "PDF 为固定布局格式,字体和排版设置不可用。", diff --git a/packages/core/src/types/book.ts b/packages/core/src/types/book.ts index f20703d71..017940226 100644 --- a/packages/core/src/types/book.ts +++ b/packages/core/src/types/book.ts @@ -96,6 +96,8 @@ export interface ReadSettings extends ViewSettings { showTopTitleProgress: boolean; showBottomTimeBattery: boolean; volumeButtonsPageTurn: boolean; + /** Android-only: keep the display awake while the reader screen is focused. */ + keepScreenOnWhileReading?: boolean; defaultHighlightColor?: HighlightColor; /** * Mobile-only opt-in: smooth reading — enables the foliate `animated` diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 560ef437a..0c85c158a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -340,6 +340,9 @@ importers: expo-font: specifier: ~14.0.11 version: 14.0.11(expo@54.0.33(@babel/core@7.29.0)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo-keep-awake: + specifier: ~15.0.8 + version: 15.0.8(expo@54.0.33(@babel/core@7.29.0)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react@19.1.0) expo-image-picker: specifier: ~17.0.11 version: 17.0.11(expo@54.0.33(@babel/core@7.29.0)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))