/
PluginHandle.kt
154 lines (141 loc) · 5.02 KB
/
PluginHandle.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// 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.Command
import app.tauri.annotation.PermissionCallback
import app.tauri.annotation.TauriPlugin
import java.lang.reflect.Method
import java.util.Arrays
class PluginHandle(private val manager: PluginManager, val name: String, val instance: Plugin, val config: JSObject) {
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 = mutableListOf<Method>()
var pluginCursor: Class<*> = instance.javaClass
while (pluginCursor.name != Any::class.java.name) {
methods.addAll(listOf(*pluginCursor.declaredMethods))
pluginCursor = pluginCursor.superclass
}
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
}
}
}
}