Skip to content

Commit 57f9d56

Browse files
committed
feat(android): prepare for multi-window support
1 parent cdc5594 commit 57f9d56

File tree

7 files changed

+103
-74
lines changed

7 files changed

+103
-74
lines changed

Cargo.lock

Lines changed: 14 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,5 @@ schemars_derive = { git = 'https://github.com/tauri-apps/schemars.git', branch =
7272
tauri = { path = "./crates/tauri" }
7373
tauri-plugin = { path = "./crates/tauri-plugin" }
7474
tauri-utils = { path = "./crates/tauri-utils" }
75+
wry = { path = "../wry" }
76+
tao = { path = "../tao" }

crates/tauri-runtime/src/webview.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ pub struct PendingWebview<T: UserEvent, R: Runtime<T>> {
217217
#[cfg(target_os = "android")]
218218
#[allow(clippy::type_complexity)]
219219
pub on_webview_created:
220-
Option<Box<dyn Fn(CreationContext<'_, '_>) -> Result<(), jni::errors::Error> + Send>>,
220+
Option<Box<dyn Fn(CreationContext<'_, '_>) -> Result<(), jni::errors::Error> + Send + Sync>>,
221221

222222
pub web_resource_request_handler: Option<Box<WebResourceRequestHandler>>,
223223

@@ -273,7 +273,7 @@ impl<T: UserEvent, R: Runtime<T>> PendingWebview<T, R> {
273273

274274
#[cfg(target_os = "android")]
275275
pub fn on_webview_created<
276-
F: Fn(CreationContext<'_, '_>) -> Result<(), jni::errors::Error> + Send + 'static,
276+
F: Fn(CreationContext<'_, '_>) -> Result<(), jni::errors::Error> + Send + Sync + 'static,
277277
>(
278278
mut self,
279279
f: F,

crates/tauri/mobile/android-codegen/TauriActivity.kt

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,43 +9,51 @@ package {{package}}
99
import android.content.Intent
1010
import android.content.res.Configuration
1111
import app.tauri.plugin.PluginManager
12+
import androidx.lifecycle.DefaultLifecycleObserver
13+
import androidx.lifecycle.LifecycleOwner
14+
import androidx.lifecycle.ProcessLifecycleOwner
15+
16+
object TauriLifecycleObserver : DefaultLifecycleObserver {
17+
override fun onResume(owner: LifecycleOwner) {
18+
super.onResume(owner)
19+
PluginManager.onResume()
20+
}
21+
22+
override fun onPause(owner: LifecycleOwner) {
23+
super.onPause(owner)
24+
PluginManager.onPause()
25+
}
26+
27+
override fun onStop(owner: LifecycleOwner) {
28+
super.onStop(owner)
29+
PluginManager.onStop()
30+
}
31+
}
1232

1333
abstract class TauriActivity : WryActivity() {
14-
var pluginManager: PluginManager = PluginManager(this)
1534
override val handleBackNavigation: Boolean = false
1635

17-
override fun onNewIntent(intent: Intent) {
18-
super.onNewIntent(intent)
19-
pluginManager.onNewIntent(intent)
20-
}
21-
22-
override fun onResume() {
23-
super.onResume()
24-
pluginManager.onResume()
36+
fun getPluginManager(): PluginManager {
37+
return PluginManager
2538
}
2639

27-
override fun onPause() {
28-
super.onPause()
29-
pluginManager.onPause()
40+
override fun onNewIntent(intent: Intent) {
41+
super.onNewIntent(intent)
42+
PluginManager.onNewIntent(intent)
3043
}
3144

3245
override fun onRestart() {
3346
super.onRestart()
34-
pluginManager.onRestart()
35-
}
36-
37-
override fun onStop() {
38-
super.onStop()
39-
pluginManager.onStop()
47+
PluginManager.onRestart(this)
4048
}
4149

4250
override fun onDestroy() {
4351
super.onDestroy()
44-
pluginManager.onDestroy()
52+
PluginManager.onDestroy(this)
4553
}
4654

4755
override fun onConfigurationChanged(newConfig: Configuration) {
4856
super.onConfigurationChanged(newConfig)
49-
pluginManager.onConfigurationChanged(newConfig)
57+
PluginManager.onConfigurationChanged(newConfig)
5058
}
5159
}

crates/tauri/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import android.content.pm.PackageManager
1111
import android.net.Uri
1212
import android.webkit.WebView
1313
import androidx.activity.result.IntentSenderRequest
14+
import androidx.appcompat.app.AppCompatActivity
1415
import androidx.core.app.ActivityCompat
1516
import app.tauri.FsUtils
1617
import app.tauri.Logger
@@ -71,10 +72,18 @@ abstract class Plugin(private val activity: Activity) {
7172
*/
7273
open fun onResume() {}
7374

75+
76+
/**
77+
* This event is called after onStop() when the current activity is being re-displayed to the user (the user has navigated back to it).
78+
* It will be followed by onStart() and then onResume().
79+
*/
80+
open fun onRestart(activity: AppCompatActivity) {}
81+
7482
/**
7583
* This event is called after onStop() when the current activity is being re-displayed to the user (the user has navigated back to it).
7684
* It will be followed by onStart() and then onResume().
7785
*/
86+
@Deprecated("use onRestart(activity: AppCompatActivity) instead")
7887
open fun onRestart() {}
7988

8089
/**
@@ -86,8 +95,23 @@ abstract class Plugin(private val activity: Activity) {
8695
/**
8796
* This event is called before the activity is destroyed.
8897
*/
98+
open fun onDestroy(activity: AppCompatActivity) {}
99+
/**
100+
* This event is called before an activity is destroyed.
101+
*/
102+
@Deprecated("use onDestroy(activity: AppCompatActivity) instead")
89103
open fun onDestroy() {}
90104

105+
internal fun triggerOnDestroy(activity: AppCompatActivity) {
106+
onDestroy(activity)
107+
onDestroy()
108+
}
109+
110+
internal fun triggerOnRestart(activity: AppCompatActivity) {
111+
onRestart(activity)
112+
onRestart()
113+
}
114+
91115
/**
92116
* This event is called when a configuration change occurs but the app does not recreate the activity.
93117
*/

crates/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
package app.tauri.plugin
66

7-
import android.app.PendingIntent
87
import android.content.res.Configuration
98
import android.content.Context
109
import android.content.Intent
@@ -26,7 +25,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
2625
import com.fasterxml.jackson.databind.module.SimpleModule
2726
import java.lang.reflect.InvocationTargetException
2827

29-
class PluginManager(val activity: AppCompatActivity) {
28+
object PluginManager {
3029
fun interface RequestPermissionsCallback {
3130
fun onResult(permissions: Map<String, Boolean>)
3231
}
@@ -35,16 +34,33 @@ class PluginManager(val activity: AppCompatActivity) {
3534
fun onResult(result: ActivityResult)
3635
}
3736

37+
lateinit var activity: AppCompatActivity
3838
private val plugins: HashMap<String, PluginHandle> = HashMap()
39-
private val startActivityForResultLauncher: ActivityResultLauncher<Intent>
40-
private val startIntentSenderForResultLauncher: ActivityResultLauncher<IntentSenderRequest>
41-
private val requestPermissionsLauncher: ActivityResultLauncher<Array<String>>
39+
private lateinit var startActivityForResultLauncher: ActivityResultLauncher<Intent>
40+
private lateinit var startIntentSenderForResultLauncher: ActivityResultLauncher<IntentSenderRequest>
41+
private lateinit var requestPermissionsLauncher: ActivityResultLauncher<Array<String>>
4242
private var requestPermissionsCallback: RequestPermissionsCallback? = null
4343
private var startActivityForResultCallback: ActivityResultCallback? = null
4444
private var startIntentSenderForResultCallback: ActivityResultCallback? = null
45-
private var jsonMapper: ObjectMapper
45+
private var jsonMapper: ObjectMapper = ObjectMapper()
46+
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
47+
.enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)
48+
.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
4649

4750
init {
51+
val channelDeserializer = ChannelDeserializer({ channelId, payload ->
52+
sendChannelData(channelId, payload)
53+
}, jsonMapper)
54+
jsonMapper
55+
.registerModule(SimpleModule().addDeserializer(Channel::class.java, channelDeserializer))
56+
}
57+
58+
fun onActivityCreate(activity: AppCompatActivity) {
59+
// TODO: on destroy, we should change to a different activity
60+
if (::activity.isInitialized) {
61+
return
62+
}
63+
this.activity = activity
4864
startActivityForResultLauncher =
4965
activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()
5066
) { result ->
@@ -68,17 +84,6 @@ class PluginManager(val activity: AppCompatActivity) {
6884
requestPermissionsCallback!!.onResult(result)
6985
}
7086
}
71-
72-
jsonMapper = ObjectMapper()
73-
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
74-
.enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)
75-
.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
76-
77-
val channelDeserializer = ChannelDeserializer({ channelId, payload ->
78-
sendChannelData(channelId, payload)
79-
}, jsonMapper)
80-
jsonMapper
81-
.registerModule(SimpleModule().addDeserializer(Channel::class.java, channelDeserializer))
8287
}
8388

8489
fun onNewIntent(intent: Intent) {
@@ -99,9 +104,9 @@ class PluginManager(val activity: AppCompatActivity) {
99104
}
100105
}
101106

102-
fun onRestart() {
107+
fun onRestart(activity: AppCompatActivity) {
103108
for (plugin in plugins.values) {
104-
plugin.instance.onRestart()
109+
plugin.instance.triggerOnRestart(activity)
105110
}
106111
}
107112

@@ -111,9 +116,9 @@ class PluginManager(val activity: AppCompatActivity) {
111116
}
112117
}
113118

114-
fun onDestroy() {
119+
fun onDestroy(activity: AppCompatActivity) {
115120
for (plugin in plugins.values) {
116-
plugin.instance.onDestroy()
121+
plugin.instance.triggerOnDestroy(activity)
117122
}
118123
}
119124

@@ -201,14 +206,12 @@ class PluginManager(val activity: AppCompatActivity) {
201206
}
202207
}
203208

204-
companion object {
205-
fun<T> loadConfig(context: Context, plugin: String, cls: Class<T>): T {
206-
val tauriConfigJson = FsUtils.readAsset(context.assets, "tauri.conf.json")
207-
val mapper = ObjectMapper()
208-
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
209-
val config = mapper.readValue(tauriConfigJson, Config::class.java)
210-
return mapper.readValue(config.plugins[plugin].toString(), cls)
211-
}
209+
fun<T> loadConfig(context: Context, plugin: String, cls: Class<T>): T {
210+
val tauriConfigJson = FsUtils.readAsset(context.assets, "tauri.conf.json")
211+
val mapper = ObjectMapper()
212+
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
213+
val config = mapper.readValue(tauriConfigJson, Config::class.java)
214+
return mapper.readValue(config.plugins[plugin].toString(), cls)
212215
}
213216

214217
private external fun handlePluginResponse(id: Int, success: String?, error: String?)

crates/tauri/src/lib.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -137,14 +137,7 @@ macro_rules! android_binding {
137137

138138
::tauri::wry::android_binding!($domain, $app_name, $wry);
139139

140-
::tauri::tao::android_binding!(
141-
$domain,
142-
$app_name,
143-
WryActivity,
144-
android_setup,
145-
$main,
146-
::tauri::tao
147-
);
140+
::tauri::tao::android_binding!($domain, $app_name, Rust, android_setup, $main, ::tauri::tao);
148141

149142
// be careful when renaming this, the `Java_app_tauri_plugin_PluginManager_handlePluginResponse` symbol is checked by the CLI
150143
::tauri::tao::platform::android::prelude::android_fn!(

0 commit comments

Comments
 (0)