expo-navigation-bar 2.4.1 → 2.5.0

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 CHANGED
@@ -10,6 +10,16 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 2.5.0 — 2023-09-04
14
+
15
+ ### 🎉 New features
16
+
17
+ - Added support for React Native 0.73. ([#24018](https://github.com/expo/expo/pull/24018) by [@kudo](https://github.com/kudo))
18
+
19
+ ### 💡 Others
20
+
21
+ - Migrate to Expo Modules API. ([#23933](https://github.com/expo/expo/pull/23933) by [@alanjhughes](https://github.com/alanjhughes))
22
+
13
23
  ## 2.4.1 — 2023-08-02
14
24
 
15
25
  ### 🐛 Bug fixes
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
3
3
  apply plugin: 'maven-publish'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '2.4.1'
6
+ version = '2.5.0'
7
7
 
8
8
  buildscript {
9
9
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
@@ -53,13 +53,16 @@ afterEvaluate {
53
53
  android {
54
54
  compileSdkVersion safeExtGet("compileSdkVersion", 33)
55
55
 
56
- compileOptions {
57
- sourceCompatibility JavaVersion.VERSION_11
58
- targetCompatibility JavaVersion.VERSION_11
59
- }
56
+ def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
57
+ if (agpVersion.tokenize('.')[0].toInteger() < 8) {
58
+ compileOptions {
59
+ sourceCompatibility JavaVersion.VERSION_11
60
+ targetCompatibility JavaVersion.VERSION_11
61
+ }
60
62
 
61
- kotlinOptions {
62
- jvmTarget = JavaVersion.VERSION_11.majorVersion
63
+ kotlinOptions {
64
+ jvmTarget = JavaVersion.VERSION_11.majorVersion
65
+ }
63
66
  }
64
67
 
65
68
  namespace "expo.modules.navigationbar"
@@ -67,7 +70,7 @@ android {
67
70
  minSdkVersion safeExtGet("minSdkVersion", 21)
68
71
  targetSdkVersion safeExtGet("targetSdkVersion", 33)
69
72
  versionCode 1
70
- versionName '2.4.1'
73
+ versionName '2.5.0'
71
74
  }
72
75
  lintOptions {
73
76
  abortOnError false
@@ -0,0 +1,6 @@
1
+ package expo.modules.navigationbar
2
+
3
+ import expo.modules.kotlin.exception.CodedException
4
+
5
+ internal class NavigationBarException(message: String) :
6
+ CodedException(message)
@@ -1,7 +1,5 @@
1
1
  package expo.modules.navigationbar
2
2
 
3
- import android.app.Activity
4
- import android.content.Context
5
3
  import android.graphics.Color
6
4
  import android.os.Build
7
5
  import android.os.Bundle
@@ -9,145 +7,87 @@ import android.view.View
9
7
  import android.view.WindowInsets
10
8
  import androidx.core.view.ViewCompat
11
9
  import androidx.core.view.WindowInsetsControllerCompat
12
- import expo.modules.core.ExportedModule
13
- import expo.modules.core.ModuleRegistry
14
- import expo.modules.core.Promise
15
- import expo.modules.core.errors.CurrentActivityNotFoundException
16
- import expo.modules.core.interfaces.ActivityProvider
17
- import expo.modules.core.interfaces.ExpoMethod
18
- import expo.modules.core.interfaces.services.EventEmitter
10
+ import expo.modules.kotlin.Promise
11
+ import expo.modules.kotlin.exception.Exceptions
12
+ import expo.modules.kotlin.functions.Queues
13
+ import expo.modules.kotlin.modules.Module
14
+ import expo.modules.kotlin.modules.ModuleDefinition
15
+ import expo.modules.navigationbar.singletons.NavigationBar
19
16
 
20
- class NavigationBarModule(context: Context) : ExportedModule(context) {
17
+ class NavigationBarModule : Module() {
18
+ private val activity
19
+ get() = appContext.currentActivity
20
+ ?: throw Exceptions.MissingActivity()
21
21
 
22
- private lateinit var mActivityProvider: ActivityProvider
23
- private lateinit var mEventEmitter: EventEmitter
22
+ override fun definition() = ModuleDefinition {
23
+ Name("ExpoNavigationBar")
24
24
 
25
- override fun getName(): String {
26
- return NAME
27
- }
28
-
29
- override fun onCreate(moduleRegistry: ModuleRegistry) {
30
- mActivityProvider = moduleRegistry.getModule(ActivityProvider::class.java)
31
- ?: throw IllegalStateException("Could not find implementation for ActivityProvider.")
32
- mEventEmitter = moduleRegistry.getModule(EventEmitter::class.java) ?: throw IllegalStateException("Could not find implementation for EventEmitter.")
33
- }
25
+ Events(VISIBILITY_EVENT_NAME)
34
26
 
35
- // Ensure that rejections are passed up to JS rather than terminating the native client.
36
- private fun safeRunOnUiThread(promise: Promise, block: (activity: Activity) -> Unit) {
37
- val activity = mActivityProvider.currentActivity
38
- if (activity == null) {
39
- promise.reject(CurrentActivityNotFoundException())
40
- return
41
- }
42
- activity.runOnUiThread {
43
- block(activity)
44
- }
45
- }
46
-
47
- @ExpoMethod
48
- fun setBackgroundColorAsync(color: Int, promise: Promise) {
49
- safeRunOnUiThread(promise) {
50
- NavigationBar.setBackgroundColor(it, color, { promise.resolve(null) }, { m -> promise.reject(ERROR_TAG, m) })
51
- }
52
- }
27
+ AsyncFunction("setBackgroundColorAsync") { color: Int, promise: Promise ->
28
+ NavigationBar.setBackgroundColor(activity, color, { promise.resolve(null) }, { m -> promise.reject(NavigationBarException(m)) })
29
+ }.runOnQueue(Queues.MAIN)
53
30
 
54
- @ExpoMethod
55
- fun getBackgroundColorAsync(promise: Promise) {
56
- safeRunOnUiThread(promise) {
57
- val color = colorToHex(it.window.navigationBarColor)
58
- promise.resolve(color)
59
- }
60
- }
31
+ AsyncFunction("getBackgroundColorAsync") {
32
+ return@AsyncFunction colorToHex(activity.window.navigationBarColor)
33
+ }.runOnQueue(Queues.MAIN)
61
34
 
62
- @ExpoMethod
63
- fun setBorderColorAsync(color: Int, promise: Promise) {
64
- safeRunOnUiThread(promise) {
65
- NavigationBar.setBorderColor(it, color, { promise.resolve(null) }, { m -> promise.reject(ERROR_TAG, m) })
66
- }
67
- }
35
+ AsyncFunction("setBorderColorAsync") { color: Int, promise: Promise ->
36
+ NavigationBar.setBorderColor(activity, color, { promise.resolve(null) }, { m -> promise.reject(NavigationBarException(m)) })
37
+ }.runOnQueue(Queues.MAIN)
68
38
 
69
- @ExpoMethod
70
- fun getBorderColorAsync(promise: Promise) {
71
- safeRunOnUiThread(promise) {
39
+ AsyncFunction("getBorderColorAsync") {
72
40
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
73
- val color = colorToHex(it.window.navigationBarDividerColor)
74
- promise.resolve(color)
41
+ return@AsyncFunction colorToHex(activity.window.navigationBarDividerColor)
75
42
  } else {
76
- promise.reject(ERROR_TAG, "'getBorderColorAsync' is only available on Android API 28 or higher")
43
+ throw NavigationBarException("'getBorderColorAsync' is only available on Android API 28 or higher")
77
44
  }
78
- }
79
- }
45
+ }.runOnQueue(Queues.MAIN)
80
46
 
81
- @ExpoMethod
82
- fun setButtonStyleAsync(buttonStyle: String, promise: Promise) {
83
- safeRunOnUiThread(promise) {
47
+ AsyncFunction("setButtonStyleAsync") { buttonStyle: String, promise: Promise ->
84
48
  NavigationBar.setButtonStyle(
85
- it, buttonStyle,
49
+ activity, buttonStyle,
86
50
  {
87
51
  promise.resolve(null)
88
52
  },
89
- { m -> promise.reject(ERROR_TAG, m) }
53
+ { m -> promise.reject(NavigationBarException(m)) }
90
54
  )
91
- }
92
- }
55
+ }.runOnQueue(Queues.MAIN)
93
56
 
94
- @ExpoMethod
95
- fun getButtonStyleAsync(promise: Promise) {
96
- safeRunOnUiThread(promise) {
97
- WindowInsetsControllerCompat(it.window, it.window.decorView).let { controller ->
98
- val style = if (controller.isAppearanceLightNavigationBars) "dark" else "light"
99
- promise.resolve(style)
57
+ AsyncFunction("getButtonStyleAsync") {
58
+ WindowInsetsControllerCompat(activity.window, activity.window.decorView).let { controller ->
59
+ return@AsyncFunction if (controller.isAppearanceLightNavigationBars) "dark" else "light"
100
60
  }
101
- }
102
- }
61
+ }.runOnQueue(Queues.MAIN)
103
62
 
104
- @ExpoMethod
105
- fun setVisibilityAsync(visibility: String, promise: Promise) {
106
- safeRunOnUiThread(promise) {
107
- NavigationBar.setVisibility(it, visibility, { promise.resolve(null) }, { m -> promise.reject(ERROR_TAG, m) })
108
- }
109
- }
63
+ AsyncFunction("setVisibilityAsync") { visibility: String, promise: Promise ->
64
+ NavigationBar.setVisibility(activity, visibility, { promise.resolve(null) }, { m -> promise.reject(NavigationBarException(m)) })
65
+ }.runOnQueue(Queues.MAIN)
110
66
 
111
- @ExpoMethod
112
- fun getVisibilityAsync(promise: Promise) {
113
- safeRunOnUiThread(promise) {
67
+ AsyncFunction("getVisibilityAsync") {
114
68
  val isVisible = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
115
- it.window.decorView.rootWindowInsets.isVisible(WindowInsets.Type.navigationBars())
69
+ activity.window.decorView.rootWindowInsets.isVisible(WindowInsets.Type.navigationBars())
116
70
  } else {
117
71
  @Suppress("DEPRECATION")
118
- (View.SYSTEM_UI_FLAG_HIDE_NAVIGATION and it.window.decorView.systemUiVisibility) == 0
72
+ (View.SYSTEM_UI_FLAG_HIDE_NAVIGATION and activity.window.decorView.systemUiVisibility) == 0
119
73
  }
120
- val visibility = if (isVisible) "visible" else "hidden"
121
- promise.resolve(visibility)
122
- }
123
- }
74
+ return@AsyncFunction if (isVisible) "visible" else "hidden"
75
+ }.runOnQueue(Queues.MAIN)
124
76
 
125
- @ExpoMethod
126
- fun setPositionAsync(position: String, promise: Promise) {
127
- safeRunOnUiThread(promise) {
128
- NavigationBar.setPosition(it, position, { promise.resolve(null) }, { m -> promise.reject(ERROR_TAG, m) })
129
- }
130
- }
77
+ AsyncFunction("setPositionAsync") { position: String, promise: Promise ->
78
+ NavigationBar.setPosition(activity, position, { promise.resolve(null) }, { m -> promise.reject(NavigationBarException(m)) })
79
+ }.runOnQueue(Queues.MAIN)
131
80
 
132
- @ExpoMethod
133
- fun unstable_getPositionAsync(promise: Promise) {
134
- safeRunOnUiThread(promise) {
135
- val position = if (ViewCompat.getFitsSystemWindows(it.window.decorView)) "relative" else "absolute"
136
- promise.resolve(position)
137
- }
138
- }
81
+ AsyncFunction("unstable_getPositionAsync") {
82
+ return@AsyncFunction if (ViewCompat.getFitsSystemWindows(activity.window.decorView)) "relative" else "absolute"
83
+ }.runOnQueue(Queues.MAIN)
139
84
 
140
- @ExpoMethod
141
- fun setBehaviorAsync(behavior: String, promise: Promise) {
142
- safeRunOnUiThread(promise) {
143
- NavigationBar.setBehavior(it, behavior, { promise.resolve(null) }, { m -> promise.reject(ERROR_TAG, m) })
144
- }
145
- }
85
+ AsyncFunction("setBehaviorAsync") { behavior: String, promise: Promise ->
86
+ NavigationBar.setBehavior(activity, behavior, { promise.resolve(null) }, { m -> promise.reject(NavigationBarException(m)) })
87
+ }.runOnQueue(Queues.MAIN)
146
88
 
147
- @ExpoMethod
148
- fun getBehaviorAsync(promise: Promise) {
149
- safeRunOnUiThread(promise) {
150
- WindowInsetsControllerCompat(it.window, it.window.decorView).let { controller ->
89
+ AsyncFunction("getBehaviorAsync") {
90
+ WindowInsetsControllerCompat(activity.window, activity.window.decorView).let { controller ->
151
91
  val behavior = when (controller.systemBarsBehavior) {
152
92
  // TODO: Maybe relative / absolute
153
93
  WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE -> "overlay-swipe"
@@ -155,22 +95,20 @@ class NavigationBarModule(context: Context) : ExportedModule(context) {
155
95
  // WindowInsetsControllerCompat.BEHAVIOR_SHOW_BARS_BY_TOUCH -> "inset-touch"
156
96
  else -> "inset-touch"
157
97
  }
158
- promise.resolve(behavior)
159
- }
160
- }
161
- }
162
98
 
163
- /* Events */
99
+ return@AsyncFunction behavior
100
+ }
101
+ }.runOnQueue(Queues.MAIN)
164
102
 
165
- @ExpoMethod
166
- fun startObserving(promise: Promise) {
167
- safeRunOnUiThread(promise) {
168
- val decorView = it.window.decorView
103
+ // We are not using `OnStartObserving` and `OnStopObserving` here because when switching threads
104
+ // they will resolve the promise quicker and the JS won't wait for observer removal
105
+ AsyncFunction("startObserving") {
106
+ val decorView = activity.window.decorView
169
107
  @Suppress("DEPRECATION")
170
108
  decorView.setOnSystemUiVisibilityChangeListener { visibility: Int ->
171
- var isNavigationBarVisible = (visibility and View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
172
- var stringVisibility = if (isNavigationBarVisible) "visible" else "hidden"
173
- mEventEmitter.emit(
109
+ val isNavigationBarVisible = (visibility and View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
110
+ val stringVisibility = if (isNavigationBarVisible) "visible" else "hidden"
111
+ sendEvent(
174
112
  VISIBILITY_EVENT_NAME,
175
113
  Bundle().apply {
176
114
  putString("visibility", stringVisibility)
@@ -178,24 +116,17 @@ class NavigationBarModule(context: Context) : ExportedModule(context) {
178
116
  }
179
117
  )
180
118
  }
181
- promise.resolve(null)
182
- }
183
- }
119
+ }.runOnQueue(Queues.MAIN)
184
120
 
185
- @ExpoMethod
186
- fun stopObserving(promise: Promise) {
187
- safeRunOnUiThread(promise) {
188
- val decorView = it.window.decorView
121
+ AsyncFunction("stopObserving") {
122
+ val decorView = activity.window.decorView
189
123
  @Suppress("DEPRECATION")
190
124
  decorView.setOnSystemUiVisibilityChangeListener(null)
191
- promise.resolve(null)
192
- }
125
+ }.runOnQueue(Queues.MAIN)
193
126
  }
194
127
 
195
128
  companion object {
196
- private const val NAME = "ExpoNavigationBar"
197
129
  private const val VISIBILITY_EVENT_NAME = "ExpoNavigationBar.didChange"
198
- private const val ERROR_TAG = "ERR_NAVIGATION_BAR"
199
130
 
200
131
  fun colorToHex(color: Int): String {
201
132
  return String.format("#%02x%02x%02x", Color.red(color), Color.green(color), Color.blue(color))
@@ -2,14 +2,10 @@ package expo.modules.navigationbar
2
2
 
3
3
  import android.content.Context
4
4
  import expo.modules.core.BasePackage
5
- import expo.modules.core.ExportedModule
6
5
  import expo.modules.core.interfaces.ReactActivityLifecycleListener
7
6
 
8
7
  class NavigationBarPackage : BasePackage() {
9
- override fun createExportedModules(context: Context): List<ExportedModule> {
10
- return listOf(NavigationBarModule(context) as ExportedModule)
11
- }
12
8
  override fun createReactActivityLifecycleListeners(activityContext: Context): List<ReactActivityLifecycleListener> {
13
- return listOf(NavigationBarReactActivityLifecycleListener(activityContext))
9
+ return listOf(NavigationBarReactActivityLifecycleListener())
14
10
  }
15
11
  }
@@ -5,46 +5,47 @@ import android.content.Context
5
5
  import android.os.Bundle
6
6
  import android.util.Log
7
7
  import expo.modules.core.interfaces.ReactActivityLifecycleListener
8
+ import expo.modules.navigationbar.singletons.NavigationBar
8
9
 
9
10
  // this needs to stay for versioning to work
10
11
  // EXPO_VERSIONING_NEEDS_EXPOVIEW_R
11
12
 
12
- class NavigationBarReactActivityLifecycleListener(activityContext: Context) : ReactActivityLifecycleListener {
13
+ class NavigationBarReactActivityLifecycleListener : ReactActivityLifecycleListener {
13
14
  override fun onCreate(activity: Activity, savedInstanceState: Bundle?) {
14
15
  // Execute static tasks before the JS engine starts.
15
16
  // These values are defined via config plugins.
16
17
 
17
- var borderColor = getBorderColor(activity)
18
+ val borderColor = getBorderColor(activity)
18
19
  if (borderColor != null) {
19
20
  NavigationBar.setBorderColor(activity, borderColor)
20
21
  }
21
22
 
22
- var visibility = getVisibility(activity)
23
- if (visibility != "") {
23
+ val visibility = getVisibility(activity)
24
+ if (visibility.isNotBlank()) {
24
25
  NavigationBar.setVisibility(activity, visibility)
25
26
  }
26
27
 
27
- var position = getPosition(activity)
28
- if (position != "") {
28
+ val position = getPosition(activity)
29
+ if (position.isNotBlank()) {
29
30
  NavigationBar.setPosition(activity, position)
30
31
  }
31
32
 
32
- var behavior = getBehavior(activity)
33
- if (behavior != "") {
33
+ val behavior = getBehavior(activity)
34
+ if (behavior.isNotBlank()) {
34
35
  NavigationBar.setBehavior(activity, behavior)
35
36
  }
36
37
 
37
- var legacyVisible = getLegacyVisible(activity)
38
- if (legacyVisible != "") {
38
+ val legacyVisible = getLegacyVisible(activity)
39
+ if (legacyVisible.isNotBlank()) {
39
40
  NavigationBar.setLegacyVisible(activity, legacyVisible)
40
41
  }
41
42
  }
42
43
 
43
44
  private fun getBorderColor(context: Context): Int? {
44
- var value = context.getString(R.string.expo_navigation_bar_border_color)
45
+ val value = context.getString(R.string.expo_navigation_bar_border_color)
45
46
 
46
- var parsed = value.toIntOrNull()
47
- if (value != null && value != "" && parsed == null) {
47
+ val parsed = value.toIntOrNull()
48
+ if (value.isNotBlank() && parsed == null) {
48
49
  Log.e(ERROR_TAG, "Invalid XML value \"$value\" for string \"expo_navigation_bar_border_color\". Expected a valid color int like \"-12177173\". Ensure the value of \"borderColor\" in the \"expo-navigation-bar\" config plugin is a valid CSS color. Skipping initial border color.")
49
50
  }
50
51
  return parsed
@@ -1,4 +1,4 @@
1
- package expo.modules.navigationbar
1
+ package expo.modules.navigationbar.singletons
2
2
 
3
3
  import android.app.Activity
4
4
  import android.os.Build
@@ -7,15 +7,10 @@ import android.view.View
7
7
  import androidx.core.view.WindowCompat
8
8
  import androidx.core.view.WindowInsetsCompat
9
9
  import androidx.core.view.WindowInsetsControllerCompat
10
- import expo.modules.core.interfaces.SingletonModule
11
10
 
12
- object NavigationBar : SingletonModule {
11
+ object NavigationBar {
13
12
  private const val TAG = "NavigationBar"
14
13
 
15
- override fun getName(): String {
16
- return "NavigationBar"
17
- }
18
-
19
14
  fun setBackgroundColor(
20
15
  activity: Activity,
21
16
  color: Int,
@@ -1,4 +1,7 @@
1
1
  {
2
2
  "name": "expo-navigation-bar",
3
- "platforms": ["android"]
3
+ "platforms": ["android"],
4
+ "android": {
5
+ "modules": ["expo.modules.navigationbar.NavigationBarModule"]
6
+ }
4
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-navigation-bar",
3
- "version": "2.4.1",
3
+ "version": "2.5.0",
4
4
  "description": "Interact with the system navigation bar",
5
5
  "main": "build/NavigationBar.js",
6
6
  "types": "build/NavigationBar.d.ts",
@@ -43,5 +43,5 @@
43
43
  "peerDependencies": {
44
44
  "expo": "*"
45
45
  },
46
- "gitHead": "2240630a92eb79a4e4bf73e1439916c394876478"
46
+ "gitHead": "79607a7325f47aa17c36d266100d09a4ff2cc544"
47
47
  }
@@ -1,6 +1,6 @@
1
- import { NavigationBarVisibility, NavigationBarBehavior, NavigationBarPosition, NavigationBarButtonStyle } from 'expo-navigation-bar';
2
1
  import { ExpoConfig } from 'expo/config';
3
2
  import { ConfigPlugin, AndroidConfig } from 'expo/config-plugins';
3
+ import { NavigationBarVisibility, NavigationBarBehavior, NavigationBarPosition, NavigationBarButtonStyle } from 'expo-navigation-bar';
4
4
  export type Props = {
5
5
  borderColor?: string;
6
6
  backgroundColor?: string | null;
@@ -2,12 +2,6 @@
2
2
  import normalizeColor from '@react-native/normalize-color';
3
3
  // @ts-ignore
4
4
  import Debug from 'debug';
5
- import {
6
- NavigationBarVisibility,
7
- NavigationBarBehavior,
8
- NavigationBarPosition,
9
- NavigationBarButtonStyle,
10
- } from 'expo-navigation-bar';
11
5
  import { ExpoConfig } from 'expo/config';
12
6
  import {
13
7
  ConfigPlugin,
@@ -18,6 +12,12 @@ import {
18
12
  withAndroidColors,
19
13
  withAndroidStyles,
20
14
  } from 'expo/config-plugins';
15
+ import {
16
+ NavigationBarVisibility,
17
+ NavigationBarBehavior,
18
+ NavigationBarPosition,
19
+ NavigationBarButtonStyle,
20
+ } from 'expo-navigation-bar';
21
21
 
22
22
  const debug = Debug('expo:system-navigation-bar:plugin');
23
23