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