1 package expo.modules.taskManager; 2 3 import android.content.Context; 4 import android.os.Bundle; 5 6 import java.lang.ref.WeakReference; 7 import java.util.ArrayList; 8 import java.util.Arrays; 9 import java.util.List; 10 import java.util.Map; 11 12 import expo.core.ModuleRegistry; 13 import expo.core.interfaces.InternalModule; 14 import expo.core.interfaces.LifecycleEventListener; 15 import expo.core.interfaces.ModuleRegistryConsumer; 16 import expo.core.interfaces.services.EventEmitter; 17 import expo.core.interfaces.services.UIManager; 18 import expo.interfaces.constants.ConstantsInterface; 19 import expo.interfaces.taskManager.TaskConsumerInterface; 20 import expo.interfaces.taskManager.TaskServiceInterface; 21 import expo.interfaces.taskManager.TaskManagerInterface; 22 23 public class TaskManagerInternalModule implements InternalModule, ModuleRegistryConsumer, 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 Arrays.<Class>asList(TaskManagerInterface.class); 40 } 41 42 //endregion 43 //region ModuleRegistryConsumer 44 45 @Override 46 public void setModuleRegistry(ModuleRegistry moduleRegistry) { 47 if (mUIManager != null) { 48 mUIManager.unregisterLifecycleEventListener(this); 49 } 50 51 mUIManager = moduleRegistry.getModule(UIManager.class); 52 mEventEmitter = moduleRegistry.getModule(EventEmitter.class); 53 mConstants = moduleRegistry.getModule(ConstantsInterface.class); 54 mTaskService = moduleRegistry.getSingletonModule("TaskService", TaskServiceInterface.class); 55 56 // Register in TaskService. 57 mTaskService.setTaskManager(this, getAppId(), getAppUrl()); 58 59 if (mUIManager != null) { 60 mUIManager.registerLifecycleEventListener(this); 61 } 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, getAppId(), getAppUrl(), consumerClass, options); 71 } 72 73 @Override 74 public void unregisterTask(String taskName, Class consumerClass) throws Exception { 75 checkTaskService(); 76 mTaskService.unregisterTask(taskName, getAppId(), consumerClass); 77 } 78 79 @Override 80 public 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(TaskManagerModule.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, getAppId(), consumerClass); 96 } 97 98 @Override 99 public void flushQueuedEvents() { 100 // Execute any events that came before this call. 101 if (mEventsQueue != null) { 102 for (Bundle body : mEventsQueue) { 103 mEventEmitter.emit(TaskManagerModule.EVENT_NAME, body); 104 } 105 mEventsQueue = null; 106 } 107 } 108 109 @Override 110 public String getAppId() { 111 if (mConstants != null) { 112 return mConstants.getAppId(); 113 } 114 return null; 115 } 116 117 @Override 118 public boolean isRunningInHeadlessMode() { 119 if (mConstants != null) { 120 return (boolean) mConstants.getConstants().get("isHeadless"); 121 } 122 return false; 123 } 124 125 //endregion 126 //region LifecycleEventListener 127 128 @Override 129 public void onHostResume() { 130 List<TaskConsumerInterface> taskConsumers = mTaskService.getTaskConsumers(getAppId()); 131 for (TaskConsumerInterface taskConsumer : taskConsumers) { 132 if (taskConsumer instanceof LifecycleEventListener) { 133 ((LifecycleEventListener) taskConsumer).onHostResume(); 134 } 135 } 136 } 137 138 @Override 139 public void onHostPause() { 140 List<TaskConsumerInterface> taskConsumers = mTaskService.getTaskConsumers(getAppId()); 141 for (TaskConsumerInterface taskConsumer : taskConsumers) { 142 if (taskConsumer instanceof LifecycleEventListener) { 143 ((LifecycleEventListener) taskConsumer).onHostPause(); 144 } 145 } 146 } 147 148 @Override 149 public void onHostDestroy() { 150 List<TaskConsumerInterface> taskConsumers = mTaskService.getTaskConsumers(getAppId()); 151 for (TaskConsumerInterface taskConsumer : taskConsumers) { 152 if (taskConsumer instanceof LifecycleEventListener) { 153 ((LifecycleEventListener) taskConsumer).onHostDestroy(); 154 } 155 } 156 } 157 158 //endregion 159 //region helpers 160 161 private String getAppUrl() { 162 // If Constants module is available and experienceUrl is provided, just return it. 163 if (mConstants != null) { 164 String experienceUrl = (String) mConstants.getConstants().get("experienceUrl"); 165 if (experienceUrl != null) { 166 return experienceUrl; 167 } 168 } 169 170 // Fallback to package name? It should work well for RN apps with just one JS app inside. 171 Context context = mContextRef.get(); 172 if (context != null) { 173 return context.getPackageName(); 174 } 175 176 return null; 177 } 178 179 private void checkTaskService() throws IllegalStateException { 180 if (mTaskService == null) { 181 throw new IllegalStateException("Unable to find TaskService singleton module in module registry."); 182 } 183 } 184 185 //endregion 186 } 187