Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 66707004e3 | |||
| baa3691851 |
Generated
+10
-11
@@ -3912,19 +3912,8 @@ name = "nym-socks5-client-core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"futures",
|
||||
"log",
|
||||
"nym-bandwidth-controller",
|
||||
"nym-client-core",
|
||||
"nym-config",
|
||||
"nym-credential-storage",
|
||||
"nym-network-defaults",
|
||||
"nym-service-providers-common",
|
||||
"nym-socks5-proxy-helpers",
|
||||
"nym-socks5-requests",
|
||||
"nym-sphinx",
|
||||
"nym-task",
|
||||
"nym-validator-client",
|
||||
"pin-project",
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
@@ -4318,6 +4307,15 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "111.25.2+1.1.1t"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320708a054ad9b3bf314688b5db87cf4d6683d64cfc835e2337924ae62bf4431"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.82"
|
||||
@@ -4327,6 +4325,7 @@ dependencies = [
|
||||
"autocfg 1.1.0",
|
||||
"cc",
|
||||
"libc",
|
||||
"openssl-src",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
@@ -54,7 +54,7 @@ features = ["time"]
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
|
||||
version = "0.14"
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
|
||||
[target."cfg(all(not(target_arch = \"wasm32\"),not(target_os = \"android\")))".dependencies.sqlx]
|
||||
version = "0.6.2"
|
||||
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
|
||||
optional = true
|
||||
|
||||
@@ -50,6 +50,10 @@ itertools = { version = "0.10", optional = true }
|
||||
zeroize = { version = "1.5.7", optional = true, features = ["zeroize_derive"] }
|
||||
cosmwasm-std = { workspace = true, optional = true }
|
||||
|
||||
[target.'cfg(android)'.dependencies.reqwest]
|
||||
version = "0.11"
|
||||
features = ["json", "native-tls-vendored"]
|
||||
|
||||
[dev-dependencies]
|
||||
bip39 = { workspace = true }
|
||||
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support", features = ["rpc", "bip32"] }
|
||||
|
||||
@@ -16,6 +16,11 @@ tap = "1.0.1"
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] }
|
||||
|
||||
nym-client-core = { path = "../client-core", features = ["fs-surb-storage"] }
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies.nym-client-core]
|
||||
path = "../client-core"
|
||||
features = []
|
||||
|
||||
futures = "0.3"
|
||||
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
|
||||
nym-config = { path = "../config" }
|
||||
|
||||
@@ -93,6 +93,11 @@ nym-validator-client = { path = "../common/client-libs/validator-client", featur
|
||||
] }
|
||||
nym-bin-common = { path = "../common/bin-common" }
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies.reqwest]
|
||||
version = "0.11.11"
|
||||
features = ["json", "native-tls-vendored"]
|
||||
|
||||
|
||||
[features]
|
||||
no-reward = []
|
||||
generate-ts = ["ts-rs"]
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"webpack:dev:onlyThis": "yarn webpack serve --config webpack.dev.js",
|
||||
"webpack:prod": "yarn webpack --progress --config webpack.prod.js",
|
||||
"tauri": "tauri",
|
||||
"tauri:dev:android": "WRY_ANDROID_PACKAGE=net.nymtech.nym_connect WRY_ANDROID_LIBRARY=nym_connect cargo tauri android dev",
|
||||
"tauri:dev:android": "cargo tauri android dev",
|
||||
"tauri:build:android": "WRY_ANDROID_PACKAGE=net.nymtech.nym_connect WRY_ANDROID_LIBRARY=nym_connect cargo tauri android build",
|
||||
"dev:android": "run-p webpack:dev:onlyThis tauri:dev:android",
|
||||
"prebuild": "yarn --cwd ../.. build",
|
||||
@@ -27,7 +27,7 @@
|
||||
"clean:node": "rm -rf node_modules",
|
||||
"clean:rust": "cargo clean --manifest-path src-tauri/Cargo.toml",
|
||||
"clean:android": "cd src-tauri/gen/android/nym_connect/ && gradlew clean",
|
||||
"clean:": "run-p clean:node clean:rust clean:android"
|
||||
"clean": "run-p clean:node clean:rust clean:android"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.7.0",
|
||||
@@ -39,7 +39,7 @@
|
||||
"@mui/system": ">= 5",
|
||||
"@mui/lab": "^5.0.0-alpha.72",
|
||||
"@nymproject/react": "^1.0.0",
|
||||
"@tauri-apps/api": "^2.0.0-alpha.0",
|
||||
"@tauri-apps/api": "^2.0.0-alpha.3",
|
||||
"@tauri-apps/tauri-forage": "^1.0.0-beta.2",
|
||||
"clsx": "^1.1.1",
|
||||
"luxon": "^2.3.0",
|
||||
@@ -64,7 +64,7 @@
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
|
||||
"@storybook/react": "^6.5.15",
|
||||
"@svgr/webpack": "^6.1.1",
|
||||
"@tauri-apps/cli": "^2.0.0-alpha.2",
|
||||
"@tauri-apps/cli": "^2.0.0-alpha.8",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^12.0.0",
|
||||
"@types/jest": "^27.0.1",
|
||||
|
||||
Generated
+934
-523
File diff suppressed because it is too large
Load Diff
@@ -16,13 +16,9 @@ rust-version = "1.58"
|
||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2.0.0-alpha.1", features = [] }
|
||||
tauri-build = { version = "2.0.0-alpha.4", features = [] }
|
||||
# tauri-build = { git = "https://github.com/tauri-apps/tauri", branch = "next", features = [] }
|
||||
|
||||
# TODO untill new tauri version includes https://github.com/tauri-apps/tauri-mobile/pull/111
|
||||
[patch.crates-io]
|
||||
tauri-mobile = { git = "https://github.com/tauri-apps/tauri-mobile", branch = "dev" }
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||
@@ -35,14 +31,14 @@ itertools = "0.10.5"
|
||||
log = { version = "0.4", features = ["serde"] }
|
||||
pretty_env_logger = "0.4.0"
|
||||
rand = "0.8"
|
||||
reqwest = { version = "0.11", features = ["json", "socks"] }
|
||||
reqwest = { version = "0.11", features = ["json", "socks", "native-tls-vendored"] }
|
||||
rust-embed = { version = "6.4.2", features = ["include-exclude"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde_repr = "0.1"
|
||||
tap = "1.0.1"
|
||||
# tauri = { git = "https://github.com/tauri-apps/tauri", branch = "next", features = ["clipboard-write-text", "native-tls-vendored", "notification-all", "shell-open"] }
|
||||
tauri = { version = "2.0.0-alpha.3", features = ["clipboard-write-text", "native-tls-vendored", "notification-all", "shell-open"] }
|
||||
tauri = { version = "2.0.0-alpha.8", features = ["clipboard-write-text", "native-tls-vendored", "notification-all", "shell-open"] }
|
||||
tendermint-rpc = "0.23.0"
|
||||
thiserror = "1.0"
|
||||
time = { version = "0.3.17", features = ["local-offset"] }
|
||||
@@ -50,14 +46,14 @@ tokio = { version = "1.24.1", features = ["sync", "time"] }
|
||||
url = "2.2"
|
||||
yaml-rust = "0.4"
|
||||
|
||||
nym-client-core = { path = "../../../common/client-core", features = ["mobile"], default-features = false }
|
||||
nym-client-core = { path = "../../../common/client-core", features = [] }
|
||||
nym-api-requests = { path = "../../../nym-api/nym-api-requests" }
|
||||
nym-contracts-common = { path = "../../../common/cosmwasm-smart-contracts/contracts-common"}
|
||||
nym-config-common = { path = "../../../common/config", package = "nym-config" }
|
||||
nym-credential-storage = { path = "../../../common/credential-storage" }
|
||||
nym-crypto = { path = "../../../common/crypto" }
|
||||
nym-bin-common = { path = "../../../common/bin-common"}
|
||||
nym-socks5-client-core = { path = "../../../common/socks5-client-core", default-features = false }
|
||||
nym-socks5-client-core = { path = "../../../common/socks5-client-core", default-features = true }
|
||||
nym-task = { path = "../../../common/task" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/build
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "app.tauri"
|
||||
compileSdk = 33
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
targetSdk = 33
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles("proguard-rules.pro")
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation("androidx.core:core-ktx:1.7.0")
|
||||
implementation("androidx.appcompat:appcompat:1.6.0")
|
||||
implementation("com.google.android.material:material:1.7.0")
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
-keep class app.tauri.** {
|
||||
@app.tauri.JniMethod public <methods>;
|
||||
native <methods>;
|
||||
}
|
||||
|
||||
-keep class app.tauri.plugin.JSArray {
|
||||
public <init>(...);
|
||||
}
|
||||
|
||||
-keepclassmembers class org.json.JSONArray {
|
||||
public put(...);
|
||||
}
|
||||
|
||||
-keep class app.tauri.plugin.JSObject {
|
||||
public <init>(...);
|
||||
public put(...);
|
||||
}
|
||||
|
||||
-keep @app.tauri.annotation.TauriPlugin public class * {
|
||||
@app.tauri.annotation.Command public <methods>;
|
||||
@app.tauri.annotation.PermissionCallback <methods>;
|
||||
@app.tauri.annotation.ActivityCallback <methods>;
|
||||
@app.tauri.annotation.Permission <methods>;
|
||||
public <init>(...);
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("app.tauri.test", appContext.packageName)
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
</manifest>
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri
|
||||
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.os.Environment
|
||||
import android.provider.DocumentsContract
|
||||
import android.provider.MediaStore
|
||||
import android.provider.OpenableColumns
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import kotlin.math.min
|
||||
|
||||
internal class FsUtils {
|
||||
companion object {
|
||||
fun getFileUrlForUri(context: Context, uri: Uri): String? {
|
||||
// DocumentProvider
|
||||
if (DocumentsContract.isDocumentUri(context, uri)) {
|
||||
// ExternalStorageProvider
|
||||
if (isExternalStorageDocument(uri)) {
|
||||
val docId: String = DocumentsContract.getDocumentId(uri)
|
||||
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }
|
||||
.toTypedArray()
|
||||
val type = split[0]
|
||||
if ("primary".equals(type, ignoreCase = true)) {
|
||||
return legacyPrimaryPath(split[1])
|
||||
} else {
|
||||
val splitIndex = docId.indexOf(':', 1)
|
||||
val tag = docId.substring(0, splitIndex)
|
||||
val path = docId.substring(splitIndex + 1)
|
||||
val nonPrimaryVolume = getPathToNonPrimaryVolume(context, tag)
|
||||
if (nonPrimaryVolume != null) {
|
||||
val result = "$nonPrimaryVolume/$path"
|
||||
val file = File(result)
|
||||
return if (file.exists() && file.canRead()) {
|
||||
result
|
||||
} else null
|
||||
}
|
||||
}
|
||||
} else if (isDownloadsDocument(uri)) {
|
||||
val id: String = DocumentsContract.getDocumentId(uri)
|
||||
val contentUri: Uri = ContentUris.withAppendedId(
|
||||
Uri.parse("content://downloads/public_downloads"),
|
||||
java.lang.Long.valueOf(id)
|
||||
)
|
||||
return getDataColumn(context, contentUri, null, null)
|
||||
} else if (isMediaDocument(uri)) {
|
||||
val docId: String = DocumentsContract.getDocumentId(uri)
|
||||
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }
|
||||
.toTypedArray()
|
||||
val type = split[0]
|
||||
var contentUri: Uri? = null
|
||||
when (type) {
|
||||
"image" -> {
|
||||
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
|
||||
}
|
||||
"video" -> {
|
||||
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
|
||||
}
|
||||
"audio" -> {
|
||||
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
|
||||
}
|
||||
}
|
||||
val selection = "_id=?"
|
||||
val selectionArgs = arrayOf(split[1])
|
||||
if (contentUri != null) {
|
||||
return getDataColumn(context, contentUri, selection, selectionArgs)
|
||||
}
|
||||
}
|
||||
} else if ("content".equals(uri.scheme, ignoreCase = true)) {
|
||||
// Return the remote address
|
||||
return if (isGooglePhotosUri(uri)) uri.lastPathSegment else getDataColumn(
|
||||
context,
|
||||
uri,
|
||||
null,
|
||||
null
|
||||
)
|
||||
} else if ("file".equals(uri.scheme, ignoreCase = true)) {
|
||||
return uri.path
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the data column for this Uri. This is useful for
|
||||
* MediaStore Uris, and other file-based ContentProviders.
|
||||
*
|
||||
* @param context The context.
|
||||
* @param uri The Uri to query.
|
||||
* @param selection (Optional) Filter used in the query.
|
||||
* @param selectionArgs (Optional) Selection arguments used in the query.
|
||||
* @return The value of the _data column, which is typically a file path.
|
||||
*/
|
||||
private fun getDataColumn(
|
||||
context: Context,
|
||||
uri: Uri,
|
||||
selection: String?,
|
||||
selectionArgs: Array<String>?
|
||||
): String? {
|
||||
var path: String? = null
|
||||
var cursor: Cursor? = null
|
||||
val column = "_data"
|
||||
val projection = arrayOf(column)
|
||||
try {
|
||||
cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
val index = cursor.getColumnIndexOrThrow(column)
|
||||
path = cursor.getString(index)
|
||||
}
|
||||
} catch (ex: IllegalArgumentException) {
|
||||
return getCopyFilePath(uri, context)
|
||||
} finally {
|
||||
cursor?.close()
|
||||
}
|
||||
return path ?: getCopyFilePath(uri, context)
|
||||
}
|
||||
|
||||
private fun getCopyFilePath(uri: Uri, context: Context): String? {
|
||||
val cursor = context.contentResolver.query(uri, null, null, null, null)!!
|
||||
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
||||
cursor.moveToFirst()
|
||||
val name = cursor.getString(nameIndex)
|
||||
val file = File(context.filesDir, name)
|
||||
try {
|
||||
val inputStream = context.contentResolver.openInputStream(uri)
|
||||
val outputStream = FileOutputStream(file)
|
||||
var read: Int
|
||||
val maxBufferSize = 1024 * 1024
|
||||
val bufferSize = min(inputStream!!.available(), maxBufferSize)
|
||||
val buffers = ByteArray(bufferSize)
|
||||
while (inputStream.read(buffers).also { read = it } != -1) {
|
||||
outputStream.write(buffers, 0, read)
|
||||
}
|
||||
inputStream.close()
|
||||
outputStream.close()
|
||||
} catch (e: Exception) {
|
||||
return null
|
||||
} finally {
|
||||
cursor.close()
|
||||
}
|
||||
return file.path
|
||||
}
|
||||
|
||||
private fun legacyPrimaryPath(pathPart: String): String {
|
||||
return Environment.getExternalStorageDirectory().toString() + "/" + pathPart
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is ExternalStorageProvider.
|
||||
*/
|
||||
private fun isExternalStorageDocument(uri: Uri): Boolean {
|
||||
return "com.android.externalstorage.documents" == uri.authority
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is DownloadsProvider.
|
||||
*/
|
||||
private fun isDownloadsDocument(uri: Uri): Boolean {
|
||||
return "com.android.providers.downloads.documents" == uri.authority
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is MediaProvider.
|
||||
*/
|
||||
private fun isMediaDocument(uri: Uri): Boolean {
|
||||
return "com.android.providers.media.documents" == uri.authority
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is Google Photos.
|
||||
*/
|
||||
private fun isGooglePhotosUri(uri: Uri): Boolean {
|
||||
return "com.google.android.apps.photos.content" == uri.authority
|
||||
}
|
||||
|
||||
private fun getPathToNonPrimaryVolume(context: Context, tag: String): String? {
|
||||
val volumes = context.externalCacheDirs
|
||||
if (volumes != null) {
|
||||
for (volume in volumes) {
|
||||
if (volume != null) {
|
||||
val path = volume.absolutePath
|
||||
val index = path.indexOf(tag)
|
||||
if (index != -1) {
|
||||
return path.substring(0, index) + tag
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri
|
||||
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
internal annotation class JniMethod
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri
|
||||
|
||||
// taken from https://github.com/ionic-team/capacitor/blob/6658bca41e78239347e458175b14ca8bd5c1d6e8/android/capacitor/src/main/java/com/getcapacitor/Logger.java
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
class Logger {
|
||||
companion object {
|
||||
private const val LOG_TAG_CORE = "Tauri"
|
||||
|
||||
fun tags(vararg subtags: String): String {
|
||||
return if (subtags.isNotEmpty()) {
|
||||
LOG_TAG_CORE + "/" + TextUtils.join("/", subtags)
|
||||
} else LOG_TAG_CORE
|
||||
}
|
||||
|
||||
fun verbose(message: String) {
|
||||
verbose(LOG_TAG_CORE, message)
|
||||
}
|
||||
|
||||
fun verbose(tag: String, message: String) {
|
||||
if (!shouldLog()) {
|
||||
return
|
||||
}
|
||||
Log.v(tag, message)
|
||||
}
|
||||
|
||||
fun debug(message: String) {
|
||||
debug(LOG_TAG_CORE, message)
|
||||
}
|
||||
|
||||
fun debug(tag: String, message: String) {
|
||||
if (!shouldLog()) {
|
||||
return
|
||||
}
|
||||
Log.d(tag, message)
|
||||
}
|
||||
|
||||
fun info(message: String) {
|
||||
info(LOG_TAG_CORE, message)
|
||||
}
|
||||
|
||||
fun info(tag: String, message: String) {
|
||||
if (!shouldLog()) {
|
||||
return
|
||||
}
|
||||
Log.i(tag, message)
|
||||
}
|
||||
|
||||
fun warn(message: String) {
|
||||
warn(LOG_TAG_CORE, message)
|
||||
}
|
||||
|
||||
fun warn(tag: String, message: String) {
|
||||
if (!shouldLog()) {
|
||||
return
|
||||
}
|
||||
Log.w(tag, message)
|
||||
}
|
||||
|
||||
fun error(message: String) {
|
||||
error(LOG_TAG_CORE, message, null)
|
||||
}
|
||||
|
||||
fun error(message: String, e: Throwable?) {
|
||||
error(LOG_TAG_CORE, message, e)
|
||||
}
|
||||
|
||||
fun error(tag: String, message: String, e: Throwable?) {
|
||||
if (!shouldLog()) {
|
||||
return
|
||||
}
|
||||
Log.e(tag, message, e)
|
||||
}
|
||||
|
||||
private fun shouldLog(): Boolean {
|
||||
return BuildConfig.DEBUG
|
||||
}
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Environment
|
||||
import app.tauri.annotation.Command
|
||||
import app.tauri.annotation.TauriPlugin
|
||||
import app.tauri.plugin.Plugin
|
||||
import app.tauri.plugin.Invoke
|
||||
import app.tauri.plugin.JSObject
|
||||
|
||||
@TauriPlugin
|
||||
class PathPlugin(private val activity: Activity): Plugin(activity) {
|
||||
private fun resolvePath(invoke: Invoke, path: String?) {
|
||||
val obj = JSObject()
|
||||
obj.put("path", path)
|
||||
invoke.resolve(obj)
|
||||
}
|
||||
|
||||
@Command
|
||||
fun getAudioDir(invoke: Invoke) {
|
||||
resolvePath(invoke, activity.getExternalFilesDir(Environment.DIRECTORY_MUSIC)?.absolutePath)
|
||||
}
|
||||
|
||||
@Command
|
||||
fun getExternalCacheDir(invoke: Invoke) {
|
||||
resolvePath(invoke, activity.externalCacheDir?.absolutePath)
|
||||
}
|
||||
|
||||
@Command
|
||||
fun getConfigDir(invoke: Invoke) {
|
||||
resolvePath(invoke, activity.dataDir.absolutePath)
|
||||
}
|
||||
|
||||
@Command
|
||||
fun getDataDir(invoke: Invoke) {
|
||||
resolvePath(invoke, activity.dataDir.absolutePath)
|
||||
}
|
||||
|
||||
@Command
|
||||
fun getDocumentDir(invoke: Invoke) {
|
||||
resolvePath(invoke, activity.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)?.absolutePath)
|
||||
}
|
||||
|
||||
@Command
|
||||
fun getDownloadDir(invoke: Invoke) {
|
||||
resolvePath(invoke, activity.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.absolutePath)
|
||||
}
|
||||
|
||||
@Command
|
||||
fun getPictureDir(invoke: Invoke) {
|
||||
resolvePath(invoke, activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES)?.absolutePath)
|
||||
}
|
||||
|
||||
@Command
|
||||
fun getPublicDir(invoke: Invoke) {
|
||||
resolvePath(invoke, activity.getExternalFilesDir(Environment.DIRECTORY_DCIM)?.absolutePath)
|
||||
}
|
||||
|
||||
@Command
|
||||
fun getVideoDir(invoke: Invoke) {
|
||||
resolvePath(invoke, activity.externalCacheDir?.absolutePath)
|
||||
}
|
||||
|
||||
@Command
|
||||
fun getResourcesDir(invoke: Invoke) {
|
||||
// TODO
|
||||
resolvePath(invoke, activity.cacheDir.absolutePath)
|
||||
}
|
||||
|
||||
@Command
|
||||
fun getCacheDir(invoke: Invoke) {
|
||||
resolvePath(invoke, activity.cacheDir.absolutePath)
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri
|
||||
|
||||
// taken from https://github.com/ionic-team/capacitor/blob/6658bca41e78239347e458175b14ca8bd5c1d6e8/android/capacitor/src/main/java/com/getcapacitor/PermissionHelper.java
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import java.util.ArrayList;
|
||||
|
||||
object PermissionHelper {
|
||||
/**
|
||||
* Checks if a list of given permissions are all granted by the user
|
||||
*
|
||||
* @param permissions Permissions to check.
|
||||
* @return True if all permissions are granted, false if at least one is not.
|
||||
*/
|
||||
fun hasPermissions(context: Context?, permissions: Array<String>): Boolean {
|
||||
for (perm in permissions) {
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
context!!,
|
||||
perm
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given permission has been defined in the AndroidManifest.xml
|
||||
*
|
||||
* @param permission A permission to check.
|
||||
* @return True if the permission has been defined in the Manifest, false if not.
|
||||
*/
|
||||
fun hasDefinedPermission(context: Context, permission: String): Boolean {
|
||||
var hasPermission = false
|
||||
val requestedPermissions = getManifestPermissions(context)
|
||||
if (requestedPermissions != null && requestedPermissions.isNotEmpty()) {
|
||||
val requestedPermissionsList = listOf(*requestedPermissions)
|
||||
val requestedPermissionsArrayList = ArrayList(requestedPermissionsList)
|
||||
if (requestedPermissionsArrayList.contains(permission)) {
|
||||
hasPermission = true
|
||||
}
|
||||
}
|
||||
return hasPermission
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether all of the given permissions have been defined in the AndroidManifest.xml
|
||||
* @param context the app context
|
||||
* @param permissions a list of permissions
|
||||
* @return true only if all permissions are defined in the AndroidManifest.xml
|
||||
*/
|
||||
fun hasDefinedPermissions(context: Context, permissions: Array<String>): Boolean {
|
||||
for (permission in permissions) {
|
||||
if (!hasDefinedPermission(context, permission)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the permissions defined in AndroidManifest.xml
|
||||
*
|
||||
* @return The permissions defined in AndroidManifest.xml
|
||||
*/
|
||||
private fun getManifestPermissions(context: Context): Array<String>? {
|
||||
var requestedPermissions: Array<String>? = null
|
||||
try {
|
||||
val pm = context.packageManager
|
||||
val packageInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
pm.getPackageInfo(context.packageName, PackageManager.PackageInfoFlags.of(PackageManager.GET_PERMISSIONS.toLong()))
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
pm.getPackageInfo(context.packageName, PackageManager.GET_PERMISSIONS)
|
||||
}
|
||||
if (packageInfo != null) {
|
||||
requestedPermissions = packageInfo.requestedPermissions
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
return requestedPermissions
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of permissions, return a new list with the ones not present in AndroidManifest.xml
|
||||
*
|
||||
* @param neededPermissions The permissions needed.
|
||||
* @return The permissions not present in AndroidManifest.xml
|
||||
*/
|
||||
fun getUndefinedPermissions(context: Context, neededPermissions: Array<String>): Array<String?> {
|
||||
val undefinedPermissions = ArrayList<String?>()
|
||||
val requestedPermissions = getManifestPermissions(context)
|
||||
if (requestedPermissions != null && requestedPermissions.isNotEmpty()) {
|
||||
val requestedPermissionsList = listOf(*requestedPermissions)
|
||||
val requestedPermissionsArrayList = ArrayList(requestedPermissionsList)
|
||||
for (permission in neededPermissions) {
|
||||
if (!requestedPermissionsArrayList.contains(permission)) {
|
||||
undefinedPermissions.add(permission)
|
||||
}
|
||||
}
|
||||
var undefinedPermissionArray = arrayOfNulls<String>(undefinedPermissions.size)
|
||||
undefinedPermissionArray = undefinedPermissions.toArray(undefinedPermissionArray)
|
||||
return undefinedPermissionArray
|
||||
}
|
||||
return neededPermissions as Array<String?>
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri
|
||||
|
||||
import java.util.*
|
||||
|
||||
enum class PermissionState(private val state: String) {
|
||||
GRANTED("granted"), DENIED("denied"), PROMPT("prompt"), PROMPT_WITH_RATIONALE("prompt-with-rationale");
|
||||
|
||||
override fun toString(): String {
|
||||
return state
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun byState(state: String): PermissionState {
|
||||
return valueOf(state.uppercase(Locale.ROOT).replace('-', '_'))
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri.annotation
|
||||
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
annotation class ActivityCallback
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri.annotation
|
||||
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class Permission(
|
||||
/**
|
||||
* An array of Android permission strings.
|
||||
* Eg: {Manifest.permission.ACCESS_COARSE_LOCATION}
|
||||
* or {"android.permission.ACCESS_COARSE_LOCATION"}
|
||||
*/
|
||||
val strings: Array<String> = [],
|
||||
/**
|
||||
* An optional name to use instead of the Android permission string.
|
||||
*/
|
||||
val alias: String = ""
|
||||
)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri.annotation
|
||||
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
annotation class PermissionCallback
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri.annotation
|
||||
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class Command
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri.annotation
|
||||
|
||||
import app.tauri.annotation.Permission
|
||||
|
||||
/**
|
||||
* Base annotation for all Plugins
|
||||
*/
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class TauriPlugin(
|
||||
/**
|
||||
* Permissions this plugin needs, in order to make permission requests
|
||||
* easy if the plugin only needs basic permission prompting
|
||||
*/
|
||||
val permissions: Array<Permission> = []
|
||||
)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri.plugin
|
||||
|
||||
internal class InvalidCommandException : Exception {
|
||||
constructor(s: String?) : super(s) {}
|
||||
constructor(t: Throwable?) : super(t) {}
|
||||
constructor(s: String?, t: Throwable?) : super(s, t) {}
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri.plugin
|
||||
|
||||
import app.tauri.Logger
|
||||
|
||||
class Invoke(
|
||||
val id: Long,
|
||||
val command: String,
|
||||
private val sendResponse: (success: PluginResult?, error: PluginResult?) -> Unit,
|
||||
val data: JSObject) {
|
||||
|
||||
fun resolve(data: JSObject?) {
|
||||
val result = PluginResult(data)
|
||||
sendResponse(result, null)
|
||||
}
|
||||
|
||||
fun resolve() {
|
||||
sendResponse(null, null)
|
||||
}
|
||||
|
||||
fun reject(msg: String?, code: String?, ex: Exception?, data: JSObject?) {
|
||||
val errorResult = PluginResult()
|
||||
if (ex != null) {
|
||||
Logger.error(Logger.tags("Plugin"), msg!!, ex)
|
||||
}
|
||||
try {
|
||||
errorResult.put("message", msg)
|
||||
errorResult.put("code", code)
|
||||
if (null != data) {
|
||||
errorResult.put("data", data)
|
||||
}
|
||||
} catch (jsonEx: Exception) {
|
||||
Logger.error(Logger.tags("Plugin"), jsonEx.message!!, jsonEx)
|
||||
}
|
||||
sendResponse(null, errorResult)
|
||||
}
|
||||
|
||||
fun reject(msg: String?, ex: Exception?, data: JSObject?) {
|
||||
reject(msg, null, ex, data)
|
||||
}
|
||||
|
||||
fun reject(msg: String?, code: String?, data: JSObject?) {
|
||||
reject(msg, code, null, data)
|
||||
}
|
||||
|
||||
fun reject(msg: String?, code: String?, ex: Exception?) {
|
||||
reject(msg, code, ex, null)
|
||||
}
|
||||
|
||||
fun reject(msg: String?, data: JSObject?) {
|
||||
reject(msg, null, null, data)
|
||||
}
|
||||
|
||||
fun reject(msg: String?, ex: Exception?) {
|
||||
reject(msg, null, ex, null)
|
||||
}
|
||||
|
||||
fun reject(msg: String?, code: String?) {
|
||||
reject(msg, code, null, null)
|
||||
}
|
||||
|
||||
fun reject(msg: String?) {
|
||||
reject(msg, null, null, null)
|
||||
}
|
||||
|
||||
fun getString(name: String): String? {
|
||||
return getStringInternal(name, null)
|
||||
}
|
||||
|
||||
fun getString(name: String, defaultValue: String): String {
|
||||
return getStringInternal(name, defaultValue)!!
|
||||
}
|
||||
|
||||
private fun getStringInternal(name: String, defaultValue: String?): String? {
|
||||
val value = data.opt(name) ?: return defaultValue
|
||||
return if (value is String) {
|
||||
value
|
||||
} else defaultValue
|
||||
}
|
||||
|
||||
fun getInt(name: String): Int? {
|
||||
return getIntInternal(name, null)
|
||||
}
|
||||
|
||||
fun getInt(name: String, defaultValue: Int): Int {
|
||||
return getIntInternal(name, defaultValue)!!
|
||||
}
|
||||
|
||||
private fun getIntInternal(name: String, defaultValue: Int?): Int? {
|
||||
val value = data.opt(name) ?: return defaultValue
|
||||
return if (value is Int) {
|
||||
value
|
||||
} else defaultValue
|
||||
}
|
||||
|
||||
fun getLong(name: String): Long? {
|
||||
return getLongInternal(name, null)
|
||||
}
|
||||
|
||||
fun getLong(name: String, defaultValue: Long): Long {
|
||||
return getLongInternal(name, defaultValue)!!
|
||||
}
|
||||
|
||||
private fun getLongInternal(name: String, defaultValue: Long?): Long? {
|
||||
val value = data.opt(name) ?: return defaultValue
|
||||
return if (value is Long) {
|
||||
value
|
||||
} else defaultValue
|
||||
}
|
||||
|
||||
fun getFloat(name: String): Float? {
|
||||
return getFloatInternal(name, null)
|
||||
}
|
||||
|
||||
fun getFloat(name: String, defaultValue: Float): Float {
|
||||
return getFloatInternal(name, defaultValue)!!
|
||||
}
|
||||
|
||||
private fun getFloatInternal(name: String, defaultValue: Float?): Float? {
|
||||
val value = data.opt(name) ?: return defaultValue
|
||||
if (value is Float) {
|
||||
return value
|
||||
}
|
||||
if (value is Double) {
|
||||
return value.toFloat()
|
||||
}
|
||||
return if (value is Int) {
|
||||
value.toFloat()
|
||||
} else defaultValue
|
||||
}
|
||||
|
||||
fun getDouble(name: String): Double? {
|
||||
return getDoubleInternal(name, null)
|
||||
}
|
||||
|
||||
fun getDouble(name: String, defaultValue: Double): Double {
|
||||
return getDoubleInternal(name, defaultValue)!!
|
||||
}
|
||||
|
||||
private fun getDoubleInternal(name: String, defaultValue: Double?): Double? {
|
||||
val value = data.opt(name) ?: return defaultValue
|
||||
if (value is Double) {
|
||||
return value
|
||||
}
|
||||
if (value is Float) {
|
||||
return value.toDouble()
|
||||
}
|
||||
return if (value is Int) {
|
||||
value.toDouble()
|
||||
} else defaultValue
|
||||
}
|
||||
|
||||
fun getBoolean(name: String): Boolean? {
|
||||
return getBooleanInternal(name, null)
|
||||
}
|
||||
|
||||
fun getBoolean(name: String, defaultValue: Boolean): Boolean {
|
||||
return getBooleanInternal(name, defaultValue)!!
|
||||
}
|
||||
|
||||
private fun getBooleanInternal(name: String, defaultValue: Boolean?): Boolean? {
|
||||
val value = data.opt(name) ?: return defaultValue
|
||||
return if (value is Boolean) {
|
||||
value
|
||||
} else defaultValue
|
||||
}
|
||||
|
||||
fun getObject(name: String): JSObject? {
|
||||
return getObjectInternal(name, null)
|
||||
}
|
||||
|
||||
fun getObject(name: String, defaultValue: JSObject): JSObject {
|
||||
return getObjectInternal(name, defaultValue)!!
|
||||
}
|
||||
|
||||
private fun getObjectInternal(name: String, defaultValue: JSObject?): JSObject? {
|
||||
val value = data.opt(name) ?: return defaultValue
|
||||
return if (value is JSObject) value else defaultValue
|
||||
}
|
||||
|
||||
fun getArray(name: String): JSArray? {
|
||||
return getArrayInternal(name, null)
|
||||
}
|
||||
|
||||
fun getArray(name: String, defaultValue: JSArray): JSArray {
|
||||
return getArrayInternal(name, defaultValue)!!
|
||||
}
|
||||
|
||||
private fun getArrayInternal(name: String, defaultValue: JSArray?): JSArray? {
|
||||
val value = data.opt(name) ?: return defaultValue
|
||||
return if (value is JSArray) value else defaultValue
|
||||
}
|
||||
|
||||
fun hasOption(name: String): Boolean {
|
||||
return data.has(name)
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri.plugin
|
||||
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONException
|
||||
|
||||
class JSArray : JSONArray {
|
||||
constructor() : super() {}
|
||||
constructor(json: String?) : super(json) {}
|
||||
constructor(copyFrom: Collection<*>?) : super(copyFrom) {}
|
||||
constructor(array: Any?) : super(array) {}
|
||||
|
||||
@Suppress("UNCHECKED_CAST", "ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
|
||||
@Throws(JSONException::class)
|
||||
fun <E> toList(): List<E> {
|
||||
val items: MutableList<E> = ArrayList()
|
||||
var o: Any? = null
|
||||
for (i in 0 until this.length()) {
|
||||
this.get(i).also { o = it }
|
||||
try {
|
||||
items.add(this.get(i) as E)
|
||||
} catch (ex: Exception) {
|
||||
throw JSONException("Not all items are instances of the given type")
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Create a new JSArray without throwing a error
|
||||
*/
|
||||
fun from(array: Any?): JSArray? {
|
||||
try {
|
||||
return JSArray(array)
|
||||
} catch (ex: JSONException) {
|
||||
//
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri.plugin
|
||||
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
|
||||
class JSObject : JSONObject {
|
||||
constructor() : super()
|
||||
constructor(json: String) : super(json)
|
||||
constructor(obj: JSONObject, names: Array<String>) : super(obj, names)
|
||||
|
||||
override fun getString(key: String): String {
|
||||
return getString(key, "")
|
||||
}
|
||||
|
||||
fun getString(key: String, defaultValue: String): String {
|
||||
try {
|
||||
val value = super.getString(key)
|
||||
if (!super.isNull(key)) {
|
||||
return value
|
||||
}
|
||||
} catch (_: JSONException) {
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
fun getInteger(key: String): Int? {
|
||||
return getInteger(key, null)
|
||||
}
|
||||
|
||||
fun getInteger(key: String, defaultValue: Int?): Int? {
|
||||
try {
|
||||
return super.getInt(key)
|
||||
} catch (_: JSONException) {
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
fun getBoolean(key: String, defaultValue: Boolean?): Boolean? {
|
||||
try {
|
||||
return super.getBoolean(key)
|
||||
} catch (_: JSONException) {
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch boolean from jsonObject
|
||||
*/
|
||||
fun getBool(key: String): Boolean? {
|
||||
return getBoolean(key, null)
|
||||
}
|
||||
|
||||
fun getJSObject(name: String): JSObject? {
|
||||
try {
|
||||
return getJSObject(name, null)
|
||||
} catch (e: JSONException) {
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@Throws(JSONException::class)
|
||||
fun getJSObject(name: String, defaultValue: JSObject?): JSObject? {
|
||||
try {
|
||||
val obj = get(name)
|
||||
if (obj is JSONObject) {
|
||||
val keysIter = obj.keys()
|
||||
val keys: MutableList<String> = ArrayList()
|
||||
while (keysIter.hasNext()) {
|
||||
keys.add(keysIter.next())
|
||||
}
|
||||
return JSObject(obj, keys.toTypedArray())
|
||||
}
|
||||
} catch (_: JSONException) {
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
override fun put(key: String, value: Boolean): JSObject {
|
||||
try {
|
||||
super.put(key, value)
|
||||
} catch (_: JSONException) {
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
override fun put(key: String, value: Int): JSObject {
|
||||
try {
|
||||
super.put(key, value)
|
||||
} catch (_: JSONException) {
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
override fun put(key: String, value: Long): JSObject {
|
||||
try {
|
||||
super.put(key, value)
|
||||
} catch (_: JSONException) {
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
override fun put(key: String, value: Double): JSObject {
|
||||
try {
|
||||
super.put(key, value)
|
||||
} catch (_: JSONException) {
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
override fun put(key: String, value: Any?): JSObject {
|
||||
try {
|
||||
super.put(key, value)
|
||||
} catch (_: JSONException) {
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun put(key: String, value: String?): JSObject {
|
||||
try {
|
||||
super.put(key, value)
|
||||
} catch (_: JSONException) {
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
@Throws(JSONException::class)
|
||||
fun putSafe(key: String, value: Any?): JSObject {
|
||||
return super.put(key, value) as JSObject
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Convert a pathetic JSONObject into a JSObject
|
||||
* @param obj
|
||||
*/
|
||||
@Throws(JSONException::class)
|
||||
fun fromJSONObject(obj: JSONObject): JSObject {
|
||||
val keysIter = obj.keys()
|
||||
val keys: MutableList<String> = ArrayList()
|
||||
while (keysIter.hasNext()) {
|
||||
keys.add(keysIter.next())
|
||||
}
|
||||
return JSObject(obj, keys.toTypedArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
+357
@@ -0,0 +1,357 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri.plugin
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.webkit.WebView
|
||||
import androidx.core.app.ActivityCompat
|
||||
import app.tauri.FsUtils
|
||||
import app.tauri.Logger
|
||||
import app.tauri.PermissionHelper
|
||||
import app.tauri.PermissionState
|
||||
import app.tauri.annotation.ActivityCallback
|
||||
import app.tauri.annotation.PermissionCallback
|
||||
import app.tauri.annotation.Command
|
||||
import app.tauri.annotation.TauriPlugin
|
||||
import org.json.JSONException
|
||||
import java.util.*
|
||||
|
||||
abstract class Plugin(private val activity: Activity) {
|
||||
var handle: PluginHandle? = null
|
||||
|
||||
open fun load(webView: WebView) {}
|
||||
|
||||
/**
|
||||
* Start activity for result with the provided Intent and resolve calling the provided callback method name.
|
||||
*
|
||||
* If there is no registered activity callback for the method name passed in, the call will
|
||||
* be rejected. Make sure a valid activity result callback method is registered using the
|
||||
* [ActivityCallback] annotation.
|
||||
*
|
||||
* @param invoke the invoke object
|
||||
* @param intent the intent used to start an activity
|
||||
* @param callbackName the name of the callback to run when the launched activity is finished
|
||||
*/
|
||||
fun startActivityForResult(invoke: Invoke, intent: Intent, callbackName: String) {
|
||||
handle!!.startActivityForResult(invoke, intent, callbackName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin log tags.
|
||||
* @param subTags
|
||||
*/
|
||||
protected fun getLogTag(vararg subTags: String): String {
|
||||
return Logger.tags(*subTags)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a log tag with the plugin's class name as subTag.
|
||||
*/
|
||||
protected fun getLogTag(): String {
|
||||
return Logger.tags(this.javaClass.simpleName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an URI to an URL that can be loaded by the webview.
|
||||
*/
|
||||
fun assetUrl(u: Uri): String {
|
||||
var path = FsUtils.getFileUrlForUri(activity, u)
|
||||
if (path?.startsWith("file://") == true) {
|
||||
path = path.replace("file://", "")
|
||||
}
|
||||
return "asset://localhost$path"
|
||||
}
|
||||
|
||||
/**
|
||||
* Exported plugin method for checking the granted status for each permission
|
||||
* declared on the plugin. This plugin call responds with a mapping of permissions to
|
||||
* the associated granted status.
|
||||
*/
|
||||
@Command
|
||||
@PermissionCallback
|
||||
fun checkPermissions(invoke: Invoke) {
|
||||
val permissionsResult: Map<String, PermissionState?> = getPermissionStates()
|
||||
if (permissionsResult.isEmpty()) {
|
||||
// if no permissions are defined on the plugin, resolve undefined
|
||||
invoke.resolve()
|
||||
} else {
|
||||
val permissionsResultJSON = JSObject()
|
||||
for ((key, value) in permissionsResult) {
|
||||
permissionsResultJSON.put(key, value)
|
||||
}
|
||||
invoke.resolve(permissionsResultJSON)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exported plugin method to request all permissions for this plugin.
|
||||
* To manually request permissions within a plugin use:
|
||||
* [.requestAllPermissions], or
|
||||
* [.requestPermissionForAlias], or
|
||||
* [.requestPermissionForAliases]
|
||||
*
|
||||
* @param invoke
|
||||
*/
|
||||
@Command
|
||||
open fun requestPermissions(invoke: Invoke) {
|
||||
val annotation = handle?.annotation
|
||||
if (annotation != null) {
|
||||
// handle permission requests for plugins defined with @TauriPlugin
|
||||
var permAliases: Array<String>? = null
|
||||
val autoGrantPerms: MutableSet<String> = HashSet()
|
||||
|
||||
// If call was made with a list of specific permission aliases to request, save them
|
||||
// to be requested
|
||||
val providedPerms: JSArray = invoke.getArray("permissions", JSArray())
|
||||
var providedPermsList: List<String?>? = null
|
||||
try {
|
||||
providedPermsList = providedPerms.toList()
|
||||
} catch (ignore: JSONException) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
// If call was made without any custom permissions, request all from plugin annotation
|
||||
val aliasSet: MutableSet<String> = HashSet()
|
||||
if (providedPermsList == null || providedPermsList.isEmpty()) {
|
||||
for (perm in annotation.permissions) {
|
||||
// If a permission is defined with no permission strings, separate it for auto-granting.
|
||||
// Otherwise, the alias is added to the list to be requested.
|
||||
if (perm.strings.isEmpty() || perm.strings.size == 1 && perm.strings[0]
|
||||
.isEmpty()
|
||||
) {
|
||||
if (!perm.alias.isEmpty()) {
|
||||
autoGrantPerms.add(perm.alias)
|
||||
}
|
||||
} else {
|
||||
aliasSet.add(perm.alias)
|
||||
}
|
||||
}
|
||||
permAliases = aliasSet.toTypedArray()
|
||||
} else {
|
||||
for (perm in annotation.permissions) {
|
||||
if (providedPermsList.contains(perm.alias)) {
|
||||
aliasSet.add(perm.alias)
|
||||
}
|
||||
}
|
||||
if (aliasSet.isEmpty()) {
|
||||
invoke.reject("No valid permission alias was requested of this plugin.")
|
||||
} else {
|
||||
permAliases = aliasSet.toTypedArray()
|
||||
}
|
||||
}
|
||||
if (permAliases != null && permAliases.isNotEmpty()) {
|
||||
// request permissions using provided aliases or all defined on the plugin
|
||||
requestPermissionForAliases(permAliases, invoke, "checkPermissions")
|
||||
} else if (autoGrantPerms.isNotEmpty()) {
|
||||
// if the plugin only has auto-grant permissions, return all as GRANTED
|
||||
val permissionsResults = JSObject()
|
||||
for (perm in autoGrantPerms) {
|
||||
permissionsResults.put(perm, PermissionState.GRANTED.toString())
|
||||
}
|
||||
invoke.resolve(permissionsResults)
|
||||
} else {
|
||||
// no permissions are defined on the plugin, resolve undefined
|
||||
invoke.resolve()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given permission alias is correctly declared in AndroidManifest.xml
|
||||
* @param alias a permission alias defined on the plugin
|
||||
* @return true only if all permissions associated with the given alias are declared in the manifest
|
||||
*/
|
||||
fun isPermissionDeclared(alias: String): Boolean {
|
||||
val annotation = handle?.annotation
|
||||
if (annotation != null) {
|
||||
for (perm in annotation.permissions) {
|
||||
if (alias.equals(perm.alias, ignoreCase = true)) {
|
||||
var result = true
|
||||
for (permString in perm.strings) {
|
||||
result = result && PermissionHelper.hasDefinedPermission(activity, permString)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
Logger.error(
|
||||
String.format(
|
||||
"isPermissionDeclared: No alias defined for %s " + "or missing @TauriPlugin annotation.",
|
||||
alias
|
||||
)
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun permissionActivityResult(
|
||||
invoke: Invoke,
|
||||
permissionStrings: Array<String>,
|
||||
callbackName: String
|
||||
) {
|
||||
handle!!.requestPermissions(invoke, permissionStrings, callbackName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Request all of the specified permissions in the TauriPlugin annotation (if any)
|
||||
*
|
||||
* If there is no registered permission callback for the Invoke passed in, the call will
|
||||
* be rejected. Make sure a valid permission callback method is registered using the
|
||||
* [PermissionCallback] annotation.
|
||||
*
|
||||
* @param invoke
|
||||
* @param callbackName the name of the callback to run when the permission request is complete
|
||||
*/
|
||||
protected fun requestAllPermissions(
|
||||
invoke: Invoke,
|
||||
callbackName: String
|
||||
) {
|
||||
val annotation = handle!!.annotation
|
||||
if (annotation != null) {
|
||||
val perms: HashSet<String> = HashSet()
|
||||
for (perm in annotation.permissions) {
|
||||
perms.addAll(perm.strings)
|
||||
}
|
||||
permissionActivityResult(invoke, perms.toArray(arrayOfNulls<String>(0)), callbackName)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request permissions using an alias defined on the plugin.
|
||||
*
|
||||
* If there is no registered permission callback for the Invoke passed in, the call will
|
||||
* be rejected. Make sure a valid permission callback method is registered using the
|
||||
* [PermissionCallback] annotation.
|
||||
*
|
||||
* @param alias an alias defined on the plugin
|
||||
* @param invoke the invoke involved in originating the request
|
||||
* @param callbackName the name of the callback to run when the permission request is complete
|
||||
*/
|
||||
protected fun requestPermissionForAlias(
|
||||
alias: String,
|
||||
call: Invoke,
|
||||
callbackName: String
|
||||
) {
|
||||
requestPermissionForAliases(arrayOf(alias), call, callbackName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Request permissions using aliases defined on the plugin.
|
||||
*
|
||||
* If there is no registered permission callback for the Invoke passed in, the call will
|
||||
* be rejected. Make sure a valid permission callback method is registered using the
|
||||
* [PermissionCallback] annotation.
|
||||
*
|
||||
* @param aliases a set of aliases defined on the plugin
|
||||
* @param invoke the invoke involved in originating the request
|
||||
* @param callbackName the name of the callback to run when the permission request is complete
|
||||
*/
|
||||
fun requestPermissionForAliases(
|
||||
aliases: Array<String>,
|
||||
invoke: Invoke,
|
||||
callbackName: String
|
||||
) {
|
||||
if (aliases.isEmpty()) {
|
||||
Logger.error("No permission alias was provided")
|
||||
return
|
||||
}
|
||||
val permissions = getPermissionStringsForAliases(aliases)
|
||||
if (permissions.isNotEmpty()) {
|
||||
permissionActivityResult(invoke, permissions, callbackName)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Android permission strings defined on the [TauriPlugin] annotation with
|
||||
* the provided aliases.
|
||||
*
|
||||
* @param aliases aliases for permissions defined on the plugin
|
||||
* @return Android permission strings associated with the provided aliases, if exists
|
||||
*/
|
||||
private fun getPermissionStringsForAliases(aliases: Array<String>): Array<String> {
|
||||
val annotation = handle?.annotation
|
||||
val perms: HashSet<String> = HashSet()
|
||||
if (annotation != null) {
|
||||
for (perm in annotation.permissions) {
|
||||
if (aliases.contains(perm.alias)) {
|
||||
perms.addAll(perm.strings)
|
||||
}
|
||||
}
|
||||
}
|
||||
return perms.toArray(arrayOfNulls(0))
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the permission state for the provided permission alias.
|
||||
*
|
||||
* @param alias the permission alias to get
|
||||
* @return the state of the provided permission alias or null
|
||||
*/
|
||||
fun getPermissionState(alias: String): PermissionState? {
|
||||
return getPermissionStates()[alias]
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to check all permissions defined on a plugin and see the state of each.
|
||||
*
|
||||
* @return A mapping of permission aliases to the associated granted status.
|
||||
*/
|
||||
open fun getPermissionStates(): Map<String, PermissionState> {
|
||||
val permissionsResults: MutableMap<String, PermissionState> = HashMap()
|
||||
val annotation = handle?.annotation
|
||||
if (annotation != null) {
|
||||
for (perm in annotation.permissions) {
|
||||
// If a permission is defined with no permission constants, return GRANTED for it.
|
||||
// Otherwise, get its true state.
|
||||
if (perm.strings.isEmpty() || perm.strings.size == 1 && perm.strings[0]
|
||||
.isEmpty()
|
||||
) {
|
||||
val key = perm.alias
|
||||
if (key.isNotEmpty()) {
|
||||
val existingResult = permissionsResults[key]
|
||||
|
||||
// auto set permission state to GRANTED if the alias is empty.
|
||||
if (existingResult == null) {
|
||||
permissionsResults[key] = PermissionState.GRANTED
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (permString in perm.strings) {
|
||||
val key = perm.alias.ifEmpty { permString }
|
||||
var permissionStatus: PermissionState
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
activity,
|
||||
permString
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
permissionStatus = PermissionState.GRANTED
|
||||
} else {
|
||||
permissionStatus = PermissionState.PROMPT
|
||||
|
||||
// Check if there is a cached permission state for the "Never ask again" state
|
||||
val prefs =
|
||||
activity.getSharedPreferences("PluginPermStates", Activity.MODE_PRIVATE)
|
||||
val state = prefs.getString(permString, null)
|
||||
if (state != null) {
|
||||
permissionStatus = PermissionState.byState(state)
|
||||
}
|
||||
}
|
||||
val existingResult = permissionsResults[key]
|
||||
|
||||
// multiple permissions with the same alias must all be true, otherwise all false.
|
||||
if (existingResult == null || existingResult === PermissionState.GRANTED) {
|
||||
permissionsResults[key] = permissionStatus
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return permissionsResults
|
||||
}
|
||||
|
||||
}
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri.plugin
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.webkit.WebView
|
||||
import androidx.core.app.ActivityCompat
|
||||
import app.tauri.PermissionHelper
|
||||
import app.tauri.PermissionState
|
||||
import app.tauri.annotation.ActivityCallback
|
||||
import app.tauri.annotation.PermissionCallback
|
||||
import app.tauri.annotation.Command
|
||||
import app.tauri.annotation.TauriPlugin
|
||||
import java.lang.reflect.Method
|
||||
|
||||
class PluginHandle(private val manager: PluginManager, val name: String, private val instance: Plugin) {
|
||||
private val commands: HashMap<String, CommandData> = HashMap()
|
||||
private val permissionCallbackMethods: HashMap<String, Method> = HashMap()
|
||||
private val startActivityCallbackMethods: HashMap<String, Method> = HashMap()
|
||||
var annotation: TauriPlugin?
|
||||
var loaded = false
|
||||
|
||||
init {
|
||||
indexMethods()
|
||||
instance.handle = this
|
||||
annotation = instance.javaClass.getAnnotation(TauriPlugin::class.java)
|
||||
}
|
||||
|
||||
fun load(webView: WebView) {
|
||||
instance.load(webView)
|
||||
loaded = true
|
||||
}
|
||||
|
||||
fun startActivityForResult(invoke: Invoke, intent: Intent, callbackName: String) {
|
||||
manager.startActivityForResult(intent) { result ->
|
||||
val method = startActivityCallbackMethods[callbackName]
|
||||
if (method != null) {
|
||||
method.isAccessible = true
|
||||
method(instance, invoke, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun requestPermissions(
|
||||
invoke: Invoke,
|
||||
permissions: Array<String>,
|
||||
callbackName: String
|
||||
) {
|
||||
manager.requestPermissions(permissions) { result ->
|
||||
if (validatePermissions(invoke, result)) {
|
||||
val method = permissionCallbackMethods[callbackName]
|
||||
if (method != null) {
|
||||
method.isAccessible = true
|
||||
method(instance, invoke)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves permission states and rejects if permissions were not correctly defined in
|
||||
* the AndroidManifest.xml file.
|
||||
*
|
||||
* @param permissions
|
||||
* @return true if permissions were saved and defined correctly, false if not
|
||||
*/
|
||||
private fun validatePermissions(
|
||||
invoke: Invoke,
|
||||
permissions: Map<String, Boolean>
|
||||
): Boolean {
|
||||
val activity = manager.activity
|
||||
val prefs =
|
||||
activity.getSharedPreferences("PluginPermStates", Activity.MODE_PRIVATE)
|
||||
for ((permString, isGranted) in permissions) {
|
||||
if (isGranted) {
|
||||
// Permission granted. If previously denied, remove cached state
|
||||
val state = prefs.getString(permString, null)
|
||||
if (state != null) {
|
||||
val editor: SharedPreferences.Editor = prefs.edit()
|
||||
editor.remove(permString)
|
||||
editor.apply()
|
||||
}
|
||||
} else {
|
||||
val editor: SharedPreferences.Editor = prefs.edit()
|
||||
if (ActivityCompat.shouldShowRequestPermissionRationale(
|
||||
activity,
|
||||
permString
|
||||
)
|
||||
) {
|
||||
// Permission denied, can prompt again with rationale
|
||||
editor.putString(permString, PermissionState.PROMPT_WITH_RATIONALE.toString())
|
||||
} else {
|
||||
// Permission denied permanently, store this state for future reference
|
||||
editor.putString(permString, PermissionState.DENIED.toString())
|
||||
}
|
||||
editor.apply()
|
||||
}
|
||||
}
|
||||
val permStrings = permissions.keys.toTypedArray()
|
||||
if (!PermissionHelper.hasDefinedPermissions(activity, permStrings)) {
|
||||
val builder = StringBuilder()
|
||||
builder.append("Missing the following permissions in AndroidManifest.xml:\n")
|
||||
val missing = PermissionHelper.getUndefinedPermissions(activity, permStrings)
|
||||
for (perm in missing) {
|
||||
builder.append(
|
||||
"""
|
||||
$perm
|
||||
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
invoke.reject(builder.toString())
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@Throws(
|
||||
InvalidCommandException::class,
|
||||
IllegalAccessException::class
|
||||
)
|
||||
fun invoke(invoke: Invoke) {
|
||||
val methodMeta = commands[invoke.command]
|
||||
?: throw InvalidCommandException("No command " + invoke.command + " found for plugin " + instance.javaClass.name)
|
||||
methodMeta.method.invoke(instance, invoke)
|
||||
}
|
||||
|
||||
private fun indexMethods() {
|
||||
val methods: Array<Method> = instance.javaClass.methods
|
||||
for (method in methods) {
|
||||
if (method.isAnnotationPresent(Command::class.java)) {
|
||||
val command = method.getAnnotation(Command::class.java) ?: continue
|
||||
val methodMeta = CommandData(method, command)
|
||||
commands[method.name] = methodMeta
|
||||
} else if (method.isAnnotationPresent(ActivityCallback::class.java)) {
|
||||
startActivityCallbackMethods[method.name] = method
|
||||
} else if (method.isAnnotationPresent(PermissionCallback::class.java)) {
|
||||
permissionCallbackMethods[method.name] = method
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri.plugin
|
||||
|
||||
import android.content.Intent
|
||||
import android.webkit.WebView
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import app.tauri.JniMethod
|
||||
import app.tauri.Logger
|
||||
|
||||
class PluginManager(val activity: AppCompatActivity) {
|
||||
fun interface RequestPermissionsCallback {
|
||||
fun onResult(permissions: Map<String, Boolean>)
|
||||
}
|
||||
|
||||
fun interface ActivityResultCallback {
|
||||
fun onResult(result: ActivityResult)
|
||||
}
|
||||
|
||||
private val plugins: HashMap<String, PluginHandle> = HashMap()
|
||||
private val startActivityForResultLauncher: ActivityResultLauncher<Intent>
|
||||
private val requestPermissionsLauncher: ActivityResultLauncher<Array<String>>
|
||||
private var requestPermissionsCallback: RequestPermissionsCallback? = null
|
||||
private var startActivityForResultCallback: ActivityResultCallback? = null
|
||||
|
||||
init {
|
||||
startActivityForResultLauncher =
|
||||
activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()
|
||||
) { result ->
|
||||
if (startActivityForResultCallback != null) {
|
||||
startActivityForResultCallback!!.onResult(result)
|
||||
}
|
||||
}
|
||||
|
||||
requestPermissionsLauncher =
|
||||
activity.registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()
|
||||
) { result ->
|
||||
if (requestPermissionsCallback != null) {
|
||||
requestPermissionsCallback!!.onResult(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun startActivityForResult(intent: Intent, callback: ActivityResultCallback) {
|
||||
startActivityForResultCallback = callback
|
||||
startActivityForResultLauncher.launch(intent)
|
||||
}
|
||||
|
||||
fun requestPermissions(
|
||||
permissionStrings: Array<String>,
|
||||
callback: RequestPermissionsCallback
|
||||
) {
|
||||
requestPermissionsCallback = callback
|
||||
requestPermissionsLauncher.launch(permissionStrings)
|
||||
}
|
||||
|
||||
@JniMethod
|
||||
fun onWebViewCreated(webView: WebView) {
|
||||
for ((_, plugin) in plugins) {
|
||||
if (!plugin.loaded) {
|
||||
plugin.load(webView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JniMethod
|
||||
fun load(webView: WebView?, name: String, plugin: Plugin) {
|
||||
val handle = PluginHandle(this, name, plugin)
|
||||
plugins[name] = handle
|
||||
if (webView != null) {
|
||||
plugin.load(webView)
|
||||
}
|
||||
}
|
||||
|
||||
@JniMethod
|
||||
fun postIpcMessage(webView: WebView, pluginId: String, command: String, data: JSObject, callback: Long, error: Long) {
|
||||
val invoke = Invoke(callback, command, { successResult, errorResult ->
|
||||
val (fn, result) = if (errorResult == null) Pair(callback, successResult) else Pair(
|
||||
error,
|
||||
errorResult
|
||||
)
|
||||
webView.evaluateJavascript("window['_$fn']($result)", null)
|
||||
}, data)
|
||||
|
||||
dispatchPluginMessage(invoke, pluginId)
|
||||
}
|
||||
|
||||
@JniMethod
|
||||
fun runCommand(id: Int, pluginId: String, command: String, data: JSObject) {
|
||||
val invoke = Invoke(id.toLong(), command, { successResult, errorResult ->
|
||||
handlePluginResponse(id, successResult?.toString(), errorResult?.toString())
|
||||
}, data)
|
||||
|
||||
dispatchPluginMessage(invoke, pluginId)
|
||||
}
|
||||
|
||||
private fun dispatchPluginMessage(invoke: Invoke, pluginId: String) {
|
||||
Logger.verbose(
|
||||
Logger.tags("Plugin"),
|
||||
"Tauri plugin: pluginId: $pluginId, command: ${invoke.command}"
|
||||
)
|
||||
|
||||
try {
|
||||
val plugin = plugins[pluginId]
|
||||
if (plugin == null) {
|
||||
invoke.reject("Plugin $pluginId not initialized")
|
||||
} else {
|
||||
plugins[pluginId]?.invoke(invoke)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
invoke.reject(e.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private external fun handlePluginResponse(id: Int, success: String?, error: String?)
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri.plugin
|
||||
|
||||
import app.tauri.annotation.Command
|
||||
import java.lang.reflect.Method
|
||||
|
||||
class CommandData(
|
||||
val method: Method, methodDecorator: Command
|
||||
) {
|
||||
|
||||
// The name of the method
|
||||
val name: String = method.name
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri.plugin
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import app.tauri.Logger
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
class PluginResult @JvmOverloads constructor(json: JSObject? = JSObject()) {
|
||||
private val json: JSObject
|
||||
|
||||
init {
|
||||
this.json = json ?: JSObject()
|
||||
}
|
||||
|
||||
fun put(name: String, value: Boolean): PluginResult {
|
||||
return jsonPut(name, value)
|
||||
}
|
||||
|
||||
fun put(name: String, value: Double): PluginResult {
|
||||
return jsonPut(name, value)
|
||||
}
|
||||
|
||||
fun put(name: String, value: Int): PluginResult {
|
||||
return jsonPut(name, value)
|
||||
}
|
||||
|
||||
fun put(name: String, value: Long): PluginResult {
|
||||
return jsonPut(name, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a date as an ISO string
|
||||
*/
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
fun put(name: String, value: Date): PluginResult {
|
||||
val tz: TimeZone = TimeZone.getTimeZone("UTC")
|
||||
val df: DateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'")
|
||||
df.timeZone = tz
|
||||
return jsonPut(name, df.format(value))
|
||||
}
|
||||
|
||||
fun put(name: String, value: Any?): PluginResult {
|
||||
return jsonPut(name, value)
|
||||
}
|
||||
|
||||
fun put(name: String, value: PluginResult): PluginResult {
|
||||
return jsonPut(name, value.json)
|
||||
}
|
||||
|
||||
private fun jsonPut(name: String, value: Any?): PluginResult {
|
||||
try {
|
||||
json.put(name, value)
|
||||
} catch (ex: Exception) {
|
||||
Logger.error(Logger.tags("Plugin"), "", ex)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return json.toString()
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app.tauri
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
|
||||
-keep class net.nymtech.nym_connect.* {
|
||||
native <methods>;
|
||||
}
|
||||
|
||||
-keepclassmembers class net.nymtech.nym_connect.TauriActivity {
|
||||
getAppClass(...);
|
||||
getVersion();
|
||||
}
|
||||
|
||||
-keep class net.nymtech.nym_connect.RustWebView {
|
||||
public <init>(...);
|
||||
loadUrlMainThread(...);
|
||||
}
|
||||
|
||||
-keep class net.nymtech.nym_connect.Ipc {
|
||||
public <init>(...);
|
||||
@android.webkit.JavascriptInterface public <methods>;
|
||||
}
|
||||
|
||||
-keep class net.nymtech.nym_connect.RustWebChromeClient,net.nymtech.nym_connect.RustWebViewClient {
|
||||
public <init>(...);
|
||||
}
|
||||
|
||||
-keep class net.nymtech.nym_connect.MainActivity {
|
||||
public getPluginManager();
|
||||
}
|
||||
|
||||
-keep class androidx.appcompat.app.AppCompatActivity { }
|
||||
@@ -0,0 +1,4 @@
|
||||
// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
val implementation by configurations
|
||||
dependencies {
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
@@ -4472,100 +4472,100 @@
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-1.2.0.tgz#1f196b3e012971227f41b98214c846430a4eb477"
|
||||
integrity sha512-lsI54KI6HGf7VImuf/T9pnoejfgkNoXveP14pVV7XarrQ46rOejIVJLFqHI9sRReJMGdh2YuCoI3cc/yCWCsrw==
|
||||
|
||||
"@tauri-apps/api@^2.0.0-alpha.0":
|
||||
version "2.0.0-alpha.0"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-2.0.0-alpha.0.tgz#901abbaf3b9515ba0437716ac0383de549e4e3bc"
|
||||
integrity sha512-PQdy1Ao6JwKwW2/C11nP+IqnrWHB7+UgbM71zbzA1W3+1yyd9Zg+K7rzZ7f3yhvD7kdxmXUN3KgSfGeiDFzZ2A==
|
||||
"@tauri-apps/api@^2.0.0-alpha.3":
|
||||
version "2.0.0-alpha.3"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-2.0.0-alpha.3.tgz#7f467c188305944d67b5b6e82be0041b8b4dbbc9"
|
||||
integrity sha512-F6seMDlcaxeCPy4gS0zJdp6Tet+0rd1qJi/fbKrOrhLM6Y5UtkiG1aSDnMPi+1udThSfadjhUwrLHINvfMCjzQ==
|
||||
|
||||
"@tauri-apps/cli-darwin-arm64@1.2.3":
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-1.2.3.tgz#dae9142e683c00199f4d7e088f22b564b08b9cac"
|
||||
integrity sha512-phJN3fN8FtZZwqXg08bcxfq1+X1JSDglLvRxOxB7VWPq+O5SuB8uLyssjJsu+PIhyZZnIhTGdjhzLSFhSXfLsw==
|
||||
|
||||
"@tauri-apps/cli-darwin-arm64@2.0.0-alpha.2":
|
||||
version "2.0.0-alpha.2"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.0.0-alpha.2.tgz#0af2c077b80778ac4d138a0d42a5f406fcc205ee"
|
||||
integrity sha512-Wu5QdZUgh0DEE0b3EKdJRkZzFoVngezxgvncQlMdXNaiKjdT767K2fB0XvQps+ycbtVLbUlG15jAwPZbWqRYGw==
|
||||
"@tauri-apps/cli-darwin-arm64@2.0.0-alpha.8":
|
||||
version "2.0.0-alpha.8"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.0.0-alpha.8.tgz#399bec52290ea85f74470bc329376142e17e9878"
|
||||
integrity sha512-ZF9nkkYCDiAEKZFwjEbuqTcFVp+DBgem3edKjsZDYPQpWg0VcZOSYr0o3/RPC81T1/FAy1lq478mkcMe0efvEw==
|
||||
|
||||
"@tauri-apps/cli-darwin-x64@1.2.3":
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-1.2.3.tgz#c6f84a11a1a7800e3e8e22c8fa5b95d0b3d1f802"
|
||||
integrity sha512-jFZ/y6z8z6v4yliIbXKBXA7BJgtZVMsITmEXSuD6s5+eCOpDhQxbRkr6CA+FFfr+/r96rWSDSgDenDQuSvPAKw==
|
||||
|
||||
"@tauri-apps/cli-darwin-x64@2.0.0-alpha.2":
|
||||
version "2.0.0-alpha.2"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.0.0-alpha.2.tgz#e82f01e53dabb23c700f82e904e36ac0ee635f38"
|
||||
integrity sha512-e5VLsT/exSW1swUWkhCEAQ/fM8mZaUMoGeyESYtO7VfTNVglS0j+VfQ9a8taRxtOkajDZmqMDvmii4tA5I1Bbw==
|
||||
"@tauri-apps/cli-darwin-x64@2.0.0-alpha.8":
|
||||
version "2.0.0-alpha.8"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.0.0-alpha.8.tgz#4935823acf880423f6e2c70097ab6584079321b3"
|
||||
integrity sha512-N5V+tbP3qeAoXrrTZXvaLIeEWKCq11tqXoNFTkIZNGNC5yQdNpZSX0LqFqzmxVR1SHiOymebvcUlx+ADIpuXGw==
|
||||
|
||||
"@tauri-apps/cli-linux-arm-gnueabihf@1.2.3":
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-1.2.3.tgz#ecccec4c255ab32903fb36e1c746ed7b4eff0d1d"
|
||||
integrity sha512-C7h5vqAwXzY0kRGSU00Fj8PudiDWFCiQqqUNI1N+fhCILrzWZB9TPBwdx33ZfXKt/U4+emdIoo/N34v3TiAOmQ==
|
||||
|
||||
"@tauri-apps/cli-linux-arm-gnueabihf@2.0.0-alpha.2":
|
||||
version "2.0.0-alpha.2"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.0.0-alpha.2.tgz#866000663c66abd8ae88682c0f6cf03734ca7c6e"
|
||||
integrity sha512-+/emaFpDPuqnTIyh+WcDqCbzc/SoREFfLDyumqdnFjRU1Uvc2Z9Eo/sMVnfuUw5vDMc2EPzYtT3uiZGez65ZTA==
|
||||
"@tauri-apps/cli-linux-arm-gnueabihf@2.0.0-alpha.8":
|
||||
version "2.0.0-alpha.8"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.0.0-alpha.8.tgz#5b46f8f38be9b0c6fe8c343e7e589c958a225775"
|
||||
integrity sha512-7LHfZA99ncMDUO/wCGMtrqmDHt1uEKZiNmuzPljWLUwVvbAn0pNWNygnvPNVLEOyav5NnZSGPqT1Zmn+L6Fpyg==
|
||||
|
||||
"@tauri-apps/cli-linux-arm64-gnu@1.2.3":
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-1.2.3.tgz#c3915de83a8fbe6f406eaa0b524a17c091a9a2cd"
|
||||
integrity sha512-buf1c8sdkuUzVDkGPQpyUdAIIdn5r0UgXU6+H5fGPq/Xzt5K69JzXaeo6fHsZEZghbV0hOK+taKV4J0m30UUMQ==
|
||||
|
||||
"@tauri-apps/cli-linux-arm64-gnu@2.0.0-alpha.2":
|
||||
version "2.0.0-alpha.2"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.0.0-alpha.2.tgz#157a893fa2d36e56ec16de213ed2d0a9b209833c"
|
||||
integrity sha512-UHAyqt8fFbp9MEbUHSiEKjJC98w/Dta3r9auE70K+/uNpt9pnP/lGturDWWAJagRIFwYKPyqSEqL5qFcKadYqw==
|
||||
"@tauri-apps/cli-linux-arm64-gnu@2.0.0-alpha.8":
|
||||
version "2.0.0-alpha.8"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.0.0-alpha.8.tgz#56bfa1ec613bfff0aaecd8d38aaab470ae117d58"
|
||||
integrity sha512-imq2MdhWdREvL2sqbU26mzH9sgSvfNWP0uvCPvPxUhK157xqdtGw+Gqm00hwnhTuT5bOFlsUNfnG2U19k1qMpA==
|
||||
|
||||
"@tauri-apps/cli-linux-arm64-musl@1.2.3":
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.2.3.tgz#40f9f7cf0b4088964661fd412eff7310cb4ac605"
|
||||
integrity sha512-x88wPS9W5xAyk392vc4uNHcKBBvCp0wf4H9JFMF9OBwB7vfd59LbQCFcPSu8f0BI7bPrOsyHqspWHuFL8ojQEA==
|
||||
|
||||
"@tauri-apps/cli-linux-arm64-musl@2.0.0-alpha.2":
|
||||
version "2.0.0-alpha.2"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.0.0-alpha.2.tgz#452fb06b5c563500206ba4a8cd5d979b1a3c71de"
|
||||
integrity sha512-3euwm11RWvmTX+ISR/Y+N0TaWTJCRIj1pDgB+r2ZptRKlVTMNTJZDTXQlyJKcWEpi1azlbdxAzRvhN8NrgDmyA==
|
||||
"@tauri-apps/cli-linux-arm64-musl@2.0.0-alpha.8":
|
||||
version "2.0.0-alpha.8"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.0.0-alpha.8.tgz#6a0e2a3f1a494481ce391d379284f688c59ba2aa"
|
||||
integrity sha512-7hXEyvCosBHIN6ahkbFOI5JoyWZAulc0sYd3hWh9V/MBfU+LlPiapsJi6fdde0zew5nnzwcCtfEKkoR737tAig==
|
||||
|
||||
"@tauri-apps/cli-linux-x64-gnu@1.2.3":
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-1.2.3.tgz#0b3e4c1fda6205dbe872f4b69506669476f60591"
|
||||
integrity sha512-ZMz1jxEVe0B4/7NJnlPHmwmSIuwiD6ViXKs8F+OWWz2Y4jn5TGxWKFg7DLx5OwQTRvEIZxxT7lXHi5CuTNAxKg==
|
||||
|
||||
"@tauri-apps/cli-linux-x64-gnu@2.0.0-alpha.2":
|
||||
version "2.0.0-alpha.2"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.0.0-alpha.2.tgz#155c7ab8c4c2b319bca38580e77bc6f6ffc2d664"
|
||||
integrity sha512-ijJ8Wij5mVd9p6lXQ+pXoFlx3Iv1JS1KQTeySICds43xzE8esGp5+HXRXDwWqQLdVmtI77P5VRIe2ssXiaeDUg==
|
||||
"@tauri-apps/cli-linux-x64-gnu@2.0.0-alpha.8":
|
||||
version "2.0.0-alpha.8"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.0.0-alpha.8.tgz#0ae1f0c19ab8fb9878161db4d40c3cbd581e37f3"
|
||||
integrity sha512-S9T/trKpXcLc7hVIv7xFrRBlaivHD/HLUz0nYAkI2izNGFmbP3QpkqQDdwpWN/fxneodNgI2/mDFC3NULClj+A==
|
||||
|
||||
"@tauri-apps/cli-linux-x64-musl@1.2.3":
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-1.2.3.tgz#edcf8f53da50337a2e763d4fda750ef56124036c"
|
||||
integrity sha512-B/az59EjJhdbZDzawEVox0LQu2ZHCZlk8rJf85AMIktIUoAZPFbwyiUv7/zjzA/sY6Nb58OSJgaPL2/IBy7E0A==
|
||||
|
||||
"@tauri-apps/cli-linux-x64-musl@2.0.0-alpha.2":
|
||||
version "2.0.0-alpha.2"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.0.0-alpha.2.tgz#9965b076965bc8faf5623c9f67724ebeb9a197e7"
|
||||
integrity sha512-sg8OTQfG/zJ4+6MA/+hk08hVb57iJn5VZDzBb3o6IpJ0cwtM8YDNv5C+6HWttBuxsn4oEoYxGml/FvowMfsOCg==
|
||||
"@tauri-apps/cli-linux-x64-musl@2.0.0-alpha.8":
|
||||
version "2.0.0-alpha.8"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.0.0-alpha.8.tgz#dbb668569ee71f041b38b05470697cc0ba761ff3"
|
||||
integrity sha512-90mx6vSFNBbctaPaKhkH+um51gOoRJwLFOadOkklHS5Q6S6GjtSa1lmBEFyKUTAfFPtmiacuNYtoKx7nqm+z1Q==
|
||||
|
||||
"@tauri-apps/cli-win32-ia32-msvc@1.2.3":
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-1.2.3.tgz#0592d3e4eee4685674579ba897eef1469c6f1cfe"
|
||||
integrity sha512-ypdO1OdC5ugNJAKO2m3sb1nsd+0TSvMS9Tr5qN/ZSMvtSduaNwrcZ3D7G/iOIanrqu/Nl8t3LYlgPZGBKlw7Ng==
|
||||
|
||||
"@tauri-apps/cli-win32-ia32-msvc@2.0.0-alpha.2":
|
||||
version "2.0.0-alpha.2"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.0.0-alpha.2.tgz#808447504cdb22a2d998ac79810cf1035c710a03"
|
||||
integrity sha512-R1AmO3GEm97ptM0tjxZjZ1fLnxzN3ZeOEKc85nR7ayqVqKVhMu+dhq5lKa/Y3GdMUR6Yj9GoCnaLp2xy4bV6JQ==
|
||||
"@tauri-apps/cli-win32-ia32-msvc@2.0.0-alpha.8":
|
||||
version "2.0.0-alpha.8"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.0.0-alpha.8.tgz#f87db96860ca883efa7a7d96095583cb69a4dc5f"
|
||||
integrity sha512-VAMsLJYfp6iVI7oJ+uIkfe8DKPRMtWDiSEkfFqvDyFX0WMTQl23B0AzYyapVwZc+WkTkLuoMLpIWMQCgAoQWfQ==
|
||||
|
||||
"@tauri-apps/cli-win32-x64-msvc@1.2.3":
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-1.2.3.tgz#89f0cc36e11e56564161602cd6add155cc7b0dfb"
|
||||
integrity sha512-CsbHQ+XhnV/2csOBBDVfH16cdK00gNyNYUW68isedmqcn8j+s0e9cQ1xXIqi+Hue3awp8g3ImYN5KPepf3UExw==
|
||||
|
||||
"@tauri-apps/cli-win32-x64-msvc@2.0.0-alpha.2":
|
||||
version "2.0.0-alpha.2"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.0.0-alpha.2.tgz#f7890a94cd8a0cc70faa831046ca8d8576a357d9"
|
||||
integrity sha512-cLJJWxCdvvQP+I0B4h6h0TMMNYISoatQu57QVxPqypbkC/lK/ljjrbD5nu7M9wTFBkLkCTGyMC7N99esCmgIBQ==
|
||||
"@tauri-apps/cli-win32-x64-msvc@2.0.0-alpha.8":
|
||||
version "2.0.0-alpha.8"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.0.0-alpha.8.tgz#d438b0231aaac35223d666d207b50c2794fc4ea6"
|
||||
integrity sha512-zvWd13hRfRM0AEJJZ4t4CeB/cyru8hvbB6c+sxYDS9GPRWfHSH5dIeKoHhnMwP5fEOPZLN7VaeEP6tC88trD6g==
|
||||
|
||||
"@tauri-apps/cli@^1.0.5", "@tauri-apps/cli@^1.2.2":
|
||||
version "1.2.3"
|
||||
@@ -4582,20 +4582,20 @@
|
||||
"@tauri-apps/cli-win32-ia32-msvc" "1.2.3"
|
||||
"@tauri-apps/cli-win32-x64-msvc" "1.2.3"
|
||||
|
||||
"@tauri-apps/cli@^2.0.0-alpha.2":
|
||||
version "2.0.0-alpha.2"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli/-/cli-2.0.0-alpha.2.tgz#bef242ce3c8b286f0ab74595c5e069b0ce7c5b4f"
|
||||
integrity sha512-M5o2ESOv9jGr7oIDl3sR3Q5++DXSW4xyfxzKCyu1JVGlOc+C9Q4y0dbKhlpd0wPCAxRa0ikbfu7z8qfEhHSpVQ==
|
||||
"@tauri-apps/cli@^2.0.0-alpha.8":
|
||||
version "2.0.0-alpha.8"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli/-/cli-2.0.0-alpha.8.tgz#84772a16889b38dcd6239fe159bb5a5f2b1e1cc9"
|
||||
integrity sha512-cXt6pxh7oiV8Htz7eTPor7if4aN9f9emn10+5h2Y82YzST7I7wKXsrjuk0HIyzUiqiQjUgl3iT9gh791zgtI3w==
|
||||
optionalDependencies:
|
||||
"@tauri-apps/cli-darwin-arm64" "2.0.0-alpha.2"
|
||||
"@tauri-apps/cli-darwin-x64" "2.0.0-alpha.2"
|
||||
"@tauri-apps/cli-linux-arm-gnueabihf" "2.0.0-alpha.2"
|
||||
"@tauri-apps/cli-linux-arm64-gnu" "2.0.0-alpha.2"
|
||||
"@tauri-apps/cli-linux-arm64-musl" "2.0.0-alpha.2"
|
||||
"@tauri-apps/cli-linux-x64-gnu" "2.0.0-alpha.2"
|
||||
"@tauri-apps/cli-linux-x64-musl" "2.0.0-alpha.2"
|
||||
"@tauri-apps/cli-win32-ia32-msvc" "2.0.0-alpha.2"
|
||||
"@tauri-apps/cli-win32-x64-msvc" "2.0.0-alpha.2"
|
||||
"@tauri-apps/cli-darwin-arm64" "2.0.0-alpha.8"
|
||||
"@tauri-apps/cli-darwin-x64" "2.0.0-alpha.8"
|
||||
"@tauri-apps/cli-linux-arm-gnueabihf" "2.0.0-alpha.8"
|
||||
"@tauri-apps/cli-linux-arm64-gnu" "2.0.0-alpha.8"
|
||||
"@tauri-apps/cli-linux-arm64-musl" "2.0.0-alpha.8"
|
||||
"@tauri-apps/cli-linux-x64-gnu" "2.0.0-alpha.8"
|
||||
"@tauri-apps/cli-linux-x64-musl" "2.0.0-alpha.8"
|
||||
"@tauri-apps/cli-win32-ia32-msvc" "2.0.0-alpha.8"
|
||||
"@tauri-apps/cli-win32-x64-msvc" "2.0.0-alpha.8"
|
||||
|
||||
"@tauri-apps/tauri-forage@^1.0.0-beta.2":
|
||||
version "1.0.0-beta.2"
|
||||
|
||||
Reference in New Issue
Block a user