1 package expo.modules.taskManager; 2 3 import android.content.Context; 4 import android.os.Bundle; 5 6 import expo.modules.core.ModuleRegistry; 7 import expo.modules.core.interfaces.InternalModule; 8 import expo.modules.core.interfaces.LifecycleEventListener; 9 import expo.modules.core.interfaces.services.EventEmitter; 10 import expo.modules.core.interfaces.services.UIManager; 11 12 import java.lang.ref.WeakReference; 13 import java.util.ArrayList; 14 import java.util.Collections; 15 import java.util.List; 16 import java.util.Map; 17 18 import expo.modules.interfaces.constants.ConstantsInterface; 19 import expo.modules.interfaces.taskManager.TaskConsumerInterface; 20 import expo.modules.interfaces.taskManager.TaskManagerInterface; 21 import expo.modules.interfaces.taskManager.TaskServiceInterface; 22 23 public class TaskManagerInternalModule implements InternalModule, TaskManagerInterface, LifecycleEventListener { 24 private UIManager mUIManager; 25 private EventEmitter mEventEmitter; 26 private ConstantsInterface mConstants; 27 private TaskServiceInterface mTaskService; 28 private WeakReference<Context> mContextRef; 29 private List<Bundle> mEventsQueue = new ArrayList<>(); 30 31 public TaskManagerInternalModule(Context context) { 32 mContextRef = new WeakReference<>(context); 33 } 34 35 //region InternalModule 36 37 @Override 38 public List<Class> getExportedInterfaces() { 39 return Collections.singletonList(TaskManagerInterface.class); 40 } 41 42 //endregion 43 //region ModuleRegistryConsumer 44 45 @Override 46 public void onCreate(ModuleRegistry moduleRegistry) { 47 mUIManager = moduleRegistry.getModule(UIManager.class); 48 mEventEmitter = moduleRegistry.getModule(EventEmitter.class); 49 mConstants = moduleRegistry.getModule(ConstantsInterface.class); 50 mTaskService = moduleRegistry.getSingletonModule("TaskService", TaskServiceInterface.class); 51 52 // Register in TaskService. 53 mTaskService.setTaskManager(this, getAppScopeKey(), getAppUrl()); 54 55 mUIManager.registerLifecycleEventListener(this); 56 } 57 58 @Override 59 public void onDestroy() { 60 mUIManager.unregisterLifecycleEventListener(this); 61 mTaskService.setTaskManager(null, getAppScopeKey(), getAppUrl()); 62 } 63 64 //endregion 65 //region TaskManagerInterface 66 67 @Override 68 public void registerTask(String taskName, Class consumerClass, Map<String, Object> options) throws Exception { 69 checkTaskService(); 70 mTaskService.registerTask(taskName, getAppScopeKey(), getAppUrl(), consumerClass, options); 71 } 72 73 @Override 74 public void unregisterTask(String taskName, Class consumerClass) throws Exception { 75 checkTaskService(); 76 mTaskService.unregisterTask(taskName, getAppScopeKey(), consumerClass); 77 } 78 79 @Override 80 public synchronized void executeTaskWithBody(Bundle body) { 81 if (mEventsQueue != null) { 82 // `startObserving` on TaskManagerModule wasn't called yet - add event body to the queue. 83 mEventsQueue.add(body); 84 } else { 85 // Manager is already being observed by JS app, so we can execute the event immediately. 86 mEventEmitter.emit(TaskManagerInterface.EVENT_NAME, body); 87 } 88 } 89 90 @Override 91 public boolean taskHasConsumerOfClass(String taskName, Class consumerClass) { 92 if (mTaskService == null) { 93 return false; 94 } 95 return mTaskService.taskHasConsumerOfClass(taskName, getAppScopeKey(), consumerClass); 96 } 97 98 @Override 99 public synchronized void flushQueuedEvents() { 100 // Execute any events that came before this call. 101 if (mEventsQueue != null) { 102 for (Bundle body : mEventsQueue) { 103 mEventEmitter.emit(TaskManagerInterface.EVENT_NAME, body); 104 } 105 mEventsQueue = null; 106 } 107 } 108 109 @Override 110 public String getAppScopeKey() { 111 if (mConstants != null) { 112 return mConstants.getAppScopeKey(); 113 } 114 return null; 115 } 116 //endregion 117 //region LifecycleEventListener 118 119 @Override 120 public void onHostResume() { 121 if (!isRunningInHeadlessMode()) { 122 List<TaskConsumerInterface> taskConsumers = mTaskService.getTaskConsumers(getAppScopeKey()); 123 for (TaskConsumerInterface taskConsumer : taskConsumers) { 124 if (taskConsumer instanceof LifecycleEventListener) { 125 ((LifecycleEventListener) taskConsumer).onHostResume(); 126 } 127 } 128 } 129 } 130 131 @Override 132 public void onHostPause() { 133 if (!isRunningInHeadlessMode()) { 134 List<TaskConsumerInterface> taskConsumers = mTaskService.getTaskConsumers(getAppScopeKey()); 135 for (TaskConsumerInterface taskConsumer : taskConsumers) { 136 if (taskConsumer instanceof LifecycleEventListener) { 137 ((LifecycleEventListener) taskConsumer).onHostPause(); 138 } 139 } 140 } 141 } 142 143 @Override 144 public void onHostDestroy() { 145 if (!isRunningInHeadlessMode()) { 146 List<TaskConsumerInterface> taskConsumers = mTaskService.getTaskConsumers(getAppScopeKey()); 147 for (TaskConsumerInterface taskConsumer : taskConsumers) { 148 if (taskConsumer instanceof LifecycleEventListener) { 149 ((LifecycleEventListener) taskConsumer).onHostDestroy(); 150 } 151 } 152 } 153 } 154 155 //endregion 156 //region helpers 157 158 private boolean isRunningInHeadlessMode() { 159 return mTaskService.isStartedByHeadlessLoader(getAppScopeKey()); 160 } 161 162 private String getAppUrl() { 163 // If Constants module is available and experienceUrl is provided, just return it. 164 if (mConstants != null) { 165 String experienceUrl = (String) mConstants.getConstants().get("experienceUrl"); 166 if (experienceUrl != null) { 167 return experienceUrl; 168 } 169 } 170 171 // Fallback to package name? It should work well for RN apps with just one JS app inside. 172 Context context = mContextRef.get(); 173 if (context != null) { 174 return context.getPackageName(); 175 } 176 177 return null; 178 } 179 180 private void checkTaskService() throws IllegalStateException { 181 if (mTaskService == null) { 182 throw new IllegalStateException("Unable to find TaskService singleton module in module registry."); 183 } 184 } 185 186 //endregion 187 } 188