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 org.unimodules.core.ModuleRegistry; 13 import org.unimodules.core.interfaces.InternalModule; 14 import org.unimodules.core.interfaces.LifecycleEventListener; 15 import org.unimodules.core.interfaces.services.EventEmitter; 16 import org.unimodules.core.interfaces.services.UIManager; 17 import org.unimodules.interfaces.constants.ConstantsInterface; 18 import org.unimodules.interfaces.taskManager.TaskConsumerInterface; 19 import org.unimodules.interfaces.taskManager.TaskServiceInterface; 20 import org.unimodules.interfaces.taskManager.TaskManagerInterface; 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 Arrays.<Class>asList(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 } 61 62 //endregion 63 //region TaskManagerInterface 64 65 @Override 66 public void registerTask(String taskName, Class consumerClass, Map<String, Object> options) throws Exception { 67 checkTaskService(); 68 mTaskService.registerTask(taskName, getAppId(), getAppUrl(), consumerClass, options); 69 } 70 71 @Override 72 public void unregisterTask(String taskName, Class consumerClass) throws Exception { 73 checkTaskService(); 74 mTaskService.unregisterTask(taskName, getAppId(), consumerClass); 75 } 76 77 @Override 78 public synchronized void executeTaskWithBody(Bundle body) { 79 if (mEventsQueue != null) { 80 // `startObserving` on TaskManagerModule wasn't called yet - add event body to the queue. 81 mEventsQueue.add(body); 82 } else { 83 // Manager is already being observed by JS app, so we can execute the event immediately. 84 mEventEmitter.emit(TaskManagerModule.EVENT_NAME, body); 85 } 86 } 87 88 @Override 89 public boolean taskHasConsumerOfClass(String taskName, Class consumerClass) { 90 if (mTaskService == null) { 91 return false; 92 } 93 return mTaskService.taskHasConsumerOfClass(taskName, getAppId(), consumerClass); 94 } 95 96 @Override 97 public synchronized void flushQueuedEvents() { 98 // Execute any events that came before this call. 99 if (mEventsQueue != null) { 100 for (Bundle body : mEventsQueue) { 101 mEventEmitter.emit(TaskManagerModule.EVENT_NAME, body); 102 } 103 mEventsQueue = null; 104 } 105 } 106 107 @Override 108 public String getAppId() { 109 if (mConstants != null) { 110 return mConstants.getAppId(); 111 } 112 return null; 113 } 114 115 @Override 116 public boolean isRunningInHeadlessMode() { 117 if (mConstants != null) { 118 return (boolean) mConstants.getConstants().get("isHeadless"); 119 } 120 return false; 121 } 122 123 //endregion 124 //region LifecycleEventListener 125 126 @Override 127 public void onHostResume() { 128 if (!isRunningInHeadlessMode()) { 129 List<TaskConsumerInterface> taskConsumers = mTaskService.getTaskConsumers(getAppId()); 130 for (TaskConsumerInterface taskConsumer : taskConsumers) { 131 if (taskConsumer instanceof LifecycleEventListener) { 132 ((LifecycleEventListener) taskConsumer).onHostResume(); 133 } 134 } 135 } 136 } 137 138 @Override 139 public void onHostPause() { 140 if (!isRunningInHeadlessMode()) { 141 List<TaskConsumerInterface> taskConsumers = mTaskService.getTaskConsumers(getAppId()); 142 for (TaskConsumerInterface taskConsumer : taskConsumers) { 143 if (taskConsumer instanceof LifecycleEventListener) { 144 ((LifecycleEventListener) taskConsumer).onHostPause(); 145 } 146 } 147 } 148 } 149 150 @Override 151 public void onHostDestroy() { 152 if (!isRunningInHeadlessMode()) { 153 List<TaskConsumerInterface> taskConsumers = mTaskService.getTaskConsumers(getAppId()); 154 for (TaskConsumerInterface taskConsumer : taskConsumers) { 155 if (taskConsumer instanceof LifecycleEventListener) { 156 ((LifecycleEventListener) taskConsumer).onHostDestroy(); 157 } 158 } 159 mTaskService.setTaskManager(null, getAppId(), getAppUrl()); 160 } 161 } 162 163 //endregion 164 //region helpers 165 166 private String getAppUrl() { 167 // If Constants module is available and experienceUrl is provided, just return it. 168 if (mConstants != null) { 169 String experienceUrl = (String) mConstants.getConstants().get("experienceUrl"); 170 if (experienceUrl != null) { 171 return experienceUrl; 172 } 173 } 174 175 // Fallback to package name? It should work well for RN apps with just one JS app inside. 176 Context context = mContextRef.get(); 177 if (context != null) { 178 return context.getPackageName(); 179 } 180 181 return null; 182 } 183 184 private void checkTaskService() throws IllegalStateException { 185 if (mTaskService == null) { 186 throw new IllegalStateException("Unable to find TaskService singleton module in module registry."); 187 } 188 } 189 190 //endregion 191 } 192