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