@@ -6,118 +6,78 @@ import androidx.compose.animation.core.EaseInOut
6
6
import androidx.compose.animation.core.keyframes
7
7
import androidx.compose.animation.core.tween
8
8
import androidx.compose.runtime.Composable
9
+ import androidx.compose.runtime.DisposableEffect
9
10
import androidx.compose.runtime.LaunchedEffect
10
11
import androidx.compose.runtime.remember
11
12
import androidx.compose.ui.Modifier
13
+ import androidx.compose.ui.platform.LocalLifecycleOwner
14
+ import androidx.compose.ui.viewinterop.AndroidView
15
+ import androidx.lifecycle.Lifecycle
16
+ import androidx.lifecycle.LifecycleEventObserver
12
17
import kotlinx.coroutines.launch
18
+ import net.mullvad.mullvadvpn.lib.map.data.CameraPosition
19
+ import net.mullvad.mullvadvpn.lib.map.data.MapConfig
13
20
import net.mullvad.mullvadvpn.lib.map.data.MapViewState
14
21
import net.mullvad.mullvadvpn.lib.map.data.Marker
15
22
import net.mullvad.mullvadvpn.lib.map.data.MarkerType
16
- import net.mullvad.mullvadvpn.lib.map.internal.MapGLShader
23
+ import net.mullvad.mullvadvpn.lib.map.internal.MapGLSurfaceView
17
24
import net.mullvad.mullvadvpn.model.LatLng
18
25
import net.mullvad.mullvadvpn.model.Latitude
19
26
import net.mullvad.mullvadvpn.model.Longitude
20
27
21
28
@Composable
22
29
fun Map (
23
30
modifier : Modifier ,
24
- animate : Boolean ,
31
+ animateCameraMovement : Boolean ,
25
32
cameraLocation : LatLng ,
26
33
marker : Marker ? ,
27
34
percent : Float ,
28
35
) {
29
36
val mapViewState =
30
- if (animate ) {
31
- animatedMapViewState( cameraLocation, marker, percent)
37
+ if (animateCameraMovement ) {
38
+ MapViewState (marker, animatedCameraPosition( cameraLocation, marker, percent) )
32
39
} else {
33
- MapViewState (
34
- zoom = marker?.type.toZoom(),
35
- cameraLatLng = cameraLocation,
36
- locationMarker = marker,
37
- percent = percent
38
- )
40
+ MapViewState (marker, CameraPosition (cameraLocation, marker?.type.toZoom(), percent))
39
41
}
40
- Log .d(" MullvadMap" , " CameraLocation: ${mapViewState.cameraLatLng} " )
41
- MapGLShader (modifier = modifier, mapViewState = mapViewState)
42
+ Map (modifier = modifier, mapViewState = mapViewState)
42
43
}
43
44
44
- @Composable
45
- fun animatedMapViewState (
46
- targetCameraLocation : LatLng ,
47
- marker : Marker ? ,
48
- percent : Float ,
49
- ): MapViewState {
50
- val tempPreviousLocation =
51
- rememberPrevious(
52
- current = targetCameraLocation,
53
- shouldUpdate = { prev, curr -> prev != curr }
54
- ) ? : targetCameraLocation
55
- val previousLocation = remember(targetCameraLocation) { tempPreviousLocation }
56
-
57
- val distance =
58
- remember(targetCameraLocation) { targetCameraLocation.distanceTo(previousLocation).toInt() }
59
- val duration = distance.toAnimationDuration()
60
-
61
- val longitudeAnimation = remember { Animatable (targetCameraLocation.longitude.value) }
62
45
63
- val latitudeAnimation = remember { Animatable (targetCameraLocation.latitude.value) }
64
- val secureZoomAnimation = remember {
65
- Animatable (if (marker?.type == MarkerType .SECURE ) SECURE_ZOOM else UNSECURE_ZOOM )
66
- }
67
- val zoomOutMultiplier = remember { Animatable (1f ) }
68
-
69
- LaunchedEffect (targetCameraLocation) {
70
- launch { latitudeAnimation.animateTo(targetCameraLocation.latitude.value, tween(duration)) }
71
- launch {
72
- // Unwind longitudeAnimation into a Longitude
73
- val currentLongitude = Longitude .fromFloat(longitudeAnimation.value)
46
+ @Composable
47
+ internal fun Map (modifier : Modifier = Modifier , mapViewState : MapViewState ) {
74
48
75
- // Resolve a vector showing us the shortest path to the target longitude, e.g going
76
- // from 170 to -170 would result in 20 since we can wrap around the globe
77
- val shortestPathVector = currentLongitude.vectorTo(targetCameraLocation.longitude)
49
+ var view: MapGLSurfaceView ? = remember { null }
78
50
79
- // Animate to the new camera location using the shortest path vector
80
- longitudeAnimation.animateTo(
81
- longitudeAnimation.value + shortestPathVector.value,
82
- tween(duration),
83
- )
51
+ val lifeCycleState = LocalLifecycleOwner .current.lifecycle
84
52
85
- // Current value animation value might be outside of range of a Longitude, so when the
86
- // animation is done we unwind the animation to the correct value
87
- longitudeAnimation.snapTo(targetCameraLocation.longitude.value)
53
+ DisposableEffect (key1 = lifeCycleState) {
54
+ val observer = LifecycleEventObserver { _, event ->
55
+ when (event) {
56
+ Lifecycle .Event .ON_RESUME -> {
57
+ view?.onResume()
58
+ }
59
+ Lifecycle .Event .ON_PAUSE -> {
60
+ view?.onPause()
61
+ }
62
+ else -> {}
63
+ }
88
64
}
89
- launch {
90
- zoomOutMultiplier.animateTo(
91
- targetValue = 1f ,
92
- animationSpec =
93
- keyframes {
94
- if (duration < SHORT_ANIMATION_MILLIS ) {
95
- durationMillis = duration
96
- 1f at duration using EaseInOut
97
- } else {
98
- durationMillis = duration
99
- 1.25f at duration / 3 using EaseInOut
100
- 1f at duration using EaseInOut
101
- }
102
- }
103
- )
65
+ lifeCycleState.addObserver(observer)
66
+
67
+ onDispose {
68
+ Log .d(" mullvad" , " AAA View Disposed ${view.hashCode()} " )
69
+ lifeCycleState.removeObserver(observer)
70
+ view?.onPause()
71
+ view = null
104
72
}
105
73
}
106
74
107
- LaunchedEffect (marker?.type) {
108
- launch { secureZoomAnimation.animateTo(targetValue = marker?.type.toZoom(), tween(2000 )) }
75
+ // TODO how to handle mapConfig changes? Can we recreate the view? make them recomposable?
76
+ AndroidView (modifier = modifier, factory = { MapGLSurfaceView (it, mapConfig = MapConfig ()) }) {
77
+ glSurfaceView ->
78
+ view = glSurfaceView
79
+ glSurfaceView.setData(mapViewState)
109
80
}
110
-
111
- return MapViewState (
112
- zoom = secureZoomAnimation.value * zoomOutMultiplier.value * 0.9f ,
113
- cameraLatLng =
114
- LatLng (
115
- Latitude (latitudeAnimation.value),
116
- Longitude .fromFloat(longitudeAnimation.value)
117
- ),
118
- locationMarker = marker,
119
- percent = percent
120
- )
121
81
}
122
82
123
83
fun MarkerType?.toZoom () =
0 commit comments