react-native-icon-switcher 0.0.0-stage → 0.1.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.
Files changed (92) hide show
  1. package/IconSwitcher.podspec +20 -0
  2. package/LICENSE +20 -0
  3. package/README.md +252 -2
  4. package/android/build.gradle +51 -0
  5. package/android/src/main/AndroidManifest.xml +2 -0
  6. package/android/src/main/java/com/iconswitcher/IconSwitcher.kt +380 -0
  7. package/android/src/main/java/com/iconswitcher/IconSwitcherExceptions.kt +24 -0
  8. package/android/src/main/java/com/iconswitcher/IconSwitcherModule.kt +67 -0
  9. package/android/src/main/java/com/iconswitcher/IconSwitcherPackage.kt +30 -0
  10. package/android/src/test/AndroidManifest.xml +39 -0
  11. package/android/src/test/java/com/iconswitcher/IconSwitcherTest.kt +392 -0
  12. package/app.plugin.js +1 -0
  13. package/ios/ISIconSwitcher.h +49 -0
  14. package/ios/ISIconSwitcher.m +205 -0
  15. package/ios/IconSwitcher.h +5 -0
  16. package/ios/IconSwitcher.mm +100 -0
  17. package/jest/index.d.ts +31 -0
  18. package/jest/index.js +98 -0
  19. package/lib/module/NativeIconSwitcher.js +5 -0
  20. package/lib/module/NativeIconSwitcher.js.map +1 -0
  21. package/lib/module/config.js +6 -0
  22. package/lib/module/config.js.map +1 -0
  23. package/lib/module/errors.js +28 -0
  24. package/lib/module/errors.js.map +1 -0
  25. package/lib/module/index.js +69 -0
  26. package/lib/module/index.js.map +1 -0
  27. package/lib/module/package.json +1 -0
  28. package/lib/module/register.js +2 -0
  29. package/lib/module/register.js.map +1 -0
  30. package/lib/module/remote/index.js +119 -0
  31. package/lib/module/remote/index.js.map +1 -0
  32. package/lib/module/types.js +2 -0
  33. package/lib/module/types.js.map +1 -0
  34. package/lib/module/typetests/icon-name.typetest.js +8 -0
  35. package/lib/module/typetests/icon-name.typetest.js.map +1 -0
  36. package/lib/module/typetests/types-in-sync.typetest.js +7 -0
  37. package/lib/module/typetests/types-in-sync.typetest.js.map +1 -0
  38. package/lib/typescript/package.json +1 -0
  39. package/lib/typescript/src/NativeIconSwitcher.d.ts +18 -0
  40. package/lib/typescript/src/NativeIconSwitcher.d.ts.map +1 -0
  41. package/lib/typescript/src/config.d.ts +4 -0
  42. package/lib/typescript/src/config.d.ts.map +1 -0
  43. package/lib/typescript/src/errors.d.ts +7 -0
  44. package/lib/typescript/src/errors.d.ts.map +1 -0
  45. package/lib/typescript/src/index.d.ts +20 -0
  46. package/lib/typescript/src/index.d.ts.map +1 -0
  47. package/lib/typescript/src/register.d.ts +7 -0
  48. package/lib/typescript/src/register.d.ts.map +1 -0
  49. package/lib/typescript/src/remote/index.d.ts +17 -0
  50. package/lib/typescript/src/remote/index.d.ts.map +1 -0
  51. package/lib/typescript/src/types.d.ts +59 -0
  52. package/lib/typescript/src/types.d.ts.map +1 -0
  53. package/package.json +251 -4
  54. package/plugin/build/cli.d.ts +3 -0
  55. package/plugin/build/cli.js +89 -0
  56. package/plugin/build/core/assets.d.ts +80 -0
  57. package/plugin/build/core/assets.js +463 -0
  58. package/plugin/build/core/errors.d.ts +4 -0
  59. package/plugin/build/core/errors.js +11 -0
  60. package/plugin/build/core/validate.d.ts +6 -0
  61. package/plugin/build/core/validate.js +229 -0
  62. package/plugin/build/index.d.ts +6 -0
  63. package/plugin/build/index.js +134 -0
  64. package/plugin/build/load-config.d.ts +3 -0
  65. package/plugin/build/load-config.js +50 -0
  66. package/plugin/build/paths.d.ts +15 -0
  67. package/plugin/build/paths.js +153 -0
  68. package/plugin/build/sync.d.ts +12 -0
  69. package/plugin/build/sync.js +162 -0
  70. package/plugin/build/transforms/android.d.ts +5 -0
  71. package/plugin/build/transforms/android.js +212 -0
  72. package/plugin/build/transforms/errors.d.ts +4 -0
  73. package/plugin/build/transforms/errors.js +11 -0
  74. package/plugin/build/transforms/index.d.ts +4 -0
  75. package/plugin/build/transforms/index.js +12 -0
  76. package/plugin/build/transforms/ios.d.ts +4 -0
  77. package/plugin/build/transforms/ios.js +111 -0
  78. package/plugin/build/transforms/sampleConfigs.d.ts +3 -0
  79. package/plugin/build/transforms/sampleConfigs.js +33 -0
  80. package/plugin/build/transforms/types.d.ts +2 -0
  81. package/plugin/build/transforms/types.js +14 -0
  82. package/plugin/build/types.d.ts +61 -0
  83. package/plugin/build/types.js +5 -0
  84. package/src/NativeIconSwitcher.ts +17 -0
  85. package/src/config.ts +15 -0
  86. package/src/errors.ts +38 -0
  87. package/src/index.tsx +78 -0
  88. package/src/register.ts +4 -0
  89. package/src/remote/index.ts +142 -0
  90. package/src/types.ts +59 -0
  91. package/src/typetests/icon-name.typetest.ts +12 -0
  92. package/src/typetests/types-in-sync.typetest.ts +16 -0
@@ -0,0 +1,380 @@
1
+ package com.iconswitcher
2
+
3
+ import android.content.ComponentName
4
+ import android.content.Context
5
+ import android.content.pm.ApplicationInfo
6
+ import android.content.pm.PackageManager
7
+ import android.os.Build
8
+ import android.os.Handler
9
+ import android.os.Looper
10
+ import androidx.lifecycle.DefaultLifecycleObserver
11
+ import androidx.lifecycle.Lifecycle
12
+ import androidx.lifecycle.LifecycleOwner
13
+ import androidx.lifecycle.ProcessLifecycleOwner
14
+ import java.util.concurrent.Callable
15
+ import java.util.concurrent.Executors
16
+ import java.util.concurrent.Future
17
+
18
+ class SetResult(
19
+ val status: String,
20
+ )
21
+
22
+ class IconSwitcher internal constructor(
23
+ private val context: Context,
24
+ private val packageManager: PackageManager,
25
+ private val packageName: String,
26
+ private val prefs: android.content.SharedPreferences,
27
+ private val lifecycleRegistrar: (DefaultLifecycleObserver) -> Unit,
28
+ private val isInForeground: () -> Boolean,
29
+ ) {
30
+ private val writeExecutor = Executors.newSingleThreadExecutor()
31
+
32
+ private val markedAliases: Map<String, ComponentName> by lazy { discoverMarkedAliases() }
33
+ private val applyOn: String by lazy { readApplyOn() }
34
+ private val skipBrands: Set<String> by lazy { readSkipBrands() }
35
+
36
+ val iconNames: List<String> by lazy { buildIconNames() }
37
+
38
+ val isSupported: Boolean by lazy {
39
+ markedAliases.isNotEmpty() && !skipBrands.contains(Build.BRAND.lowercase())
40
+ }
41
+
42
+ constructor(context: Context) : this(
43
+ context = context.applicationContext,
44
+ packageManager = context.applicationContext.packageManager,
45
+ packageName = context.applicationContext.packageName,
46
+ prefs = context.applicationContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE),
47
+ lifecycleRegistrar = { observer ->
48
+ if (Looper.myLooper() == Looper.getMainLooper()) {
49
+ ProcessLifecycleOwner.get().lifecycle.addObserver(observer)
50
+ } else {
51
+ Handler(Looper.getMainLooper()).post {
52
+ ProcessLifecycleOwner.get().lifecycle.addObserver(observer)
53
+ }
54
+ }
55
+ },
56
+ isInForeground = {
57
+ ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)
58
+ },
59
+ )
60
+
61
+ internal constructor(
62
+ context: Context,
63
+ packageManager: PackageManager,
64
+ ) : this(
65
+ context = context.applicationContext,
66
+ packageManager = packageManager,
67
+ packageName = context.applicationContext.packageName,
68
+ prefs = context.applicationContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE),
69
+ lifecycleRegistrar = { },
70
+ isInForeground = { false },
71
+ )
72
+
73
+ fun onModuleCreate() {
74
+ lifecycleRegistrar(
75
+ object : DefaultLifecycleObserver {
76
+ override fun onStop(owner: LifecycleOwner) {
77
+ val pending = prefs.getString(KEY_PENDING, null)
78
+ if (pending != null) {
79
+ runApply(pending, deferred = true)
80
+ }
81
+ }
82
+ },
83
+ )
84
+ runColdStart()
85
+ }
86
+
87
+ fun current(): String {
88
+ val fromPm = readCurrentFromPackageManager()
89
+ if (fromPm != null) {
90
+ return fromPm
91
+ }
92
+ return prefs.getString(KEY_CURRENT, DEFAULT_ICON_NAME) ?: DEFAULT_ICON_NAME
93
+ }
94
+
95
+ fun set(name: String): SetResult {
96
+ if (!isSupported) {
97
+ throw ErrUnsupportedException()
98
+ }
99
+ if (!markedAliases.containsKey(name)) {
100
+ throw ErrUnknownIconException("Unknown icon name: $name")
101
+ }
102
+
103
+ val pending = prefs.getString(KEY_PENDING, null)
104
+ if (name == current() && pending == null) {
105
+ return SetResult(STATUS_APPLIED)
106
+ }
107
+
108
+ if (applyOn == APPLY_ON_BACKGROUND) {
109
+ prefs.edit().putString(KEY_PENDING, name).apply()
110
+ return SetResult(STATUS_PENDING)
111
+ }
112
+
113
+ runApply(name, deferred = false)
114
+ return SetResult(STATUS_APPLIED)
115
+ }
116
+
117
+ fun applyPendingNow(): SetResult {
118
+ val pending = prefs.getString(KEY_PENDING, null)
119
+ if (pending == null) {
120
+ return SetResult(STATUS_NONE)
121
+ }
122
+ if (isInForeground()) {
123
+ throw ErrForegroundException()
124
+ }
125
+ runApply(pending, deferred = false)
126
+ return SetResult(STATUS_APPLIED)
127
+ }
128
+
129
+ private fun runColdStart() {
130
+ if (prefs.getString(KEY_PENDING, null) != null) {
131
+ return
132
+ }
133
+
134
+ val stored = prefs.getString(KEY_CURRENT, DEFAULT_ICON_NAME) ?: DEFAULT_ICON_NAME
135
+ val fromPm = readCurrentFromPackageManager()
136
+ if (fromPm != null && fromPm != stored) {
137
+ prefs.edit().putString(KEY_PENDING, stored).apply()
138
+ }
139
+ }
140
+
141
+ private fun runApply(name: String, deferred: Boolean) {
142
+ val future = submitOnWriteExecutor { applyOnExecutor(name, deferred) }
143
+ if (deferred) {
144
+ return
145
+ }
146
+ try {
147
+ future.get()
148
+ } catch (e: ErrOsRejectedException) {
149
+ throw e
150
+ } catch (e: Exception) {
151
+ val cause = e.cause
152
+ if (cause is ErrOsRejectedException) {
153
+ throw cause
154
+ }
155
+ throw e
156
+ }
157
+ }
158
+
159
+ private fun applyOnExecutor(name: String, deferred: Boolean) {
160
+ if (!markedAliases.containsKey(name)) {
161
+ if (deferred) {
162
+ prefs.edit().putString(KEY_LAST_ERROR, "Unknown icon: $name").apply()
163
+ } else {
164
+ throw ErrUnknownIconException("Unknown icon name: $name")
165
+ }
166
+ return
167
+ }
168
+
169
+ val settings = buildComponentSettings(name)
170
+
171
+ try {
172
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
173
+ val apiSettings = settings.map { setting ->
174
+ PackageManager.ComponentEnabledSetting(
175
+ setting.componentName,
176
+ setting.enabledState,
177
+ PackageManager.DONT_KILL_APP,
178
+ )
179
+ }
180
+ packageManager.setComponentEnabledSettings(apiSettings)
181
+ } else {
182
+ for (setting in settings) {
183
+ packageManager.setComponentEnabledSetting(
184
+ setting.componentName,
185
+ setting.enabledState,
186
+ PackageManager.DONT_KILL_APP,
187
+ )
188
+ }
189
+ }
190
+ } catch (e: Exception) {
191
+ val message = e.message ?: "Failed to update component enabled state"
192
+ if (deferred) {
193
+ prefs.edit().putString(KEY_LAST_ERROR, message).apply()
194
+ return
195
+ }
196
+ throw ErrOsRejectedException(message)
197
+ }
198
+
199
+ if (!verifyReadBack(name)) {
200
+ val message = "Component enabled state read-back did not match expected values"
201
+ if (deferred) {
202
+ prefs.edit().putString(KEY_LAST_ERROR, message).apply()
203
+ return
204
+ }
205
+ throw ErrOsRejectedException(message)
206
+ }
207
+
208
+ prefs.edit()
209
+ .putString(KEY_CURRENT, name)
210
+ .remove(KEY_PENDING)
211
+ .remove(KEY_LAST_ERROR)
212
+ .apply()
213
+ }
214
+
215
+ private data class ComponentSetting(
216
+ val componentName: ComponentName,
217
+ val enabledState: Int,
218
+ )
219
+
220
+ private fun buildComponentSettings(targetName: String): List<ComponentSetting> {
221
+ return markedAliases.map { (iconName, component) ->
222
+ val state = if (iconName == targetName) {
223
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED
224
+ } else {
225
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED
226
+ }
227
+ ComponentSetting(component, state)
228
+ }
229
+ }
230
+
231
+ internal fun applyIcon(name: String, deferred: Boolean = false) {
232
+ runApply(name, deferred)
233
+ }
234
+
235
+ private fun verifyReadBack(expectedActive: String): Boolean {
236
+ for ((iconName, component) in markedAliases) {
237
+ val expectedEnabled = iconName == expectedActive
238
+ val actual = packageManager.getComponentEnabledSetting(component)
239
+ val isEnabled = actual == PackageManager.COMPONENT_ENABLED_STATE_ENABLED ||
240
+ (actual == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT && expectedEnabled && _isDefaultEnabledInManifest(component))
241
+ val isDisabled = actual == PackageManager.COMPONENT_ENABLED_STATE_DISABLED ||
242
+ (actual == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT && !expectedEnabled && !_isDefaultEnabledInManifest(component))
243
+
244
+ if (expectedEnabled && !isEnabled) {
245
+ return false
246
+ }
247
+ if (!expectedEnabled && !isDisabled) {
248
+ return false
249
+ }
250
+ }
251
+ return true
252
+ }
253
+
254
+ private fun _isDefaultEnabledInManifest(component: ComponentName): Boolean {
255
+ val flags = PackageManager.GET_ACTIVITIES or
256
+ PackageManager.GET_META_DATA or
257
+ PackageManager.MATCH_DISABLED_COMPONENTS
258
+ val info = packageManager.getPackageInfo(packageName, flags)
259
+ val activity = info.activities?.find { it.name == component.className } ?: return false
260
+ return activity.enabled
261
+ }
262
+
263
+ private fun readCurrentFromPackageManager(): String? {
264
+ if (markedAliases.isEmpty()) {
265
+ return null
266
+ }
267
+ val enabled = mutableListOf<String>()
268
+ for ((name, component) in markedAliases) {
269
+ val state = packageManager.getComponentEnabledSetting(component)
270
+ val isOn = state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED ||
271
+ (state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT && _isDefaultEnabledInManifest(component))
272
+ if (isOn) {
273
+ enabled.add(name)
274
+ }
275
+ }
276
+ if (enabled.size == 1) {
277
+ return enabled[0]
278
+ }
279
+ if (enabled.size > 1) {
280
+ val stored = prefs.getString(KEY_CURRENT, null)
281
+ if (stored != null && enabled.contains(stored)) {
282
+ return stored
283
+ }
284
+ return enabled.first()
285
+ }
286
+ return null
287
+ }
288
+
289
+ private fun discoverMarkedAliases(): Map<String, ComponentName> {
290
+ val flags = PackageManager.GET_ACTIVITIES or
291
+ PackageManager.GET_META_DATA or
292
+ PackageManager.MATCH_DISABLED_COMPONENTS
293
+ val packageInfo = try {
294
+ packageManager.getPackageInfo(packageName, flags)
295
+ } catch (_: PackageManager.NameNotFoundException) {
296
+ return emptyMap()
297
+ }
298
+
299
+ val result = linkedMapOf<String, ComponentName>()
300
+ val activities = packageInfo.activities ?: return emptyMap()
301
+ for (activity in activities) {
302
+ val meta = activity.metaData ?: continue
303
+ if (!meta.containsKey(META_ICON)) {
304
+ continue
305
+ }
306
+ val iconName = meta.getString(META_ICON) ?: continue
307
+ result[iconName] = ComponentName(packageName, activity.name)
308
+ }
309
+ return result
310
+ }
311
+
312
+ private fun buildIconNames(): List<String> {
313
+ val marked = markedAliases.keys
314
+ val configured = readConfiguredIconOrder()
315
+ val ordered = mutableListOf<String>()
316
+ for (name in configured) {
317
+ if (marked.contains(name)) {
318
+ ordered.add(name)
319
+ }
320
+ }
321
+ val missing = marked.filter { !ordered.contains(it) }.sorted()
322
+ ordered.addAll(missing)
323
+ return if (ordered.contains(DEFAULT_ICON_NAME)) {
324
+ ordered.remove(DEFAULT_ICON_NAME)
325
+ listOf(DEFAULT_ICON_NAME) + ordered
326
+ } else {
327
+ ordered
328
+ }
329
+ }
330
+
331
+ private fun readConfiguredIconOrder(): List<String> {
332
+ val appInfo = applicationInfoOrNull() ?: return emptyList()
333
+ val raw = appInfo.metaData?.getString(META_ICONS) ?: return emptyList()
334
+ return raw.split(',').map { it.trim() }.filter { it.isNotEmpty() }
335
+ }
336
+
337
+ private fun readApplyOn(): String {
338
+ val appInfo = applicationInfoOrNull() ?: return APPLY_ON_BACKGROUND
339
+ val value = appInfo.metaData?.getString(META_APPLY_ON) ?: return APPLY_ON_BACKGROUND
340
+ return if (value == APPLY_ON_IMMEDIATE) APPLY_ON_IMMEDIATE else APPLY_ON_BACKGROUND
341
+ }
342
+
343
+ private fun readSkipBrands(): Set<String> {
344
+ val appInfo = applicationInfoOrNull() ?: return emptySet()
345
+ val raw = appInfo.metaData?.getString(META_SKIP_BRANDS) ?: return emptySet()
346
+ return raw.split(',').map { it.trim().lowercase() }.filter { it.isNotEmpty() }.toSet()
347
+ }
348
+
349
+ private fun applicationInfoOrNull(): ApplicationInfo? {
350
+ return try {
351
+ packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
352
+ } catch (_: PackageManager.NameNotFoundException) {
353
+ null
354
+ }
355
+ }
356
+
357
+ private fun <T> submitOnWriteExecutor(callable: Callable<T>): Future<T> {
358
+ return writeExecutor.submit(callable)
359
+ }
360
+
361
+ companion object {
362
+ const val PREFS_NAME = "icon_switcher"
363
+ const val KEY_CURRENT = "current"
364
+ const val KEY_PENDING = "pending"
365
+ const val KEY_LAST_ERROR = "lastError"
366
+
367
+ const val META_ICON = "com.iconswitcher.icon"
368
+ const val META_ICONS = "com.iconswitcher.icons"
369
+ const val META_APPLY_ON = "com.iconswitcher.applyOn"
370
+ const val META_SKIP_BRANDS = "com.iconswitcher.skipBrands"
371
+
372
+ const val APPLY_ON_BACKGROUND = "background"
373
+ const val APPLY_ON_IMMEDIATE = "immediate"
374
+
375
+ const val DEFAULT_ICON_NAME = "default"
376
+ const val STATUS_APPLIED = "applied"
377
+ const val STATUS_PENDING = "pending"
378
+ const val STATUS_NONE = "none"
379
+ }
380
+ }
@@ -0,0 +1,24 @@
1
+ package com.iconswitcher
2
+
3
+ internal open class IconSwitcherException(
4
+ val code: String,
5
+ message: String,
6
+ ) : Exception(message)
7
+
8
+ internal class ErrUnsupportedException(
9
+ message: String = "Alternate app icons are not supported on this device",
10
+ ) : IconSwitcherException("ERR_UNSUPPORTED", message)
11
+
12
+ internal class ErrUnknownIconException(
13
+ message: String = "Unknown icon name",
14
+ ) : IconSwitcherException("ERR_UNKNOWN_ICON", message)
15
+
16
+ internal class ErrOsRejectedException(
17
+ message: String = "The OS rejected the icon change",
18
+ ) : IconSwitcherException("ERR_OS_REJECTED", message)
19
+
20
+ internal class ErrForegroundException(
21
+ message: String =
22
+ "Cannot apply a pending icon change while the app is in the foreground; " +
23
+ "doing so would close the visible screen. The change is applied automatically when the app goes to the background.",
24
+ ) : IconSwitcherException("ERR_FOREGROUND", message)
@@ -0,0 +1,67 @@
1
+ package com.iconswitcher
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.Promise
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.bridge.WritableMap
7
+
8
+ class IconSwitcherModule(reactContext: ReactApplicationContext) :
9
+ NativeIconSwitcherSpec(reactContext) {
10
+
11
+ private val switcher = IconSwitcher(reactApplicationContext)
12
+
13
+ override fun initialize() {
14
+ super.initialize()
15
+ switcher.onModuleCreate()
16
+ }
17
+
18
+ override fun getTypedExportedConstants(): Map<String, Any> {
19
+ return mapOf(
20
+ "isSupported" to switcher.isSupported,
21
+ "iconNames" to switcher.iconNames,
22
+ )
23
+ }
24
+
25
+ override fun getCurrent(promise: Promise) {
26
+ try {
27
+ promise.resolve(switcher.current())
28
+ } catch (e: Throwable) {
29
+ rejectPromise(promise, e)
30
+ }
31
+ }
32
+
33
+ override fun set(name: String, promise: Promise) {
34
+ try {
35
+ val result = switcher.set(name)
36
+ promise.resolve(statusMap(result.status))
37
+ } catch (e: Throwable) {
38
+ rejectPromise(promise, e)
39
+ }
40
+ }
41
+
42
+ override fun applyPending(promise: Promise) {
43
+ try {
44
+ val result = switcher.applyPendingNow()
45
+ promise.resolve(statusMap(result.status))
46
+ } catch (e: Throwable) {
47
+ rejectPromise(promise, e)
48
+ }
49
+ }
50
+
51
+ companion object {
52
+ const val NAME = NativeIconSwitcherSpec.NAME
53
+
54
+ private fun statusMap(status: String): WritableMap {
55
+ val map = Arguments.createMap()
56
+ map.putString("status", status)
57
+ return map
58
+ }
59
+
60
+ private fun rejectPromise(promise: Promise, error: Throwable) {
61
+ when (error) {
62
+ is IconSwitcherException -> promise.reject(error.code, error.message, error)
63
+ else -> promise.reject("ERR_OS_REJECTED", error.message ?: "Unknown error", error)
64
+ }
65
+ }
66
+ }
67
+ }
@@ -0,0 +1,30 @@
1
+ package com.iconswitcher
2
+
3
+ import com.facebook.react.BaseReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.module.model.ReactModuleInfo
7
+ import com.facebook.react.module.model.ReactModuleInfoProvider
8
+
9
+ class IconSwitcherPackage : BaseReactPackage() {
10
+ override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
11
+ return if (name == IconSwitcherModule.NAME) {
12
+ IconSwitcherModule(reactContext)
13
+ } else {
14
+ null
15
+ }
16
+ }
17
+
18
+ override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
19
+ mapOf(
20
+ IconSwitcherModule.NAME to ReactModuleInfo(
21
+ name = IconSwitcherModule.NAME,
22
+ className = IconSwitcherModule.NAME,
23
+ canOverrideExistingModule = false,
24
+ needsEagerInit = false,
25
+ isCxxModule = false,
26
+ isTurboModule = true
27
+ )
28
+ )
29
+ }
30
+ }
@@ -0,0 +1,39 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+ package="com.iconswitcher.test">
3
+
4
+ <application android:label="Test">
5
+ <meta-data
6
+ android:name="com.iconswitcher.applyOn"
7
+ android:value="background" />
8
+
9
+ <activity
10
+ android:name=".MainActivity"
11
+ android:exported="true" />
12
+
13
+ <activity-alias
14
+ android:name=".IconSwitcher_default"
15
+ android:enabled="true"
16
+ android:exported="true"
17
+ android:targetActivity=".MainActivity">
18
+ <meta-data
19
+ android:name="com.iconswitcher.icon"
20
+ android:value="default" />
21
+ </activity-alias>
22
+
23
+ <activity-alias
24
+ android:name=".IconSwitcher_halloween"
25
+ android:enabled="false"
26
+ android:exported="true"
27
+ android:targetActivity=".MainActivity">
28
+ <meta-data
29
+ android:name="com.iconswitcher.icon"
30
+ android:value="halloween" />
31
+ </activity-alias>
32
+
33
+ <activity-alias
34
+ android:name=".UnmarkedAlias"
35
+ android:enabled="false"
36
+ android:exported="true"
37
+ android:targetActivity=".MainActivity" />
38
+ </application>
39
+ </manifest>