From b4ca061a4a914553b92157998be8edebf424ff5c Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Tue, 10 Dec 2024 16:36:14 +0100 Subject: [PATCH 1/9] feat!: bootstrap local module on Andorid --- modules/background-task/android/build.gradle | 126 ++++++++++++++++++ .../background-task/android/gradle.properties | 5 + .../android/src/main/AndroidManifest.xml | 3 + .../android/src/main/AndroidManifestNew.xml | 2 + .../ReactNativeBackgroundTaskModule.kt | 24 ++++ .../ReactNativeBackgroundTaskPackage.kt | 35 +++++ .../newarch/ReactNativeBackgroundTaskSpec.kt | 7 + .../oldarch/ReactNativeBackgroundTaskSpec.kt | 11 ++ .../background-task/react-native.config.js | 4 +- src/setup/backgroundTask/index.ts | 9 ++ 10 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 modules/background-task/android/build.gradle create mode 100644 modules/background-task/android/gradle.properties create mode 100644 modules/background-task/android/src/main/AndroidManifest.xml create mode 100644 modules/background-task/android/src/main/AndroidManifestNew.xml create mode 100644 modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt create mode 100644 modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskPackage.kt create mode 100644 modules/background-task/android/src/newarch/ReactNativeBackgroundTaskSpec.kt create mode 100644 modules/background-task/android/src/oldarch/ReactNativeBackgroundTaskSpec.kt diff --git a/modules/background-task/android/build.gradle b/modules/background-task/android/build.gradle new file mode 100644 index 000000000000..335b1fa6bde2 --- /dev/null +++ b/modules/background-task/android/build.gradle @@ -0,0 +1,126 @@ +buildscript { + // Buildscript is evaluated before everything else so we can't use getExtOrDefault + def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["ReactNativeBackgroundTask_kotlinVersion"] + + repositories { + google() + mavenCentral() + } + + dependencies { + classpath "com.android.tools.build:gradle:7.2.1" + // noinspection DifferentKotlinGradleVersion + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +def reactNativeArchitectures() { + def value = rootProject.getProperties().get("reactNativeArchitectures") + return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] +} + +def isNewArchitectureEnabled() { + return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true" +} + +apply plugin: "com.android.library" +apply plugin: "kotlin-android" + +if (isNewArchitectureEnabled()) { + apply plugin: "com.facebook.react" +} + +def getExtOrDefault(name) { + return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["ReactNativeBackgroundTask_" + name] +} + +def getExtOrIntegerDefault(name) { + return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["ReactNativeBackgroundTask_" + name]).toInteger() +} + +def supportsNamespace() { + def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.') + def major = parsed[0].toInteger() + def minor = parsed[1].toInteger() + + // Namespace support was added in 7.3.0 + return (major == 7 && minor >= 3) || major >= 8 +} + +android { + if (supportsNamespace()) { + namespace "com.expensify.reactnativebackgroundtask" + + sourceSets { + main { + manifest.srcFile "src/main/AndroidManifestNew.xml" + } + } + } + + compileSdkVersion getExtOrIntegerDefault("compileSdkVersion") + + defaultConfig { + minSdkVersion getExtOrIntegerDefault("minSdkVersion") + targetSdkVersion getExtOrIntegerDefault("targetSdkVersion") + buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() + + } + + buildFeatures { + buildConfig true + } + + buildTypes { + release { + minifyEnabled false + } + } + + lintOptions { + disable "GradleCompatible" + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + sourceSets { + main { + if (isNewArchitectureEnabled()) { + java.srcDirs += [ + "src/newarch", + // Codegen specs + "generated/java", + "generated/jni" + ] + } else { + java.srcDirs += ["src/oldarch"] + } + } + } +} + +repositories { + mavenCentral() + google() +} + +def kotlin_version = getExtOrDefault("kotlinVersion") + +dependencies { + // For < 0.71, this will be from the local maven repo + // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin + //noinspection GradleDynamicVersion + implementation "com.facebook.react:react-native:+" + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" +} + +if (isNewArchitectureEnabled()) { + react { + jsRootDir = file("../src/") + libraryName = "ReactNativeBackgroundTask" + codegenJavaPackageName = "com.expensify.reactnativebackgroundtask" + } +} diff --git a/modules/background-task/android/gradle.properties b/modules/background-task/android/gradle.properties new file mode 100644 index 000000000000..6ed0dc89b17c --- /dev/null +++ b/modules/background-task/android/gradle.properties @@ -0,0 +1,5 @@ +ReactNativeBackgroundTask_kotlinVersion=1.7.0 +ReactNativeBackgroundTask_minSdkVersion=21 +ReactNativeBackgroundTask_targetSdkVersion=31 +ReactNativeBackgroundTask_compileSdkVersion=31 +ReactNativeBackgroundTask_ndkversion=21.4.7075529 diff --git a/modules/background-task/android/src/main/AndroidManifest.xml b/modules/background-task/android/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..327870652628 --- /dev/null +++ b/modules/background-task/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/modules/background-task/android/src/main/AndroidManifestNew.xml b/modules/background-task/android/src/main/AndroidManifestNew.xml new file mode 100644 index 000000000000..a2f47b6057db --- /dev/null +++ b/modules/background-task/android/src/main/AndroidManifestNew.xml @@ -0,0 +1,2 @@ + + diff --git a/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt b/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt new file mode 100644 index 000000000000..0a2c19a640e2 --- /dev/null +++ b/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt @@ -0,0 +1,24 @@ +package com.expensify.reactnativebackgroundtask + +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactMethod +import com.facebook.react.bridge.Promise +import com.facebook.react.bridge.Callback + +class ReactNativeBackgroundTaskModule internal constructor(context: ReactApplicationContext) : + ReactNativeBackgroundTaskSpec(context) { + + override fun getName(): String { + return NAME + } + + @ReactMethod + override fun defineTask(taskName: String, taskExecutor: Callback, promise: Promise) { + promise.resolve(taskName); + emitOnBackgroundTaskExecution(taskName); + } + + companion object { + const val NAME = "ReactNativeBackgroundTask" + } +} diff --git a/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskPackage.kt b/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskPackage.kt new file mode 100644 index 000000000000..dd0547f7ef35 --- /dev/null +++ b/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskPackage.kt @@ -0,0 +1,35 @@ +package com.expensify.reactnativebackgroundtask + +import com.facebook.react.TurboReactPackage +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.NativeModule +import com.facebook.react.module.model.ReactModuleInfoProvider +import com.facebook.react.module.model.ReactModuleInfo +import java.util.HashMap + +class ReactNativeBackgroundTaskPackage : TurboReactPackage() { + override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? { + return if (name == ReactNativeBackgroundTaskModule.NAME) { + ReactNativeBackgroundTaskModule(reactContext) + } else { + null + } + } + + override fun getReactModuleInfoProvider(): ReactModuleInfoProvider { + return ReactModuleInfoProvider { + val moduleInfos: MutableMap = HashMap() + val isTurboModule: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED + moduleInfos[ReactNativeBackgroundTaskModule.NAME] = ReactModuleInfo( + ReactNativeBackgroundTaskModule.NAME, + ReactNativeBackgroundTaskModule.NAME, + false, // canOverrideExistingModule + false, // needsEagerInit + true, // hasConstants + false, // isCxxModule + isTurboModule // isTurboModule + ) + moduleInfos + } + } +} diff --git a/modules/background-task/android/src/newarch/ReactNativeBackgroundTaskSpec.kt b/modules/background-task/android/src/newarch/ReactNativeBackgroundTaskSpec.kt new file mode 100644 index 000000000000..2ee39c49c66d --- /dev/null +++ b/modules/background-task/android/src/newarch/ReactNativeBackgroundTaskSpec.kt @@ -0,0 +1,7 @@ +package com.expensify.reactnativebackgroundtask + +import com.facebook.react.bridge.ReactApplicationContext + +abstract class ReactNativeBackgroundTaskSpec internal constructor(context: ReactApplicationContext) : + NativeReactNativeBackgroundTaskSpec(context) { +} diff --git a/modules/background-task/android/src/oldarch/ReactNativeBackgroundTaskSpec.kt b/modules/background-task/android/src/oldarch/ReactNativeBackgroundTaskSpec.kt new file mode 100644 index 000000000000..771e6a13734b --- /dev/null +++ b/modules/background-task/android/src/oldarch/ReactNativeBackgroundTaskSpec.kt @@ -0,0 +1,11 @@ +package com.expensify.reactnativebackgroundtask + +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactContextBaseJavaModule +import com.facebook.react.bridge.Promise + +abstract class ReactNativeBackgroundTaskSpec internal constructor(context: ReactApplicationContext) : + ReactContextBaseJavaModule(context) { + + abstract fun multiply(a: Double, b: Double, promise: Promise) +} diff --git a/modules/background-task/react-native.config.js b/modules/background-task/react-native.config.js index d532440e69b0..5bad2c0ec408 100644 --- a/modules/background-task/react-native.config.js +++ b/modules/background-task/react-native.config.js @@ -4,7 +4,9 @@ module.exports = { dependency: { platforms: { - android: null, + android: { + cmakeListsPath: 'build/generated/source/codegen/jni/CMakeLists.txt', + }, }, }, }; diff --git a/src/setup/backgroundTask/index.ts b/src/setup/backgroundTask/index.ts index e69de29bb2d1..d629a2e34ecb 100644 --- a/src/setup/backgroundTask/index.ts +++ b/src/setup/backgroundTask/index.ts @@ -0,0 +1,9 @@ +// FIXME: .android.ts doesn't seem to work, .native.ts neither. +import TaskManager from '@expensify/react-native-background-task'; +import * as SequentialQueue from '@libs/Network/SequentialQueue'; + +const BACKGROUND_FETCH_TASK = 'FLUSH-SEQUENTIAL-QUEUE-BACKGROUND-FETCH'; + +TaskManager.defineTask(BACKGROUND_FETCH_TASK, () => { + SequentialQueue.flush(); +}); From 8d89ff5891450b008e1a6cbbff1329b23223de7e Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Tue, 10 Dec 2024 22:26:11 +0100 Subject: [PATCH 2/9] fix: share code between iOS and Android --- .../backgroundTask/{index.ios.ts => index.native.ts} | 0 src/setup/backgroundTask/index.ts | 9 --------- 2 files changed, 9 deletions(-) rename src/setup/backgroundTask/{index.ios.ts => index.native.ts} (100%) diff --git a/src/setup/backgroundTask/index.ios.ts b/src/setup/backgroundTask/index.native.ts similarity index 100% rename from src/setup/backgroundTask/index.ios.ts rename to src/setup/backgroundTask/index.native.ts diff --git a/src/setup/backgroundTask/index.ts b/src/setup/backgroundTask/index.ts index d629a2e34ecb..e69de29bb2d1 100644 --- a/src/setup/backgroundTask/index.ts +++ b/src/setup/backgroundTask/index.ts @@ -1,9 +0,0 @@ -// FIXME: .android.ts doesn't seem to work, .native.ts neither. -import TaskManager from '@expensify/react-native-background-task'; -import * as SequentialQueue from '@libs/Network/SequentialQueue'; - -const BACKGROUND_FETCH_TASK = 'FLUSH-SEQUENTIAL-QUEUE-BACKGROUND-FETCH'; - -TaskManager.defineTask(BACKGROUND_FETCH_TASK, () => { - SequentialQueue.flush(); -}); From 72e49eff9428845d760cec9b0bf5ef1fb1939ddf Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Tue, 10 Dec 2024 22:26:40 +0100 Subject: [PATCH 3/9] feat: setup JobScheduler --- android/app/src/main/AndroidManifest.xml | 5 +++ .../android/src/main/AndroidManifestNew.xml | 1 + .../BackgroundJobService.kt | 29 +++++++++++++++++ .../ReactNativeBackgroundTaskModule.kt | 32 +++++++++++++++++-- 4 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/BackgroundJobService.kt diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 142d919a7a18..33a12b929e98 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -22,6 +22,11 @@ android:theme="@style/AppTheme" tools:replace="android:supportsRtl"> + + + diff --git a/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/BackgroundJobService.kt b/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/BackgroundJobService.kt new file mode 100644 index 000000000000..09097ebb6e84 --- /dev/null +++ b/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/BackgroundJobService.kt @@ -0,0 +1,29 @@ +package com.expensify.reactnativebackgroundtask + +import android.app.job.JobParameters +import android.app.job.JobService +import android.util.Log +import com.facebook.react.ReactApplication + +class BackgroundJobService : JobService() { + override fun onStartJob(params: JobParameters?): Boolean { + // Get the stored taskName + val extras = params?.extras + val taskName = extras?.getString("taskName") + + taskName?.let { + val reactApplication = application as ReactApplication + val reactNativeHost = reactApplication.reactNativeHost + val reactContext = reactNativeHost.reactInstanceManager.currentReactContext + + reactContext?.getNativeModule(ReactNativeBackgroundTaskModule::class.java) + ?.emitOnBackgroundTaskExecution(it) + } + return false + } + + override fun onStopJob(params: JobParameters?): Boolean { + // Return true to reschedule the job + return true + } +} diff --git a/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt b/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt index 0a2c19a640e2..94d34c3cdf3e 100644 --- a/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt +++ b/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt @@ -4,6 +4,11 @@ import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactMethod import com.facebook.react.bridge.Promise import com.facebook.react.bridge.Callback +import android.app.job.JobScheduler +import android.app.job.JobInfo +import android.content.ComponentName +import android.content.Context +import android.os.PersistableBundle class ReactNativeBackgroundTaskModule internal constructor(context: ReactApplicationContext) : ReactNativeBackgroundTaskSpec(context) { @@ -14,8 +19,31 @@ class ReactNativeBackgroundTaskModule internal constructor(context: ReactApplica @ReactMethod override fun defineTask(taskName: String, taskExecutor: Callback, promise: Promise) { - promise.resolve(taskName); - emitOnBackgroundTaskExecution(taskName); + try { + val jobScheduler = reactApplicationContext.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler + val componentName = ComponentName(reactApplicationContext, BackgroundJobService::class.java) + + val extras = PersistableBundle().apply { + putString("taskName", taskName) + } + + val jobInfo = JobInfo.Builder(taskName.hashCode() and 0xFFFFFF, componentName) + .setPeriodic(15 * 60 * 1000L) // 15 minutes in milliseconds + .setPersisted(true) // Job persists after reboot + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) + .setExtras(extras) + .build() + + val resultCode = jobScheduler.schedule(jobInfo) + if (resultCode == JobScheduler.RESULT_SUCCESS) { + + promise.resolve(null); + } else { + promise.reject("ERROR", "Failed to schedule job") + } + } catch (e: Exception) { + promise.reject("ERROR", e.message) + } } companion object { From a6e210d18e108c5ff373d7bf2a9f715e2a05c5cf Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Wed, 11 Dec 2024 11:37:53 +0100 Subject: [PATCH 4/9] feat: add a broadcaster to properly emit events from module --- .../BackgroundJobService.kt | 18 +++++++----------- .../ReactNativeBackgroundTaskModule.kt | 17 +++++++++++++++++ .../oldarch/ReactNativeBackgroundTaskSpec.kt | 2 +- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/BackgroundJobService.kt b/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/BackgroundJobService.kt index 09097ebb6e84..4184727677a0 100644 --- a/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/BackgroundJobService.kt +++ b/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/BackgroundJobService.kt @@ -2,23 +2,19 @@ package com.expensify.reactnativebackgroundtask import android.app.job.JobParameters import android.app.job.JobService +import android.content.Intent import android.util.Log import com.facebook.react.ReactApplication class BackgroundJobService : JobService() { override fun onStartJob(params: JobParameters?): Boolean { - // Get the stored taskName - val extras = params?.extras - val taskName = extras?.getString("taskName") - - taskName?.let { - val reactApplication = application as ReactApplication - val reactNativeHost = reactApplication.reactNativeHost - val reactContext = reactNativeHost.reactInstanceManager.currentReactContext - - reactContext?.getNativeModule(ReactNativeBackgroundTaskModule::class.java) - ?.emitOnBackgroundTaskExecution(it) + val taskName = params?.extras?.getString("taskName") + val intent = Intent("com.expensify.reactnativebackgroundtask.TASK_ACTION").apply { + putExtra("taskName", taskName) } + sendBroadcast(intent) + + // Job is done, return false if no more work is needed return false } diff --git a/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt b/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt index 94d34c3cdf3e..395f822645b3 100644 --- a/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt +++ b/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt @@ -6,13 +6,30 @@ import com.facebook.react.bridge.Promise import com.facebook.react.bridge.Callback import android.app.job.JobScheduler import android.app.job.JobInfo +import android.content.BroadcastReceiver import android.content.ComponentName import android.content.Context +import android.content.Intent +import android.content.IntentFilter import android.os.PersistableBundle +import android.util.Log class ReactNativeBackgroundTaskModule internal constructor(context: ReactApplicationContext) : ReactNativeBackgroundTaskSpec(context) { + private val taskReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + val taskName = intent?.getStringExtra("taskName") + Log.d("ReactNativeBackgroundTaskModule", "Received task: $taskName") + emitOnBackgroundTaskExecution(taskName) + } + } + + init { + val filter = IntentFilter("com.expensify.reactnativebackgroundtask.TASK_ACTION") + reactApplicationContext.registerReceiver(taskReceiver, filter) + } + override fun getName(): String { return NAME } diff --git a/modules/background-task/android/src/oldarch/ReactNativeBackgroundTaskSpec.kt b/modules/background-task/android/src/oldarch/ReactNativeBackgroundTaskSpec.kt index 771e6a13734b..138a1cc1d4af 100644 --- a/modules/background-task/android/src/oldarch/ReactNativeBackgroundTaskSpec.kt +++ b/modules/background-task/android/src/oldarch/ReactNativeBackgroundTaskSpec.kt @@ -7,5 +7,5 @@ import com.facebook.react.bridge.Promise abstract class ReactNativeBackgroundTaskSpec internal constructor(context: ReactApplicationContext) : ReactContextBaseJavaModule(context) { - abstract fun multiply(a: Double, b: Double, promise: Promise) + abstract fun defineTask(taskName: String, taskExecutor: Callback, promise: Promise) } From 1285aaed9fae0d660727e7c47bcfd43ab04d249b Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Mon, 30 Dec 2024 12:04:37 +0100 Subject: [PATCH 5/9] fix: update outdated build tools versions --- modules/background-task/android/build.gradle | 35 ++++--------------- .../background-task/android/gradle.properties | 10 +++--- .../android/src/main/AndroidManifest.xml | 4 +-- .../android/src/main/AndroidManifestNew.xml | 3 -- 4 files changed, 14 insertions(+), 38 deletions(-) delete mode 100644 modules/background-task/android/src/main/AndroidManifestNew.xml diff --git a/modules/background-task/android/build.gradle b/modules/background-task/android/build.gradle index 335b1fa6bde2..b2ea4cda795e 100644 --- a/modules/background-task/android/build.gradle +++ b/modules/background-task/android/build.gradle @@ -1,6 +1,8 @@ buildscript { // Buildscript is evaluated before everything else so we can't use getExtOrDefault - def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["ReactNativeBackgroundTask_kotlinVersion"] + ext.getExtOrDefault = {name -> + return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['<%- project.name -%>_' + name] + } repositories { google() @@ -8,9 +10,9 @@ buildscript { } dependencies { - classpath "com.android.tools.build:gradle:7.2.1" + classpath "com.android.tools.build:gradle:8.7.2" // noinspection DifferentKotlinGradleVersion - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}" } } @@ -30,33 +32,12 @@ if (isNewArchitectureEnabled()) { apply plugin: "com.facebook.react" } -def getExtOrDefault(name) { - return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["ReactNativeBackgroundTask_" + name] -} - def getExtOrIntegerDefault(name) { return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["ReactNativeBackgroundTask_" + name]).toInteger() } -def supportsNamespace() { - def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.') - def major = parsed[0].toInteger() - def minor = parsed[1].toInteger() - - // Namespace support was added in 7.3.0 - return (major == 7 && minor >= 3) || major >= 8 -} - android { - if (supportsNamespace()) { - namespace "com.expensify.reactnativebackgroundtask" - - sourceSets { - main { - manifest.srcFile "src/main/AndroidManifestNew.xml" - } - } - } + namespace "com.expensify.reactnativebackgroundtask" compileSdkVersion getExtOrIntegerDefault("compileSdkVersion") @@ -110,10 +91,8 @@ repositories { def kotlin_version = getExtOrDefault("kotlinVersion") dependencies { - // For < 0.71, this will be from the local maven repo - // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin //noinspection GradleDynamicVersion - implementation "com.facebook.react:react-native:+" + implementation "com.facebook.react:react-android" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" } diff --git a/modules/background-task/android/gradle.properties b/modules/background-task/android/gradle.properties index 6ed0dc89b17c..4eec12b62c7e 100644 --- a/modules/background-task/android/gradle.properties +++ b/modules/background-task/android/gradle.properties @@ -1,5 +1,5 @@ -ReactNativeBackgroundTask_kotlinVersion=1.7.0 -ReactNativeBackgroundTask_minSdkVersion=21 -ReactNativeBackgroundTask_targetSdkVersion=31 -ReactNativeBackgroundTask_compileSdkVersion=31 -ReactNativeBackgroundTask_ndkversion=21.4.7075529 +ReactNativeBackgroundTask_kotlinVersion=1.9.24 +ReactNativeBackgroundTask_minSdkVersion=23 +ReactNativeBackgroundTask_targetSdkVersion=34 +ReactNativeBackgroundTask_compileSdkVersion=34 +ReactNativeBackgroundTask_ndkversion=26.1.10909125 diff --git a/modules/background-task/android/src/main/AndroidManifest.xml b/modules/background-task/android/src/main/AndroidManifest.xml index 327870652628..cf2838eb09ee 100644 --- a/modules/background-task/android/src/main/AndroidManifest.xml +++ b/modules/background-task/android/src/main/AndroidManifest.xml @@ -1,3 +1,3 @@ - + + diff --git a/modules/background-task/android/src/main/AndroidManifestNew.xml b/modules/background-task/android/src/main/AndroidManifestNew.xml deleted file mode 100644 index cf2838eb09ee..000000000000 --- a/modules/background-task/android/src/main/AndroidManifestNew.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - From 7b3d7c4a87104e97fcba40ec398c0803a85007e2 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Wed, 8 Jan 2025 09:19:07 +0100 Subject: [PATCH 6/9] fix: mock module --- tests/ui/GroupChatNameTests.tsx | 5 +++++ tests/ui/PaginationTest.tsx | 5 +++++ tests/ui/SwitchToExpensifyClassicTest.tsx | 5 +++++ tests/ui/UnreadIndicatorsTest.tsx | 5 +++++ tests/ui/WorkspaceSwitcherTest.tsx | 5 +++++ tests/unit/loginTest.tsx | 5 +++++ 6 files changed, 30 insertions(+) diff --git a/tests/ui/GroupChatNameTests.tsx b/tests/ui/GroupChatNameTests.tsx index fc383efe4e28..eef08d7cbc40 100644 --- a/tests/ui/GroupChatNameTests.tsx +++ b/tests/ui/GroupChatNameTests.tsx @@ -22,6 +22,11 @@ import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct' // We need a large timeout here as we are lazy loading React Navigation screens and this test is running against the entire mounted App jest.setTimeout(50000); +jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ + defineTask: jest.fn(), + onBackgroundTaskExecution: jest.fn(), +})); + jest.mock('../../src/components/ConfirmedRoute.tsx'); // Needed for: https://stackoverflow.com/questions/76903168/mocking-libraries-in-jest diff --git a/tests/ui/PaginationTest.tsx b/tests/ui/PaginationTest.tsx index 8fcd6dbac1d6..59e164180075 100644 --- a/tests/ui/PaginationTest.tsx +++ b/tests/ui/PaginationTest.tsx @@ -21,6 +21,11 @@ import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct' // We need a large timeout here as we are lazy loading React Navigation screens and this test is running against the entire mounted App jest.setTimeout(60000); +jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ + defineTask: jest.fn(), + onBackgroundTaskExecution: jest.fn(), +})); + jest.mock('@react-navigation/native'); jest.mock('../../src/libs/Notification/LocalNotification'); jest.mock('../../src/components/Icon/Expensicons'); diff --git a/tests/ui/SwitchToExpensifyClassicTest.tsx b/tests/ui/SwitchToExpensifyClassicTest.tsx index c8fe13b7e2e9..35c5ec1e58bb 100644 --- a/tests/ui/SwitchToExpensifyClassicTest.tsx +++ b/tests/ui/SwitchToExpensifyClassicTest.tsx @@ -16,6 +16,11 @@ const USER_A_EMAIL = 'user_a@test.com'; jest.setTimeout(60000); +jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ + defineTask: jest.fn(), + onBackgroundTaskExecution: jest.fn(), +})); + jest.mock('@react-navigation/native'); TestHelper.setupApp(); diff --git a/tests/ui/UnreadIndicatorsTest.tsx b/tests/ui/UnreadIndicatorsTest.tsx index 6f4313a9f02c..9509ee81db0d 100644 --- a/tests/ui/UnreadIndicatorsTest.tsx +++ b/tests/ui/UnreadIndicatorsTest.tsx @@ -32,6 +32,11 @@ import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct' // We need a large timeout here as we are lazy loading React Navigation screens and this test is running against the entire mounted App jest.setTimeout(60000); +jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ + defineTask: jest.fn(), + onBackgroundTaskExecution: jest.fn(), +})); + jest.mock('@react-navigation/native'); jest.mock('../../src/libs/Notification/LocalNotification'); jest.mock('../../src/components/Icon/Expensicons'); diff --git a/tests/ui/WorkspaceSwitcherTest.tsx b/tests/ui/WorkspaceSwitcherTest.tsx index 614ed4e5ab70..3ae442823726 100644 --- a/tests/ui/WorkspaceSwitcherTest.tsx +++ b/tests/ui/WorkspaceSwitcherTest.tsx @@ -18,6 +18,11 @@ import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct' // We need a large timeout here as we are lazy loading React Navigation screens and this test is running against the entire mounted App jest.setTimeout(60000); +jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ + defineTask: jest.fn(), + onBackgroundTaskExecution: jest.fn(), +})); + jest.mock('@react-navigation/native', () => { const actualNav = jest.requireActual('@react-navigation/native'); return { diff --git a/tests/unit/loginTest.tsx b/tests/unit/loginTest.tsx index d5084299bb08..21b0bdd528f6 100644 --- a/tests/unit/loginTest.tsx +++ b/tests/unit/loginTest.tsx @@ -4,6 +4,11 @@ import 'react-native'; import renderer from 'react-test-renderer'; import App from '@src/App'; +jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ + defineTask: jest.fn(), + onBackgroundTaskExecution: jest.fn(), +})); + // Needed for: https://stackoverflow.com/questions/76903168/mocking-libraries-in-jest jest.mock('react-native/Libraries/LogBox/LogBox', () => ({ // eslint-disable-next-line @typescript-eslint/naming-convention From 716c0e4dcf3f0e0d746df13b693ca3e46732b621 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Wed, 15 Jan 2025 00:01:21 +0100 Subject: [PATCH 7/9] chore: remove useless mocks --- tests/ui/GroupChatNameTests.tsx | 5 ----- tests/ui/PaginationTest.tsx | 5 ----- tests/ui/SwitchToExpensifyClassicTest.tsx | 5 ----- tests/ui/UnreadIndicatorsTest.tsx | 5 ----- tests/ui/WorkspaceSwitcherTest.tsx | 5 ----- tests/unit/loginTest.tsx | 5 ----- 6 files changed, 30 deletions(-) diff --git a/tests/ui/GroupChatNameTests.tsx b/tests/ui/GroupChatNameTests.tsx index eef08d7cbc40..fc383efe4e28 100644 --- a/tests/ui/GroupChatNameTests.tsx +++ b/tests/ui/GroupChatNameTests.tsx @@ -22,11 +22,6 @@ import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct' // We need a large timeout here as we are lazy loading React Navigation screens and this test is running against the entire mounted App jest.setTimeout(50000); -jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ - defineTask: jest.fn(), - onBackgroundTaskExecution: jest.fn(), -})); - jest.mock('../../src/components/ConfirmedRoute.tsx'); // Needed for: https://stackoverflow.com/questions/76903168/mocking-libraries-in-jest diff --git a/tests/ui/PaginationTest.tsx b/tests/ui/PaginationTest.tsx index 59e164180075..8fcd6dbac1d6 100644 --- a/tests/ui/PaginationTest.tsx +++ b/tests/ui/PaginationTest.tsx @@ -21,11 +21,6 @@ import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct' // We need a large timeout here as we are lazy loading React Navigation screens and this test is running against the entire mounted App jest.setTimeout(60000); -jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ - defineTask: jest.fn(), - onBackgroundTaskExecution: jest.fn(), -})); - jest.mock('@react-navigation/native'); jest.mock('../../src/libs/Notification/LocalNotification'); jest.mock('../../src/components/Icon/Expensicons'); diff --git a/tests/ui/SwitchToExpensifyClassicTest.tsx b/tests/ui/SwitchToExpensifyClassicTest.tsx index 35c5ec1e58bb..c8fe13b7e2e9 100644 --- a/tests/ui/SwitchToExpensifyClassicTest.tsx +++ b/tests/ui/SwitchToExpensifyClassicTest.tsx @@ -16,11 +16,6 @@ const USER_A_EMAIL = 'user_a@test.com'; jest.setTimeout(60000); -jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ - defineTask: jest.fn(), - onBackgroundTaskExecution: jest.fn(), -})); - jest.mock('@react-navigation/native'); TestHelper.setupApp(); diff --git a/tests/ui/UnreadIndicatorsTest.tsx b/tests/ui/UnreadIndicatorsTest.tsx index 9509ee81db0d..6f4313a9f02c 100644 --- a/tests/ui/UnreadIndicatorsTest.tsx +++ b/tests/ui/UnreadIndicatorsTest.tsx @@ -32,11 +32,6 @@ import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct' // We need a large timeout here as we are lazy loading React Navigation screens and this test is running against the entire mounted App jest.setTimeout(60000); -jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ - defineTask: jest.fn(), - onBackgroundTaskExecution: jest.fn(), -})); - jest.mock('@react-navigation/native'); jest.mock('../../src/libs/Notification/LocalNotification'); jest.mock('../../src/components/Icon/Expensicons'); diff --git a/tests/ui/WorkspaceSwitcherTest.tsx b/tests/ui/WorkspaceSwitcherTest.tsx index 3ae442823726..614ed4e5ab70 100644 --- a/tests/ui/WorkspaceSwitcherTest.tsx +++ b/tests/ui/WorkspaceSwitcherTest.tsx @@ -18,11 +18,6 @@ import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct' // We need a large timeout here as we are lazy loading React Navigation screens and this test is running against the entire mounted App jest.setTimeout(60000); -jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ - defineTask: jest.fn(), - onBackgroundTaskExecution: jest.fn(), -})); - jest.mock('@react-navigation/native', () => { const actualNav = jest.requireActual('@react-navigation/native'); return { diff --git a/tests/unit/loginTest.tsx b/tests/unit/loginTest.tsx index 21b0bdd528f6..d5084299bb08 100644 --- a/tests/unit/loginTest.tsx +++ b/tests/unit/loginTest.tsx @@ -4,11 +4,6 @@ import 'react-native'; import renderer from 'react-test-renderer'; import App from '@src/App'; -jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ - defineTask: jest.fn(), - onBackgroundTaskExecution: jest.fn(), -})); - // Needed for: https://stackoverflow.com/questions/76903168/mocking-libraries-in-jest jest.mock('react-native/Libraries/LogBox/LogBox', () => ({ // eslint-disable-next-line @typescript-eslint/naming-convention From 6426ffd875e78e3094f05194c64a9f633ac80fb0 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Wed, 15 Jan 2025 16:35:55 +0100 Subject: [PATCH 8/9] chore: remove useless gradle version declaration --- modules/background-task/android/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/background-task/android/build.gradle b/modules/background-task/android/build.gradle index b2ea4cda795e..ffb17153aae3 100644 --- a/modules/background-task/android/build.gradle +++ b/modules/background-task/android/build.gradle @@ -10,7 +10,6 @@ buildscript { } dependencies { - classpath "com.android.tools.build:gradle:8.7.2" // noinspection DifferentKotlinGradleVersion classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}" } From 7472c60e01a674034577be5c900c726cb215ff1c Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Wed, 15 Jan 2025 16:36:27 +0100 Subject: [PATCH 9/9] feat: add a note why `src/setup/backgroundTask/index.ts` is empty --- src/setup/backgroundTask/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/setup/backgroundTask/index.ts b/src/setup/backgroundTask/index.ts index e69de29bb2d1..bb307a04703f 100644 --- a/src/setup/backgroundTask/index.ts +++ b/src/setup/backgroundTask/index.ts @@ -0,0 +1,2 @@ +// This file is intentionally empty as Background Tasks are currently only implemented +// for native mobile platforms. See `index.native.ts` for the native implementation.