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