@@ -299,5 +299,9 @@
|
||||
"Link Color": "لون الرابط",
|
||||
"Volume Keys for Page Flip": "مفاتيح الصوت لتقليب الصفحات",
|
||||
"Clicks for Page Flip": "نقرات لتقليب الصفحات",
|
||||
"Swap Clicks Area": "تبديل منطقة النقرات"
|
||||
"Swap Clicks Area": "تبديل منطقة النقرات",
|
||||
"Screen": "الشاشة",
|
||||
"Orientation": "الاتجاه",
|
||||
"Portrait": "العمودي",
|
||||
"Landscape": "الأفقي"
|
||||
}
|
||||
|
||||
@@ -299,5 +299,9 @@
|
||||
"Link Color": "Linkfarbe",
|
||||
"Volume Keys for Page Flip": "Volumen-Tasten für Seitenwechsel",
|
||||
"Clicks for Page Flip": "Tippen zum Blättern",
|
||||
"Swap Clicks Area": "Tippbereiche tauschen"
|
||||
"Swap Clicks Area": "Tippbereiche tauschen",
|
||||
"Screen": "Bildschirm",
|
||||
"Orientation": "Orientierung",
|
||||
"Portrait": "Hochformat",
|
||||
"Landscape": "Querformat"
|
||||
}
|
||||
|
||||
@@ -299,5 +299,9 @@
|
||||
"Link Color": "Χρώμα συνδέσμου",
|
||||
"Volume Keys for Page Flip": "Πλήκτρα έντασης για ανατροπή σελίδας",
|
||||
"Clicks for Page Flip": "Κλικ για ανατροπή σελίδας",
|
||||
"Swap Clicks Area": "Ανταλλαγή περιοχής κλικ"
|
||||
"Swap Clicks Area": "Ανταλλαγή περιοχής κλικ",
|
||||
"Screen": "Οθόνη",
|
||||
"Orientation": "Προσανατολισμός",
|
||||
"Portrait": "Πορτραίτο",
|
||||
"Landscape": "Τοπίο"
|
||||
}
|
||||
|
||||
@@ -299,5 +299,9 @@
|
||||
"Link Color": "Color del enlace",
|
||||
"Volume Keys for Page Flip": "Botones de volumen para pasar página",
|
||||
"Clicks for Page Flip": "Toques para pasar página",
|
||||
"Swap Clicks Area": "Invertir zonas de toque"
|
||||
"Swap Clicks Area": "Invertir zonas de toque",
|
||||
"Screen": "Pantalla",
|
||||
"Orientation": "Orientación",
|
||||
"Portrait": "Vertical",
|
||||
"Landscape": "Horizontal"
|
||||
}
|
||||
|
||||
@@ -299,5 +299,9 @@
|
||||
"Link Color": "Couleur du lien",
|
||||
"Volume Keys for Page Flip": "Boutons volume pour tourner la page",
|
||||
"Clicks for Page Flip": "Taps pour tourner la page",
|
||||
"Swap Clicks Area": "Inverser zones de tap"
|
||||
"Swap Clicks Area": "Inverser zones de tap",
|
||||
"Screen": "Écran",
|
||||
"Orientation": "Orientation",
|
||||
"Portrait": "Portrait",
|
||||
"Landscape": "Paysage"
|
||||
}
|
||||
|
||||
@@ -299,5 +299,9 @@
|
||||
"Link Color": "लिंक रंग",
|
||||
"Volume Keys for Page Flip": "पृष्ठ फ़्लिप के लिए वॉल्यूम कुंजियाँ",
|
||||
"Clicks for Page Flip": "पृष्ठ फ़्लिप के लिए क्लिक करें",
|
||||
"Swap Clicks Area": "क्लिक क्षेत्र स्वैप करें"
|
||||
"Swap Clicks Area": "क्लिक क्षेत्र स्वैप करें",
|
||||
"Screen": "स्क्रीन",
|
||||
"Orientation": "अवयव",
|
||||
"Portrait": "पोर्ट्रेट",
|
||||
"Landscape": "लैंडस्केप"
|
||||
}
|
||||
|
||||
@@ -299,5 +299,9 @@
|
||||
"Link Color": "Warna Tautan",
|
||||
"Volume Keys for Page Flip": "Volume Keys untuk Pembalikan Halaman",
|
||||
"Clicks for Page Flip": "Klik untuk Pembalikan Halaman",
|
||||
"Swap Clicks Area": "Area Klik Tukar"
|
||||
"Swap Clicks Area": "Area Klik Tukar",
|
||||
"Screen": "Screen",
|
||||
"Orientation": "Orientasi",
|
||||
"Portrait": "Potret",
|
||||
"Landscape": "Lanskap"
|
||||
}
|
||||
|
||||
@@ -299,5 +299,9 @@
|
||||
"Link Color": "Colore link",
|
||||
"Volume Keys for Page Flip": "Tasti volume per girare pagina",
|
||||
"Clicks for Page Flip": "Clic per girare pagina",
|
||||
"Swap Clicks Area": "Inverti area dei clic"
|
||||
"Swap Clicks Area": "Inverti area dei clic",
|
||||
"Screen": "Schermo",
|
||||
"Orientation": "Orientamento",
|
||||
"Portrait": "Ritratto",
|
||||
"Landscape": "Paesaggio"
|
||||
}
|
||||
|
||||
@@ -299,5 +299,9 @@
|
||||
"Link Color": "リンクカラー",
|
||||
"Volume Keys for Page Flip": "音量キーでページめくり",
|
||||
"Clicks for Page Flip": "タップでページめくり",
|
||||
"Swap Clicks Area": "タップ領域を入れ替え"
|
||||
"Swap Clicks Area": "タップ領域を入れ替え",
|
||||
"Screen": "画面",
|
||||
"Orientation": "向き",
|
||||
"Portrait": "縦",
|
||||
"Landscape": "横"
|
||||
}
|
||||
|
||||
@@ -299,5 +299,9 @@
|
||||
"Link Color": "링크 색상",
|
||||
"Volume Keys for Page Flip": "페이지 넘김을 위한 볼륨 키",
|
||||
"Clicks for Page Flip": "페이지 넘김을 위한 클릭",
|
||||
"Swap Clicks Area": "클릭 영역 교환"
|
||||
"Swap Clicks Area": "클릭 영역 교환",
|
||||
"Screen": "화면",
|
||||
"Orientation": "방향",
|
||||
"Portrait": "세로",
|
||||
"Landscape": "가로"
|
||||
}
|
||||
|
||||
@@ -299,5 +299,9 @@
|
||||
"Link Color": "Kolor linku",
|
||||
"Volume Keys for Page Flip": "Klucze głośności do przewracania stron",
|
||||
"Clicks for Page Flip": "Kliknięcia do przewracania stron",
|
||||
"Swap Clicks Area": "Zamień obszar kliknięcia"
|
||||
"Swap Clicks Area": "Zamień obszar kliknięcia",
|
||||
"Screen": "Ekran",
|
||||
"Orientation": "Orientacja",
|
||||
"Portrait": "Portret",
|
||||
"Landscape": "Krajobraz"
|
||||
}
|
||||
|
||||
@@ -299,5 +299,9 @@
|
||||
"Link Color": "Cor do Link",
|
||||
"Volume Keys for Page Flip": "Teclas de Volume para Virar Página",
|
||||
"Clicks for Page Flip": "Cliques para Virar Página",
|
||||
"Swap Clicks Area": "Trocar Área de Cliques"
|
||||
"Swap Clicks Area": "Trocar Área de Cliques",
|
||||
"Screen": "Tela",
|
||||
"Orientation": "Orientação",
|
||||
"Portrait": "Retrato",
|
||||
"Landscape": "Paisagem"
|
||||
}
|
||||
|
||||
@@ -299,5 +299,9 @@
|
||||
"Link Color": "Цвет ссылки",
|
||||
"Volume Keys for Page Flip": "Клавиши громкости для перелистывания страниц",
|
||||
"Clicks for Page Flip": "Клики для перелистывания страниц",
|
||||
"Swap Clicks Area": "Переключить область кликов"
|
||||
"Swap Clicks Area": "Переключить область кликов",
|
||||
"Screen": "Экран",
|
||||
"Orientation": "Ориентация",
|
||||
"Portrait": "Портрет",
|
||||
"Landscape": "Ландшафт"
|
||||
}
|
||||
|
||||
@@ -299,5 +299,9 @@
|
||||
"Link Color": "Bağlantı Rengi",
|
||||
"Volume Keys for Page Flip": "Sayfa Çevirme için Ses Seviyesi Tuşları",
|
||||
"Clicks for Page Flip": "Sayfa Çevirme için Tıklamalar",
|
||||
"Swap Clicks Area": "Tıklama Alanını Değiştir"
|
||||
"Swap Clicks Area": "Tıklama Alanını Değiştir",
|
||||
"Screen": "Ekran",
|
||||
"Orientation": "Yönlendirme",
|
||||
"Portrait": "Portre",
|
||||
"Landscape": "Yatay"
|
||||
}
|
||||
|
||||
@@ -299,5 +299,9 @@
|
||||
"Link Color": "Колір посилання",
|
||||
"Volume Keys for Page Flip": "Клавіші гучності для перевороту сторінки",
|
||||
"Clicks for Page Flip": "Клацання для перевороту сторінки",
|
||||
"Swap Clicks Area": "Змінити область клацання"
|
||||
"Swap Clicks Area": "Змінити область клацання",
|
||||
"Screen": "Екран",
|
||||
"Orientation": "Орієнтація",
|
||||
"Portrait": "Портрет",
|
||||
"Landscape": "Ландшафт"
|
||||
}
|
||||
|
||||
@@ -299,5 +299,9 @@
|
||||
"Link Color": "Màu liên kết",
|
||||
"Volume Keys for Page Flip": "Phím âm lượng để lật trang",
|
||||
"Clicks for Page Flip": "Nhấp để lật trang",
|
||||
"Swap Clicks Area": "Đổi khu vực nhấp"
|
||||
"Swap Clicks Area": "Đổi khu vực nhấp",
|
||||
"Screen": "Màn hình",
|
||||
"Orientation": "Định hướng",
|
||||
"Portrait": "Chân dung",
|
||||
"Landscape": "Phong cảnh"
|
||||
}
|
||||
|
||||
@@ -299,5 +299,9 @@
|
||||
"Link Color": "链接颜色",
|
||||
"Volume Keys for Page Flip": "音量键翻页",
|
||||
"Clicks for Page Flip": "点击翻页",
|
||||
"Swap Clicks Area": "交换点击区域"
|
||||
"Swap Clicks Area": "交换点击区域",
|
||||
"Screen": "屏幕",
|
||||
"Orientation": "屏幕方向",
|
||||
"Portrait": "竖屏",
|
||||
"Landscape": "横屏"
|
||||
}
|
||||
|
||||
@@ -299,5 +299,9 @@
|
||||
"Link Color": "連結顏色",
|
||||
"Volume Keys for Page Flip": "音量鍵翻頁",
|
||||
"Clicks for Page Flip": "點擊翻頁",
|
||||
"Swap Clicks Area": "交換點擊區域"
|
||||
"Swap Clicks Area": "交換點擊區域",
|
||||
"Screen": "螢幕",
|
||||
"Orientation": "螢幕方向",
|
||||
"Portrait": "豎屏",
|
||||
"Landscape": "橫屏"
|
||||
}
|
||||
|
||||
+22
@@ -12,6 +12,7 @@ import android.view.WindowManager
|
||||
import android.view.WindowInsetsController
|
||||
import android.graphics.Color
|
||||
import android.webkit.WebView
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.graphics.fonts.SystemFonts
|
||||
import android.graphics.fonts.Font
|
||||
import androidx.core.view.WindowCompat
|
||||
@@ -56,6 +57,11 @@ class InterceptKeysRequestArgs {
|
||||
var backKey: Boolean? = null
|
||||
}
|
||||
|
||||
@InvokeArg
|
||||
class LockScreenOrientationRequestArgs {
|
||||
var orientation: String? = null
|
||||
}
|
||||
|
||||
interface KeyDownInterceptor {
|
||||
fun interceptVolumeKeys(enabled: Boolean)
|
||||
fun interceptBackKey(enabled: Boolean)
|
||||
@@ -326,4 +332,20 @@ class NativeBridgePlugin(private val activity: Activity): Plugin(activity) {
|
||||
}
|
||||
invoke.resolve()
|
||||
}
|
||||
|
||||
@Command
|
||||
fun lock_screen_orientation(invoke: Invoke) {
|
||||
val args = invoke.parseArgs(LockScreenOrientationRequestArgs::class.java)
|
||||
val orientation = args.orientation ?: "auto"
|
||||
when (orientation) {
|
||||
"portrait" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
"landscape" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
||||
"auto" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR
|
||||
else -> {
|
||||
invoke.reject("Invalid orientation mode")
|
||||
return
|
||||
}
|
||||
}
|
||||
invoke.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ const COMMANDS: &[&str] = &[
|
||||
"get_status_bar_height",
|
||||
"get_sys_fonts_list",
|
||||
"intercept_keys",
|
||||
"lock_screen_orientation",
|
||||
];
|
||||
|
||||
fn main() {
|
||||
|
||||
+80
@@ -44,6 +44,10 @@ class InterceptKeysRequestArgs: Decodable {
|
||||
let volumeKeys: Bool?
|
||||
}
|
||||
|
||||
class LockScreenOrientationRequestArgs: Decodable {
|
||||
let orientation: String?
|
||||
}
|
||||
|
||||
class VolumeKeyHandler: NSObject {
|
||||
private var audioSession: AVAudioSession?
|
||||
private var originalVolume: Float = 0.0
|
||||
@@ -150,6 +154,9 @@ class VolumeKeyHandler: NSObject {
|
||||
class NativeBridgePlugin: Plugin {
|
||||
private var webView: WKWebView?
|
||||
private var authSession: ASWebAuthenticationSession?
|
||||
private var isOrientationLocked = false
|
||||
private var currentOrientationMask: UIInterfaceOrientationMask = .all
|
||||
private var orientationObserver: NSObjectProtocol?
|
||||
|
||||
@objc public override func load(webview: WKWebView) {
|
||||
self.webView = webview
|
||||
@@ -174,6 +181,10 @@ class NativeBridgePlugin: Plugin {
|
||||
if volumeKeyHandler != nil {
|
||||
activateVolumeKeyInterception()
|
||||
}
|
||||
|
||||
if orientationObserver != nil {
|
||||
self.setupOrientationObserver()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func appDidEnterBackground() {
|
||||
@@ -338,6 +349,75 @@ class NativeBridgePlugin: Plugin {
|
||||
invoke.reject(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
@objc public func lock_screen_orientation(_ invoke: Invoke) throws {
|
||||
guard let args = try? invoke.parseArgs(LockScreenOrientationRequestArgs.self) else {
|
||||
return invoke.reject("Invalid arguments")
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
|
||||
let orientation = args.orientation ?? "auto"
|
||||
switch orientation.lowercased() {
|
||||
case "portrait":
|
||||
self.isOrientationLocked = true
|
||||
self.currentOrientationMask = .portrait
|
||||
self.forceOrientation(.portrait)
|
||||
self.setupOrientationObserver()
|
||||
case "landscape":
|
||||
self.isOrientationLocked = true
|
||||
self.currentOrientationMask = .landscape
|
||||
self.forceOrientation(.landscapeRight)
|
||||
self.setupOrientationObserver()
|
||||
case "auto":
|
||||
self.isOrientationLocked = false
|
||||
self.currentOrientationMask = .all
|
||||
default:
|
||||
invoke.reject("Invalid orientation mode")
|
||||
return
|
||||
}
|
||||
|
||||
invoke.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
private func forceOrientation(_ orientation: UIInterfaceOrientation) {
|
||||
if #available(iOS 16.0, *) {
|
||||
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
|
||||
let orientationMask: UIInterfaceOrientationMask =
|
||||
orientation.isPortrait ? .portrait : .landscape
|
||||
|
||||
for window in windowScene.windows {
|
||||
if let rootVC = window.rootViewController {
|
||||
rootVC.setNeedsUpdateOfSupportedInterfaceOrientations()
|
||||
}
|
||||
}
|
||||
|
||||
windowScene.requestGeometryUpdate(.iOS(interfaceOrientations: orientationMask)) { error in
|
||||
print("Orientation update error: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
UIDevice.current.setValue(orientation.rawValue, forKey: "orientation")
|
||||
UIViewController.attemptRotationToDeviceOrientation()
|
||||
}
|
||||
}
|
||||
|
||||
private func setupOrientationObserver() {
|
||||
orientationObserver = NotificationCenter.default.addObserver(
|
||||
forName: UIDevice.orientationDidChangeNotification,
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { [weak self] _ in
|
||||
guard let self = self, self.isOrientationLocked else { return }
|
||||
|
||||
if self.currentOrientationMask == .portrait {
|
||||
self.forceOrientation(.portrait)
|
||||
} else if self.currentOrientationMask == .landscape {
|
||||
self.forceOrientation(.landscapeRight)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@_cdecl("init_plugin_native_bridge")
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-lock-screen-orientation"
|
||||
description = "Enables the lock_screen_orientation command without any pre-configured scope."
|
||||
commands.allow = ["lock_screen_orientation"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-lock-screen-orientation"
|
||||
description = "Denies the lock_screen_orientation command without any pre-configured scope."
|
||||
commands.deny = ["lock_screen_orientation"]
|
||||
+27
@@ -13,6 +13,7 @@ Default permissions for the plugin
|
||||
- `allow-get-status-bar-height`
|
||||
- `allow-get-sys-fonts-list`
|
||||
- `allow-intercept-keys`
|
||||
- `allow-lock-screen-orientation`
|
||||
|
||||
## Permission Table
|
||||
|
||||
@@ -208,6 +209,32 @@ Denies the intercept_keys command without any pre-configured scope.
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`native-bridge:allow-lock-screen-orientation`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the lock_screen_orientation command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`native-bridge:deny-lock-screen-orientation`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the lock_screen_orientation command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`native-bridge:allow-set-system-ui-visibility`
|
||||
|
||||
</td>
|
||||
|
||||
@@ -10,4 +10,5 @@ permissions = [
|
||||
"allow-get-status-bar-height",
|
||||
"allow-get-sys-fonts-list",
|
||||
"allow-intercept-keys",
|
||||
"allow-lock-screen-orientation",
|
||||
]
|
||||
|
||||
+14
-2
@@ -378,6 +378,18 @@
|
||||
"const": "deny-intercept-keys",
|
||||
"markdownDescription": "Denies the intercept_keys command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the lock_screen_orientation command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-lock-screen-orientation",
|
||||
"markdownDescription": "Enables the lock_screen_orientation command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the lock_screen_orientation command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-lock-screen-orientation",
|
||||
"markdownDescription": "Denies the lock_screen_orientation command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the set_system_ui_visibility command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -403,10 +415,10 @@
|
||||
"markdownDescription": "Denies the use_background_audio command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-auth-with-safari`\n- `allow-auth-with-custom-tab`\n- `allow-copy-uri-to-path`\n- `allow-use-background-audio`\n- `allow-install-package`\n- `allow-set-system-ui-visibility`\n- `allow-get-status-bar-height`\n- `allow-get-sys-fonts-list`\n- `allow-intercept-keys`",
|
||||
"description": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-auth-with-safari`\n- `allow-auth-with-custom-tab`\n- `allow-copy-uri-to-path`\n- `allow-use-background-audio`\n- `allow-install-package`\n- `allow-set-system-ui-visibility`\n- `allow-get-status-bar-height`\n- `allow-get-sys-fonts-list`\n- `allow-intercept-keys`\n- `allow-lock-screen-orientation`",
|
||||
"type": "string",
|
||||
"const": "default",
|
||||
"markdownDescription": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-auth-with-safari`\n- `allow-auth-with-custom-tab`\n- `allow-copy-uri-to-path`\n- `allow-use-background-audio`\n- `allow-install-package`\n- `allow-set-system-ui-visibility`\n- `allow-get-status-bar-height`\n- `allow-get-sys-fonts-list`\n- `allow-intercept-keys`"
|
||||
"markdownDescription": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-auth-with-safari`\n- `allow-auth-with-custom-tab`\n- `allow-copy-uri-to-path`\n- `allow-use-background-audio`\n- `allow-install-package`\n- `allow-set-system-ui-visibility`\n- `allow-get-status-bar-height`\n- `allow-get-sys-fonts-list`\n- `allow-intercept-keys`\n- `allow-lock-screen-orientation`"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -73,3 +73,11 @@ pub(crate) async fn intercept_keys<R: Runtime>(
|
||||
) -> Result<()> {
|
||||
app.native_bridge().intercept_keys(payload)
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub(crate) async fn lock_screen_orientation<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
payload: LockScreenOrientationRequest,
|
||||
) -> Result<()> {
|
||||
app.native_bridge().lock_screen_orientation(payload)
|
||||
}
|
||||
|
||||
@@ -61,4 +61,11 @@ impl<R: Runtime> NativeBridge<R> {
|
||||
pub fn intercept_keys(&self, _payload: InterceptKeysRequest) -> crate::Result<()> {
|
||||
Err(crate::Error::UnsupportedPlatformError)
|
||||
}
|
||||
|
||||
pub fn lock_screen_orientation(
|
||||
&self,
|
||||
_payload: LockScreenOrientationRequest,
|
||||
) -> crate::Result<()> {
|
||||
Err(crate::Error::UnsupportedPlatformError)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
commands::get_status_bar_height,
|
||||
commands::get_sys_fonts_list,
|
||||
commands::intercept_keys,
|
||||
commands::lock_screen_orientation,
|
||||
])
|
||||
.setup(|app, api| {
|
||||
#[cfg(mobile)]
|
||||
|
||||
@@ -101,3 +101,14 @@ impl<R: Runtime> NativeBridge<R> {
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Runtime> NativeBridge<R> {
|
||||
pub fn lock_screen_orientation(
|
||||
&self,
|
||||
payload: LockScreenOrientationRequest,
|
||||
) -> crate::Result<()> {
|
||||
self.0
|
||||
.run_mobile_plugin("lock_screen_orientation", payload)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,3 +80,9 @@ pub struct InterceptKeysRequest {
|
||||
pub volume_keys: Option<bool>,
|
||||
pub back_key: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct LockScreenOrientationRequest {
|
||||
pub orientation: String,
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import { useBooksSync } from './hooks/useBooksSync';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
import { useScreenWakeLock } from '@/hooks/useScreenWakeLock';
|
||||
import { useOpenWithBooks } from '@/hooks/useOpenWithBooks';
|
||||
import { lockScreenOrientation } from '@/utils/bridge';
|
||||
import {
|
||||
tauriHandleSetAlwaysOnTop,
|
||||
tauriHandleToggleFullScreen,
|
||||
@@ -116,6 +117,12 @@ const LibraryPageContent = ({ searchParams }: { searchParams: ReadonlyURLSearchP
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [settings]);
|
||||
|
||||
useEffect(() => {
|
||||
if (appService?.isMobileApp) {
|
||||
lockScreenOrientation({ orientation: 'portrait' });
|
||||
}
|
||||
}, [appService]);
|
||||
|
||||
const handleDropedFiles = async (files: File[] | string[]) => {
|
||||
if (files.length === 0) return;
|
||||
const supportedFiles = files.filter((file) => {
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useEffect, useRef, useState } from 'react';
|
||||
import { BookDoc, getDirection } from '@/libs/document';
|
||||
import { BookConfig } from '@/types/book';
|
||||
import { FoliateView, wrappedFoliateView } from '@/types/view';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
import { useParallelViewStore } from '@/store/parallelViewStore';
|
||||
@@ -26,12 +27,14 @@ import {
|
||||
import { getMaxInlineSize } from '@/utils/config';
|
||||
import { getDirFromUILanguage } from '@/utils/rtl';
|
||||
import { transformContent } from '@/services/transformService';
|
||||
import { lockScreenOrientation } from '@/utils/bridge';
|
||||
|
||||
const FoliateViewer: React.FC<{
|
||||
bookKey: string;
|
||||
bookDoc: BookDoc;
|
||||
config: BookConfig;
|
||||
}> = ({ bookKey, bookDoc, config }) => {
|
||||
const { appService } = useEnv();
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const viewRef = useRef<FoliateView | null>(null);
|
||||
const isViewCreated = useRef(false);
|
||||
@@ -188,6 +191,10 @@ const FoliateViewer: React.FC<{
|
||||
const maxColumnCount = viewSettings.maxColumnCount!;
|
||||
const maxInlineSize = getMaxInlineSize(viewSettings);
|
||||
const maxBlockSize = viewSettings.maxBlockSize!;
|
||||
const screenOrientation = viewSettings.screenOrientation!;
|
||||
if (appService?.isMobileApp) {
|
||||
await lockScreenOrientation({ orientation: screenOrientation });
|
||||
}
|
||||
if (animated) {
|
||||
view.renderer.setAttribute('animated', '');
|
||||
} else {
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import clsx from 'clsx';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import i18n from 'i18next';
|
||||
import { IoPhoneLandscapeOutline, IoPhonePortraitOutline } from 'react-icons/io5';
|
||||
import { MdOutlineScreenRotation } from 'react-icons/md';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
import { useSettingsStore } from '@/store/settingsStore';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { useDeviceControlStore } from '@/store/deviceStore';
|
||||
import { getStyles } from '@/utils/style';
|
||||
import { lockScreenOrientation } from '@/utils/bridge';
|
||||
import { saveViewSettings } from '../../utils/viewSettingsHelper';
|
||||
import { TRANSLATED_LANGS } from '@/services/constants';
|
||||
import cssbeautify from 'cssbeautify';
|
||||
@@ -28,6 +31,7 @@ const MiscPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
const [isContinuousScroll, setIsContinuousScroll] = useState(viewSettings.continuousScroll!);
|
||||
const [draftStylesheet, setDraftStylesheet] = useState(viewSettings.userStylesheet!);
|
||||
const [draftStylesheetSaved, setDraftStylesheetSaved] = useState(true);
|
||||
const [screenOrientation, setScreenOrientation] = useState(viewSettings.screenOrientation!);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const [inputFocusInAndroid, setInputFocusInAndroid] = useState(false);
|
||||
@@ -160,6 +164,14 @@ const MiscPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isContinuousScroll]);
|
||||
|
||||
useEffect(() => {
|
||||
saveViewSettings(envConfig, bookKey, 'screenOrientation', screenOrientation, false, false);
|
||||
if (appService?.isMobileApp) {
|
||||
lockScreenOrientation({ orientation: screenOrientation });
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [screenOrientation]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
@@ -200,6 +212,45 @@ const MiscPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='w-full'>
|
||||
<h2 className='mb-2 font-medium'>{_('Screen')}</h2>
|
||||
<div className='card border-base-200 bg-base-100 border shadow'>
|
||||
<div className='divide-base-200 divide-y'>
|
||||
<div className='config-item'>
|
||||
<span className=''>{_('Orientation')}</span>
|
||||
<div className='flex gap-4'>
|
||||
<div className='lg:tooltip lg:tooltip-bottom' data-tip={_('Auto')}>
|
||||
<button
|
||||
className={`btn btn-ghost btn-circle btn-sm ${screenOrientation === 'auto' ? 'btn-active bg-base-300' : ''}`}
|
||||
onClick={() => setScreenOrientation('auto')}
|
||||
>
|
||||
<MdOutlineScreenRotation />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className='lg:tooltip lg:tooltip-bottom' data-tip={_('Portrait')}>
|
||||
<button
|
||||
className={`btn btn-ghost btn-circle btn-sm ${screenOrientation === 'portrait' ? 'btn-active bg-base-300' : ''}`}
|
||||
onClick={() => setScreenOrientation('portrait')}
|
||||
>
|
||||
<IoPhonePortraitOutline />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className='lg:tooltip lg:tooltip-bottom' data-tip={_('Landscape')}>
|
||||
<button
|
||||
className={`btn btn-ghost btn-circle btn-sm ${screenOrientation === 'landscape' ? 'btn-active bg-base-300' : ''}`}
|
||||
onClick={() => setScreenOrientation('landscape')}
|
||||
>
|
||||
<IoPhoneLandscapeOutline />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='w-full'>
|
||||
<h2 className='mb-2 font-medium'>{_('Behavior')}</h2>
|
||||
<div className='card border-base-200 bg-base-100 border shadow'>
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
DEFAULT_SYSTEM_SETTINGS,
|
||||
DEFAULT_CJK_VIEW_SETTINGS,
|
||||
DEFAULT_MOBILE_READSETTINGS,
|
||||
DEFAULT_SCREEN_CONFIG,
|
||||
} from './constants';
|
||||
import { getOSPlatform, isCJKEnv, isContentURI, isValidURL } from '@/utils/misc';
|
||||
import { deserializeConfig, serializeConfig } from '@/utils/serializer';
|
||||
@@ -95,6 +96,7 @@ export abstract class BaseAppService implements AppService {
|
||||
...(isCJKEnv() ? DEFAULT_CJK_VIEW_SETTINGS : {}),
|
||||
...DEFAULT_VIEW_CONFIG,
|
||||
...DEFAULT_TTS_CONFIG,
|
||||
...DEFAULT_SCREEN_CONFIG,
|
||||
...settings.globalViewSettings,
|
||||
};
|
||||
} catch {
|
||||
@@ -114,6 +116,7 @@ export abstract class BaseAppService implements AppService {
|
||||
...(isCJKEnv() ? DEFAULT_CJK_VIEW_SETTINGS : {}),
|
||||
...DEFAULT_VIEW_CONFIG,
|
||||
...DEFAULT_TTS_CONFIG,
|
||||
...DEFAULT_SCREEN_CONFIG,
|
||||
},
|
||||
} as SystemSettings;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
BookSearchConfig,
|
||||
BookStyle,
|
||||
HighlightColor,
|
||||
ScreenConfig,
|
||||
TTSConfig,
|
||||
ViewConfig,
|
||||
ViewSettings,
|
||||
@@ -139,6 +140,10 @@ export const DEFAULT_TTS_CONFIG: TTSConfig = {
|
||||
ttsVoice: '',
|
||||
};
|
||||
|
||||
export const DEFAULT_SCREEN_CONFIG: ScreenConfig = {
|
||||
screenOrientation: 'auto',
|
||||
};
|
||||
|
||||
export const DEFAULT_BOOK_SEARCH_CONFIG: BookSearchConfig = {
|
||||
scope: 'book',
|
||||
matchCase: false,
|
||||
|
||||
@@ -124,7 +124,17 @@ export interface TTSConfig {
|
||||
ttsVoice: string;
|
||||
}
|
||||
|
||||
export interface ViewSettings extends BookLayout, BookStyle, BookFont, ViewConfig, TTSConfig {}
|
||||
export interface ScreenConfig {
|
||||
screenOrientation: 'auto' | 'portrait' | 'landscape';
|
||||
}
|
||||
|
||||
export interface ViewSettings
|
||||
extends BookLayout,
|
||||
BookStyle,
|
||||
BookFont,
|
||||
ViewConfig,
|
||||
TTSConfig,
|
||||
ScreenConfig {}
|
||||
|
||||
export interface BookProgress {
|
||||
location: string;
|
||||
|
||||
@@ -48,6 +48,10 @@ export interface InterceptKeysRequest {
|
||||
backKey?: boolean;
|
||||
}
|
||||
|
||||
export interface LockScreenRequest {
|
||||
orientation: 'portrait' | 'landscape' | 'auto';
|
||||
}
|
||||
|
||||
export async function copyURIToPath(request: CopyURIRequest): Promise<CopyURIResponse> {
|
||||
const result = await invoke<CopyURIResponse>('plugin:native-bridge|copy_uri_to_path', {
|
||||
payload: request,
|
||||
@@ -108,3 +112,9 @@ export async function interceptKeys(request: InterceptKeysRequest): Promise<void
|
||||
payload: request,
|
||||
});
|
||||
}
|
||||
|
||||
export async function lockScreenOrientation(request: LockScreenRequest): Promise<void> {
|
||||
await invoke('plugin:native-bridge|lock_screen_orientation', {
|
||||
payload: request,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user