This commit is contained in:
+49
@@ -4,6 +4,7 @@ import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.KeyEvent
|
||||
import android.webkit.WebView
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
@@ -16,6 +17,8 @@ import androidx.activity.OnBackPressedCallback
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import app.tauri.plugin.JSArray
|
||||
import app.tauri.plugin.JSObject
|
||||
import com.readest.native_bridge.KeyDownInterceptor
|
||||
import com.readest.native_bridge.NativeBridgePlugin
|
||||
|
||||
@@ -113,6 +116,8 @@ class MainActivity : TauriActivity(), KeyDownInterceptor {
|
||||
enableEdgeToEdge()
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
handleIncomingIntent(intent)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
setTaskDescription(
|
||||
ActivityManager.TaskDescription(
|
||||
@@ -164,4 +169,48 @@ class MainActivity : TauriActivity(), KeyDownInterceptor {
|
||||
|
||||
NativeBridgePlugin.getInstance()?.handleActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
intent?.let { handleIncomingIntent(it) }
|
||||
}
|
||||
|
||||
private fun handleIncomingIntent(intent: Intent) {
|
||||
when (intent.action) {
|
||||
Intent.ACTION_SEND -> {
|
||||
if (intent.type != null) {
|
||||
handleSingleFile(intent)
|
||||
}
|
||||
}
|
||||
Intent.ACTION_SEND_MULTIPLE -> {
|
||||
if (intent.type != null) {
|
||||
handleMultipleFiles(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSingleFile(intent: Intent) {
|
||||
val uri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
|
||||
uri?.let { fileUri ->
|
||||
val payload = JSObject().apply {
|
||||
var urls = JSArray()
|
||||
urls.put(fileUri.toString())
|
||||
put("urls", urls)
|
||||
}
|
||||
NativeBridgePlugin.getInstance()?.triggerEvent("shared-intent", payload)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleMultipleFiles(intent: Intent) {
|
||||
val uris = intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
|
||||
uris?.let { fileUris ->
|
||||
val payload = JSObject().apply {
|
||||
var urls = JSArray()
|
||||
fileUris.forEach { urls.put(it.toString()) }
|
||||
put("urls", urls)
|
||||
}
|
||||
NativeBridgePlugin.getInstance()?.triggerEvent("shared-intent", payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -773,4 +773,10 @@ class NativeBridgePlugin(private val activity: Activity): Plugin(activity) {
|
||||
path
|
||||
}
|
||||
}
|
||||
|
||||
fun triggerEvent(eventName: String, payload: JSObject) {
|
||||
activity.runOnUiThread {
|
||||
trigger(eventName, payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ const COMMANDS: &[&str] = &[
|
||||
"get_external_sdcard_path",
|
||||
"open_external_url",
|
||||
"select_directory",
|
||||
"register_listener",
|
||||
"remove_listener",
|
||||
"request_manage_storage_permission",
|
||||
"check_permissions",
|
||||
"request_permissions",
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-register-listener"
|
||||
description = "Enables the register_listener command without any pre-configured scope."
|
||||
commands.allow = ["register_listener"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-register-listener"
|
||||
description = "Denies the register_listener command without any pre-configured scope."
|
||||
commands.deny = ["register_listener"]
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-remove-listener"
|
||||
description = "Enables the remove_listener command without any pre-configured scope."
|
||||
commands.allow = ["remove_listener"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-remove-listener"
|
||||
description = "Denies the remove_listener command without any pre-configured scope."
|
||||
commands.deny = ["remove_listener"]
|
||||
+54
@@ -26,6 +26,8 @@ Default permissions for the plugin
|
||||
- `allow-open-external-url`
|
||||
- `allow-select-directory`
|
||||
- `allow-request-manage-storage-permission`
|
||||
- `allow-register-listener`
|
||||
- `allow-remove-listener`
|
||||
- `allow-check-permissions`
|
||||
- `allow-request-permissions`
|
||||
- `allow-checkPermissions`
|
||||
@@ -563,6 +565,58 @@ Denies the open_external_url command without any pre-configured scope.
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`native-bridge:allow-register-listener`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the register_listener command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`native-bridge:deny-register-listener`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the register_listener command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`native-bridge:allow-remove-listener`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the remove_listener command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`native-bridge:deny-remove-listener`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the remove_listener command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`native-bridge:allow-request-permissions`
|
||||
|
||||
</td>
|
||||
|
||||
@@ -23,6 +23,8 @@ permissions = [
|
||||
"allow-open-external-url",
|
||||
"allow-select-directory",
|
||||
"allow-request-manage-storage-permission",
|
||||
"allow-register-listener",
|
||||
"allow-remove-listener",
|
||||
"allow-check-permissions",
|
||||
"allow-request-permissions",
|
||||
"allow-checkPermissions",
|
||||
|
||||
+26
-2
@@ -534,6 +534,30 @@
|
||||
"const": "deny-open-external-url",
|
||||
"markdownDescription": "Denies the open_external_url command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the register_listener command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-register-listener",
|
||||
"markdownDescription": "Enables the register_listener command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the register_listener command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-register-listener",
|
||||
"markdownDescription": "Denies the register_listener command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the remove_listener command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-remove-listener",
|
||||
"markdownDescription": "Enables the remove_listener command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the remove_listener command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-remove-listener",
|
||||
"markdownDescription": "Denies the remove_listener command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the request-permissions command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -631,10 +655,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`\n- `allow-lock-screen-orientation`\n- `allow-iap-initialize`\n- `allow-iap-fetch-products`\n- `allow-iap-purchase-product`\n- `allow-iap-restore-purchases`\n- `allow-get-system-color-scheme`\n- `allow-get-safe-area-insets`\n- `allow-get-screen-brightness`\n- `allow-set-screen-brightness`\n- `allow-get-external-sdcard-path`\n- `allow-open-external-url`\n- `allow-select-directory`\n- `allow-request-manage-storage-permission`\n- `allow-check-permissions`\n- `allow-request-permissions`\n- `allow-checkPermissions`\n- `allow-requestPermissions`",
|
||||
"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`\n- `allow-iap-initialize`\n- `allow-iap-fetch-products`\n- `allow-iap-purchase-product`\n- `allow-iap-restore-purchases`\n- `allow-get-system-color-scheme`\n- `allow-get-safe-area-insets`\n- `allow-get-screen-brightness`\n- `allow-set-screen-brightness`\n- `allow-get-external-sdcard-path`\n- `allow-open-external-url`\n- `allow-select-directory`\n- `allow-request-manage-storage-permission`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-check-permissions`\n- `allow-request-permissions`\n- `allow-checkPermissions`\n- `allow-requestPermissions`",
|
||||
"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`\n- `allow-lock-screen-orientation`\n- `allow-iap-initialize`\n- `allow-iap-fetch-products`\n- `allow-iap-purchase-product`\n- `allow-iap-restore-purchases`\n- `allow-get-system-color-scheme`\n- `allow-get-safe-area-insets`\n- `allow-get-screen-brightness`\n- `allow-set-screen-brightness`\n- `allow-get-external-sdcard-path`\n- `allow-open-external-url`\n- `allow-select-directory`\n- `allow-request-manage-storage-permission`\n- `allow-check-permissions`\n- `allow-request-permissions`\n- `allow-checkPermissions`\n- `allow-requestPermissions`"
|
||||
"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`\n- `allow-iap-initialize`\n- `allow-iap-fetch-products`\n- `allow-iap-purchase-product`\n- `allow-iap-restore-purchases`\n- `allow-get-system-color-scheme`\n- `allow-get-safe-area-insets`\n- `allow-get-screen-brightness`\n- `allow-set-screen-brightness`\n- `allow-get-external-sdcard-path`\n- `allow-open-external-url`\n- `allow-select-directory`\n- `allow-request-manage-storage-permission`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-check-permissions`\n- `allow-request-permissions`\n- `allow-checkPermissions`\n- `allow-requestPermissions`"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useRouter } from 'next/navigation';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useLibraryStore } from '@/store/libraryStore';
|
||||
import { useSettingsStore } from '@/store/settingsStore';
|
||||
import { addPluginListener } from '@tauri-apps/api/core';
|
||||
import { onOpenUrl } from '@tauri-apps/plugin-deep-link';
|
||||
import { getCurrentWindow, getAllWindows } from '@tauri-apps/api/window';
|
||||
import { isTauriAppPlatform } from '@/services/environment';
|
||||
@@ -13,6 +14,10 @@ interface SingleInstancePayload {
|
||||
cwd: string;
|
||||
}
|
||||
|
||||
interface SharedIntentPayload {
|
||||
urls: string[];
|
||||
}
|
||||
|
||||
export function useOpenWithBooks() {
|
||||
const router = useRouter();
|
||||
const { appService } = useEnv();
|
||||
@@ -26,20 +31,25 @@ export function useOpenWithBooks() {
|
||||
return sortedWindows[0]?.label === currentWindow.label;
|
||||
};
|
||||
|
||||
const handleOpenWithFileUrl = async (url: string) => {
|
||||
console.log('Handle Open with URL:', url);
|
||||
let filePath = url;
|
||||
if (filePath.startsWith('file://')) {
|
||||
filePath = decodeURI(filePath.replace('file://', ''));
|
||||
const handleOpenWithFileUrl = async (urls: string[]) => {
|
||||
console.log('Handle Open with URL:', urls);
|
||||
const filePaths = [];
|
||||
for (let url of urls) {
|
||||
if (url.startsWith('file://')) {
|
||||
url = decodeURI(url.replace('file://', ''));
|
||||
}
|
||||
if (!/^(https?:|data:|blob:)/i.test(url)) {
|
||||
filePaths.push(url);
|
||||
}
|
||||
}
|
||||
if (!/^(https?:|data:|blob:)/i.test(filePath)) {
|
||||
if (filePaths.length > 0) {
|
||||
const settings = useSettingsStore.getState().settings;
|
||||
if (appService?.hasWindow && settings.openBookInNewWindow) {
|
||||
if (await isFirstWindow()) {
|
||||
showLibraryWindow(appService, [filePath]);
|
||||
showLibraryWindow(appService, filePaths);
|
||||
}
|
||||
} else {
|
||||
window.OPEN_WITH_FILES = [filePath];
|
||||
window.OPEN_WITH_FILES = filePaths;
|
||||
setCheckOpenWithBooks(true);
|
||||
navigateToLibrary(router, `reload=${Date.now()}`);
|
||||
}
|
||||
@@ -55,19 +65,27 @@ export function useOpenWithBooks() {
|
||||
console.log('Received deep link:', event, payload);
|
||||
const { args } = payload as SingleInstancePayload;
|
||||
if (args?.[1]) {
|
||||
handleOpenWithFileUrl(args[1]);
|
||||
handleOpenWithFileUrl([args[1]]);
|
||||
}
|
||||
});
|
||||
const unlistenSharedIntent = addPluginListener<SharedIntentPayload>(
|
||||
'native-bridge',
|
||||
'shared-intent',
|
||||
(payload) => {
|
||||
console.log('Received shared intent:', payload);
|
||||
const { urls } = payload;
|
||||
handleOpenWithFileUrl(urls);
|
||||
},
|
||||
);
|
||||
const listenOpenWithFiles = async () => {
|
||||
return await onOpenUrl((urls) => {
|
||||
urls.forEach((url) => {
|
||||
handleOpenWithFileUrl(url);
|
||||
});
|
||||
handleOpenWithFileUrl(urls);
|
||||
});
|
||||
};
|
||||
const unlistenOpenUrl = listenOpenWithFiles();
|
||||
return () => {
|
||||
unlistenDeeplink.then((f) => f());
|
||||
unlistenSharedIntent.then((f) => f.unregister());
|
||||
unlistenOpenUrl.then((f) => f());
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
||||
Reference in New Issue
Block a user