expo-dev-launcher 0.7.0 → 0.8.3

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.
Files changed (57) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +1 -1
  3. package/android/build.gradle +2 -1
  4. package/android/src/debug/assets/expo_dev_launcher_android.bundle +396 -403
  5. package/android/src/debug/java/expo/modules/devlauncher/DevLauncherController.kt +10 -6
  6. package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherAppLoaderFactory.kt +5 -5
  7. package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherExpoAppLoader.kt +5 -5
  8. package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherLocalAppLoader.kt +3 -3
  9. package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherPublishedAppLoader.kt +2 -6
  10. package/android/src/debug/java/expo/modules/devlauncher/modules/DevLauncherInternalModule.kt +6 -1
  11. package/android/src/debug/java/expo/modules/devlauncher/modules/DevLauncherModule.kt +1 -1
  12. package/android/src/main/java/expo/modules/devlauncher/launcher/DevLauncherControllerInterface.kt +2 -2
  13. package/android/src/main/java/expo/modules/devlauncher/launcher/configurators/DevLauncherExpoActivityConfigurator.kt +12 -11
  14. package/android/src/main/java/expo/modules/devlauncher/launcher/manifest/DevLauncherManifestParser.kt +4 -2
  15. package/android/src/main/java/expo/modules/devlauncher/launcher/manifest/DevLauncherManifestTypes.kt +11 -26
  16. package/android/src/main/java/expo/modules/devlauncher/launcher/menu/DevLauncherMenuDelegate.kt +3 -3
  17. package/android/src/release/java/expo/modules/devlauncher/DevLauncherController.kt +2 -2
  18. package/android/src/testDebug/java/expo/modules/devlauncher/DevLauncherControllerTest.kt +50 -0
  19. package/android/src/testDebug/java/expo/modules/devlauncher/launcher/DevLauncherRecentlyOpenedAppsRegistryTest.kt +43 -0
  20. package/android/src/testDebug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherAppLoaderFactoryTest.kt +3 -3
  21. package/android/src/testDebug/java/expo/modules/devlauncher/modules/DevLauncherModuleTest.kt +38 -0
  22. package/build/DevLauncherErrorManager.js +7 -0
  23. package/build/DevLauncherErrorManager.js.map +1 -1
  24. package/expo-dev-launcher.podspec +2 -0
  25. package/ios/EXDevLauncher.m +8 -2
  26. package/ios/EXDevLauncher.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  27. package/ios/EXDevLauncher.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  28. package/ios/EXDevLauncher.xcodeproj/xcuserdata/brentvatne.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
  29. package/ios/EXDevLauncher.xcworkspace/xcuserdata/brentvatne.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  30. package/ios/EXDevLauncherController.h +2 -2
  31. package/ios/EXDevLauncherController.m +22 -11
  32. package/ios/EXDevLauncherInternal.m +5 -0
  33. package/ios/EXDevLauncherMenuDelegate.swift +3 -3
  34. package/ios/Errors/EXDevLauncherRedBox.m +5 -0
  35. package/ios/Manifest/EXDevLauncherManifestHelper.swift +15 -12
  36. package/ios/Manifest/EXDevLauncherManifestParser.h +3 -3
  37. package/ios/Manifest/EXDevLauncherManifestParser.m +17 -5
  38. package/ios/Tests/EXDevLauncherManifestHelperTests.swift +28 -0
  39. package/ios/Tests/EXDevLauncherManifestParserTests.m +113 -0
  40. package/ios/Tests/EXDevLauncherModuleTests.m +39 -0
  41. package/ios/devvyc 2021-05-14 18-19-10/devvyc.ipa +0 -0
  42. package/ios/main.jsbundle +414 -420
  43. package/package.json +6 -6
  44. package/plugin/build/constants.d.ts +1 -0
  45. package/plugin/build/constants.js +4 -0
  46. package/plugin/build/utils.d.ts +2 -0
  47. package/plugin/build/utils.js +24 -0
  48. package/plugin/build/withDevLauncher.js +27 -39
  49. package/plugin/build/withDevLauncherAppDelegate.d.ts +1 -0
  50. package/plugin/build/withDevLauncherAppDelegate.js +161 -16
  51. package/plugin/src/constants.ts +1 -0
  52. package/plugin/src/utils.ts +25 -0
  53. package/plugin/src/withDevLauncher.ts +32 -45
  54. package/plugin/src/withDevLauncherAppDelegate.ts +211 -23
  55. package/src/DevLauncherErrorManager.ts +7 -0
  56. package/android/src/main/java/expo/modules/devlauncher/launcher/manifest/DevLauncherManifest.kt +0 -111
  57. package/ios/Manifest/EXDevLauncherManifest.swift +0 -158
@@ -28,12 +28,12 @@ import expo.modules.devlauncher.launcher.DevLauncherLifecycle
28
28
  import expo.modules.devlauncher.launcher.DevLauncherReactActivityDelegateSupplier
29
29
  import expo.modules.devlauncher.launcher.DevLauncherRecentlyOpenedAppsRegistry
30
30
  import expo.modules.devlauncher.launcher.loaders.DevLauncherAppLoaderFactoryInterface
31
- import expo.modules.devlauncher.launcher.manifest.DevLauncherManifest
32
31
  import expo.modules.devlauncher.launcher.manifest.DevLauncherManifestParser
33
32
  import expo.modules.devlauncher.launcher.menu.DevLauncherMenuDelegate
34
33
  import expo.modules.devlauncher.react.activitydelegates.DevLauncherReactActivityNOPDelegate
35
34
  import expo.modules.devlauncher.react.activitydelegates.DevLauncherReactActivityRedirectDelegate
36
35
  import expo.modules.devlauncher.tests.DevLauncherTestInterceptor
36
+ import expo.modules.manifests.core.Manifest
37
37
  import expo.modules.updatesinterface.UpdatesInterface
38
38
  import kotlinx.coroutines.GlobalScope
39
39
  import kotlinx.coroutines.launch
@@ -72,7 +72,7 @@ class DevLauncherController private constructor()
72
72
  override val devClientHost = DevLauncherClientHost((context as Application), DEV_LAUNCHER_HOST)
73
73
 
74
74
  private val recentlyOpedAppsRegistry = DevLauncherRecentlyOpenedAppsRegistry(context)
75
- override var manifest: DevLauncherManifest? = null
75
+ override var manifest: Manifest? = null
76
76
  private set
77
77
  override var manifestURL: Uri? = null
78
78
  private set
@@ -188,16 +188,20 @@ class DevLauncherController private constructor()
188
188
  }
189
189
  return true
190
190
  }
191
+
192
+ intent?.let {
193
+ return handleExternalIntent(it)
194
+ }
195
+
191
196
  return false
192
197
  }
193
198
 
194
199
  private fun handleExternalIntent(intent: Intent): Boolean {
195
- if (mode == Mode.APP) {
196
- return false
200
+ if (mode != Mode.APP && intent.action != Intent.ACTION_MAIN) {
201
+ pendingIntentRegistry.intent = intent
197
202
  }
198
203
 
199
- pendingIntentRegistry.intent = intent
200
- return true
204
+ return false
201
205
  }
202
206
 
203
207
  private fun ensureHostWasCleared(host: ReactNativeHost, activityToBeInvalidated: ReactActivity? = null) {
@@ -8,15 +8,15 @@ import expo.modules.devlauncher.helpers.loadUpdate
8
8
  import expo.modules.devlauncher.koin.DevLauncherKoinComponent
9
9
  import expo.modules.devlauncher.koin.optInject
10
10
  import expo.modules.devlauncher.launcher.DevLauncherControllerInterface
11
- import expo.modules.devlauncher.launcher.manifest.DevLauncherManifest
12
11
  import expo.modules.devlauncher.launcher.manifest.DevLauncherManifestParser
12
+ import expo.modules.manifests.core.Manifest
13
13
  import expo.modules.updatesinterface.UpdatesInterface
14
14
  import org.koin.core.component.inject
15
15
  import java.lang.IllegalStateException
16
16
 
17
17
  interface DevLauncherAppLoaderFactoryInterface {
18
18
  suspend fun createAppLoader(url: Uri, manifestParser: DevLauncherManifestParser): DevLauncherAppLoader
19
- fun getManifest(): DevLauncherManifest?
19
+ fun getManifest(): Manifest?
20
20
  fun shouldUseDeveloperSupport(): Boolean
21
21
  }
22
22
 
@@ -27,7 +27,7 @@ class DevLauncherAppLoaderFactory : DevLauncherKoinComponent, DevLauncherAppLoad
27
27
  private val controller: DevLauncherControllerInterface by inject()
28
28
 
29
29
  private var instanceWasCreated = false
30
- private var manifest: DevLauncherManifest? = null
30
+ private var manifest: Manifest? = null
31
31
  private var useDeveloperSupport = true
32
32
 
33
33
  override suspend fun createAppLoader(url: Uri, manifestParser: DevLauncherManifestParser): DevLauncherAppLoader {
@@ -45,7 +45,7 @@ class DevLauncherAppLoaderFactory : DevLauncherKoinComponent, DevLauncherAppLoad
45
45
  } else {
46
46
  val configuration = createUpdatesConfigurationWithUrl(url)
47
47
  val update = updatesInterface!!.loadUpdate(configuration, context) {
48
- manifest = DevLauncherManifest.fromJson(it.toString().reader())
48
+ manifest = Manifest.fromManifestJson(it) // TODO: might be able to pass actual manifest object in here
49
49
  return@loadUpdate !manifest!!.isUsingDeveloperTool()
50
50
  }
51
51
  if (manifest!!.isUsingDeveloperTool()) {
@@ -59,7 +59,7 @@ class DevLauncherAppLoaderFactory : DevLauncherKoinComponent, DevLauncherAppLoad
59
59
  }
60
60
  }
61
61
 
62
- override fun getManifest(): DevLauncherManifest? = checkIfInstanceWasCreated { manifest }
62
+ override fun getManifest(): Manifest? = checkIfInstanceWasCreated { manifest }
63
63
 
64
64
  override fun shouldUseDeveloperSupport(): Boolean = checkIfInstanceWasCreated { useDeveloperSupport }
65
65
 
@@ -12,11 +12,11 @@ import expo.modules.devlauncher.helpers.isValidColor
12
12
  import expo.modules.devlauncher.helpers.setProtectedDeclaredField
13
13
  import expo.modules.devlauncher.launcher.DevLauncherControllerInterface
14
14
  import expo.modules.devlauncher.launcher.configurators.DevLauncherExpoActivityConfigurator
15
- import expo.modules.devlauncher.launcher.manifest.DevLauncherManifest
16
15
  import expo.modules.devlauncher.launcher.manifest.DevLauncherUserInterface
16
+ import expo.modules.manifests.core.Manifest
17
17
 
18
18
  abstract class DevLauncherExpoAppLoader(
19
- private val manifest: DevLauncherManifest,
19
+ private val manifest: Manifest,
20
20
  appHost: ReactNativeHost,
21
21
  context: Context,
22
22
  controller: DevLauncherControllerInterface,
@@ -39,7 +39,7 @@ abstract class DevLauncherExpoAppLoader(
39
39
  }
40
40
 
41
41
  private fun applyUserInterfaceStyle(context: ReactContext) {
42
- val userInterfaceStyle = when (manifest.userInterfaceStyle) {
42
+ val userInterfaceStyle = when (manifest.getAndroidUserInterfaceStyle()) {
43
43
  DevLauncherUserInterface.DARK -> "dark"
44
44
  DevLauncherUserInterface.LIGHT -> "light"
45
45
  else -> return
@@ -70,7 +70,7 @@ abstract class DevLauncherExpoAppLoader(
70
70
  }
71
71
 
72
72
  private fun applyBackgroundColor(view: View) {
73
- val backgroundColor = manifest.backgroundColor ?: return
73
+ val backgroundColor = manifest.getAndroidBackgroundColor() ?: return
74
74
  if (!isValidColor(backgroundColor)) {
75
75
  return
76
76
  }
@@ -78,6 +78,6 @@ abstract class DevLauncherExpoAppLoader(
78
78
  }
79
79
 
80
80
  override fun getAppName(): String? {
81
- return manifest.name
81
+ return manifest.getName()
82
82
  }
83
83
  }
@@ -5,19 +5,19 @@ import android.net.Uri
5
5
  import com.facebook.react.ReactNativeHost
6
6
  import expo.modules.devlauncher.helpers.injectReactInterceptor
7
7
  import expo.modules.devlauncher.launcher.DevLauncherControllerInterface
8
- import expo.modules.devlauncher.launcher.manifest.DevLauncherManifest
8
+ import expo.modules.manifests.core.Manifest
9
9
 
10
10
  /**
11
11
  * Implementation of DevLauncherExpoAppLoader which is used for loading locally served projects in
12
12
  * development.
13
13
  */
14
14
  class DevLauncherLocalAppLoader(
15
- private val manifest: DevLauncherManifest,
15
+ private val manifest: Manifest,
16
16
  private val appHost: ReactNativeHost,
17
17
  private val context: Context,
18
18
  controller: DevLauncherControllerInterface
19
19
  ) : DevLauncherExpoAppLoader(manifest, appHost, context, controller) {
20
20
  override fun injectBundleLoader(): Boolean {
21
- return injectReactInterceptor(context, appHost, Uri.parse(manifest.bundleUrl))
21
+ return injectReactInterceptor(context, appHost, Uri.parse(manifest.getBundleURL()))
22
22
  }
23
23
  }
@@ -3,19 +3,15 @@ package expo.modules.devlauncher.launcher.loaders
3
3
  import android.content.Context
4
4
  import com.facebook.react.ReactNativeHost
5
5
  import expo.modules.devlauncher.helpers.injectLocalBundleLoader
6
- import expo.modules.devlauncher.helpers.isValidColor
7
- import expo.modules.devlauncher.helpers.setProtectedDeclaredField
8
6
  import expo.modules.devlauncher.launcher.DevLauncherControllerInterface
9
- import expo.modules.devlauncher.launcher.configurators.DevLauncherExpoActivityConfigurator
10
- import expo.modules.devlauncher.launcher.manifest.DevLauncherUserInterface
11
- import expo.modules.devlauncher.launcher.manifest.DevLauncherManifest
7
+ import expo.modules.manifests.core.Manifest
12
8
 
13
9
  /**
14
10
  * Implementation of DevLauncherExpoAppLoader which is used for loading published projects served
15
11
  * remotely.
16
12
  */
17
13
  class DevLauncherPublishedAppLoader(
18
- manifest: DevLauncherManifest,
14
+ manifest: Manifest,
19
15
  private val localBundlePath: String,
20
16
  private val appHost: ReactNativeHost,
21
17
  context: Context,
@@ -100,7 +100,12 @@ class DevLauncherInternalModule(reactContext: ReactApplicationContext?)
100
100
 
101
101
  @ReactMethod
102
102
  fun getPendingDeepLink(promise: Promise) {
103
- promise.resolve(intentRegistry.intent?.data?.toString())
103
+ intentRegistry.intent?.data?.let {
104
+ promise.resolve(it.toString())
105
+ return
106
+ }
107
+
108
+ promise.resolve(intentRegistry.intent?.action)
104
109
  }
105
110
 
106
111
  private fun onNewPendingIntent(intent: Intent) {
@@ -15,7 +15,7 @@ class DevLauncherModule(reactContext: ReactApplicationContext?) : ReactContextBa
15
15
 
16
16
  override fun getConstants(): Map<String, Any?> {
17
17
  val manifestString = try {
18
- controller?.manifest?.rawData
18
+ controller?.manifest?.getRawJson().toString()
19
19
  } catch (_: IllegalStateException) {
20
20
  null
21
21
  }
@@ -7,7 +7,7 @@ import com.facebook.react.ReactActivityDelegate
7
7
  import com.facebook.react.ReactNativeHost
8
8
  import com.facebook.react.bridge.ReactContext
9
9
  import expo.modules.devlauncher.DevLauncherController
10
- import expo.modules.devlauncher.launcher.manifest.DevLauncherManifest
10
+ import expo.modules.manifests.core.Manifest
11
11
  import expo.modules.updatesinterface.UpdatesInterface
12
12
 
13
13
  interface DevLauncherControllerInterface {
@@ -21,7 +21,7 @@ interface DevLauncherControllerInterface {
21
21
  fun getCurrentReactActivityDelegate(activity: ReactActivity, delegateSupplierDevLauncher: DevLauncherReactActivityDelegateSupplier): ReactActivityDelegate
22
22
  fun handleIntent(intent: Intent?, activityToBeInvalidated: ReactActivity?): Boolean
23
23
 
24
- val manifest: DevLauncherManifest?
24
+ val manifest: Manifest?
25
25
  val manifestURL: Uri?
26
26
  val devClientHost: DevLauncherClientHost
27
27
  val mode: DevLauncherController.Mode
@@ -16,29 +16,29 @@ import com.facebook.react.ReactActivity
16
16
  import expo.modules.devlauncher.helpers.RGBAtoARGB
17
17
  import expo.modules.devlauncher.helpers.isValidColor
18
18
  import expo.modules.devlauncher.launcher.manifest.DevLauncherOrientation
19
- import expo.modules.devlauncher.launcher.manifest.DevLauncherManifest
20
19
  import expo.modules.devlauncher.launcher.manifest.DevLauncherStatusBarStyle
20
+ import expo.modules.manifests.core.Manifest
21
21
 
22
22
  class DevLauncherExpoActivityConfigurator(
23
- private var manifest: DevLauncherManifest,
23
+ private var manifest: Manifest,
24
24
  private val context: Context
25
25
  ) {
26
26
  fun applyTaskDescription(activity: Activity) {
27
- if (!isValidColor(manifest.primaryColor)) {
27
+ if (!isValidColor(manifest.getPrimaryColor())) {
28
28
  return
29
29
  }
30
- val color = Color.parseColor(manifest.primaryColor)
30
+ val color = Color.parseColor(manifest.getPrimaryColor())
31
31
  val icon = BitmapFactory.decodeResource(context.resources, context.applicationInfo.icon)
32
32
 
33
33
  activity.setTaskDescription(ActivityManager.TaskDescription(
34
- manifest.name,
34
+ manifest.getName(),
35
35
  icon,
36
36
  color
37
37
  ))
38
38
  }
39
39
 
40
40
  fun applyOrientation(activity: ReactActivity) {
41
- when (manifest.orientation) {
41
+ when (manifest.getOrientation()) {
42
42
  DevLauncherOrientation.DEFAULT -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
43
43
  DevLauncherOrientation.PORTRAIT -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
44
44
  DevLauncherOrientation.LANDSCAPE -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
@@ -47,10 +47,11 @@ class DevLauncherExpoActivityConfigurator(
47
47
  }
48
48
 
49
49
  fun applyStatusBarConfiguration(activity: ReactActivity) {
50
- val style = manifest.androidStatusBar?.barStyle
51
- val backgroundColor = manifest.androidStatusBar?.backgroundColor
52
- val translucent = manifest.androidStatusBar?.translucent ?: true
53
- val hidden = manifest.androidStatusBar?.hidden ?: false
50
+ val statusBarOptions = manifest.getAndroidStatusBarOptions()
51
+ val style = statusBarOptions?.optString("barStyle")
52
+ val backgroundColor = statusBarOptions?.optString("backgroundColor")
53
+ val translucent = statusBarOptions == null || statusBarOptions.optBoolean("translucent", true)
54
+ val hidden = statusBarOptions != null && statusBarOptions.optBoolean("hidden", false)
54
55
 
55
56
  activity.runOnUiThread {
56
57
  // clear android:windowTranslucentStatus flag from Window as RN achieves translucency using WindowInsets
@@ -80,7 +81,7 @@ class DevLauncherExpoActivityConfigurator(
80
81
  }
81
82
 
82
83
  @UiThread
83
- private fun setStyle(style: DevLauncherStatusBarStyle?, activity: Activity): DevLauncherStatusBarStyle {
84
+ private fun setStyle(style: String?, activity: Activity): String {
84
85
  var appliedStatusBarStyle = DevLauncherStatusBarStyle.LIGHT
85
86
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
86
87
  val decorView = activity.window.decorView
@@ -3,7 +3,9 @@ package expo.modules.devlauncher.launcher.manifest
3
3
  import android.net.Uri
4
4
  import expo.modules.devlauncher.helpers.await
5
5
  import expo.modules.devlauncher.helpers.fetch
6
+ import expo.modules.manifests.core.Manifest
6
7
  import okhttp3.OkHttpClient
8
+ import org.json.JSONObject
7
9
  import java.io.Reader
8
10
 
9
11
  class DevLauncherManifestParser(
@@ -28,9 +30,9 @@ class DevLauncherManifestParser(
28
30
  return response.body()!!.charStream()
29
31
  }
30
32
 
31
- suspend fun parseManifest(): DevLauncherManifest {
33
+ suspend fun parseManifest(): Manifest {
32
34
  downloadManifest().use {
33
- return DevLauncherManifest.fromJson(it)
35
+ return Manifest.fromManifestJson(JSONObject(it.readText()))
34
36
  }
35
37
  }
36
38
  }
@@ -1,33 +1,18 @@
1
1
  package expo.modules.devlauncher.launcher.manifest
2
2
 
3
- import com.google.gson.annotations.SerializedName
4
-
5
- enum class DevLauncherOrientation {
6
- @SerializedName("default")
7
- DEFAULT,
8
-
9
- @SerializedName("portrait")
10
- PORTRAIT,
11
-
12
- @SerializedName("landscape")
13
- LANDSCAPE
3
+ object DevLauncherOrientation {
4
+ const val DEFAULT = "default"
5
+ const val PORTRAIT = "portrait"
6
+ const val LANDSCAPE = "landscape"
14
7
  }
15
8
 
16
- enum class DevLauncherUserInterface {
17
- @SerializedName("automatic")
18
- AUTOMATIC,
19
-
20
- @SerializedName("dark")
21
- DARK,
22
-
23
- @SerializedName("light")
24
- LIGHT
9
+ object DevLauncherUserInterface {
10
+ const val AUTOMATIC = "automatic"
11
+ const val DARK = "dark"
12
+ const val LIGHT = "light"
25
13
  }
26
14
 
27
- enum class DevLauncherStatusBarStyle {
28
- @SerializedName("dark-content")
29
- DARK,
30
-
31
- @SerializedName("light-content")
32
- LIGHT
15
+ object DevLauncherStatusBarStyle {
16
+ const val DARK = "dark-content"
17
+ const val LIGHT = "light-content"
33
18
  }
@@ -26,10 +26,10 @@ private class LauncherDelegate(private val controller: DevLauncherControllerInte
26
26
 
27
27
  private class AppDelegate(private val controller: DevLauncherControllerInterface) : DevMenuDelegateInterface {
28
28
  override fun appInfo(): Bundle = Bundle().apply {
29
- putString("appName", controller.manifest?.name ?: "Development Client - App")
30
- putString("appVersion", controller.manifest?.version)
29
+ putString("appName", controller.manifest?.getName() ?: "Development Client - App")
30
+ putString("appVersion", controller.manifest?.getVersion())
31
31
  putString("appIcon", null)
32
- putString("hostUrl", controller.manifest?.hostUri
32
+ putString("hostUrl", controller.manifest?.getHostUri()
33
33
  ?: reactInstanceManager().devSupportManager?.sourceUrl)
34
34
  }
35
35
 
@@ -10,7 +10,7 @@ import com.facebook.react.bridge.ReactContext
10
10
  import expo.modules.devlauncher.launcher.DevLauncherClientHost
11
11
  import expo.modules.devlauncher.launcher.DevLauncherControllerInterface
12
12
  import expo.modules.devlauncher.launcher.DevLauncherReactActivityDelegateSupplier
13
- import expo.modules.devlauncher.launcher.manifest.DevLauncherManifest
13
+ import expo.modules.manifests.core.Manifest
14
14
  import expo.modules.updatesinterface.UpdatesInterface
15
15
 
16
16
  const val DEV_LAUNCHER_IS_NOT_AVAILABLE = "DevLauncher isn't available in release builds"
@@ -28,7 +28,7 @@ class DevLauncherController private constructor() : DevLauncherControllerInterfa
28
28
  override val devClientHost: DevLauncherClientHost
29
29
  get() = throw IllegalStateException(DEV_LAUNCHER_IS_NOT_AVAILABLE)
30
30
 
31
- override val manifest: DevLauncherManifest
31
+ override val manifest: Manifest
32
32
  get() = throw IllegalStateException(DEV_LAUNCHER_IS_NOT_AVAILABLE)
33
33
 
34
34
  override val manifestURL: Uri
@@ -0,0 +1,50 @@
1
+ package expo.modules.devlauncher
2
+
3
+ import android.content.Intent
4
+ import android.net.Uri
5
+ import androidx.test.core.app.ApplicationProvider
6
+ import com.facebook.react.ReactNativeHost
7
+ import com.google.common.truth.Truth
8
+ import expo.interfaces.devmenu.DevMenuManagerInterface
9
+ import expo.modules.devlauncher.koin.DevLauncherKoinContext
10
+ import expo.modules.devlauncher.tests.DevLauncherTestInterceptor
11
+ import io.mockk.mockk
12
+ import io.mockk.verify
13
+ import org.junit.Before
14
+ import org.junit.Test
15
+ import org.junit.runner.RunWith
16
+ import org.robolectric.RobolectricTestRunner
17
+
18
+ @RunWith(RobolectricTestRunner::class)
19
+ class DevLauncherControllerTest {
20
+
21
+ internal class DevLauncherTestInterceptorAllowReinitialization : DevLauncherTestInterceptor {
22
+ override fun allowReinitialization() = true
23
+ }
24
+
25
+ @Before
26
+ fun setup() {
27
+ val reactNativeHost = mockk<ReactNativeHost>(relaxed = true)
28
+ DevLauncherKoinContext.reinitialize()
29
+ DevLauncherKoinContext.app.koin.declare(DevLauncherTestInterceptorAllowReinitialization())
30
+ DevLauncherController.initialize(ApplicationProvider.getApplicationContext(), reactNativeHost)
31
+ }
32
+
33
+ @Test
34
+ fun `sets shouldAutoLaunch on dev menu manager`() {
35
+ val controller = DevLauncherController.instance as DevLauncherController
36
+ val mockDevMenuManager = mockk<DevMenuManagerInterface>(relaxed = true)
37
+ controller.devMenuManager = mockDevMenuManager
38
+ Truth.assertThat(controller.canLaunchDevMenuOnStart).isTrue()
39
+
40
+ controller.handleIntent(Intent().apply {
41
+ data = Uri.parse("https://expo-development-client")
42
+ putExtra("EXDevMenuDisableAutoLaunch", true)
43
+ }, null)
44
+
45
+ verify {
46
+ mockDevMenuManager.setCanLaunchDevMenuOnStart(false)
47
+ }
48
+ Truth.assertThat(controller.canLaunchDevMenuOnStart).isFalse()
49
+ }
50
+ }
@@ -0,0 +1,43 @@
1
+ package expo.modules.devlauncher.launcher
2
+
3
+ import android.net.Uri
4
+ import androidx.test.core.app.ApplicationProvider
5
+ import com.google.common.truth.Truth
6
+ import io.mockk.every
7
+ import io.mockk.mockkObject
8
+ import io.mockk.unmockkAll
9
+ import org.junit.Test
10
+ import org.junit.runner.RunWith
11
+ import org.robolectric.RobolectricTestRunner
12
+
13
+ @RunWith(RobolectricTestRunner::class)
14
+ internal class DevLauncherRecentlyOpenedAppsRegistryTest {
15
+ @Test
16
+ fun `adds app to registry`() {
17
+ val urlString = "http://localhost:8081"
18
+ val registry = DevLauncherRecentlyOpenedAppsRegistry(ApplicationProvider.getApplicationContext())
19
+ registry.appWasOpened(Uri.parse(urlString), "test-app")
20
+ val apps = registry.getRecentlyOpenedApps()
21
+ Truth.assertThat(apps[urlString]).isEqualTo("test-app")
22
+ }
23
+
24
+ @Test
25
+ fun `removes app from registry after 3 days have elapsed`() {
26
+ val currentTime = System.currentTimeMillis()
27
+ val time3DaysAnd1SecondAgo = currentTime - ((1000 * 60 * 60 * 24 * 3) + 1000)
28
+
29
+ mockkObject(DevLauncherRecentlyOpenedAppsRegistry.TimeHelper)
30
+ every {
31
+ DevLauncherRecentlyOpenedAppsRegistry.TimeHelper.getCurrentTime()
32
+ } returns time3DaysAnd1SecondAgo
33
+
34
+ val urlString = "http://localhost:8081"
35
+ val registry = DevLauncherRecentlyOpenedAppsRegistry(ApplicationProvider.getApplicationContext())
36
+ registry.appWasOpened(Uri.parse(urlString), "test-app")
37
+
38
+ unmockkAll()
39
+
40
+ val apps = registry.getRecentlyOpenedApps()
41
+ Truth.assertThat(apps[urlString]).isNull()
42
+ }
43
+ }
@@ -8,8 +8,8 @@ import com.google.common.truth.Truth
8
8
  import expo.modules.devlauncher.helpers.loadUpdate
9
9
  import expo.modules.devlauncher.koin.DevLauncherKoinContext
10
10
  import expo.modules.devlauncher.launcher.DevLauncherControllerInterface
11
- import expo.modules.devlauncher.launcher.manifest.DevLauncherManifest
12
11
  import expo.modules.devlauncher.launcher.manifest.DevLauncherManifestParser
12
+ import expo.modules.manifests.core.Manifest
13
13
  import expo.modules.updatesinterface.UpdatesInterface
14
14
  import io.mockk.coEvery
15
15
  import io.mockk.mockk
@@ -64,7 +64,7 @@ internal class DevLauncherAppLoaderFactoryTest {
64
64
  val appLoaderFactory = DevLauncherAppLoaderFactory()
65
65
 
66
66
  val manifestParser = mockk<DevLauncherManifestParser>()
67
- val manifest = DevLauncherManifest.fromJson(developmentManifestJSONString.reader())
67
+ val manifest = Manifest.fromManifestJson(JSONObject(developmentManifestJSONString))
68
68
  coEvery { manifestParser.isManifestUrl() } returns true
69
69
  coEvery { manifestParser.parseManifest() } returns manifest
70
70
 
@@ -78,7 +78,7 @@ internal class DevLauncherAppLoaderFactoryTest {
78
78
  val appLoaderFactory = DevLauncherAppLoaderFactory()
79
79
 
80
80
  val manifestParser = mockk<DevLauncherManifestParser>()
81
- val manifest = DevLauncherManifest.fromJson(publishedManifestJSONString.reader())
81
+ val manifest = Manifest.fromManifestJson(JSONObject(publishedManifestJSONString))
82
82
  coEvery { manifestParser.isManifestUrl() } returns true
83
83
  coEvery { manifestParser.parseManifest() } returns manifest
84
84
 
@@ -0,0 +1,38 @@
1
+ package expo.modules.devlauncher.modules
2
+
3
+ import android.net.Uri
4
+ import com.facebook.react.bridge.ReactApplicationContext
5
+ import com.google.common.truth.Truth
6
+ import expo.modules.devlauncher.koin.DevLauncherKoinContext
7
+ import expo.modules.devlauncher.launcher.DevLauncherControllerInterface
8
+ import io.mockk.every
9
+ import io.mockk.mockk
10
+ import org.junit.Test
11
+ import org.junit.runner.RunWith
12
+ import org.koin.dsl.module
13
+ import org.robolectric.RobolectricTestRunner
14
+
15
+ @RunWith(RobolectricTestRunner::class)
16
+ class DevLauncherModuleTest {
17
+
18
+ @Test
19
+ fun `exports manifestURL for currently loaded app`() {
20
+ // used by snack
21
+
22
+ val urlString = "https://exp.host/@test/test?query=param"
23
+ val manifestURL = Uri.parse(urlString)
24
+
25
+ val devLauncherController = mockk<DevLauncherControllerInterface>(relaxed = true)
26
+ DevLauncherKoinContext.app.koin.loadModules(listOf(
27
+ module {
28
+ single { devLauncherController }
29
+ }
30
+ ))
31
+ every { devLauncherController.manifestURL } returns manifestURL
32
+
33
+ val reactApplicationContext = mockk<ReactApplicationContext>(relaxed = true)
34
+ val module = DevLauncherModule(reactApplicationContext)
35
+ val constants = module.constants
36
+ Truth.assertThat(constants["manifestURL"]).isEqualTo(urlString)
37
+ }
38
+ }
@@ -1,4 +1,5 @@
1
1
  let isErrorHandlingEnabled = true;
2
+ let wasHit = false; // whether the original error handler was called
2
3
  const unavailableErrorPossibleSolutions = `Some possible solutions:
3
4
  - Make sure that the method is available on the current platform.
4
5
  - Make sure you're using the newest available version of this development client.
@@ -32,8 +33,14 @@ function customizeError(error) {
32
33
  }
33
34
  function errorHandler(originalHandler, error, isFatal) {
34
35
  if (error instanceof Error) {
36
+ // Suppresses `"main" has not been registered` error only if it was caused by a different error.
37
+ // Otherwise, we want to show it, cause the user may forget to call `AppRegistry.registerComponent`.
38
+ if (wasHit && error.message?.includes('has not been registered. This can happen if')) {
39
+ return;
40
+ }
35
41
  customizeError(error);
36
42
  }
43
+ wasHit = true;
37
44
  originalHandler(error, isFatal);
38
45
  }
39
46
  export function createErrorHandler(originalHandler) {
@@ -1 +1 @@
1
- {"version":3,"file":"DevLauncherErrorManager.js","sourceRoot":"","sources":["../src/DevLauncherErrorManager.ts"],"names":[],"mappings":"AAGA,IAAI,sBAAsB,GAAG,IAAI,CAAC;AAElC,MAAM,iCAAiC,GAAG;;;;mGAIyD,CAAC;AAEpG,MAAM,gCAAgC,GAAG;;;mGAG0D,CAAC;AAEpG,SAAS,2BAA2B,CAAC,KAAiB;IACpD,KAAK,CAAC,OAAO,IAAI,MAAM,GAAG,iCAAiC,CAAC;AAC9D,CAAC;AAED,SAAS,+BAA+B,CAAC,KAAY;IACnD,KAAK,CAAC,OAAO,GAAG;;EAEhB,gCAAgC,EAAE,CAAC;AACrC,CAAC;AAED,SAAS,cAAc,CAAC,KAAyB;IAC/C,IAAI,MAAM,IAAI,KAAK,EAAE;QACnB,sCAAsC;QACtC,QAAQ,KAAK,CAAC,IAAI,EAAE;YAClB,KAAK,iBAAiB,CAAC,CAAC;gBACtB,2BAA2B,CAAC,KAAK,CAAC,CAAC;gBACnC,MAAM;aACP;SACF;KACF;SAAM,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,8BAA8B,CAAC,EAAE;QACjE,+BAA+B,CAAC,KAAK,CAAC,CAAC;KACxC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,eAAe,EAAE,KAAK,EAAE,OAAO;IACnD,IAAI,KAAK,YAAY,KAAK,EAAE;QAC1B,cAAc,CAAC,KAAK,CAAC,CAAC;KACvB;IAED,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,eAAe;IAChD,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QACxB,IAAI,sBAAsB,EAAE;YAC1B,YAAY,CAAC,eAAe,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;YAC9C,OAAO;SACR;QAED,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAClC,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,sBAAsB,GAAG,KAAK,CAAC;AACjC,CAAC","sourcesContent":["// Similar interface to the one used in expo modules.\ntype CodedError = Error & { code?: string };\n\nlet isErrorHandlingEnabled = true;\n\nconst unavailableErrorPossibleSolutions = `Some possible solutions:\n- Make sure that the method is available on the current platform.\n- Make sure you're using the newest available version of this development client.\n- Make sure you're running a compatible version of your JavaScript code.\n- If you've installed a new library recently, you may need to make a new development client build.`;\n\nconst moduleIsMissingPossibleSolutions = `Some possible solutions:\n- Make sure you're using the newest available version of this development client.\n- Make sure you're running a compatible version of your JavaScript code.\n- If you've installed a new library recently, you may need to make a new development client build.`;\n\nfunction customizeUnavailableMessage(error: CodedError) {\n error.message += '\\n\\n' + unavailableErrorPossibleSolutions;\n}\n\nfunction customizeModuleIsMissingMessage(error: Error) {\n error.message = `Your JavaScript code tried to access a native module that doesn't exist in this development client. \n\n${moduleIsMissingPossibleSolutions}`;\n}\n\nfunction customizeError(error: Error | CodedError) {\n if ('code' in error) {\n // It's a CodedError from expo modules\n switch (error.code) {\n case 'ERR_UNAVAILABLE': {\n customizeUnavailableMessage(error);\n break;\n }\n }\n } else if (error.message.includes('Native module cannot be null')) {\n customizeModuleIsMissingMessage(error);\n }\n}\n\nfunction errorHandler(originalHandler, error, isFatal) {\n if (error instanceof Error) {\n customizeError(error);\n }\n\n originalHandler(error, isFatal);\n}\n\nexport function createErrorHandler(originalHandler) {\n return (error, isFatal) => {\n if (isErrorHandlingEnabled) {\n errorHandler(originalHandler, error, isFatal);\n return;\n }\n\n originalHandler(error, isFatal);\n };\n}\n\nexport function disableErrorHandling() {\n isErrorHandlingEnabled = false;\n}\n"]}
1
+ {"version":3,"file":"DevLauncherErrorManager.js","sourceRoot":"","sources":["../src/DevLauncherErrorManager.ts"],"names":[],"mappings":"AAGA,IAAI,sBAAsB,GAAG,IAAI,CAAC;AAClC,IAAI,MAAM,GAAG,KAAK,CAAC,CAAC,gDAAgD;AAEpE,MAAM,iCAAiC,GAAG;;;;mGAIyD,CAAC;AAEpG,MAAM,gCAAgC,GAAG;;;mGAG0D,CAAC;AAEpG,SAAS,2BAA2B,CAAC,KAAiB;IACpD,KAAK,CAAC,OAAO,IAAI,MAAM,GAAG,iCAAiC,CAAC;AAC9D,CAAC;AAED,SAAS,+BAA+B,CAAC,KAAY;IACnD,KAAK,CAAC,OAAO,GAAG;;EAEhB,gCAAgC,EAAE,CAAC;AACrC,CAAC;AAED,SAAS,cAAc,CAAC,KAAyB;IAC/C,IAAI,MAAM,IAAI,KAAK,EAAE;QACnB,sCAAsC;QACtC,QAAQ,KAAK,CAAC,IAAI,EAAE;YAClB,KAAK,iBAAiB,CAAC,CAAC;gBACtB,2BAA2B,CAAC,KAAK,CAAC,CAAC;gBACnC,MAAM;aACP;SACF;KACF;SAAM,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,8BAA8B,CAAC,EAAE;QACjE,+BAA+B,CAAC,KAAK,CAAC,CAAC;KACxC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,eAAe,EAAE,KAAK,EAAE,OAAO;IACnD,IAAI,KAAK,YAAY,KAAK,EAAE;QAC1B,gGAAgG;QAChG,oGAAoG;QACpG,IAAI,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,6CAA6C,CAAC,EAAE;YACpF,OAAO;SACR;QACD,cAAc,CAAC,KAAK,CAAC,CAAC;KACvB;IAED,MAAM,GAAG,IAAI,CAAC;IACd,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,eAAe;IAChD,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QACxB,IAAI,sBAAsB,EAAE;YAC1B,YAAY,CAAC,eAAe,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;YAC9C,OAAO;SACR;QAED,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAClC,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,sBAAsB,GAAG,KAAK,CAAC;AACjC,CAAC","sourcesContent":["// Similar interface to the one used in expo modules.\ntype CodedError = Error & { code?: string };\n\nlet isErrorHandlingEnabled = true;\nlet wasHit = false; // whether the original error handler was called\n\nconst unavailableErrorPossibleSolutions = `Some possible solutions:\n- Make sure that the method is available on the current platform.\n- Make sure you're using the newest available version of this development client.\n- Make sure you're running a compatible version of your JavaScript code.\n- If you've installed a new library recently, you may need to make a new development client build.`;\n\nconst moduleIsMissingPossibleSolutions = `Some possible solutions:\n- Make sure you're using the newest available version of this development client.\n- Make sure you're running a compatible version of your JavaScript code.\n- If you've installed a new library recently, you may need to make a new development client build.`;\n\nfunction customizeUnavailableMessage(error: CodedError) {\n error.message += '\\n\\n' + unavailableErrorPossibleSolutions;\n}\n\nfunction customizeModuleIsMissingMessage(error: Error) {\n error.message = `Your JavaScript code tried to access a native module that doesn't exist in this development client. \n\n${moduleIsMissingPossibleSolutions}`;\n}\n\nfunction customizeError(error: Error | CodedError) {\n if ('code' in error) {\n // It's a CodedError from expo modules\n switch (error.code) {\n case 'ERR_UNAVAILABLE': {\n customizeUnavailableMessage(error);\n break;\n }\n }\n } else if (error.message.includes('Native module cannot be null')) {\n customizeModuleIsMissingMessage(error);\n }\n}\n\nfunction errorHandler(originalHandler, error, isFatal) {\n if (error instanceof Error) {\n // Suppresses `\"main\" has not been registered` error only if it was caused by a different error.\n // Otherwise, we want to show it, cause the user may forget to call `AppRegistry.registerComponent`.\n if (wasHit && error.message?.includes('has not been registered. This can happen if')) {\n return;\n }\n customizeError(error);\n }\n\n wasHit = true;\n originalHandler(error, isFatal);\n}\n\nexport function createErrorHandler(originalHandler) {\n return (error, isFatal) => {\n if (isErrorHandlingEnabled) {\n errorHandler(originalHandler, error, isFatal);\n return;\n }\n\n originalHandler(error, isFatal);\n };\n}\n\nexport function disableErrorHandling() {\n isErrorHandlingEnabled = false;\n}\n"]}
@@ -13,6 +13,7 @@ Pod::Spec.new do |s|
13
13
  s.platform = :ios, '11.0'
14
14
  s.swift_version = '5.2'
15
15
  s.source = { :git => 'https://github.com/github_account/expo-development-client.git', :tag => "#{s.version}" }
16
+ s.static_framework = true
16
17
  s.source_files = 'ios/**/*.{h,m,swift,cpp}'
17
18
  s.preserve_paths = 'ios/**/*.{h,m,swift}'
18
19
  s.exclude_files = ['ios/Unsafe/**/*.{h,m,mm,swift,cpp}', 'ios/Tests/**/*.{h,m,swift}']
@@ -37,6 +38,7 @@ Pod::Spec.new do |s|
37
38
 
38
39
  s.dependency "React-Core"
39
40
  s.dependency "expo-dev-menu-interface"
41
+ s.dependency "EXManifests"
40
42
  s.dependency "EXUpdatesInterface"
41
43
 
42
44
  s.subspec 'Unsafe' do |unsafe|
@@ -3,7 +3,12 @@
3
3
  #import "EXDevLauncher.h"
4
4
  #import "EXDevLauncherController.h"
5
5
 
6
+ #if __has_include(<EXDevLauncher/EXDevLauncher-Swift.h>)
7
+ // For cocoapods framework, the generated swift header will be inside EXDevLauncher module
8
+ #import <EXDevLauncher/EXDevLauncher-Swift.h>
9
+ #else
6
10
  #import <EXDevLauncher-Swift.h>
11
+ #endif
7
12
 
8
13
  @implementation EXDevLauncher
9
14
 
@@ -16,10 +21,11 @@ RCT_EXPORT_MODULE()
16
21
 
17
22
  - (NSDictionary *)constantsToExport
18
23
  {
19
- NSString *manifestString = [EXDevLauncherController.sharedInstance appManifest].rawData;
24
+ NSDictionary *rawManifestJSON = [EXDevLauncherController.sharedInstance appManifest].rawManifestJSON;
25
+ NSData *manifestStringData = rawManifestJSON ? [NSJSONSerialization dataWithJSONObject:rawManifestJSON options:kNilOptions error:NULL] : nil;
20
26
  NSString *manifestURLString = [EXDevLauncherController.sharedInstance appManifestURL].absoluteString;
21
27
  return @{
22
- @"manifestString": manifestString ?: [NSNull null],
28
+ @"manifestString": manifestStringData ? [[NSString alloc] initWithData:manifestStringData encoding:NSUTF8StringEncoding] : [NSNull null],
23
29
  @"manifestURL": manifestURLString ?: [NSNull null]
24
30
  };
25
31
  }
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Workspace
3
+ version = "1.0">
4
+ <FileRef
5
+ location = "self:">
6
+ </FileRef>
7
+ </Workspace>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>IDEDidComputeMac32BitWarning</key>
6
+ <true/>
7
+ </dict>
8
+ </plist>