1 package expo.modules.taskManager;
2 
3 import android.content.Context;
4 import android.os.Bundle;
5 import android.util.Log;
6 
7 import java.lang.ref.WeakReference;
8 import java.util.ArrayList;
9 import java.util.Arrays;
10 import java.util.List;
11 import java.util.Map;
12 
13 import expo.core.ModuleRegistry;
14 import expo.core.interfaces.InternalModule;
15 import expo.core.interfaces.ModuleRegistryConsumer;
16 import expo.core.interfaces.services.EventEmitter;
17 import expo.interfaces.constants.ConstantsInterface;
18 import expo.interfaces.taskManager.TaskConsumerInterface;
19 import expo.interfaces.taskManager.TaskServiceInterface;
20 import expo.interfaces.taskManager.TaskManagerInterface;
21 
22 public class TaskManagerInternalModule implements InternalModule, ModuleRegistryConsumer, TaskManagerInterface {
23   private EventEmitter mEventEmitter;
24   private ConstantsInterface mConstants;
25   private TaskServiceInterface mTaskService;
26   private WeakReference<Context> mContextRef;
27   private List<Bundle> mEventsQueue = new ArrayList<>();
28 
29   public TaskManagerInternalModule(Context context) {
30     mContextRef = new WeakReference<>(context);
31   }
32 
33   //region InternalModule
34 
35   @Override
36   public List<Class> getExportedInterfaces() {
37     return Arrays.<Class>asList(TaskManagerInterface.class);
38   }
39 
40   //endregion
41   //region ModuleRegistryConsumer
42 
43   @Override
44   public void setModuleRegistry(ModuleRegistry moduleRegistry) {
45     mEventEmitter = moduleRegistry.getModule(EventEmitter.class);
46     mConstants = moduleRegistry.getModule(ConstantsInterface.class);
47     mTaskService = moduleRegistry.getSingletonModule("TaskService", TaskServiceInterface.class);
48 
49     // Register in TaskService.
50     mTaskService.setTaskManager(this, getAppId(), getAppUrl());
51   }
52 
53   //endregion
54   //region TaskManagerInterface
55 
56   @Override
57   public void registerTask(String taskName, Class consumerClass, Map<String, Object> options) throws Exception {
58     checkTaskService();
59     mTaskService.registerTask(taskName, getAppId(), getAppUrl(), consumerClass, options);
60   }
61 
62   @Override
63   public void unregisterTask(String taskName, Class consumerClass) throws Exception {
64     checkTaskService();
65     mTaskService.unregisterTask(taskName, getAppId(), consumerClass);
66   }
67 
68   @Override
69   public void executeTaskWithBody(Bundle body) {
70     if (mEventsQueue != null) {
71       // `startObserving` on TaskManagerModule wasn't called yet - add event body to the queue.
72       mEventsQueue.add(body);
73     } else {
74       // Manager is already being observed by JS app, so we can execute the event immediately.
75       mEventEmitter.emit(TaskManagerModule.EVENT_NAME, body);
76     }
77   }
78 
79   @Override
80   public boolean taskHasConsumerOfClass(String taskName, Class consumerClass) {
81     if (mTaskService == null) {
82       return false;
83     }
84     return mTaskService.taskHasConsumerOfClass(taskName, getAppId(), consumerClass);
85   }
86 
87   @Override
88   public void flushQueuedEvents() {
89     // Execute any events that came before this call.
90     if (mEventsQueue != null) {
91       for (Bundle body : mEventsQueue) {
92         mEventEmitter.emit(TaskManagerModule.EVENT_NAME, body);
93       }
94       mEventsQueue = null;
95     }
96   }
97 
98   @Override
99   public String getAppId() {
100     if (mConstants != null) {
101       return mConstants.getAppId();
102     }
103     return null;
104   }
105 
106   @Override
107   public boolean isRunningInHeadlessMode() {
108     if (mConstants != null) {
109       return (boolean) mConstants.getConstants().get("isHeadless");
110     }
111     return false;
112   }
113 
114   //endregion
115   //region helpers
116 
117   private String getAppUrl() {
118     // If Constants module is available and experienceUrl is provided, just return it.
119     if (mConstants != null) {
120       String experienceUrl = (String) mConstants.getConstants().get("experienceUrl");
121       if (experienceUrl != null) {
122         return experienceUrl;
123       }
124     }
125 
126     // Fallback to package name? It should work well for RN apps with just one JS app inside.
127     Context context = mContextRef.get();
128     if (context != null) {
129       return context.getPackageName();
130     }
131 
132     return null;
133   }
134 
135   private void checkTaskService() throws IllegalStateException {
136     if (mTaskService == null) {
137       throw new IllegalStateException("Unable to find TaskService singleton module in module registry.");
138     }
139   }
140 
141   //endregion
142 }
143