diff --git a/android/expoview/src/main/AndroidManifest.xml b/android/expoview/src/main/AndroidManifest.xml
index 50be3c41656ee..9ebf0551156fa 100644
--- a/android/expoview/src/main/AndroidManifest.xml
+++ b/android/expoview/src/main/AndroidManifest.xml
@@ -106,6 +106,11 @@
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
+
+
diff --git a/android/expoview/src/main/java/host/exp/exponent/ExpoApplication.java b/android/expoview/src/main/java/host/exp/exponent/ExpoApplication.java
index 40035a7e60d52..9c1fd4dd1f823 100644
--- a/android/expoview/src/main/java/host/exp/exponent/ExpoApplication.java
+++ b/android/expoview/src/main/java/host/exp/exponent/ExpoApplication.java
@@ -53,8 +53,6 @@ public void onCreate() {
if (!Constants.isStandaloneApp()) {
KernelConstants.MAIN_ACTIVITY_CLASS = LauncherActivity.class;
}
-
- AppLoaderProvider.registerLoader(this, "react-native-headless", ExpoHeadlessAppLoader.class);
KernelProvider.setFactory(new KernelProvider.KernelFactory() {
@Override
public KernelInterface create() {
@@ -62,7 +60,7 @@ public KernelInterface create() {
}
});
- ExponentKernelModuleProvider.setFactory(reactContext -> new ExponentKernelModule(reactContext));
+ ExponentKernelModuleProvider.setFactory(ExponentKernelModule::new);
Exponent.initialize(this, this);
NativeModuleDepsProvider.getInstance().add(Kernel.class, KernelProvider.getInstance());
diff --git a/android/versioned-abis/expoview-abi37_0_0/src/main/java/abi37_0_0/org/unimodules/adapters/react/ReactAdapterPackage.java b/android/versioned-abis/expoview-abi37_0_0/src/main/java/abi37_0_0/org/unimodules/adapters/react/ReactAdapterPackage.java
index b2ac8831b4941..93e9258d1d1f1 100644
--- a/android/versioned-abis/expoview-abi37_0_0/src/main/java/abi37_0_0/org/unimodules/adapters/react/ReactAdapterPackage.java
+++ b/android/versioned-abis/expoview-abi37_0_0/src/main/java/abi37_0_0/org/unimodules/adapters/react/ReactAdapterPackage.java
@@ -26,7 +26,6 @@ public class ReactAdapterPackage extends BasePackage {
@Override
public List createInternalModules(Context context) {
- AppLoaderProvider.registerLoader(context, "react-native-headless", RNHeadlessAppLoader.class);
// We can force-cast here, because this package will only be used in React Native context.
ReactContext reactContext = (ReactContext) context;
return Arrays.asList(
diff --git a/packages/@unimodules/react-native-adapter/android/src/main/AndroidManifest.xml b/packages/@unimodules/react-native-adapter/android/src/main/AndroidManifest.xml
index e63893dfe2b96..ad5d93b80b46d 100644
--- a/packages/@unimodules/react-native-adapter/android/src/main/AndroidManifest.xml
+++ b/packages/@unimodules/react-native-adapter/android/src/main/AndroidManifest.xml
@@ -1,5 +1,11 @@
+
-
+
+
+
diff --git a/packages/@unimodules/react-native-adapter/android/src/main/java/org/unimodules/adapters/react/ReactAdapterPackage.java b/packages/@unimodules/react-native-adapter/android/src/main/java/org/unimodules/adapters/react/ReactAdapterPackage.java
index 4613be46e7771..20fd42d8a2ec6 100644
--- a/packages/@unimodules/react-native-adapter/android/src/main/java/org/unimodules/adapters/react/ReactAdapterPackage.java
+++ b/packages/@unimodules/react-native-adapter/android/src/main/java/org/unimodules/adapters/react/ReactAdapterPackage.java
@@ -26,7 +26,6 @@ public class ReactAdapterPackage extends BasePackage {
@Override
public List createInternalModules(Context context) {
- AppLoaderProvider.registerLoader(context, "react-native-headless", RNHeadlessAppLoader.class);
// We can force-cast here, because this package will only be used in React Native context.
ReactContext reactContext = (ReactContext) context;
return Arrays.asList(
diff --git a/packages/expo-background-fetch/CHANGELOG.md b/packages/expo-background-fetch/CHANGELOG.md
index bea04155f08bc..a4a0bad684184 100644
--- a/packages/expo-background-fetch/CHANGELOG.md
+++ b/packages/expo-background-fetch/CHANGELOG.md
@@ -8,6 +8,4 @@
### 🐛 Bug fixes
-## 8.2.0 — 2020-05-27
-
-*This version does not introduce any user-facing changes.*
+- Upgrading an application does not cause `BackgroundFetch` tasks to unregister. ([#8348](https://github.com/expo/expo/pull/8438) by [@mczernek](https://github.com/mczernek))
diff --git a/packages/expo-background-fetch/android/src/main/java/expo/modules/backgroundfetch/BackgroundFetchTaskConsumer.java b/packages/expo-background-fetch/android/src/main/java/expo/modules/backgroundfetch/BackgroundFetchTaskConsumer.java
index b97a7453fcb04..3fda3ea600958 100644
--- a/packages/expo-background-fetch/android/src/main/java/expo/modules/backgroundfetch/BackgroundFetchTaskConsumer.java
+++ b/packages/expo-background-fetch/android/src/main/java/expo/modules/backgroundfetch/BackgroundFetchTaskConsumer.java
@@ -39,8 +39,8 @@ public String taskType() {
@Override
public boolean canReceiveCustomBroadcast(String action) {
// Let the TaskService know that we want to receive custom broadcasts
- // having "android.intent.action.BOOT_COMPLETED" action.
- return Intent.ACTION_BOOT_COMPLETED.equals(action);
+ // having "android.intent.action.BOOT_COMPLETED" or "Intent.ACTION_MY_PACKAGE_REPLACED" action.
+ return Intent.ACTION_BOOT_COMPLETED.equals(action) || Intent.ACTION_MY_PACKAGE_REPLACED.equals(action);
}
@Override
@@ -65,13 +65,16 @@ public void didReceiveBroadcast(Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
- // Device has just been booted up - restore an alarm if "startOnBoot" option is enabled.
+ // Device has just been booted up, so we need restore an alarm if "startOnBoot" option is enabled.
Map options = mTask.getOptions();
boolean startOnBoot = options.containsKey("startOnBoot") && (boolean) options.get("startOnBoot");
if (startOnBoot) {
startAlarm();
}
+ } else if(Intent.ACTION_MY_PACKAGE_REPLACED.equals(action)) {
+ // App has just been reinstalled, so we need restore an alarm.
+ startAlarm();
} else {
Context context = getContext();
TaskManagerUtilsInterface taskManagerUtils = getTaskManagerUtils();
diff --git a/packages/expo-task-manager/CHANGELOG.md b/packages/expo-task-manager/CHANGELOG.md
index bea04155f08bc..a4a0bad684184 100644
--- a/packages/expo-task-manager/CHANGELOG.md
+++ b/packages/expo-task-manager/CHANGELOG.md
@@ -8,6 +8,4 @@
### 🐛 Bug fixes
-## 8.2.0 — 2020-05-27
-
-*This version does not introduce any user-facing changes.*
+- Upgrading an application does not cause `BackgroundFetch` tasks to unregister. ([#8348](https://github.com/expo/expo/pull/8438) by [@mczernek](https://github.com/mczernek))
diff --git a/packages/expo-task-manager/android/src/main/AndroidManifest.xml b/packages/expo-task-manager/android/src/main/AndroidManifest.xml
index d008016078e2e..d86a93deb18ba 100644
--- a/packages/expo-task-manager/android/src/main/AndroidManifest.xml
+++ b/packages/expo-task-manager/android/src/main/AndroidManifest.xml
@@ -8,6 +8,7 @@
+
+
+
diff --git a/packages/expo-task-manager/android/src/main/java/expo/modules/taskManager/TaskService.java b/packages/expo-task-manager/android/src/main/java/expo/modules/taskManager/TaskService.java
index 3c8a33b99bbd0..5bac3b8341720 100644
--- a/packages/expo-task-manager/android/src/main/java/expo/modules/taskManager/TaskService.java
+++ b/packages/expo-task-manager/android/src/main/java/expo/modules/taskManager/TaskService.java
@@ -11,8 +11,6 @@
import android.os.PersistableBundle;
import android.util.Log;
-import org.json.JSONArray;
-import org.json.JSONException;
import org.json.JSONObject;
import org.unimodules.apploader.AppLoaderProvider;
import org.unimodules.apploader.HeadlessAppLoader;
@@ -26,10 +24,8 @@
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -38,6 +34,11 @@
import expo.modules.taskManager.exceptions.InvalidConsumerClassException;
import expo.modules.taskManager.exceptions.TaskNotFoundException;
import expo.modules.taskManager.exceptions.TaskRegisteringFailedException;
+import expo.modules.taskManager.repository.TasksAndEventsRepository;
+
+import static expo.modules.taskManager.Utils.getConsumerVersion;
+import static expo.modules.taskManager.Utils.jsonToMap;
+import static expo.modules.taskManager.Utils.unversionedClassForClass;
// @tsapeta: TaskService is a funny kind of singleton module... because it's actually not a singleton :D
// Since it would make too much troubles in order to get the singleton instance (from ModuleRegistryProvider)
@@ -52,9 +53,6 @@ public class TaskService implements SingletonModule, TaskServiceInterface {
private WeakReference mContextRef;
private TaskManagerUtilsInterface mTaskManagerUtils;
- // { "": { "": TaskInterface } }
- private static Map> sTasksTable = null;
-
// Map with task managers of running (foregrounded) apps. { "": WeakReference(TaskManagerInterface) }
private static final Map> sTaskManagers = new HashMap<>();
@@ -64,8 +62,7 @@ public class TaskService implements SingletonModule, TaskServiceInterface {
// { "": List(eventIds...) }
private static final Map> sEvents = new HashMap<>();
- // { "": List(eventBodies...) }
- private static final Map> sEventsQueues = new HashMap<>();
+ private TasksAndEventsRepository mTasksAndEventsRepository;
// Map of callbacks for task execution events. Schema: { "": TaskExecutionCallback }
private static final Map sTaskCallbacks = new HashMap<>();
@@ -73,9 +70,10 @@ public class TaskService implements SingletonModule, TaskServiceInterface {
public TaskService(Context context) {
super();
mContextRef = new WeakReference<>(context);
+ mTasksAndEventsRepository = TasksAndEventsRepository.create(context);
- if (sTasksTable == null) {
- sTasksTable = new HashMap<>();
+ if (!mTasksAndEventsRepository.tasksExist()) {
+ mTasksAndEventsRepository.createTasks();
restoreTasks();
}
}
@@ -104,7 +102,7 @@ public void registerTask(String taskName, String appId, String appUrl, Class con
} else {
internalRegisterTask(taskName, appId, appUrl, consumerClass, options);
}
- saveTasksForAppWithId(appId);
+ mTasksAndEventsRepository.persistTasksForAppId(getSharedPreferences(), appId);
}
@Override
@@ -122,7 +120,7 @@ public void unregisterTask(String taskName, String appId, Class consumerClass) t
throw new InvalidConsumerClassException(taskName);
}
- Map appTasks = sTasksTable.get(appId);
+ Map appTasks = mTasksAndEventsRepository.getTasks(appId);
if (appTasks != null) {
appTasks.remove(taskName);
@@ -131,12 +129,12 @@ public void unregisterTask(String taskName, String appId, Class consumerClass) t
Log.i(TAG, "Unregistering task '" + taskName + "' for app '" + appId + "'.");
task.getConsumer().didUnregister();
- saveTasksForAppWithId(appId);
+ mTasksAndEventsRepository.persistTasksForAppId(getSharedPreferences(), appId);
}
@Override
public void unregisterAllTasksForAppId(String appId) {
- Map appTasks = sTasksTable.get(appId);
+ Map appTasks = mTasksAndEventsRepository.getTasks(appId);
if (appTasks != null) {
Log.i(TAG, "Unregistering all tasks for app '" + appId + "'.");
@@ -168,7 +166,7 @@ public Bundle getTaskOptions(String taskName, String appId) {
@Override
public List getTasksForAppId(String appId) {
- Map appTasks = sTasksTable.get(appId);
+ Map appTasks = mTasksAndEventsRepository.getTasks(appId);
List tasks = new ArrayList<>();
if (appTasks != null) {
@@ -187,7 +185,7 @@ public List getTasksForAppId(String appId) {
@Override
public List getTaskConsumers(String appId) {
- Map appTasks = sTasksTable.get(appId);
+ Map appTasks = mTasksAndEventsRepository.getTasks(appId);
List taskConsumers = new ArrayList<>();
if (appTasks != null) {
@@ -250,7 +248,7 @@ public void setTaskManager(TaskManagerInterface taskManager, String appId, Strin
taskManagers.put(appId, new WeakReference<>(taskManager));
// Execute events waiting for the task manager.
- List eventsQueue = sEventsQueues.get(appId);
+ List eventsQueue = mTasksAndEventsRepository.getEvents(appId);
if (eventsQueue != null) {
for (Bundle body : eventsQueue) {
@@ -259,7 +257,7 @@ public void setTaskManager(TaskManagerInterface taskManager, String appId, Strin
}
// Remove events queue for that app.
- sEventsQueues.remove(appId);
+ mTasksAndEventsRepository.removeEvents(appId);
if (!isHeadless) {
// Maybe update app url in user defaults. It might change only in non-headless mode.
@@ -285,7 +283,7 @@ public void handleIntent(Intent intent) {
Log.i(TAG, "Handling intent with action '" + action + "'.");
- for (String appId : sTasksTable.keySet()) {
+ for (String appId : mTasksAndEventsRepository.allAppIdsWithTasks()) {
List taskConsumers = getTaskConsumers(appId);
for (TaskConsumerInterface consumer : taskConsumers) {
@@ -405,17 +403,17 @@ public void executeTask(TaskInterface task, Bundle data, Error error, TaskExecut
// The app is not fully loaded as its task manager is not there yet.
// We need to add event's body to the queue from which events will be executed once the task manager is ready.
- if (!sEventsQueues.containsKey(appId)) {
- sEventsQueues.put(appId, new ArrayList<>());
+ if (!mTasksAndEventsRepository.hasEvents(appId)) {
+ mTasksAndEventsRepository.putEvents(appId, new ArrayList<>());
}
- sEventsQueues.get(appId).add(body);
+ mTasksAndEventsRepository.putEventForAppId(appId, body);
try {
getAppLoader().loadApp(mContextRef.get(), new HeadlessAppLoader.Params(appId, task.getAppUrl()), () -> {
}, success -> {
if (!success) {
sEvents.remove(appId);
- sEventsQueues.remove(appId);
+ mTasksAndEventsRepository.removeEvents(appId);
// Host unreachable? Unregister all tasks for that app.
unregisterAllTasksForAppId(appId);
@@ -429,7 +427,7 @@ public void executeTask(TaskInterface task, Bundle data, Error error, TaskExecut
}
appEvents.remove(eventId);
- sEventsQueues.remove(appId);
+ mTasksAndEventsRepository.removeEvents(appId);
}
}
@@ -462,9 +460,9 @@ private void internalRegisterTask(String taskName, String appId, String appUrl,
Task task = new Task(taskName, appId, appUrl, consumer, options, this);
- Map appTasks = sTasksTable.containsKey(appId) ? sTasksTable.get(appId) : new HashMap();
+ Map appTasks = mTasksAndEventsRepository.hasTasks(appId) ? mTasksAndEventsRepository.getTasks(appId) : new HashMap();
appTasks.put(taskName, task);
- sTasksTable.put(appId, appTasks);
+ mTasksAndEventsRepository.putTasks(appId, appTasks);
Log.i(TAG, "Registered task with name '" + taskName + "' for app with ID '" + appId + "'.");
@@ -496,8 +494,8 @@ private Bundle errorBundleForError(Error error) {
return errorBundle;
}
- private static TaskInterface getTask(String taskName, String appId) {
- Map appTasks = sTasksTable.get(appId);
+ private TaskInterface getTask(String taskName, String appId) {
+ Map appTasks = mTasksAndEventsRepository.getTasks(appId);
return appTasks != null ? appTasks.get(taskName) : null;
}
@@ -541,13 +539,12 @@ private void maybeUpdateAppUrlForAppId(String appUrl, String appId) {
@SuppressWarnings("unchecked")
private void restoreTasks() {
- SharedPreferences preferences = getSharedPreferences();
- Map config = preferences.getAll();
+ Map apps = mTasksAndEventsRepository.readPersistedTasks(getSharedPreferences());
- for (Map.Entry entry : config.entrySet()) {
- Map appConfig = jsonToMap(entry.getValue().toString());
- Map tasksConfig = (HashMap) appConfig.get("tasks");
- String appUrl = (String) appConfig.get("appUrl");
+ for (Map.Entry entry : apps.entrySet()) {
+ String appId = entry.getKey();
+ String appUrl = entry.getValue().appUrl;
+ Map tasksConfig = entry.getValue().tasks;
if (appUrl != null && tasksConfig != null && tasksConfig.size() > 0) {
for (String taskName : tasksConfig.keySet()) {
@@ -565,7 +562,7 @@ private void restoreTasks() {
try {
// register the task using internal method which doesn't change shared preferences.
- internalRegisterTask(taskName, entry.getKey(), appUrl, consumerClass, options);
+ internalRegisterTask(taskName, appId, appUrl, consumerClass, options);
} catch (TaskRegisteringFailedException e) {
Log.e(TAG, e.getMessage());
}
@@ -581,41 +578,10 @@ private void restoreTasks() {
}
// Update tasks for the app to unregister tasks that couldn't be restored.
- saveTasksForAppWithId(entry.getKey());
+ mTasksAndEventsRepository.persistTasksForAppId(getSharedPreferences(), entry.getKey());
}
}
- private void saveTasksForAppWithId(String appId) {
- SharedPreferences preferences = getSharedPreferences();
- Map appRow = sTasksTable.get(appId);
-
- if (preferences == null) {
- return;
- }
- if (appRow == null || appRow.size() == 0) {
- preferences.edit().remove(appId).apply();
- return;
- }
-
- Map appConfig = new HashMap<>();
- Map tasks = new HashMap<>();
- String appUrl = null;
-
- for (TaskInterface task : appRow.values()) {
- Map taskConfig = exportTaskToMap(task);
- tasks.put(task.getName(), taskConfig);
- appUrl = task.getAppUrl();
- }
-
- appConfig.put("appUrl", appUrl);
- appConfig.put("tasks", tasks);
-
- preferences
- .edit()
- .putString(appId, new JSONObject(appConfig).toString())
- .apply();
- }
-
private void removeAppFromConfig(String appId) {
getSharedPreferences().edit().remove(appId).apply();
}
@@ -634,19 +600,6 @@ private TaskManagerInterface getTaskManager(String appId) {
return weakRef == null ? null : weakRef.get();
}
- private Map exportTaskToMap(TaskInterface task) {
- Map map = new HashMap<>();
- Class consumerClass = task.getConsumer().getClass();
- String consumerClassName = unversionedClassNameForClass(consumerClass);
-
- map.put("name", task.getName());
- map.put("consumerClass", consumerClassName);
- map.put("consumerVersion", getConsumerVersion(consumerClass));
- map.put("options", task.getOptions());
-
- return map;
- }
-
private void invalidateAppRecord(String appId) {
HeadlessAppLoader appLoader = getAppLoader();
if (appLoader != null) {
@@ -666,100 +619,4 @@ public void run() {
}, timeout);
}
- private static Map jsonToMap(String jsonStr) {
- try {
- return jsonToMap(new JSONObject(jsonStr));
- } catch (JSONException e) {
- return new HashMap<>();
- }
- }
-
- private static Map jsonToMap(JSONObject json) {
- Map map = new HashMap<>();
-
- try {
- Iterator> keys = json.keys();
-
- while (keys.hasNext()) {
- String key = (String) keys.next();
- Object value = jsonObjectToObject(json.get(key));
-
- map.put(key, value);
- }
- } catch (JSONException e) {
- e.printStackTrace();
- }
- return map;
- }
-
- private static List