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