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.ModuleRegistryConsumer;
16 import org.unimodules.core.interfaces.services.EventEmitter;
17 import org.unimodules.core.interfaces.services.UIManager;
18 import org.unimodules.interfaces.constants.ConstantsInterface;
19 import org.unimodules.interfaces.taskManager.TaskConsumerInterface;
20 import org.unimodules.interfaces.taskManager.TaskServiceInterface;
21 import org.unimodules.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     if (!isRunningInHeadlessMode()) {
131       List<TaskConsumerInterface> taskConsumers = mTaskService.getTaskConsumers(getAppId());
132       for (TaskConsumerInterface taskConsumer : taskConsumers) {
133         if (taskConsumer instanceof LifecycleEventListener) {
134           ((LifecycleEventListener) taskConsumer).onHostResume();
135         }
136       }
137     }
138   }
139 
140   @Override
141   public void onHostPause() {
142     if (!isRunningInHeadlessMode()) {
143       List<TaskConsumerInterface> taskConsumers = mTaskService.getTaskConsumers(getAppId());
144       for (TaskConsumerInterface taskConsumer : taskConsumers) {
145         if (taskConsumer instanceof LifecycleEventListener) {
146           ((LifecycleEventListener) taskConsumer).onHostPause();
147         }
148       }
149     }
150   }
151 
152   @Override
153   public void onHostDestroy() {
154     if (!isRunningInHeadlessMode()) {
155       List<TaskConsumerInterface> taskConsumers = mTaskService.getTaskConsumers(getAppId());
156       for (TaskConsumerInterface taskConsumer : taskConsumers) {
157         if (taskConsumer instanceof LifecycleEventListener) {
158           ((LifecycleEventListener) taskConsumer).onHostDestroy();
159         }
160       }
161       mTaskService.setTaskManager(null, getAppId(), getAppUrl());
162     }
163   }
164 
165   //endregion
166   //region helpers
167 
168   private String getAppUrl() {
169     // If Constants module is available and experienceUrl is provided, just return it.
170     if (mConstants != null) {
171       String experienceUrl = (String) mConstants.getConstants().get("experienceUrl");
172       if (experienceUrl != null) {
173         return experienceUrl;
174       }
175     }
176 
177     // Fallback to package name? It should work well for RN apps with just one JS app inside.
178     Context context = mContextRef.get();
179     if (context != null) {
180       return context.getPackageName();
181     }
182 
183     return null;
184   }
185 
186   private void checkTaskService() throws IllegalStateException {
187     if (mTaskService == null) {
188       throw new IllegalStateException("Unable to find TaskService singleton module in module registry.");
189     }
190   }
191 
192   //endregion
193 }
194