expo-dev-launcher 5.1.5 → 5.1.6
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/CHANGELOG.md +7 -0
- package/android/build.gradle +2 -2
- package/android/src/debug/java/expo/modules/devlauncher/launcher/DevLauncherActivity.kt +3 -0
- package/android/src/debug/java/expo/modules/devlauncher/launcher/errors/DevLauncherErrorActivity.kt +21 -0
- package/android/src/main/res/layout/error_fragment.xml +2 -0
- package/android/src/main/res/values/styles.xml +12 -1
- package/ios/DevLauncherAuth.swift +30 -15
- package/ios/EXDevLauncherURLHelper.swift +32 -30
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,13 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 5.1.6 — 2025-04-25
|
|
14
|
+
|
|
15
|
+
### 💡 Others
|
|
16
|
+
|
|
17
|
+
- [iOS] Remove usage of deprecated `SFAuthenticationSession` for user login. ([#36395](https://github.com/expo/expo/pull/36395) by [@alanjhughes](https://github.com/alanjhughes))
|
|
18
|
+
- [Android] Enable edge-to-edge. ([#36363](https://github.com/expo/expo/pull/36363) by [@behenate](https://github.com/behenate))
|
|
19
|
+
|
|
13
20
|
## 5.1.5 — 2025-04-23
|
|
14
21
|
|
|
15
22
|
### 🐛 Bug fixes
|
package/android/build.gradle
CHANGED
|
@@ -8,13 +8,13 @@ expoModule {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
group = "host.exp.exponent"
|
|
11
|
-
version = "5.1.
|
|
11
|
+
version = "5.1.6"
|
|
12
12
|
|
|
13
13
|
android {
|
|
14
14
|
namespace "expo.modules.devlauncher"
|
|
15
15
|
defaultConfig {
|
|
16
16
|
versionCode 9
|
|
17
|
-
versionName "5.1.
|
|
17
|
+
versionName "5.1.6"
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
buildTypes {
|
|
@@ -6,6 +6,7 @@ import android.view.KeyEvent
|
|
|
6
6
|
import android.view.MotionEvent
|
|
7
7
|
import android.view.View
|
|
8
8
|
import android.view.ViewGroup
|
|
9
|
+
import androidx.core.view.WindowCompat
|
|
9
10
|
import com.facebook.react.ReactActivity
|
|
10
11
|
import com.facebook.react.ReactActivityDelegate
|
|
11
12
|
import com.facebook.react.ReactInstanceEventListener
|
|
@@ -51,6 +52,8 @@ class DevLauncherActivity : ReactActivity(), ReactInstanceEventListener, DevLaun
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
55
|
+
// Enables edge-to-edge
|
|
56
|
+
WindowCompat.setDecorFitsSystemWindows(window, false)
|
|
54
57
|
super.onCreate(savedInstanceState)
|
|
55
58
|
|
|
56
59
|
contentView = findViewById(android.R.id.content) ?: return
|
package/android/src/debug/java/expo/modules/devlauncher/launcher/errors/DevLauncherErrorActivity.kt
CHANGED
|
@@ -4,6 +4,11 @@ import android.app.Activity
|
|
|
4
4
|
import android.content.Context
|
|
5
5
|
import android.content.Intent
|
|
6
6
|
import android.os.Bundle
|
|
7
|
+
import android.widget.LinearLayout
|
|
8
|
+
import androidx.core.view.ViewCompat
|
|
9
|
+
import androidx.core.view.WindowCompat
|
|
10
|
+
import androidx.core.view.WindowInsetsCompat
|
|
11
|
+
import androidx.core.view.updatePadding
|
|
7
12
|
import androidx.fragment.app.FragmentActivity
|
|
8
13
|
import com.facebook.react.ReactActivity
|
|
9
14
|
import expo.modules.devlauncher.databinding.ErrorFragmentBinding
|
|
@@ -21,12 +26,28 @@ class DevLauncherErrorActivity :
|
|
|
21
26
|
private val adapter = DevLauncherStackAdapter(this, null)
|
|
22
27
|
|
|
23
28
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
29
|
+
// Enables edge-to-edge
|
|
30
|
+
WindowCompat.setDecorFitsSystemWindows(window, false)
|
|
24
31
|
super.onCreate(savedInstanceState)
|
|
25
32
|
|
|
26
33
|
binding = ErrorFragmentBinding.inflate(layoutInflater)
|
|
27
34
|
binding.homeButton.setOnClickListener { this.launchHome() }
|
|
28
35
|
binding.reloadButton.setOnClickListener { this.reload() }
|
|
29
36
|
|
|
37
|
+
// Set footer padding to avoid the navigation bar
|
|
38
|
+
ViewCompat.setOnApplyWindowInsetsListener(binding.errorFooterContent) { view, windowInsets ->
|
|
39
|
+
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
|
40
|
+
view.updatePadding(bottom = insets.bottom)
|
|
41
|
+
WindowInsetsCompat.CONSUMED
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Set title padding to account for status bar
|
|
45
|
+
ViewCompat.setOnApplyWindowInsetsListener(binding.errorTitle) { view, windowInsets ->
|
|
46
|
+
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
|
47
|
+
view.updatePadding(top = insets.top)
|
|
48
|
+
WindowInsetsCompat.CONSUMED
|
|
49
|
+
}
|
|
50
|
+
|
|
30
51
|
synchronized(DevLauncherErrorActivity) {
|
|
31
52
|
val error = currentError
|
|
32
53
|
if (error != null) {
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
android:weightSum="1">
|
|
21
21
|
|
|
22
22
|
<TextView
|
|
23
|
+
android:id="@+id/error_title"
|
|
23
24
|
android:layout_width="wrap_content"
|
|
24
25
|
android:layout_height="wrap_content"
|
|
25
26
|
android:tag="DevLauncherErrorScreen"
|
|
@@ -62,6 +63,7 @@
|
|
|
62
63
|
android:layout_alignParentBottom="true">
|
|
63
64
|
|
|
64
65
|
<LinearLayout
|
|
66
|
+
android:id="@+id/error_footer_content"
|
|
65
67
|
android:layout_width="match_parent"
|
|
66
68
|
android:layout_height="wrap_content"
|
|
67
69
|
android:background="@color/dev_launcher_white"
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
-
<resources>
|
|
2
|
+
<resources xmlns:tools="http://schemas.android.com/tools">
|
|
3
3
|
<style name="Theme.DevLauncher.LauncherActivity" parent="Theme.AppCompat.Light.NoActionBar">
|
|
4
|
+
<item name="android:navigationBarColor">@android:color/transparent</item>
|
|
5
|
+
<item name="android:statusBarColor">@android:color/transparent</item>
|
|
6
|
+
<item name="android:windowLightStatusBar">true</item>
|
|
7
|
+
<item name="android:enforceNavigationBarContrast" tools:targetApi="q">false</item>
|
|
8
|
+
<item name="android:windowLightNavigationBar" tools:targetApi="o_mr1">true</item>
|
|
9
|
+
|
|
4
10
|
<item name="android:windowContentOverlay">@null</item>
|
|
5
11
|
<item name="android:windowNoTitle">true</item>
|
|
6
12
|
<item name="android:windowIsFloating">false</item>
|
|
@@ -9,7 +15,12 @@
|
|
|
9
15
|
</style>
|
|
10
16
|
|
|
11
17
|
<style name="Theme.DevLauncher.ErrorActivity" parent="Theme.AppCompat.Light.NoActionBar">
|
|
18
|
+
<item name="android:navigationBarColor">@android:color/transparent</item>
|
|
12
19
|
<item name="android:statusBarColor">@android:color/transparent</item>
|
|
20
|
+
<item name="android:windowLightStatusBar">true</item>
|
|
21
|
+
<item name="android:enforceNavigationBarContrast" tools:targetApi="q">false</item>
|
|
22
|
+
<item name="android:windowLightNavigationBar" tools:targetApi="o_mr1">true</item>
|
|
23
|
+
|
|
13
24
|
<item name="colorPrimary">@color/dev_launcher_primary</item>
|
|
14
25
|
<item name="colorPrimaryDark">@color/dev_launcher_colorPrimaryDark</item>
|
|
15
26
|
<item name="colorAccent">@color/dev_launcher_colorAccentDark</item>
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
2
|
|
|
3
3
|
import ExpoModulesCore
|
|
4
|
-
import
|
|
5
|
-
import React
|
|
4
|
+
import AuthenticationServices
|
|
6
5
|
|
|
7
6
|
private let DEV_LAUNCHER_DEFAULT_SCHEME = "expo-dev-launcher"
|
|
8
7
|
|
|
9
8
|
public class DevLauncherAuth: Module {
|
|
10
9
|
private var redirectPromise: Promise?
|
|
11
|
-
private var authSession:
|
|
10
|
+
private var authSession: ASWebAuthenticationSession?
|
|
11
|
+
private let presentationContext = DevLauncherAuthPresentationContext()
|
|
12
12
|
|
|
13
13
|
public func definition() -> ModuleDefinition {
|
|
14
14
|
Name("ExpoDevLauncherAuth")
|
|
15
15
|
|
|
16
|
-
AsyncFunction("openAuthSessionAsync") { (authURL: URL,
|
|
16
|
+
AsyncFunction("openAuthSessionAsync") { (authURL: URL, _: String, promise: Promise) in
|
|
17
17
|
if self.redirectPromise != nil {
|
|
18
18
|
throw WebBrowserAlreadyPresentedException()
|
|
19
19
|
}
|
|
@@ -34,20 +34,18 @@ public class DevLauncherAuth: Module {
|
|
|
34
34
|
self?.flowDidFinish()
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
// With ASWebAuthenticationSession, all that is required for the callbackURLScheme is the scheme defined in CFBundleURLSchemes.
|
|
38
|
+
// ://auth is not necessary.
|
|
39
|
+
let scheme = getAuthScheme()
|
|
40
|
+
|
|
41
|
+
self.authSession = ASWebAuthenticationSession(url: authURL, callbackURLScheme: scheme, completionHandler: completionHandler)
|
|
42
|
+
self.authSession?.presentationContextProvider = presentationContext
|
|
43
|
+
self.authSession?.prefersEphemeralWebBrowserSession = true
|
|
38
44
|
self.authSession?.start()
|
|
39
|
-
}
|
|
45
|
+
}.runOnQueue(.main)
|
|
40
46
|
|
|
41
47
|
AsyncFunction("getAuthSchemeAsync") { () -> String in
|
|
42
|
-
|
|
43
|
-
for urlType in urlTypes {
|
|
44
|
-
if let schemes = urlType["CFBundleURLSchemes"] as? [String], !schemes.isEmpty {
|
|
45
|
-
return schemes[0]
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return DEV_LAUNCHER_DEFAULT_SCHEME
|
|
48
|
+
getAuthScheme()
|
|
51
49
|
}
|
|
52
50
|
|
|
53
51
|
AsyncFunction("setSessionAsync") { (session: String?) in
|
|
@@ -59,7 +57,24 @@ public class DevLauncherAuth: Module {
|
|
|
59
57
|
}
|
|
60
58
|
}
|
|
61
59
|
|
|
60
|
+
private func getAuthScheme() -> String {
|
|
61
|
+
guard let urlTypes = Bundle.main.object(forInfoDictionaryKey: "CFBundleURLTypes") as? [[String: Any]] else {
|
|
62
|
+
return DEV_LAUNCHER_DEFAULT_SCHEME
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return urlTypes.compactMap { urlType in
|
|
66
|
+
(urlType["CFBundleURLSchemes"] as? [String])?.first
|
|
67
|
+
}.first ?? DEV_LAUNCHER_DEFAULT_SCHEME
|
|
68
|
+
}
|
|
69
|
+
|
|
62
70
|
private func flowDidFinish() {
|
|
63
71
|
self.redirectPromise = nil
|
|
64
72
|
}
|
|
65
73
|
}
|
|
74
|
+
|
|
75
|
+
private class DevLauncherAuthPresentationContext: NSObject, ASWebAuthenticationPresentationContextProviding {
|
|
76
|
+
func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
|
|
77
|
+
let window = UIApplication.shared.windows.first { $0.isKeyWindow }
|
|
78
|
+
return window ?? ASPresentationAnchor()
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -14,16 +14,13 @@ public class EXDevLauncherUrl: NSObject {
|
|
|
14
14
|
@objc
|
|
15
15
|
public init(_ url: URL) {
|
|
16
16
|
self.queryParams = EXDevLauncherURLHelper.getQueryParamsForUrl(url)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
self.url = EXDevLauncherURLHelper.replaceEXPScheme(urlFromParam, to: "http")
|
|
23
|
-
}
|
|
24
|
-
}
|
|
17
|
+
|
|
18
|
+
if EXDevLauncherURLHelper.isDevLauncherURL(url),
|
|
19
|
+
let urlParam = queryParams["url"],
|
|
20
|
+
let urlFromParam = URL(string: urlParam) {
|
|
21
|
+
self.url = EXDevLauncherURLHelper.replaceEXPScheme(urlFromParam, to: "http")
|
|
25
22
|
} else {
|
|
26
|
-
self.url = EXDevLauncherURLHelper.replaceEXPScheme(
|
|
23
|
+
self.url = EXDevLauncherURLHelper.replaceEXPScheme(url, to: "http")
|
|
27
24
|
}
|
|
28
25
|
|
|
29
26
|
super.init()
|
|
@@ -39,48 +36,53 @@ public class EXDevLauncherURLHelper: NSObject {
|
|
|
39
36
|
|
|
40
37
|
@objc
|
|
41
38
|
public static func hasUrlQueryParam(_ url: URL) -> Bool {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (components?.queryItems?.contains(where: {
|
|
47
|
-
$0.name == "url" && $0.value != nil
|
|
48
|
-
})) ?? false {
|
|
49
|
-
hasUrlQueryParam = true
|
|
39
|
+
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
|
|
40
|
+
let queryItems = components.queryItems else {
|
|
41
|
+
return false
|
|
50
42
|
}
|
|
51
43
|
|
|
52
|
-
return
|
|
44
|
+
return queryItems.contains { $0.name == "url" && $0.value != nil }
|
|
53
45
|
}
|
|
54
46
|
|
|
55
47
|
@objc
|
|
56
48
|
public static func disableOnboardingPopupIfNeeded(_ url: URL) {
|
|
57
|
-
let components = URLComponents
|
|
49
|
+
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
|
|
50
|
+
let queryItems = components.queryItems else {
|
|
51
|
+
return
|
|
52
|
+
}
|
|
58
53
|
|
|
59
|
-
|
|
54
|
+
let shouldDisable = queryItems.contains {
|
|
60
55
|
$0.name == "disableOnboarding" && ($0.value ?? "") == "1"
|
|
61
|
-
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if shouldDisable {
|
|
62
59
|
DevMenuPreferences.isOnboardingFinished = true
|
|
63
60
|
}
|
|
64
61
|
}
|
|
65
62
|
|
|
66
63
|
@objc
|
|
67
64
|
public static func replaceEXPScheme(_ url: URL, to scheme: String) -> URL {
|
|
68
|
-
var components = URLComponents(url: url, resolvingAgainstBaseURL: false)
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
guard var components = URLComponents(url: url, resolvingAgainstBaseURL: false),
|
|
66
|
+
components.scheme == "exp" else {
|
|
67
|
+
return url
|
|
71
68
|
}
|
|
72
|
-
|
|
69
|
+
|
|
70
|
+
components.scheme = scheme
|
|
71
|
+
return components.url ?? url
|
|
73
72
|
}
|
|
74
73
|
|
|
75
74
|
@objc
|
|
76
75
|
public static func getQueryParamsForUrl(_ url: URL) -> [String: String] {
|
|
77
|
-
let components = URLComponents
|
|
78
|
-
|
|
76
|
+
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
|
|
77
|
+
let queryItems = components.queryItems else {
|
|
78
|
+
return [:]
|
|
79
|
+
}
|
|
79
80
|
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
var params: [String: String] = [:]
|
|
82
|
+
for item in queryItems {
|
|
83
|
+
params[item.name] = item.value?.removingPercentEncoding ?? ""
|
|
82
84
|
}
|
|
83
85
|
|
|
84
|
-
return
|
|
86
|
+
return params
|
|
85
87
|
}
|
|
86
88
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-dev-launcher",
|
|
3
3
|
"title": "Expo Development Launcher",
|
|
4
|
-
"version": "5.1.
|
|
4
|
+
"version": "5.1.6",
|
|
5
5
|
"description": "Pre-release version of the Expo development launcher package for testing.",
|
|
6
6
|
"main": "build/DevLauncher.js",
|
|
7
7
|
"types": "build/DevLauncher.d.ts",
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"homepage": "https://docs.expo.dev",
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"ajv": "8.11.0",
|
|
34
|
-
"expo-dev-menu": "6.1.
|
|
35
|
-
"expo-manifests": "~0.16.
|
|
34
|
+
"expo-dev-menu": "6.1.6",
|
|
35
|
+
"expo-manifests": "~0.16.3",
|
|
36
36
|
"resolve-from": "^5.0.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
@@ -43,10 +43,10 @@
|
|
|
43
43
|
"@testing-library/jest-native": "^4.0.4",
|
|
44
44
|
"@testing-library/react-native": "^13.1.0",
|
|
45
45
|
"babel-plugin-module-resolver": "^5.0.0",
|
|
46
|
-
"babel-preset-expo": "~13.1.
|
|
46
|
+
"babel-preset-expo": "~13.1.7",
|
|
47
47
|
"date-fns": "^2.28.0",
|
|
48
|
-
"expo-dev-client-components": "2.1.
|
|
49
|
-
"expo-module-scripts": "^4.1.
|
|
48
|
+
"expo-dev-client-components": "2.1.3",
|
|
49
|
+
"expo-module-scripts": "^4.1.5",
|
|
50
50
|
"graphql": "^16.0.1",
|
|
51
51
|
"graphql-request": "^3.6.1",
|
|
52
52
|
"react": "19.0.0",
|
|
@@ -67,5 +67,5 @@
|
|
|
67
67
|
"/node_modules/(?!((jest-)?react-native|@react-native(-community)?)/|@react-navigation/)"
|
|
68
68
|
]
|
|
69
69
|
},
|
|
70
|
-
"gitHead": "
|
|
70
|
+
"gitHead": "3cd208465df78e385ca9380531bbbfe33ca68e81"
|
|
71
71
|
}
|