expo-dev-launcher 5.2.0-canary-20250709-136b77f → 5.2.0-canary-20250722-599a28f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle +3 -3
- package/android/src/debug/java/expo/modules/devlauncher/DevLauncherController.kt +22 -18
- package/android/src/debug/java/expo/modules/devlauncher/DevLauncherPackageDelegate.kt +1 -3
- package/android/src/debug/java/expo/modules/devlauncher/compose/AuthActivity.kt +2 -2
- package/android/src/debug/java/expo/modules/devlauncher/compose/DevLauncherBottomTabsNavigator.kt +59 -18
- package/android/src/debug/java/expo/modules/devlauncher/compose/models/CrashReportModel.kt +22 -0
- package/android/src/debug/java/expo/modules/devlauncher/compose/{HomeViewModel.kt → models/HomeViewModel.kt} +45 -16
- package/android/src/debug/java/expo/modules/devlauncher/compose/{ProfileViewModel.kt → models/ProfileViewModel.kt} +2 -1
- package/android/src/debug/java/expo/modules/devlauncher/compose/models/SettingsViewModel.kt +80 -0
- package/android/src/debug/java/expo/modules/devlauncher/compose/routes/CrashReport.kt +38 -0
- package/android/src/debug/java/expo/modules/devlauncher/compose/routes/Home.kt +16 -3
- package/android/src/debug/java/expo/modules/devlauncher/compose/routes/Profile.kt +2 -2
- package/android/src/debug/java/expo/modules/devlauncher/compose/routes/Settings.kt +7 -1
- package/android/src/debug/java/expo/modules/devlauncher/compose/screens/CrashReportScreen.kt +185 -0
- package/android/src/debug/java/expo/modules/devlauncher/compose/screens/HomeScreen.kt +289 -48
- package/android/src/debug/java/expo/modules/devlauncher/compose/screens/SettingsScreen.kt +63 -9
- package/android/src/debug/java/expo/modules/devlauncher/compose/ui/AccountSelector.kt +5 -3
- package/android/src/debug/java/expo/modules/devlauncher/compose/ui/ActionButton.kt +3 -1
- package/android/src/debug/java/expo/modules/devlauncher/compose/ui/AppHeader.kt +0 -1
- package/android/src/debug/java/expo/modules/devlauncher/compose/ui/BottomTabButton.kt +1 -1
- package/android/src/debug/java/expo/modules/devlauncher/compose/ui/DevelopmentServerHelp.kt +52 -0
- package/android/src/debug/java/expo/modules/devlauncher/compose/ui/RunningAppCard.kt +18 -26
- package/android/src/debug/java/expo/modules/devlauncher/compose/ui/ServerUrlInput.kt +78 -0
- package/android/src/debug/java/expo/modules/devlauncher/compose/utils/WithIsLast.kt +32 -0
- package/android/src/debug/java/expo/modules/devlauncher/launcher/DevLauncherActivity.kt +11 -105
- package/android/src/debug/java/expo/modules/devlauncher/services/ApolloClientService.kt +5 -1
- package/android/src/debug/java/expo/modules/devlauncher/services/AppService.kt +82 -0
- package/android/src/debug/java/expo/modules/devlauncher/services/DependencyInjection.kt +19 -12
- package/android/src/debug/java/expo/modules/devlauncher/services/ErrorRegistryService.kt +13 -0
- package/android/src/debug/java/expo/modules/devlauncher/services/HttpClientService.kt +78 -3
- package/android/src/debug/java/expo/modules/devlauncher/services/PackagerService.kt +118 -45
- package/android/src/debug/java/expo/modules/devlauncher/services/SessionService.kt +4 -1
- package/android/src/main/java/com/facebook/react/devsupport/DevLauncherDevServerHelper.kt +3 -3
- package/android/src/main/java/expo/modules/devlauncher/launcher/DevLauncherControllerInterface.kt +0 -1
- package/android/src/main/java/expo/modules/devlauncher/launcher/DevLauncherRecentlyOpenedAppsRegistry.kt +10 -9
- package/android/src/main/java/expo/modules/devlauncher/launcher/errors/DevLauncherErrorRegistry.kt +7 -6
- package/android/src/release/java/expo/modules/devlauncher/DevLauncherController.kt +0 -3
- package/expo-module.config.json +0 -1
- package/package.json +4 -4
- package/android/src/debug/assets/expo_dev_launcher_android.bundle +0 -1418
- package/android/src/debug/java/expo/modules/devlauncher/launcher/DevLauncherReactHost.kt +0 -104
- package/android/src/debug/java/expo/modules/devlauncher/launcher/DevLauncherReactNativeHost.kt +0 -65
- package/android/src/debug/java/expo/modules/devlauncher/modules/DevLauncherAuth.kt +0 -72
- package/android/src/main/java/expo/modules/devlauncher/modules/DevLauncherInternalModule.kt +0 -234
package/android/build.gradle
CHANGED
|
@@ -20,13 +20,13 @@ expoModule {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
group = "host.exp.exponent"
|
|
23
|
-
version = "5.2.0-canary-
|
|
23
|
+
version = "5.2.0-canary-20250722-599a28f"
|
|
24
24
|
|
|
25
25
|
android {
|
|
26
26
|
namespace "expo.modules.devlauncher"
|
|
27
27
|
defaultConfig {
|
|
28
28
|
versionCode 9
|
|
29
|
-
versionName "5.2.0-canary-
|
|
29
|
+
versionName "5.2.0-canary-20250722-599a28f"
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
buildTypes {
|
|
@@ -100,7 +100,7 @@ dependencies {
|
|
|
100
100
|
|
|
101
101
|
implementation("com.apollographql.apollo:apollo-runtime:4.3.1")
|
|
102
102
|
|
|
103
|
-
implementation("com.composables:core:1.
|
|
103
|
+
implementation("com.composables:core:1.37.0")
|
|
104
104
|
|
|
105
105
|
testImplementation 'androidx.test:core:1.4.0'
|
|
106
106
|
testImplementation 'androidx.test:core-ktx:1.4.0'
|
|
@@ -6,6 +6,7 @@ import android.content.Intent
|
|
|
6
6
|
import android.net.Uri
|
|
7
7
|
import android.os.Bundle
|
|
8
8
|
import androidx.annotation.UiThread
|
|
9
|
+
import androidx.core.net.toUri
|
|
9
10
|
import com.facebook.react.ReactActivity
|
|
10
11
|
import com.facebook.react.ReactActivityDelegate
|
|
11
12
|
import com.facebook.react.ReactApplication
|
|
@@ -30,8 +31,6 @@ import expo.modules.devlauncher.launcher.DevLauncherIntentRegistryInterface
|
|
|
30
31
|
import expo.modules.devlauncher.launcher.DevLauncherLifecycle
|
|
31
32
|
import expo.modules.devlauncher.launcher.DevLauncherNetworkInterceptor
|
|
32
33
|
import expo.modules.devlauncher.launcher.DevLauncherReactActivityDelegateSupplier
|
|
33
|
-
import expo.modules.devlauncher.launcher.DevLauncherReactHost
|
|
34
|
-
import expo.modules.devlauncher.launcher.DevLauncherReactNativeHost
|
|
35
34
|
import expo.modules.devlauncher.launcher.DevLauncherRecentlyOpenedAppsRegistry
|
|
36
35
|
import expo.modules.devlauncher.launcher.errors.DevLauncherAppError
|
|
37
36
|
import expo.modules.devlauncher.launcher.errors.DevLauncherErrorActivity
|
|
@@ -40,6 +39,7 @@ import expo.modules.devlauncher.launcher.loaders.DevLauncherAppLoaderFactoryInte
|
|
|
40
39
|
import expo.modules.devlauncher.launcher.manifest.DevLauncherManifestParser
|
|
41
40
|
import expo.modules.devlauncher.react.activitydelegates.DevLauncherReactActivityNOPDelegate
|
|
42
41
|
import expo.modules.devlauncher.react.activitydelegates.DevLauncherReactActivityRedirectDelegate
|
|
42
|
+
import expo.modules.devlauncher.services.DependencyInjection
|
|
43
43
|
import expo.modules.devlauncher.tests.DevLauncherTestInterceptor
|
|
44
44
|
import expo.modules.devmenu.DevMenuManager
|
|
45
45
|
import expo.modules.manifests.core.Manifest
|
|
@@ -74,27 +74,30 @@ class DevLauncherController private constructor() :
|
|
|
74
74
|
var devMenuManager: DevMenuManager = DevMenuManager
|
|
75
75
|
override var updatesInterface: UpdatesInterface?
|
|
76
76
|
get() = internalUpdatesInterface
|
|
77
|
-
set(value) =
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
set(value) = run {
|
|
78
|
+
if (value != null) {
|
|
79
|
+
DependencyInjection.appService.setUpUpdateInterface(value, context)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
DevLauncherKoinContext.app.koin.loadModules(
|
|
83
|
+
listOf(
|
|
84
|
+
module {
|
|
85
|
+
single { value }
|
|
86
|
+
}
|
|
87
|
+
)
|
|
82
88
|
)
|
|
83
|
-
|
|
89
|
+
}
|
|
90
|
+
|
|
84
91
|
override val coroutineScope = CoroutineScope(Dispatchers.Default)
|
|
85
92
|
|
|
86
|
-
override val devClientHost by lazy {
|
|
87
|
-
ReactHostWrapper(
|
|
88
|
-
reactNativeHost = DevLauncherReactNativeHost(context as Application, DEV_LAUNCHER_HOST),
|
|
89
|
-
reactHostProvider = { DevLauncherReactHost.create(context as Application, DEV_LAUNCHER_HOST) }
|
|
90
|
-
)
|
|
91
|
-
}
|
|
92
93
|
|
|
93
94
|
private val recentlyOpedAppsRegistry = DevLauncherRecentlyOpenedAppsRegistry(context)
|
|
94
95
|
override var manifest: Manifest? = null
|
|
95
96
|
private set
|
|
97
|
+
|
|
96
98
|
override var manifestURL: Uri? = null
|
|
97
99
|
private set
|
|
100
|
+
|
|
98
101
|
override var latestLoadedApp: Uri? = null
|
|
99
102
|
override var useDeveloperSupport = true
|
|
100
103
|
var canLaunchDevMenuOnStart = false
|
|
@@ -144,7 +147,7 @@ class DevLauncherController private constructor() :
|
|
|
144
147
|
// default to the EXPO_UPDATE_URL value configured in AndroidManifest.xml when project url is unspecified for an EAS update
|
|
145
148
|
if (isEASUpdate && projectUrl == null) {
|
|
146
149
|
val projectUrlString = getMetadataValue(context, "expo.modules.updates.EXPO_UPDATE_URL")
|
|
147
|
-
parsedProjectUrl =
|
|
150
|
+
parsedProjectUrl = projectUrlString.toUri()
|
|
148
151
|
}
|
|
149
152
|
|
|
150
153
|
val manifestParser = DevLauncherManifestParser(httpClient, parsedUrl, installationIDHelper.getOrCreateInstallationID(context))
|
|
@@ -206,7 +209,8 @@ class DevLauncherController private constructor() :
|
|
|
206
209
|
}
|
|
207
210
|
}
|
|
208
211
|
|
|
209
|
-
override fun getRecentlyOpenedApps(): List<DevLauncherAppEntry> =
|
|
212
|
+
override fun getRecentlyOpenedApps(): List<DevLauncherAppEntry> =
|
|
213
|
+
recentlyOpedAppsRegistry.getRecentlyOpenedApps()
|
|
210
214
|
|
|
211
215
|
override fun clearRecentlyOpenedApps() {
|
|
212
216
|
recentlyOpedAppsRegistry.clearRegistry()
|
|
@@ -270,8 +274,8 @@ class DevLauncherController private constructor() :
|
|
|
270
274
|
if (shouldTryToLaunchLastOpenedBundle && lastOpenedApp != null) {
|
|
271
275
|
coroutineScope.launch {
|
|
272
276
|
try {
|
|
273
|
-
loadApp(
|
|
274
|
-
} catch (
|
|
277
|
+
loadApp(lastOpenedApp.url.toUri(), activityToBeInvalidated)
|
|
278
|
+
} catch (_: Throwable) {
|
|
275
279
|
navigateToLauncher()
|
|
276
280
|
}
|
|
277
281
|
}
|
|
@@ -13,7 +13,6 @@ import expo.modules.core.interfaces.ReactActivityHandler
|
|
|
13
13
|
import expo.modules.core.interfaces.ReactActivityLifecycleListener
|
|
14
14
|
import expo.modules.devlauncher.launcher.DevLauncherReactActivityDelegateSupplier
|
|
15
15
|
import expo.modules.devlauncher.modules.DevLauncherModule
|
|
16
|
-
import expo.modules.devlauncher.modules.DevLauncherAuth
|
|
17
16
|
import expo.modules.core.interfaces.ReactNativeHostHandler
|
|
18
17
|
import expo.modules.devlauncher.modules.DevLauncherDevMenuExtension
|
|
19
18
|
import expo.modules.devlauncher.react.DevLauncherReactNativeHostHandler
|
|
@@ -22,8 +21,7 @@ object DevLauncherPackageDelegate {
|
|
|
22
21
|
fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> =
|
|
23
22
|
listOf(
|
|
24
23
|
DevLauncherModule(reactContext),
|
|
25
|
-
DevLauncherDevMenuExtension(reactContext)
|
|
26
|
-
DevLauncherAuth(reactContext)
|
|
24
|
+
DevLauncherDevMenuExtension(reactContext)
|
|
27
25
|
)
|
|
28
26
|
|
|
29
27
|
fun createApplicationLifecycleListeners(context: Context?): List<ApplicationLifecycleListener> =
|
|
@@ -35,10 +35,10 @@ sealed interface AuthResult {
|
|
|
35
35
|
|
|
36
36
|
class AuthActivity : AppCompatActivity() {
|
|
37
37
|
class Contract : ActivityResultContract<AuthRequestType, AuthResult>() {
|
|
38
|
-
override fun createIntent(context: Context,
|
|
38
|
+
override fun createIntent(context: Context, input: AuthRequestType): Intent {
|
|
39
39
|
return Intent(context, AuthActivity::class.java).apply {
|
|
40
40
|
action = ACTION_VIEW
|
|
41
|
-
putExtra(AUTH_REQUEST_TYPE_KEY,
|
|
41
|
+
putExtra(AUTH_REQUEST_TYPE_KEY, input.type)
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
package/android/src/debug/java/expo/modules/devlauncher/compose/DevLauncherBottomTabsNavigator.kt
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
package expo.modules.devlauncher.compose
|
|
2
2
|
|
|
3
|
+
import androidx.compose.animation.AnimatedContentTransitionScope
|
|
3
4
|
import androidx.compose.animation.EnterTransition
|
|
4
5
|
import androidx.compose.animation.ExitTransition
|
|
6
|
+
import androidx.compose.animation.core.tween
|
|
5
7
|
import androidx.compose.foundation.background
|
|
6
8
|
import androidx.compose.foundation.layout.Box
|
|
7
9
|
import androidx.compose.foundation.layout.fillMaxSize
|
|
8
|
-
import androidx.compose.foundation.rememberScrollState
|
|
9
|
-
import androidx.compose.foundation.verticalScroll
|
|
10
10
|
import androidx.compose.runtime.Composable
|
|
11
11
|
import androidx.compose.ui.Modifier
|
|
12
12
|
import androidx.compose.ui.graphics.painter.Painter
|
|
@@ -14,6 +14,8 @@ import androidx.navigation.compose.NavHost
|
|
|
14
14
|
import androidx.navigation.compose.composable
|
|
15
15
|
import androidx.navigation.compose.rememberNavController
|
|
16
16
|
import expo.modules.devlauncher.compose.primitives.DefaultScaffold
|
|
17
|
+
import expo.modules.devlauncher.compose.routes.CrashReport
|
|
18
|
+
import expo.modules.devlauncher.compose.routes.CrashReportRoute
|
|
17
19
|
import expo.modules.devlauncher.compose.routes.Home
|
|
18
20
|
import expo.modules.devlauncher.compose.routes.HomeRoute
|
|
19
21
|
import expo.modules.devlauncher.compose.routes.ProfileRoute
|
|
@@ -23,17 +25,15 @@ import expo.modules.devlauncher.compose.ui.BottomTabBar
|
|
|
23
25
|
import expo.modules.devlauncher.compose.ui.Full
|
|
24
26
|
import expo.modules.devlauncher.compose.ui.rememberBottomSheetState
|
|
25
27
|
import expo.modules.devmenu.compose.theme.Theme
|
|
28
|
+
import kotlinx.serialization.Serializable
|
|
26
29
|
|
|
27
30
|
@Composable
|
|
28
31
|
fun DefaultScreenContainer(
|
|
29
32
|
content: @Composable () -> Unit
|
|
30
33
|
) {
|
|
31
|
-
val scrollState = rememberScrollState()
|
|
32
|
-
|
|
33
34
|
Box(
|
|
34
35
|
modifier = Modifier
|
|
35
36
|
.fillMaxSize()
|
|
36
|
-
.verticalScroll(scrollState)
|
|
37
37
|
.background(Theme.colors.background.secondary)
|
|
38
38
|
) {
|
|
39
39
|
content()
|
|
@@ -46,30 +46,71 @@ data class Tab(
|
|
|
46
46
|
val screen: Any
|
|
47
47
|
)
|
|
48
48
|
|
|
49
|
+
@Serializable
|
|
50
|
+
object Main
|
|
51
|
+
|
|
49
52
|
@Composable
|
|
50
53
|
fun DevLauncherBottomTabsNavigator() {
|
|
51
|
-
val
|
|
54
|
+
val mainNavController = rememberNavController()
|
|
55
|
+
val bottomTabsNavController = rememberNavController()
|
|
52
56
|
val bottomSheetState = rememberBottomSheetState()
|
|
53
57
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
startDestination = Home,
|
|
58
|
+
NavHost(
|
|
59
|
+
navController = mainNavController,
|
|
60
|
+
startDestination = Main
|
|
61
|
+
) {
|
|
62
|
+
composable<Main>(
|
|
60
63
|
enterTransition = {
|
|
61
|
-
|
|
64
|
+
slideIntoContainer(
|
|
65
|
+
AnimatedContentTransitionScope.SlideDirection.Right,
|
|
66
|
+
animationSpec = tween(700)
|
|
67
|
+
)
|
|
62
68
|
},
|
|
63
69
|
exitTransition = {
|
|
64
|
-
|
|
70
|
+
slideOutOfContainer(
|
|
71
|
+
AnimatedContentTransitionScope.SlideDirection.Left,
|
|
72
|
+
animationSpec = tween(700)
|
|
73
|
+
)
|
|
65
74
|
}
|
|
66
75
|
) {
|
|
67
|
-
|
|
68
|
-
|
|
76
|
+
DefaultScaffold(bottomTab = {
|
|
77
|
+
BottomTabBar(bottomTabsNavController)
|
|
78
|
+
}) {
|
|
79
|
+
NavHost(
|
|
80
|
+
navController = bottomTabsNavController,
|
|
81
|
+
startDestination = Home,
|
|
82
|
+
enterTransition = {
|
|
83
|
+
EnterTransition.None
|
|
84
|
+
},
|
|
85
|
+
exitTransition = {
|
|
86
|
+
ExitTransition.None
|
|
87
|
+
}
|
|
88
|
+
) {
|
|
89
|
+
composable<Home> {
|
|
90
|
+
HomeRoute(navController = mainNavController, onProfileClick = { bottomSheetState.jumpTo(Full) })
|
|
91
|
+
}
|
|
92
|
+
composable<Settings> {
|
|
93
|
+
SettingsRoute()
|
|
94
|
+
}
|
|
95
|
+
}
|
|
69
96
|
}
|
|
70
|
-
|
|
71
|
-
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
composable<CrashReport>(
|
|
100
|
+
enterTransition = {
|
|
101
|
+
slideIntoContainer(
|
|
102
|
+
AnimatedContentTransitionScope.SlideDirection.Left,
|
|
103
|
+
animationSpec = tween(700)
|
|
104
|
+
)
|
|
105
|
+
},
|
|
106
|
+
exitTransition = {
|
|
107
|
+
slideOutOfContainer(
|
|
108
|
+
AnimatedContentTransitionScope.SlideDirection.Right,
|
|
109
|
+
animationSpec = tween(700)
|
|
110
|
+
)
|
|
72
111
|
}
|
|
112
|
+
) {
|
|
113
|
+
CrashReportRoute()
|
|
73
114
|
}
|
|
74
115
|
}
|
|
75
116
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
package expo.modules.devlauncher.compose.models
|
|
2
|
+
|
|
3
|
+
import androidx.compose.runtime.mutableStateOf
|
|
4
|
+
import androidx.lifecycle.SavedStateHandle
|
|
5
|
+
import androidx.lifecycle.ViewModel
|
|
6
|
+
import androidx.navigation.toRoute
|
|
7
|
+
import expo.modules.devlauncher.compose.routes.CrashReport
|
|
8
|
+
|
|
9
|
+
data class CrashReportState(
|
|
10
|
+
val report: CrashReport
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
class CrashReportModel(
|
|
14
|
+
savedStateHandle: SavedStateHandle
|
|
15
|
+
) : ViewModel() {
|
|
16
|
+
private val _state = mutableStateOf(
|
|
17
|
+
savedStateHandle.toRoute<CrashReport>()
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
val state
|
|
21
|
+
get() = _state.value
|
|
22
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
package expo.modules.devlauncher.compose
|
|
1
|
+
package expo.modules.devlauncher.compose.models
|
|
2
2
|
|
|
3
3
|
import android.util.Log
|
|
4
4
|
import androidx.compose.runtime.mutableStateOf
|
|
@@ -7,7 +7,10 @@ import androidx.lifecycle.ViewModel
|
|
|
7
7
|
import androidx.lifecycle.viewModelScope
|
|
8
8
|
import expo.modules.devlauncher.DevLauncherController
|
|
9
9
|
import expo.modules.devlauncher.MeQuery
|
|
10
|
-
import expo.modules.devlauncher.
|
|
10
|
+
import expo.modules.devlauncher.launcher.DevLauncherAppEntry
|
|
11
|
+
import expo.modules.devlauncher.launcher.errors.DevLauncherErrorInstance
|
|
12
|
+
import expo.modules.devlauncher.services.AppService
|
|
13
|
+
import expo.modules.devlauncher.services.ErrorRegistryService
|
|
11
14
|
import expo.modules.devlauncher.services.PackagerInfo
|
|
12
15
|
import expo.modules.devlauncher.services.PackagerService
|
|
13
16
|
import expo.modules.devlauncher.services.SessionService
|
|
@@ -15,34 +18,42 @@ import expo.modules.devlauncher.services.UserState
|
|
|
15
18
|
import expo.modules.devlauncher.services.inject
|
|
16
19
|
import kotlinx.coroutines.flow.launchIn
|
|
17
20
|
import kotlinx.coroutines.flow.onEach
|
|
21
|
+
import kotlinx.coroutines.flow.onStart
|
|
18
22
|
import kotlinx.coroutines.launch
|
|
19
23
|
|
|
20
24
|
sealed interface HomeAction {
|
|
21
25
|
class OpenApp(val url: String) : HomeAction
|
|
26
|
+
object RefetchRunningApps : HomeAction
|
|
27
|
+
object ResetRecentlyOpenedApps : HomeAction
|
|
28
|
+
class NavigateToCrashReport(val crashReport: DevLauncherErrorInstance) : HomeAction
|
|
22
29
|
}
|
|
23
30
|
|
|
24
|
-
typealias HomeActionHandler = (HomeAction) -> Unit
|
|
25
|
-
|
|
26
31
|
data class HomeState(
|
|
27
|
-
val appName: String = "
|
|
32
|
+
val appName: String = "Unknown App",
|
|
28
33
|
val runningPackagers: Set<PackagerInfo> = emptySet(),
|
|
29
|
-
val
|
|
34
|
+
val isFetchingPackagers: Boolean = false,
|
|
35
|
+
val currentAccount: MeQuery.Account? = null,
|
|
36
|
+
val recentlyOpenedApps: List<DevLauncherAppEntry> = emptyList(),
|
|
37
|
+
val crashReport: DevLauncherErrorInstance? = null
|
|
30
38
|
)
|
|
31
39
|
|
|
32
40
|
class HomeViewModel() : ViewModel() {
|
|
33
|
-
val httpClientService = inject<HttpClientService>()
|
|
34
41
|
val devLauncherController = inject<DevLauncherController>()
|
|
35
42
|
val sessionService = inject<SessionService>()
|
|
36
|
-
|
|
37
|
-
val
|
|
43
|
+
val packagerService = inject<PackagerService>()
|
|
44
|
+
val appService = inject<AppService>()
|
|
45
|
+
val errorRegistryService = inject<ErrorRegistryService>()
|
|
38
46
|
|
|
39
47
|
private var _state = mutableStateOf(
|
|
40
48
|
HomeState(
|
|
49
|
+
appName = appService.applicationInfo.appName,
|
|
41
50
|
runningPackagers = packagerService.runningPackagers.value,
|
|
42
51
|
currentAccount = when (val userState = sessionService.user.value) {
|
|
43
52
|
UserState.Fetching, UserState.LoggedOut -> null
|
|
44
53
|
is UserState.LoggedIn -> userState.selectedAccount
|
|
45
|
-
}
|
|
54
|
+
},
|
|
55
|
+
recentlyOpenedApps = devLauncherController.getRecentlyOpenedApps(),
|
|
56
|
+
crashReport = errorRegistryService.consumeException()
|
|
46
57
|
)
|
|
47
58
|
)
|
|
48
59
|
|
|
@@ -50,11 +61,14 @@ class HomeViewModel() : ViewModel() {
|
|
|
50
61
|
get() = _state.value
|
|
51
62
|
|
|
52
63
|
init {
|
|
53
|
-
packagerService
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
64
|
+
packagerService
|
|
65
|
+
.runningPackagers
|
|
66
|
+
.onEach { newPackagers ->
|
|
67
|
+
_state.value = _state.value.copy(
|
|
68
|
+
runningPackagers = newPackagers
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
.launchIn(viewModelScope)
|
|
58
72
|
|
|
59
73
|
sessionService.user.onEach { newUser ->
|
|
60
74
|
when (newUser) {
|
|
@@ -67,18 +81,33 @@ class HomeViewModel() : ViewModel() {
|
|
|
67
81
|
)
|
|
68
82
|
}
|
|
69
83
|
}.launchIn(viewModelScope)
|
|
84
|
+
|
|
85
|
+
packagerService.isLoading.onEach { isLoading ->
|
|
86
|
+
_state.value = _state.value.copy(
|
|
87
|
+
isFetchingPackagers = isLoading
|
|
88
|
+
)
|
|
89
|
+
}.launchIn(viewModelScope)
|
|
70
90
|
}
|
|
71
91
|
|
|
72
92
|
fun onAction(action: HomeAction) {
|
|
73
93
|
when (action) {
|
|
74
94
|
is HomeAction.OpenApp ->
|
|
75
|
-
|
|
95
|
+
devLauncherController.coroutineScope.launch {
|
|
76
96
|
try {
|
|
77
97
|
devLauncherController.loadApp(action.url.toUri(), mainActivity = null)
|
|
78
98
|
} catch (e: Exception) {
|
|
79
99
|
Log.e("DevLauncher", "Failed to open app: ${action.url}", e)
|
|
80
100
|
}
|
|
81
101
|
}
|
|
102
|
+
|
|
103
|
+
HomeAction.RefetchRunningApps -> packagerService.refetchedPackager()
|
|
104
|
+
|
|
105
|
+
HomeAction.ResetRecentlyOpenedApps -> viewModelScope.launch {
|
|
106
|
+
devLauncherController.clearRecentlyOpenedApps()
|
|
107
|
+
_state.value = _state.value.copy(recentlyOpenedApps = emptyList())
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
is HomeAction.NavigateToCrashReport -> IllegalStateException("Navigation action should be handled by the UI layer, not the ViewModel.")
|
|
82
111
|
}
|
|
83
112
|
}
|
|
84
113
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
package expo.modules.devlauncher.compose
|
|
1
|
+
package expo.modules.devlauncher.compose.models
|
|
2
2
|
|
|
3
3
|
import androidx.lifecycle.ViewModel
|
|
4
4
|
import androidx.lifecycle.viewModelScope
|
|
5
|
+
import expo.modules.devlauncher.compose.Session
|
|
5
6
|
import expo.modules.devlauncher.services.SessionService
|
|
6
7
|
import expo.modules.devlauncher.services.UserState
|
|
7
8
|
import expo.modules.devlauncher.services.inject
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
package expo.modules.devlauncher.compose.models
|
|
2
|
+
|
|
3
|
+
import android.app.Application
|
|
4
|
+
import androidx.compose.runtime.mutableStateOf
|
|
5
|
+
import androidx.lifecycle.AndroidViewModel
|
|
6
|
+
import expo.modules.devlauncher.services.AppService
|
|
7
|
+
import expo.modules.devlauncher.services.ApplicationInfo
|
|
8
|
+
import expo.modules.devlauncher.services.inject
|
|
9
|
+
import expo.modules.devmenu.DevMenuPreferencesHandle
|
|
10
|
+
|
|
11
|
+
data class SettingsState(
|
|
12
|
+
val showMenuAtLaunch: Boolean = false,
|
|
13
|
+
val isShakeEnable: Boolean = true,
|
|
14
|
+
val isThreeFingerLongPressEnable: Boolean = true,
|
|
15
|
+
val isKeyCommandEnabled: Boolean = true,
|
|
16
|
+
val applicationInfo: ApplicationInfo? = null
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
sealed interface SettingsAction {
|
|
20
|
+
data class ToggleShowMenuAtLaunch(val newValue: Boolean) : SettingsAction
|
|
21
|
+
data class ToggleShakeEnable(val newValue: Boolean) : SettingsAction
|
|
22
|
+
data class ToggleThreeFingerLongPressEnable(val newValue: Boolean) : SettingsAction
|
|
23
|
+
data class ToggleKeyCommandEnable(val newValue: Boolean) : SettingsAction
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class SettingsViewModel(application: Application) : AndroidViewModel(application) {
|
|
27
|
+
private val menuPreferences = DevMenuPreferencesHandle
|
|
28
|
+
private val appService = inject<AppService>()
|
|
29
|
+
|
|
30
|
+
private val _state = mutableStateOf(
|
|
31
|
+
SettingsState(
|
|
32
|
+
showMenuAtLaunch = menuPreferences.showsAtLaunch,
|
|
33
|
+
isShakeEnable = menuPreferences.motionGestureEnabled,
|
|
34
|
+
isThreeFingerLongPressEnable = menuPreferences.touchGestureEnabled,
|
|
35
|
+
isKeyCommandEnabled = menuPreferences.keyCommandsEnabled,
|
|
36
|
+
applicationInfo = appService.applicationInfo
|
|
37
|
+
)
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
val state
|
|
41
|
+
get() = _state.value
|
|
42
|
+
|
|
43
|
+
private val listener = {
|
|
44
|
+
_state.value = SettingsState(
|
|
45
|
+
showMenuAtLaunch = menuPreferences.showsAtLaunch,
|
|
46
|
+
isShakeEnable = menuPreferences.motionGestureEnabled,
|
|
47
|
+
isThreeFingerLongPressEnable = menuPreferences.touchGestureEnabled,
|
|
48
|
+
isKeyCommandEnabled = menuPreferences.keyCommandsEnabled
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
init {
|
|
53
|
+
menuPreferences.addOnChangeListener(listener)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
override fun onCleared() {
|
|
57
|
+
super.onCleared()
|
|
58
|
+
menuPreferences.removeOnChangeListener(listener)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
fun onAction(action: SettingsAction) {
|
|
62
|
+
when (action) {
|
|
63
|
+
is SettingsAction.ToggleShowMenuAtLaunch -> {
|
|
64
|
+
menuPreferences.showsAtLaunch = action.newValue
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
is SettingsAction.ToggleShakeEnable -> {
|
|
68
|
+
menuPreferences.motionGestureEnabled = action.newValue
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
is SettingsAction.ToggleThreeFingerLongPressEnable -> {
|
|
72
|
+
menuPreferences.touchGestureEnabled = action.newValue
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
is SettingsAction.ToggleKeyCommandEnable -> {
|
|
76
|
+
menuPreferences.keyCommandsEnabled = action.newValue
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
package expo.modules.devlauncher.compose.routes
|
|
2
|
+
|
|
3
|
+
import androidx.compose.runtime.Composable
|
|
4
|
+
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
5
|
+
import expo.modules.devlauncher.compose.DefaultScreenContainer
|
|
6
|
+
import expo.modules.devlauncher.compose.models.CrashReportModel
|
|
7
|
+
import expo.modules.devlauncher.compose.screens.CrashReportScreen
|
|
8
|
+
import expo.modules.devlauncher.launcher.errors.DevLauncherErrorInstance
|
|
9
|
+
import kotlinx.serialization.Serializable
|
|
10
|
+
|
|
11
|
+
@Serializable
|
|
12
|
+
class CrashReport(
|
|
13
|
+
val timestamp: Long,
|
|
14
|
+
val message: String,
|
|
15
|
+
val stack: String
|
|
16
|
+
) {
|
|
17
|
+
companion object {
|
|
18
|
+
fun fromErrorInstance(errorInstance: DevLauncherErrorInstance): CrashReport {
|
|
19
|
+
return CrashReport(
|
|
20
|
+
timestamp = errorInstance.timestamp,
|
|
21
|
+
message = errorInstance.throwable.message ?: "Unknown",
|
|
22
|
+
stack = errorInstance.throwable.stackTraceToString()
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@Composable
|
|
29
|
+
fun CrashReportRoute() {
|
|
30
|
+
DefaultScreenContainer {
|
|
31
|
+
val viewModel = viewModel<CrashReportModel>()
|
|
32
|
+
CrashReportScreen(
|
|
33
|
+
timestamp = viewModel.state.timestamp,
|
|
34
|
+
message = viewModel.state.message,
|
|
35
|
+
stack = viewModel.state.stack
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -2,8 +2,10 @@ package expo.modules.devlauncher.compose.routes
|
|
|
2
2
|
|
|
3
3
|
import androidx.compose.runtime.Composable
|
|
4
4
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
5
|
+
import androidx.navigation.NavController
|
|
5
6
|
import expo.modules.devlauncher.compose.DefaultScreenContainer
|
|
6
|
-
import expo.modules.devlauncher.compose.
|
|
7
|
+
import expo.modules.devlauncher.compose.models.HomeAction
|
|
8
|
+
import expo.modules.devlauncher.compose.models.HomeViewModel
|
|
7
9
|
import expo.modules.devlauncher.compose.screens.HomeScreen
|
|
8
10
|
import kotlinx.serialization.Serializable
|
|
9
11
|
|
|
@@ -11,12 +13,23 @@ import kotlinx.serialization.Serializable
|
|
|
11
13
|
object Home
|
|
12
14
|
|
|
13
15
|
@Composable
|
|
14
|
-
fun HomeRoute(
|
|
16
|
+
fun HomeRoute(
|
|
17
|
+
navController: NavController,
|
|
18
|
+
onProfileClick: () -> Unit
|
|
19
|
+
) {
|
|
15
20
|
DefaultScreenContainer {
|
|
16
21
|
val viewModel = viewModel<HomeViewModel>()
|
|
17
22
|
HomeScreen(
|
|
18
23
|
state = viewModel.state,
|
|
19
|
-
onAction =
|
|
24
|
+
onAction = { action ->
|
|
25
|
+
when (action) {
|
|
26
|
+
is HomeAction.NavigateToCrashReport -> navController.navigate(
|
|
27
|
+
CrashReport.fromErrorInstance(action.crashReport)
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
else -> viewModel.onAction(action)
|
|
31
|
+
}
|
|
32
|
+
},
|
|
20
33
|
onProfileClick = onProfileClick
|
|
21
34
|
)
|
|
22
35
|
}
|
|
@@ -10,8 +10,8 @@ import com.composables.core.SheetDetent.Companion.Hidden
|
|
|
10
10
|
import expo.modules.devlauncher.compose.AuthActivity
|
|
11
11
|
import expo.modules.devlauncher.compose.AuthRequestType
|
|
12
12
|
import expo.modules.devlauncher.compose.AuthResult
|
|
13
|
-
import expo.modules.devlauncher.compose.ProfileState
|
|
14
|
-
import expo.modules.devlauncher.compose.ProfileViewModel
|
|
13
|
+
import expo.modules.devlauncher.compose.models.ProfileState
|
|
14
|
+
import expo.modules.devlauncher.compose.models.ProfileViewModel
|
|
15
15
|
import expo.modules.devlauncher.compose.ui.AccountSelector
|
|
16
16
|
import expo.modules.devlauncher.compose.ui.BottomSheet
|
|
17
17
|
import expo.modules.devlauncher.compose.ui.ProfileLayout
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
package expo.modules.devlauncher.compose.routes
|
|
2
2
|
|
|
3
3
|
import androidx.compose.runtime.Composable
|
|
4
|
+
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
4
5
|
import expo.modules.devlauncher.compose.DefaultScreenContainer
|
|
6
|
+
import expo.modules.devlauncher.compose.models.SettingsViewModel
|
|
5
7
|
import expo.modules.devlauncher.compose.screens.SettingsScreen
|
|
6
8
|
import kotlinx.serialization.Serializable
|
|
7
9
|
|
|
@@ -11,6 +13,10 @@ object Settings
|
|
|
11
13
|
@Composable
|
|
12
14
|
fun SettingsRoute() {
|
|
13
15
|
DefaultScreenContainer {
|
|
14
|
-
|
|
16
|
+
val viewModel = viewModel<SettingsViewModel>()
|
|
17
|
+
SettingsScreen(
|
|
18
|
+
state = viewModel.state,
|
|
19
|
+
onAction = viewModel::onAction
|
|
20
|
+
)
|
|
15
21
|
}
|
|
16
22
|
}
|