1090208b5SStanisław Chmiela package io.branch.rnbranch;
2090208b5SStanisław Chmiela 
3090208b5SStanisław Chmiela import android.app.Activity;
4090208b5SStanisław Chmiela import android.content.Context;
5090208b5SStanisław Chmiela import android.content.Intent;
6090208b5SStanisław Chmiela import android.content.IntentFilter;
7090208b5SStanisław Chmiela import android.content.BroadcastReceiver;
8090208b5SStanisław Chmiela import android.net.Uri;
9ef35242dSStanisław Chmiela 
10db136923SSzymon20000 import androidx.annotation.Nullable;
11db136923SSzymon20000 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
12090208b5SStanisław Chmiela import android.util.Log;
13090208b5SStanisław Chmiela import android.os.Handler;
14090208b5SStanisław Chmiela 
15090208b5SStanisław Chmiela import com.facebook.react.bridge.*;
16090208b5SStanisław Chmiela import com.facebook.react.bridge.Promise;
17090208b5SStanisław Chmiela import com.facebook.react.modules.core.*;
18090208b5SStanisław Chmiela import com.facebook.react.bridge.ReadableMap;
19090208b5SStanisław Chmiela 
20090208b5SStanisław Chmiela import io.branch.referral.*;
21090208b5SStanisław Chmiela import io.branch.referral.Branch.BranchLinkCreateListener;
22090208b5SStanisław Chmiela import io.branch.referral.BuildConfig;
23090208b5SStanisław Chmiela import io.branch.referral.util.*;
24090208b5SStanisław Chmiela import io.branch.referral.Branch;
25090208b5SStanisław Chmiela import io.branch.indexing.*;
26090208b5SStanisław Chmiela 
27090208b5SStanisław Chmiela import org.json.*;
28090208b5SStanisław Chmiela 
29090208b5SStanisław Chmiela import java.text.ParseException;
30090208b5SStanisław Chmiela import java.text.SimpleDateFormat;
31090208b5SStanisław Chmiela import java.util.*;
32090208b5SStanisław Chmiela 
33*de56ef51SStanisław Chmiela import javax.annotation.Nonnull;
34*de56ef51SStanisław Chmiela 
35090208b5SStanisław Chmiela public class RNBranchModule extends ReactContextBaseJavaModule {
36090208b5SStanisław Chmiela     public static final String REACT_CLASS = "RNBranch";
37090208b5SStanisław Chmiela     public static final String REACT_MODULE_NAME = "RNBranch";
38090208b5SStanisław Chmiela     public static final String NATIVE_INIT_SESSION_FINISHED_EVENT = "io.branch.rnbranch.RNBranchModule.onInitSessionFinished";
39090208b5SStanisław Chmiela     public static final String NATIVE_INIT_SESSION_FINISHED_EVENT_BRANCH_UNIVERSAL_OBJECT = "branch_universal_object";
40090208b5SStanisław Chmiela     public static final String NATIVE_INIT_SESSION_FINISHED_EVENT_LINK_PROPERTIES = "link_properties";
41090208b5SStanisław Chmiela     public static final String NATIVE_INIT_SESSION_FINISHED_EVENT_PARAMS = "params";
42090208b5SStanisław Chmiela     public static final String NATIVE_INIT_SESSION_FINISHED_EVENT_ERROR = "error";
43090208b5SStanisław Chmiela     public static final String NATIVE_INIT_SESSION_FINISHED_EVENT_URI = "uri";
44ef35242dSStanisław Chmiela     public static final String NATIVE_INIT_SESSION_STARTED_EVENT = "io.branch.rnbranch.RNBranchModule.onInitSessionStarted";
45ef35242dSStanisław Chmiela     public static final String NATIVE_INIT_SESSION_STARTED_EVENT_URI = "uri";
46090208b5SStanisław Chmiela     private static final String RN_INIT_SESSION_SUCCESS_EVENT = "RNBranch.initSessionSuccess";
47090208b5SStanisław Chmiela     private static final String RN_INIT_SESSION_ERROR_EVENT = "RNBranch.initSessionError";
48ef35242dSStanisław Chmiela     private static final String RN_INIT_SESSION_START_EVENT = "RNBranch.initSessionStart";
49090208b5SStanisław Chmiela     private static final String INIT_SESSION_SUCCESS = "INIT_SESSION_SUCCESS";
50090208b5SStanisław Chmiela     private static final String INIT_SESSION_ERROR = "INIT_SESSION_ERROR";
51ef35242dSStanisław Chmiela     private static final String INIT_SESSION_START = "INIT_SESSION_START";
52090208b5SStanisław Chmiela 
53090208b5SStanisław Chmiela     private static final String STANDARD_EVENT_ADD_TO_CART = "STANDARD_EVENT_ADD_TO_CART";
54090208b5SStanisław Chmiela     private static final String STANDARD_EVENT_ADD_TO_WISHLIST = "STANDARD_EVENT_ADD_TO_WISHLIST";
55090208b5SStanisław Chmiela     private static final String STANDARD_EVENT_VIEW_CART = "STANDARD_EVENT_VIEW_CART";
56090208b5SStanisław Chmiela     private static final String STANDARD_EVENT_INITIATE_PURCHASE = "STANDARD_EVENT_INITIATE_PURCHASE";
57090208b5SStanisław Chmiela     private static final String STANDARD_EVENT_ADD_PAYMENT_INFO = "STANDARD_EVENT_ADD_PAYMENT_INFO";
58090208b5SStanisław Chmiela     private static final String STANDARD_EVENT_PURCHASE = "STANDARD_EVENT_PURCHASE";
59090208b5SStanisław Chmiela     private static final String STANDARD_EVENT_SPEND_CREDITS = "STANDARD_EVENT_SPEND_CREDITS";
60ef35242dSStanisław Chmiela     private static final String STANDARD_EVENT_VIEW_AD = "STANDARD_EVENT_VIEW_AD";
61ef35242dSStanisław Chmiela     private static final String STANDARD_EVENT_CLICK_AD = "STANDARD_EVENT_CLICK_AD";
62090208b5SStanisław Chmiela 
63090208b5SStanisław Chmiela     private static final String STANDARD_EVENT_SEARCH = "STANDARD_EVENT_SEARCH";
64090208b5SStanisław Chmiela     private static final String STANDARD_EVENT_VIEW_ITEM = "STANDARD_EVENT_VIEW_ITEM";
65090208b5SStanisław Chmiela     private static final String STANDARD_EVENT_VIEW_ITEMS = "STANDARD_EVENT_VIEW_ITEMS";
66090208b5SStanisław Chmiela     private static final String STANDARD_EVENT_RATE = "STANDARD_EVENT_RATE";
67090208b5SStanisław Chmiela     private static final String STANDARD_EVENT_SHARE = "STANDARD_EVENT_SHARE";
68090208b5SStanisław Chmiela 
69090208b5SStanisław Chmiela     private static final String STANDARD_EVENT_COMPLETE_REGISTRATION = "STANDARD_EVENT_COMPLETE_REGISTRATION";
70090208b5SStanisław Chmiela     private static final String STANDARD_EVENT_COMPLETE_TUTORIAL = "STANDARD_EVENT_COMPLETE_TUTORIAL";
71090208b5SStanisław Chmiela     private static final String STANDARD_EVENT_ACHIEVE_LEVEL = "STANDARD_EVENT_ACHIEVE_LEVEL";
72090208b5SStanisław Chmiela     private static final String STANDARD_EVENT_UNLOCK_ACHIEVEMENT = "STANDARD_EVENT_UNLOCK_ACHIEVEMENT";
73ef35242dSStanisław Chmiela     private static final String STANDARD_EVENT_INVITE = "STANDARD_EVENT_INVITE";
74ef35242dSStanisław Chmiela     private static final String STANDARD_EVENT_LOGIN = "STANDARD_EVENT_LOGIN";
75ef35242dSStanisław Chmiela     private static final String STANDARD_EVENT_RESERVE = "STANDARD_EVENT_RESERVE";
76ef35242dSStanisław Chmiela     private static final String STANDARD_EVENT_SUBSCRIBE = "STANDARD_EVENT_SUBSCRIBE";
77ef35242dSStanisław Chmiela     private static final String STANDARD_EVENT_START_TRIAL = "STANDARD_EVENT_START_TRIAL";
78090208b5SStanisław Chmiela 
79090208b5SStanisław Chmiela     private static final String IDENT_FIELD_NAME = "ident";
80090208b5SStanisław Chmiela     public static final String UNIVERSAL_OBJECT_NOT_FOUND_ERROR_CODE = "RNBranch::Error::BUONotFound";
81000da56bSStanisław Chmiela     public static final String GENERIC_ERROR = "RNBranch::Error";
82090208b5SStanisław Chmiela     private static final long AGING_HASH_TTL = 3600000;
83ef35242dSStanisław Chmiela     private static final String PLUGIN_NAME = "ReactNative";
84090208b5SStanisław Chmiela 
85090208b5SStanisław Chmiela     private static JSONObject initSessionResult = null;
86ef35242dSStanisław Chmiela     private BroadcastReceiver mInitSessionFinishedEventReceiver = null;
87ef35242dSStanisław Chmiela     private BroadcastReceiver mInitSessionStartedEventReceiver = null;
88090208b5SStanisław Chmiela     private static Branch.BranchUniversalReferralInitListener initListener = null;
89090208b5SStanisław Chmiela 
90090208b5SStanisław Chmiela     private static Activity mActivity = null;
91090208b5SStanisław Chmiela     private static boolean mUseDebug = false;
92090208b5SStanisław Chmiela     private static boolean mInitialized = false;
93*de56ef51SStanisław Chmiela     private static volatile boolean mNewIntent = true;
94090208b5SStanisław Chmiela     private static JSONObject mRequestMetadata = new JSONObject();
95090208b5SStanisław Chmiela 
96090208b5SStanisław Chmiela     private AgingHash<String, BranchUniversalObject> mUniversalObjectMap = new AgingHash<>(AGING_HASH_TTL);
97090208b5SStanisław Chmiela 
98000da56bSStanisław Chmiela     private static Branch.BranchReferralInitListener referralInitListener = null;
99000da56bSStanisław Chmiela 
getAutoInstance(Context context)100090208b5SStanisław Chmiela     public static void getAutoInstance(Context context) {
101090208b5SStanisław Chmiela         RNBranchConfig config = new RNBranchConfig(context);
102090208b5SStanisław Chmiela         String branchKey = config.getBranchKey();
103090208b5SStanisław Chmiela         String liveKey = config.getLiveKey();
104090208b5SStanisław Chmiela         String testKey = config.getTestKey();
105090208b5SStanisław Chmiela         boolean useTest = config.getUseTestInstance();
106090208b5SStanisław Chmiela 
107ef35242dSStanisław Chmiela         Branch.registerPlugin(PLUGIN_NAME, expo.modules.branch.BuildConfig.RNBRANCH_VERSION);
108ef35242dSStanisław Chmiela 
109090208b5SStanisław Chmiela         if (branchKey != null) {
110090208b5SStanisław Chmiela             Branch.getAutoInstance(context, branchKey);
111090208b5SStanisław Chmiela         }
112090208b5SStanisław Chmiela         else if (useTest && testKey != null) {
113090208b5SStanisław Chmiela             Branch.getAutoInstance(context, testKey);
114090208b5SStanisław Chmiela         }
115090208b5SStanisław Chmiela         else if (!useTest && liveKey != null) {
116090208b5SStanisław Chmiela             Branch.getAutoInstance(context, liveKey);
117090208b5SStanisław Chmiela         }
118090208b5SStanisław Chmiela         else {
119090208b5SStanisław Chmiela             Branch.getAutoInstance(context);
120090208b5SStanisław Chmiela         }
121090208b5SStanisław Chmiela     }
122090208b5SStanisław Chmiela 
reInitSession(Activity reactActivity)123000da56bSStanisław Chmiela     public static void reInitSession(Activity reactActivity) {
124000da56bSStanisław Chmiela         Branch branch = Branch.getInstance();
125ef35242dSStanisław Chmiela         Intent intent = reactActivity.getIntent();
126ef35242dSStanisław Chmiela         if (intent != null) {
127ef35242dSStanisław Chmiela             intent.putExtra("branch_force_new_session", true);
128ef35242dSStanisław Chmiela             notifyJSOfInitSessionStart(reactActivity, intent.getData());
129ef35242dSStanisław Chmiela             Branch.sessionBuilder(reactActivity).withCallback(referralInitListener).reInit();
130ef35242dSStanisław Chmiela         } else {
131ef35242dSStanisław Chmiela             Log.w(REACT_CLASS, "reInitSession was called but the Intent is null");
132ef35242dSStanisław Chmiela         }
133000da56bSStanisław Chmiela     }
134000da56bSStanisław Chmiela 
initSession(final Uri uri, Activity reactActivity, Branch.BranchUniversalReferralInitListener anInitListener)135090208b5SStanisław Chmiela     public static void initSession(final Uri uri, Activity reactActivity, Branch.BranchUniversalReferralInitListener anInitListener) {
136090208b5SStanisław Chmiela         initListener = anInitListener;
137090208b5SStanisław Chmiela         initSession(uri, reactActivity);
138090208b5SStanisław Chmiela     }
139090208b5SStanisław Chmiela 
initSession(final Uri uri, Activity reactActivity)140090208b5SStanisław Chmiela     public static void initSession(final Uri uri, Activity reactActivity) {
141090208b5SStanisław Chmiela         Branch branch = setupBranch(reactActivity.getApplicationContext());
142090208b5SStanisław Chmiela 
143090208b5SStanisław Chmiela         mActivity = reactActivity;
144*de56ef51SStanisław Chmiela         final boolean isNewIntent = mNewIntent;
145000da56bSStanisław Chmiela         referralInitListener = new Branch.BranchReferralInitListener(){
146090208b5SStanisław Chmiela 
147090208b5SStanisław Chmiela             private Activity mmActivity = null;
148090208b5SStanisław Chmiela 
149090208b5SStanisław Chmiela             @Override
150090208b5SStanisław Chmiela             public void onInitFinished(JSONObject referringParams, BranchError error) {
151ef35242dSStanisław Chmiela                 // react native currently expects this to never be null
152ef35242dSStanisław Chmiela                 if (referringParams == null) {
153ef35242dSStanisław Chmiela                     referringParams = new JSONObject();
154ef35242dSStanisław Chmiela                 }
155090208b5SStanisław Chmiela 
156090208b5SStanisław Chmiela                 Log.d(REACT_CLASS, "onInitFinished");
157090208b5SStanisław Chmiela                 JSONObject result = new JSONObject();
158ef35242dSStanisław Chmiela 
159090208b5SStanisław Chmiela                 try {
160090208b5SStanisław Chmiela                     result.put(NATIVE_INIT_SESSION_FINISHED_EVENT_PARAMS, referringParams);
161090208b5SStanisław Chmiela                     result.put(NATIVE_INIT_SESSION_FINISHED_EVENT_ERROR, error != null ? error.getMessage() : JSONObject.NULL);
162*de56ef51SStanisław Chmiela                     result.put(NATIVE_INIT_SESSION_FINISHED_EVENT_URI, isNewIntent && uri != null ? uri.toString() : JSONObject.NULL);
163ef35242dSStanisław Chmiela                 }
164ef35242dSStanisław Chmiela                 catch (JSONException e) {
165ef35242dSStanisław Chmiela 
166090208b5SStanisław Chmiela                 }
167090208b5SStanisław Chmiela                 initSessionResult = result;
168090208b5SStanisław Chmiela 
169090208b5SStanisław Chmiela                 BranchUniversalObject branchUniversalObject =  BranchUniversalObject.getReferredBranchUniversalObject();
170090208b5SStanisław Chmiela                 LinkProperties linkProperties = LinkProperties.getReferredLinkProperties();
171090208b5SStanisław Chmiela 
172090208b5SStanisław Chmiela                 if (initListener != null) {
173090208b5SStanisław Chmiela                     initListener.onInitFinished(branchUniversalObject, linkProperties, error);
174090208b5SStanisław Chmiela                 }
175ef35242dSStanisław Chmiela                 generateLocalBroadcast(referringParams, uri, branchUniversalObject, linkProperties, error);
176090208b5SStanisław Chmiela             }
177090208b5SStanisław Chmiela 
178090208b5SStanisław Chmiela             private Branch.BranchReferralInitListener init(Activity activity) {
179090208b5SStanisław Chmiela                 mmActivity = activity;
180090208b5SStanisław Chmiela                 return this;
181090208b5SStanisław Chmiela             }
182090208b5SStanisław Chmiela 
183090208b5SStanisław Chmiela             private void generateLocalBroadcast(JSONObject referringParams,
184090208b5SStanisław Chmiela                                                 Uri uri,
185090208b5SStanisław Chmiela                                                 BranchUniversalObject branchUniversalObject,
186090208b5SStanisław Chmiela                                                 LinkProperties linkProperties,
187090208b5SStanisław Chmiela                                                 BranchError error) {
188090208b5SStanisław Chmiela                 Intent broadcastIntent = new Intent(NATIVE_INIT_SESSION_FINISHED_EVENT);
189090208b5SStanisław Chmiela 
190090208b5SStanisław Chmiela                 if (referringParams != null) {
191090208b5SStanisław Chmiela                     broadcastIntent.putExtra(NATIVE_INIT_SESSION_FINISHED_EVENT_PARAMS, referringParams.toString());
192090208b5SStanisław Chmiela                 }
193090208b5SStanisław Chmiela 
194090208b5SStanisław Chmiela                 if (branchUniversalObject != null) {
195090208b5SStanisław Chmiela                     broadcastIntent.putExtra(NATIVE_INIT_SESSION_FINISHED_EVENT_BRANCH_UNIVERSAL_OBJECT, branchUniversalObject);
196090208b5SStanisław Chmiela                 }
197090208b5SStanisław Chmiela 
198090208b5SStanisław Chmiela                 if (linkProperties != null) {
199090208b5SStanisław Chmiela                     broadcastIntent.putExtra(NATIVE_INIT_SESSION_FINISHED_EVENT_LINK_PROPERTIES, linkProperties);
200090208b5SStanisław Chmiela                 }
201090208b5SStanisław Chmiela 
202*de56ef51SStanisław Chmiela                 /*
203*de56ef51SStanisław Chmiela                  * isNewIntent is a capture of the value of mNewIntent above, so does not change when
204*de56ef51SStanisław Chmiela                  * mNewIntent changes in onNewIntent.
205*de56ef51SStanisław Chmiela                  */
206*de56ef51SStanisław Chmiela                 if (isNewIntent && uri != null) {
207090208b5SStanisław Chmiela                     broadcastIntent.putExtra(NATIVE_INIT_SESSION_FINISHED_EVENT_URI, uri.toString());
208090208b5SStanisław Chmiela                 }
209090208b5SStanisław Chmiela 
210090208b5SStanisław Chmiela                 if (error != null) {
211090208b5SStanisław Chmiela                     broadcastIntent.putExtra(NATIVE_INIT_SESSION_FINISHED_EVENT_ERROR, error.getMessage());
212090208b5SStanisław Chmiela                 }
213090208b5SStanisław Chmiela 
214090208b5SStanisław Chmiela                 LocalBroadcastManager.getInstance(mmActivity).sendBroadcast(broadcastIntent);
215090208b5SStanisław Chmiela             }
216000da56bSStanisław Chmiela         }.init(reactActivity);
217000da56bSStanisław Chmiela 
218ef35242dSStanisław Chmiela         notifyJSOfInitSessionStart(reactActivity, uri);
219ef35242dSStanisław Chmiela         Branch.sessionBuilder(reactActivity).withCallback(referralInitListener).withData(uri).init();
220ef35242dSStanisław Chmiela     }
221ef35242dSStanisław Chmiela 
222ef35242dSStanisław Chmiela     /**
223*de56ef51SStanisław Chmiela      * Call from Activity.onNewIntent:
224*de56ef51SStanisław Chmiela      *   @Override
225*de56ef51SStanisław Chmiela      *   public void onNewIntent(Intent intent) {
226*de56ef51SStanisław Chmiela      *     super.onNewIntent(intent);
227*de56ef51SStanisław Chmiela      *     RNBranchModule.onNewIntent(intent);
228*de56ef51SStanisław Chmiela      *   }
229*de56ef51SStanisław Chmiela      * @param intent the new Intent received via Activity.onNewIntent
230*de56ef51SStanisław Chmiela      */
onNewIntent(@onnull Intent intent)231*de56ef51SStanisław Chmiela     public static void onNewIntent(@Nonnull Intent intent) {
232*de56ef51SStanisław Chmiela         mActivity.setIntent(intent);
233*de56ef51SStanisław Chmiela         mNewIntent = true;
234*de56ef51SStanisław Chmiela         reInitSession(mActivity);
235*de56ef51SStanisław Chmiela     }
236*de56ef51SStanisław Chmiela 
237*de56ef51SStanisław Chmiela     /**
238ef35242dSStanisław Chmiela      * Notify JavaScript of init session start. This generates an RNBranch.initSessionStart
239ef35242dSStanisław Chmiela      * event to JS via the RN native event emitter.
240ef35242dSStanisław Chmiela      * @param context a Context for the LocalBroadcastManager
241ef35242dSStanisław Chmiela      * @param uri the URI to include in the notification or null
242ef35242dSStanisław Chmiela      */
notifyJSOfInitSessionStart(Context context, Uri uri)243ef35242dSStanisław Chmiela     private static void notifyJSOfInitSessionStart(Context context, Uri uri) {
244*de56ef51SStanisław Chmiela         /*
245*de56ef51SStanisław Chmiela          * This check just ensures that we only generate one RNBranch.initSessionStart
246*de56ef51SStanisław Chmiela          * event per call to onNewIntent().
247*de56ef51SStanisław Chmiela          */
248*de56ef51SStanisław Chmiela         if (!mNewIntent) return;
249*de56ef51SStanisław Chmiela         mNewIntent = false;
250*de56ef51SStanisław Chmiela 
251ef35242dSStanisław Chmiela         Intent broadcastIntent = new Intent(NATIVE_INIT_SESSION_STARTED_EVENT);
252ef35242dSStanisław Chmiela         if (uri != null) {
253ef35242dSStanisław Chmiela             broadcastIntent.putExtra(NATIVE_INIT_SESSION_STARTED_EVENT_URI, uri);
254ef35242dSStanisław Chmiela         }
255ef35242dSStanisław Chmiela 
256ef35242dSStanisław Chmiela         LocalBroadcastManager.getInstance(context).sendBroadcast(broadcastIntent);
257ef35242dSStanisław Chmiela         Log.d(REACT_CLASS, "Sent session start broadcast for " + uri);
258090208b5SStanisław Chmiela     }
259090208b5SStanisław Chmiela 
setDebug()260090208b5SStanisław Chmiela     public static void setDebug() {
261090208b5SStanisław Chmiela         mUseDebug = true;
262090208b5SStanisław Chmiela     }
263090208b5SStanisław Chmiela 
setRequestMetadata(String key, String val)264090208b5SStanisław Chmiela     public static void setRequestMetadata(String key, String val) {
265090208b5SStanisław Chmiela         if (key == null) {
266090208b5SStanisław Chmiela             return;
267090208b5SStanisław Chmiela         }
268090208b5SStanisław Chmiela 
269090208b5SStanisław Chmiela         if (mRequestMetadata.has(key) && val == null) {
270090208b5SStanisław Chmiela             mRequestMetadata.remove(key);
271090208b5SStanisław Chmiela         }
272090208b5SStanisław Chmiela 
273090208b5SStanisław Chmiela         try {
274090208b5SStanisław Chmiela             mRequestMetadata.put(key, val);
275090208b5SStanisław Chmiela         } catch (JSONException e) {
276090208b5SStanisław Chmiela             // no-op
277090208b5SStanisław Chmiela         }
278090208b5SStanisław Chmiela     }
279090208b5SStanisław Chmiela 
RNBranchModule(ReactApplicationContext reactContext)280090208b5SStanisław Chmiela     public RNBranchModule(ReactApplicationContext reactContext) {
281090208b5SStanisław Chmiela         super(reactContext);
282ef35242dSStanisław Chmiela         listenForInitSessionEventsToReactNative(reactContext);
283090208b5SStanisław Chmiela     }
284090208b5SStanisław Chmiela 
285090208b5SStanisław Chmiela     @javax.annotation.Nullable
286090208b5SStanisław Chmiela     @Override
getConstants()287090208b5SStanisław Chmiela     public Map<String, Object> getConstants() {
288090208b5SStanisław Chmiela         final Map<String, Object> constants = new HashMap<>();
289090208b5SStanisław Chmiela         // RN events transmitted to JS
290090208b5SStanisław Chmiela 
291090208b5SStanisław Chmiela         constants.put(INIT_SESSION_SUCCESS, RN_INIT_SESSION_SUCCESS_EVENT);
292090208b5SStanisław Chmiela         constants.put(INIT_SESSION_ERROR, RN_INIT_SESSION_ERROR_EVENT);
293ef35242dSStanisław Chmiela         constants.put(INIT_SESSION_START, RN_INIT_SESSION_START_EVENT);
294090208b5SStanisław Chmiela 
295090208b5SStanisław Chmiela         // constants for use with BranchEvent
296090208b5SStanisław Chmiela 
297090208b5SStanisław Chmiela         // Commerce events
298090208b5SStanisław Chmiela 
299090208b5SStanisław Chmiela         constants.put(STANDARD_EVENT_ADD_TO_CART, BRANCH_STANDARD_EVENT.ADD_TO_CART.getName());
300090208b5SStanisław Chmiela         constants.put(STANDARD_EVENT_ADD_TO_WISHLIST, BRANCH_STANDARD_EVENT.ADD_TO_WISHLIST.getName());
301090208b5SStanisław Chmiela         constants.put(STANDARD_EVENT_VIEW_CART, BRANCH_STANDARD_EVENT.VIEW_CART.getName());
302090208b5SStanisław Chmiela         constants.put(STANDARD_EVENT_INITIATE_PURCHASE, BRANCH_STANDARD_EVENT.INITIATE_PURCHASE.getName());
303090208b5SStanisław Chmiela         constants.put(STANDARD_EVENT_ADD_PAYMENT_INFO, BRANCH_STANDARD_EVENT.ADD_PAYMENT_INFO.getName());
304090208b5SStanisław Chmiela         constants.put(STANDARD_EVENT_PURCHASE, BRANCH_STANDARD_EVENT.PURCHASE.getName());
305090208b5SStanisław Chmiela         constants.put(STANDARD_EVENT_SPEND_CREDITS, BRANCH_STANDARD_EVENT.SPEND_CREDITS.getName());
306ef35242dSStanisław Chmiela         constants.put(STANDARD_EVENT_VIEW_AD, BRANCH_STANDARD_EVENT.VIEW_AD.getName());
307ef35242dSStanisław Chmiela         constants.put(STANDARD_EVENT_CLICK_AD, BRANCH_STANDARD_EVENT.CLICK_AD.getName());
308090208b5SStanisław Chmiela 
309090208b5SStanisław Chmiela         // Content Events
310090208b5SStanisław Chmiela 
311090208b5SStanisław Chmiela         constants.put(STANDARD_EVENT_SEARCH, BRANCH_STANDARD_EVENT.SEARCH.getName());
312090208b5SStanisław Chmiela         constants.put(STANDARD_EVENT_VIEW_ITEM, BRANCH_STANDARD_EVENT.VIEW_ITEM.getName());
313090208b5SStanisław Chmiela         constants.put(STANDARD_EVENT_VIEW_ITEMS , BRANCH_STANDARD_EVENT.VIEW_ITEMS.getName());
314090208b5SStanisław Chmiela         constants.put(STANDARD_EVENT_RATE, BRANCH_STANDARD_EVENT.RATE.getName());
315090208b5SStanisław Chmiela         constants.put(STANDARD_EVENT_SHARE, BRANCH_STANDARD_EVENT.SHARE.getName());
316090208b5SStanisław Chmiela 
317090208b5SStanisław Chmiela         // User Lifecycle Events
318090208b5SStanisław Chmiela 
319090208b5SStanisław Chmiela         constants.put(STANDARD_EVENT_COMPLETE_REGISTRATION, BRANCH_STANDARD_EVENT.COMPLETE_REGISTRATION.getName());
320090208b5SStanisław Chmiela         constants.put(STANDARD_EVENT_COMPLETE_TUTORIAL , BRANCH_STANDARD_EVENT.COMPLETE_TUTORIAL.getName());
321090208b5SStanisław Chmiela         constants.put(STANDARD_EVENT_ACHIEVE_LEVEL, BRANCH_STANDARD_EVENT.ACHIEVE_LEVEL.getName());
322090208b5SStanisław Chmiela         constants.put(STANDARD_EVENT_UNLOCK_ACHIEVEMENT, BRANCH_STANDARD_EVENT.UNLOCK_ACHIEVEMENT.getName());
323ef35242dSStanisław Chmiela         constants.put(STANDARD_EVENT_INVITE, BRANCH_STANDARD_EVENT.INVITE.getName());
324ef35242dSStanisław Chmiela         constants.put(STANDARD_EVENT_LOGIN , BRANCH_STANDARD_EVENT.LOGIN.getName());
325ef35242dSStanisław Chmiela         constants.put(STANDARD_EVENT_RESERVE, BRANCH_STANDARD_EVENT.RESERVE.getName());
326ef35242dSStanisław Chmiela         constants.put(STANDARD_EVENT_SUBSCRIBE, BRANCH_STANDARD_EVENT.SUBSCRIBE.getName());
327ef35242dSStanisław Chmiela         constants.put(STANDARD_EVENT_START_TRIAL, BRANCH_STANDARD_EVENT.START_TRIAL.getName());
328090208b5SStanisław Chmiela 
329090208b5SStanisław Chmiela         return constants;
330090208b5SStanisław Chmiela     }
331090208b5SStanisław Chmiela 
listenForInitSessionEventsToReactNative(ReactApplicationContext reactContext)332ef35242dSStanisław Chmiela     private void listenForInitSessionEventsToReactNative(ReactApplicationContext reactContext) {
333ef35242dSStanisław Chmiela         mInitSessionFinishedEventReceiver = new BroadcastReceiver() {
334090208b5SStanisław Chmiela             RNBranchModule mBranchModule;
335090208b5SStanisław Chmiela 
336090208b5SStanisław Chmiela             @Override
337090208b5SStanisław Chmiela             public void onReceive(Context context, Intent intent) {
338090208b5SStanisław Chmiela                 final boolean hasError = (initSessionResult.has("error") && !initSessionResult.isNull("error"));
339090208b5SStanisław Chmiela                 final String eventName = hasError ? RN_INIT_SESSION_ERROR_EVENT : RN_INIT_SESSION_SUCCESS_EVENT;
340090208b5SStanisław Chmiela                 mBranchModule.sendRNEvent(eventName, convertJsonToMap(initSessionResult));
341090208b5SStanisław Chmiela             }
342090208b5SStanisław Chmiela 
343090208b5SStanisław Chmiela             private BroadcastReceiver init(RNBranchModule branchModule) {
344090208b5SStanisław Chmiela                 mBranchModule = branchModule;
345090208b5SStanisław Chmiela                 return this;
346090208b5SStanisław Chmiela             }
347090208b5SStanisław Chmiela         }.init(this);
348090208b5SStanisław Chmiela 
349ef35242dSStanisław Chmiela         LocalBroadcastManager.getInstance(reactContext).registerReceiver(mInitSessionFinishedEventReceiver, new IntentFilter(NATIVE_INIT_SESSION_FINISHED_EVENT));
350ef35242dSStanisław Chmiela 
351ef35242dSStanisław Chmiela         mInitSessionStartedEventReceiver = new BroadcastReceiver() {
352ef35242dSStanisław Chmiela             RNBranchModule mBranchModule;
353ef35242dSStanisław Chmiela 
354ef35242dSStanisław Chmiela             @Override
355ef35242dSStanisław Chmiela             public void onReceive(Context context, Intent intent) {
356ef35242dSStanisław Chmiela                 Uri uri = intent.getParcelableExtra(NATIVE_INIT_SESSION_STARTED_EVENT_URI);
357ef35242dSStanisław Chmiela                 WritableMap payload = new WritableNativeMap();
358ef35242dSStanisław Chmiela                 if (uri != null) {
359ef35242dSStanisław Chmiela                     payload.putString(NATIVE_INIT_SESSION_STARTED_EVENT_URI, uri.toString());
360ef35242dSStanisław Chmiela                 }
361ef35242dSStanisław Chmiela                 else {
362ef35242dSStanisław Chmiela                     payload.putNull(NATIVE_INIT_SESSION_STARTED_EVENT_URI);
363ef35242dSStanisław Chmiela                 }
364ef35242dSStanisław Chmiela                 mBranchModule.sendRNEvent(RN_INIT_SESSION_START_EVENT, payload);
365ef35242dSStanisław Chmiela             }
366ef35242dSStanisław Chmiela 
367ef35242dSStanisław Chmiela             private BroadcastReceiver init(RNBranchModule branchModule) {
368ef35242dSStanisław Chmiela                 mBranchModule = branchModule;
369ef35242dSStanisław Chmiela                 return this;
370ef35242dSStanisław Chmiela             }
371ef35242dSStanisław Chmiela         }.init(this);
372ef35242dSStanisław Chmiela 
373ef35242dSStanisław Chmiela         LocalBroadcastManager.getInstance(reactContext).registerReceiver(mInitSessionStartedEventReceiver, new IntentFilter(NATIVE_INIT_SESSION_STARTED_EVENT));
374090208b5SStanisław Chmiela     }
375090208b5SStanisław Chmiela 
376090208b5SStanisław Chmiela     @Override
onCatalystInstanceDestroy()377090208b5SStanisław Chmiela     public void onCatalystInstanceDestroy() {
378ef35242dSStanisław Chmiela         LocalBroadcastManager.getInstance(getReactApplicationContext()).unregisterReceiver(mInitSessionFinishedEventReceiver);
379ef35242dSStanisław Chmiela         LocalBroadcastManager.getInstance(getReactApplicationContext()).unregisterReceiver(mInitSessionStartedEventReceiver);
380090208b5SStanisław Chmiela     }
381090208b5SStanisław Chmiela 
382090208b5SStanisław Chmiela     @Override
getName()383090208b5SStanisław Chmiela     public String getName() {
384090208b5SStanisław Chmiela         return REACT_MODULE_NAME;
385090208b5SStanisław Chmiela     }
386090208b5SStanisław Chmiela 
387090208b5SStanisław Chmiela     @ReactMethod
disableTracking(boolean disable)388090208b5SStanisław Chmiela     public void disableTracking(boolean disable) {
389090208b5SStanisław Chmiela         Branch branch = Branch.getInstance();
390090208b5SStanisław Chmiela         branch.disableTracking(disable);
391090208b5SStanisław Chmiela     }
392090208b5SStanisław Chmiela 
393090208b5SStanisław Chmiela     @ReactMethod
isTrackingDisabled(Promise promise)394090208b5SStanisław Chmiela     public void isTrackingDisabled(Promise promise) {
395090208b5SStanisław Chmiela         Branch branch = Branch.getInstance();
396090208b5SStanisław Chmiela         promise.resolve(branch.isTrackingDisabled());
397090208b5SStanisław Chmiela     }
398090208b5SStanisław Chmiela 
399090208b5SStanisław Chmiela     @ReactMethod
createUniversalObject(ReadableMap universalObjectMap, Promise promise)400090208b5SStanisław Chmiela     public void createUniversalObject(ReadableMap universalObjectMap, Promise promise) {
401090208b5SStanisław Chmiela         String ident = UUID.randomUUID().toString();
402090208b5SStanisław Chmiela         BranchUniversalObject universalObject = createBranchUniversalObject(universalObjectMap);
403090208b5SStanisław Chmiela         mUniversalObjectMap.put(ident, universalObject);
404090208b5SStanisław Chmiela 
405090208b5SStanisław Chmiela         WritableMap response = new WritableNativeMap();
406090208b5SStanisław Chmiela         response.putString(IDENT_FIELD_NAME, ident);
407090208b5SStanisław Chmiela         promise.resolve(response);
408090208b5SStanisław Chmiela     }
409090208b5SStanisław Chmiela 
410090208b5SStanisław Chmiela     @ReactMethod
releaseUniversalObject(String ident)411090208b5SStanisław Chmiela     public void releaseUniversalObject(String ident) {
412090208b5SStanisław Chmiela         mUniversalObjectMap.remove(ident);
413090208b5SStanisław Chmiela     }
414090208b5SStanisław Chmiela 
415090208b5SStanisław Chmiela     @ReactMethod
redeemInitSessionResult(Promise promise)416090208b5SStanisław Chmiela     public void redeemInitSessionResult(Promise promise) {
417090208b5SStanisław Chmiela         promise.resolve(convertJsonToMap(initSessionResult));
418090208b5SStanisław Chmiela     }
419090208b5SStanisław Chmiela 
420090208b5SStanisław Chmiela     @ReactMethod
getLatestReferringParams(boolean synchronous, Promise promise)421090208b5SStanisław Chmiela     public void getLatestReferringParams(boolean synchronous, Promise promise) {
422090208b5SStanisław Chmiela         Branch branch = Branch.getInstance();
423090208b5SStanisław Chmiela         if (synchronous)
424090208b5SStanisław Chmiela             promise.resolve(convertJsonToMap(branch.getLatestReferringParamsSync()));
425090208b5SStanisław Chmiela         else
426090208b5SStanisław Chmiela             promise.resolve(convertJsonToMap(branch.getLatestReferringParams()));
427090208b5SStanisław Chmiela     }
428090208b5SStanisław Chmiela 
429090208b5SStanisław Chmiela     @ReactMethod
getFirstReferringParams(Promise promise)430090208b5SStanisław Chmiela     public void getFirstReferringParams(Promise promise) {
431090208b5SStanisław Chmiela         Branch branch = Branch.getInstance();
432090208b5SStanisław Chmiela         promise.resolve(convertJsonToMap(branch.getFirstReferringParams()));
433090208b5SStanisław Chmiela     }
434090208b5SStanisław Chmiela 
435090208b5SStanisław Chmiela     @ReactMethod
setIdentity(String identity)436090208b5SStanisław Chmiela     public void setIdentity(String identity) {
437090208b5SStanisław Chmiela         Branch branch = Branch.getInstance();
438090208b5SStanisław Chmiela         branch.setIdentity(identity);
439090208b5SStanisław Chmiela     }
440090208b5SStanisław Chmiela 
441090208b5SStanisław Chmiela     @ReactMethod
setRequestMetadataKey(String key, String value)442ef35242dSStanisław Chmiela     public void setRequestMetadataKey(String key, String value) {
443ef35242dSStanisław Chmiela         // setRequestMetadata does not do what it appears to do.  Call directly to the native code.
444ef35242dSStanisław Chmiela         Branch branch = Branch.getInstance();
445ef35242dSStanisław Chmiela         branch.setRequestMetadata(key, value);
446ef35242dSStanisław Chmiela     }
447ef35242dSStanisław Chmiela 
448ef35242dSStanisław Chmiela     @ReactMethod
logout()449090208b5SStanisław Chmiela     public void logout() {
450090208b5SStanisław Chmiela         Branch branch = Branch.getInstance();
451090208b5SStanisław Chmiela         branch.logout();
452090208b5SStanisław Chmiela     }
453090208b5SStanisław Chmiela 
454090208b5SStanisław Chmiela     @ReactMethod
logEvent(ReadableArray contentItems, String eventName, ReadableMap params, Promise promise)455090208b5SStanisław Chmiela     public void logEvent(ReadableArray contentItems, String eventName, ReadableMap params, Promise promise) {
456090208b5SStanisław Chmiela         List<BranchUniversalObject> buos = new ArrayList<>();
457090208b5SStanisław Chmiela         for (int i = 0; i < contentItems.size(); ++ i) {
458090208b5SStanisław Chmiela             String ident = contentItems.getString(i);
459090208b5SStanisław Chmiela             BranchUniversalObject universalObject = findUniversalObjectOrReject(ident, promise);
460090208b5SStanisław Chmiela             if (universalObject == null) return;
461090208b5SStanisław Chmiela             buos.add(universalObject);
462090208b5SStanisław Chmiela         }
463090208b5SStanisław Chmiela 
464090208b5SStanisław Chmiela         BranchEvent event = createBranchEvent(eventName, params);
465090208b5SStanisław Chmiela         event.addContentItems(buos);
466090208b5SStanisław Chmiela         event.logEvent(mActivity);
467090208b5SStanisław Chmiela         promise.resolve(null);
468090208b5SStanisław Chmiela     }
469090208b5SStanisław Chmiela 
470090208b5SStanisław Chmiela     @ReactMethod
userCompletedAction(String event, ReadableMap appState)471090208b5SStanisław Chmiela     public void userCompletedAction(String event, ReadableMap appState) throws JSONException {
472090208b5SStanisław Chmiela         Branch branch = Branch.getInstance();
473090208b5SStanisław Chmiela         branch.userCompletedAction(event, convertMapToJson(appState));
474090208b5SStanisław Chmiela     }
475090208b5SStanisław Chmiela 
476090208b5SStanisław Chmiela     @ReactMethod
userCompletedActionOnUniversalObject(String ident, String event, ReadableMap state, Promise promise)477090208b5SStanisław Chmiela     public void userCompletedActionOnUniversalObject(String ident, String event, ReadableMap state, Promise promise) {
478090208b5SStanisław Chmiela         BranchUniversalObject universalObject = findUniversalObjectOrReject(ident, promise);
479090208b5SStanisław Chmiela         if (universalObject == null) return;
480090208b5SStanisław Chmiela 
481090208b5SStanisław Chmiela         universalObject.userCompletedAction(event, convertMapToParams(state));
482090208b5SStanisław Chmiela         promise.resolve(null);
483090208b5SStanisław Chmiela     }
484090208b5SStanisław Chmiela 
485090208b5SStanisław Chmiela     @ReactMethod
sendCommerceEvent(String revenue, ReadableMap metadata, final Promise promise)486090208b5SStanisław Chmiela     public void sendCommerceEvent(String revenue, ReadableMap metadata, final Promise promise) throws JSONException {
487090208b5SStanisław Chmiela         Branch branch = Branch.getInstance();
488090208b5SStanisław Chmiela 
489090208b5SStanisław Chmiela         CommerceEvent commerceEvent = new CommerceEvent();
490090208b5SStanisław Chmiela         commerceEvent.setRevenue(Double.parseDouble(revenue));
491090208b5SStanisław Chmiela 
492090208b5SStanisław Chmiela         JSONObject jsonMetadata = null;
493090208b5SStanisław Chmiela         if (metadata != null) {
494090208b5SStanisław Chmiela             jsonMetadata = convertMapToJson(metadata);
495090208b5SStanisław Chmiela         }
496090208b5SStanisław Chmiela 
497090208b5SStanisław Chmiela         branch.sendCommerceEvent(commerceEvent, jsonMetadata, null);
498090208b5SStanisław Chmiela         promise.resolve(null);
499090208b5SStanisław Chmiela     }
500090208b5SStanisław Chmiela 
501090208b5SStanisław Chmiela     @ReactMethod
showShareSheet(String ident, ReadableMap shareOptionsMap, ReadableMap linkPropertiesMap, ReadableMap controlParamsMap, Promise promise)502090208b5SStanisław Chmiela     public void showShareSheet(String ident, ReadableMap shareOptionsMap, ReadableMap linkPropertiesMap, ReadableMap controlParamsMap, Promise promise) {
503090208b5SStanisław Chmiela         Context context = getReactApplicationContext();
504090208b5SStanisław Chmiela 
505090208b5SStanisław Chmiela         Handler mainHandler = new Handler(context.getMainLooper());
506090208b5SStanisław Chmiela 
507090208b5SStanisław Chmiela         Runnable myRunnable = new Runnable() {
508090208b5SStanisław Chmiela             Promise mPm;
509090208b5SStanisław Chmiela             Context mContext;
510090208b5SStanisław Chmiela             ReadableMap shareOptionsMap, linkPropertiesMap, controlParamsMap;
511090208b5SStanisław Chmiela             String ident;
512090208b5SStanisław Chmiela 
513090208b5SStanisław Chmiela             private Runnable init(ReadableMap _shareOptionsMap, String _ident, ReadableMap _linkPropertiesMap, ReadableMap _controlParamsMap, Promise promise, Context context) {
514090208b5SStanisław Chmiela                 mPm = promise;
515090208b5SStanisław Chmiela                 mContext = context;
516090208b5SStanisław Chmiela                 shareOptionsMap = _shareOptionsMap;
517090208b5SStanisław Chmiela                 ident = _ident;
518090208b5SStanisław Chmiela                 linkPropertiesMap = _linkPropertiesMap;
519090208b5SStanisław Chmiela                 controlParamsMap = _controlParamsMap;
520090208b5SStanisław Chmiela                 return this;
521090208b5SStanisław Chmiela             }
522090208b5SStanisław Chmiela 
523090208b5SStanisław Chmiela             @Override
524090208b5SStanisław Chmiela             public void run() {
525090208b5SStanisław Chmiela                 String messageHeader = shareOptionsMap.hasKey("messageHeader") ? shareOptionsMap.getString("messageHeader") : "";
526090208b5SStanisław Chmiela                 String messageBody = shareOptionsMap.hasKey("messageBody") ? shareOptionsMap.getString("messageBody") : "";
527090208b5SStanisław Chmiela                 ShareSheetStyle shareSheetStyle = new ShareSheetStyle(mContext, messageHeader, messageBody)
528090208b5SStanisław Chmiela                         .setCopyUrlStyle(mContext.getResources().getDrawable(android.R.drawable.ic_menu_send), "Copy", "Added to clipboard")
529090208b5SStanisław Chmiela                         .setMoreOptionStyle(mContext.getResources().getDrawable(android.R.drawable.ic_menu_search), "Show more")
530090208b5SStanisław Chmiela                         .addPreferredSharingOption(SharingHelper.SHARE_WITH.EMAIL)
531090208b5SStanisław Chmiela                         .addPreferredSharingOption(SharingHelper.SHARE_WITH.TWITTER)
532090208b5SStanisław Chmiela                         .addPreferredSharingOption(SharingHelper.SHARE_WITH.MESSAGE)
533090208b5SStanisław Chmiela                         .addPreferredSharingOption(SharingHelper.SHARE_WITH.FACEBOOK);
534090208b5SStanisław Chmiela 
535090208b5SStanisław Chmiela                 BranchUniversalObject branchUniversalObject = findUniversalObjectOrReject(ident, mPm);
536090208b5SStanisław Chmiela                 if (branchUniversalObject == null) {
537090208b5SStanisław Chmiela                     return;
538090208b5SStanisław Chmiela                 }
539090208b5SStanisław Chmiela 
540090208b5SStanisław Chmiela                 LinkProperties linkProperties = createLinkProperties(linkPropertiesMap, controlParamsMap);
541090208b5SStanisław Chmiela 
542090208b5SStanisław Chmiela                 branchUniversalObject.showShareSheet(
543090208b5SStanisław Chmiela                         getCurrentActivity(),
544090208b5SStanisław Chmiela                         linkProperties,
545090208b5SStanisław Chmiela                         shareSheetStyle,
546090208b5SStanisław Chmiela                         new Branch.BranchLinkShareListener() {
547090208b5SStanisław Chmiela                             private Promise mPromise = null;
548090208b5SStanisław Chmiela 
549090208b5SStanisław Chmiela                             @Override
550090208b5SStanisław Chmiela                             public void onShareLinkDialogLaunched() {
551090208b5SStanisław Chmiela                             }
552090208b5SStanisław Chmiela 
553090208b5SStanisław Chmiela                             @Override
554090208b5SStanisław Chmiela                             public void onShareLinkDialogDismissed() {
555090208b5SStanisław Chmiela                                 if(mPromise == null) {
556090208b5SStanisław Chmiela                                     return;
557090208b5SStanisław Chmiela                                 }
558090208b5SStanisław Chmiela 
559090208b5SStanisław Chmiela                                 WritableMap map = new WritableNativeMap();
560090208b5SStanisław Chmiela                                 map.putString("channel", null);
561090208b5SStanisław Chmiela                                 map.putBoolean("completed", false);
562090208b5SStanisław Chmiela                                 map.putString("error", null);
563090208b5SStanisław Chmiela                                 mPromise.resolve(map);
564090208b5SStanisław Chmiela                                 mPromise = null;
565090208b5SStanisław Chmiela                             }
566090208b5SStanisław Chmiela 
567090208b5SStanisław Chmiela                             @Override
568090208b5SStanisław Chmiela                             public void onLinkShareResponse(String sharedLink, String sharedChannel, BranchError error) {
569090208b5SStanisław Chmiela                                 if(mPromise == null) {
570090208b5SStanisław Chmiela                                     return;
571090208b5SStanisław Chmiela                                 }
572090208b5SStanisław Chmiela 
573090208b5SStanisław Chmiela                                 WritableMap map = new WritableNativeMap();
574090208b5SStanisław Chmiela                                 map.putString("channel", sharedChannel);
575090208b5SStanisław Chmiela                                 map.putBoolean("completed", true);
576090208b5SStanisław Chmiela                                 map.putString("error", (error != null ? error.getMessage() : null));
577090208b5SStanisław Chmiela                                 mPromise.resolve(map);
578090208b5SStanisław Chmiela                                 mPromise = null;
579090208b5SStanisław Chmiela                             }
580090208b5SStanisław Chmiela                             @Override
581090208b5SStanisław Chmiela                             public void onChannelSelected(String channelName) {
582090208b5SStanisław Chmiela                             }
583090208b5SStanisław Chmiela 
584090208b5SStanisław Chmiela                             private Branch.BranchLinkShareListener init(Promise promise) {
585090208b5SStanisław Chmiela                                 mPromise = promise;
586090208b5SStanisław Chmiela                                 return this;
587090208b5SStanisław Chmiela                             }
588090208b5SStanisław Chmiela                         }.init(mPm));
589090208b5SStanisław Chmiela             }
590090208b5SStanisław Chmiela         }.init(shareOptionsMap, ident, linkPropertiesMap, controlParamsMap, promise, context);
591090208b5SStanisław Chmiela 
592090208b5SStanisław Chmiela         mainHandler.post(myRunnable);
593090208b5SStanisław Chmiela     }
594090208b5SStanisław Chmiela 
595090208b5SStanisław Chmiela     @ReactMethod
registerView(String ident, Promise promise)596090208b5SStanisław Chmiela     public void registerView(String ident, Promise promise) {
597090208b5SStanisław Chmiela         BranchUniversalObject branchUniversalObject = findUniversalObjectOrReject(ident, promise);
598090208b5SStanisław Chmiela         if (branchUniversalObject == null) {
599090208b5SStanisław Chmiela              return;
600090208b5SStanisław Chmiela         }
601090208b5SStanisław Chmiela 
602090208b5SStanisław Chmiela         branchUniversalObject.registerView();
603090208b5SStanisław Chmiela         promise.resolve(null);
604090208b5SStanisław Chmiela     }
605090208b5SStanisław Chmiela 
606090208b5SStanisław Chmiela     @ReactMethod
generateShortUrl(String ident, ReadableMap linkPropertiesMap, ReadableMap controlParamsMap, final Promise promise)607090208b5SStanisław Chmiela     public void generateShortUrl(String ident, ReadableMap linkPropertiesMap, ReadableMap controlParamsMap, final Promise promise) {
608090208b5SStanisław Chmiela         LinkProperties linkProperties = createLinkProperties(linkPropertiesMap, controlParamsMap);
609090208b5SStanisław Chmiela 
610090208b5SStanisław Chmiela         BranchUniversalObject branchUniversalObject = findUniversalObjectOrReject(ident, promise);
611090208b5SStanisław Chmiela         if (branchUniversalObject == null) {
612090208b5SStanisław Chmiela             return;
613090208b5SStanisław Chmiela         }
614090208b5SStanisław Chmiela 
615090208b5SStanisław Chmiela         branchUniversalObject.generateShortUrl(mActivity, linkProperties, new BranchLinkCreateListener() {
616090208b5SStanisław Chmiela             @Override
617090208b5SStanisław Chmiela             public void onLinkCreate(String url, BranchError error) {
618090208b5SStanisław Chmiela                 Log.d(REACT_CLASS, "onLinkCreate " + url);
619090208b5SStanisław Chmiela                 if (error != null) {
620090208b5SStanisław Chmiela                     if (error.getErrorCode() == BranchError.ERR_BRANCH_DUPLICATE_URL) {
621090208b5SStanisław Chmiela                         promise.reject("RNBranch::Error::DuplicateResourceError", error.getMessage());
622090208b5SStanisław Chmiela                     }
623090208b5SStanisław Chmiela                     else {
624000da56bSStanisław Chmiela                         promise.reject(GENERIC_ERROR, error.getMessage());
625090208b5SStanisław Chmiela                     }
626090208b5SStanisław Chmiela                     return;
627090208b5SStanisław Chmiela                 }
628090208b5SStanisław Chmiela 
629090208b5SStanisław Chmiela                 WritableMap map = new WritableNativeMap();
630090208b5SStanisław Chmiela                 map.putString("url", url);
631090208b5SStanisław Chmiela                 promise.resolve(map);
632090208b5SStanisław Chmiela             }
633090208b5SStanisław Chmiela         });
634090208b5SStanisław Chmiela     }
635090208b5SStanisław Chmiela 
636090208b5SStanisław Chmiela     @ReactMethod
openURL(String url, ReadableMap options)637090208b5SStanisław Chmiela     public void openURL(String url, ReadableMap options) {
638090208b5SStanisław Chmiela         if (mActivity == null) {
639090208b5SStanisław Chmiela             // initSession is called before JS loads. This probably indicates failure to call initSession
640090208b5SStanisław Chmiela             // in an activity.
641090208b5SStanisław Chmiela             Log.e(REACT_CLASS, "Branch native Android SDK not initialized in openURL");
642090208b5SStanisław Chmiela             return;
643090208b5SStanisław Chmiela         }
644090208b5SStanisław Chmiela 
645*de56ef51SStanisław Chmiela         /*
646*de56ef51SStanisław Chmiela          * Using Intent.ACTION_VIEW here will open a browser for non-Branch links unless the
647*de56ef51SStanisław Chmiela          * domain is registered in an intent-filter in the manifest. Instead specify the host
648*de56ef51SStanisław Chmiela          * Activity.
649*de56ef51SStanisław Chmiela          */
650090208b5SStanisław Chmiela         Intent intent = new Intent(mActivity, mActivity.getClass());
651*de56ef51SStanisław Chmiela         intent.setData(Uri.parse(url));
652090208b5SStanisław Chmiela         intent.putExtra("branch_force_new_session", true);
653090208b5SStanisław Chmiela 
654090208b5SStanisław Chmiela         mActivity.startActivity(intent);
655090208b5SStanisław Chmiela     }
656090208b5SStanisław Chmiela 
createBranchEvent(String eventName, ReadableMap params)657090208b5SStanisław Chmiela     public static BranchEvent createBranchEvent(String eventName, ReadableMap params) {
658090208b5SStanisław Chmiela         BranchEvent event;
659090208b5SStanisław Chmiela         try {
660090208b5SStanisław Chmiela             BRANCH_STANDARD_EVENT standardEvent = BRANCH_STANDARD_EVENT.valueOf(eventName);
661090208b5SStanisław Chmiela             // valueOf on BRANCH_STANDARD_EVENT Enum has succeeded, so this is a standard event.
662090208b5SStanisław Chmiela             event = new BranchEvent(standardEvent);
663090208b5SStanisław Chmiela         } catch (IllegalArgumentException e) {
664090208b5SStanisław Chmiela             // The event name is not found in standard events.
665090208b5SStanisław Chmiela             // So use custom event mode.
666090208b5SStanisław Chmiela             event = new BranchEvent(eventName);
667090208b5SStanisław Chmiela         }
668090208b5SStanisław Chmiela 
669090208b5SStanisław Chmiela         if (params.hasKey("currency")) {
670090208b5SStanisław Chmiela             String currencyString = params.getString("currency");
671090208b5SStanisław Chmiela             CurrencyType currency = CurrencyType.getValue(currencyString);
672090208b5SStanisław Chmiela             if (currency != null) {
673090208b5SStanisław Chmiela                 event.setCurrency(currency);
674090208b5SStanisław Chmiela             }
675090208b5SStanisław Chmiela             else {
676090208b5SStanisław Chmiela                 Log.w(REACT_CLASS, "Invalid currency " + currencyString);
677090208b5SStanisław Chmiela             }
678090208b5SStanisław Chmiela         }
679090208b5SStanisław Chmiela 
680090208b5SStanisław Chmiela         if (params.hasKey("transactionID")) event.setTransactionID(params.getString("transactionID"));
681090208b5SStanisław Chmiela         if (params.hasKey("revenue")) event.setRevenue(Double.parseDouble(params.getString("revenue")));
682090208b5SStanisław Chmiela         if (params.hasKey("shipping")) event.setShipping(Double.parseDouble(params.getString("shipping")));
683090208b5SStanisław Chmiela         if (params.hasKey("tax")) event.setTax(Double.parseDouble(params.getString("tax")));
684090208b5SStanisław Chmiela         if (params.hasKey("coupon")) event.setCoupon(params.getString("coupon"));
685090208b5SStanisław Chmiela         if (params.hasKey("affiliation")) event.setAffiliation(params.getString("affiliation"));
686090208b5SStanisław Chmiela         if (params.hasKey("description")) event.setDescription(params.getString("description"));
687090208b5SStanisław Chmiela         if (params.hasKey("searchQuery")) event.setSearchQuery(params.getString("searchQuery"));
688ef35242dSStanisław Chmiela         if (params.hasKey("alias")) event.setCustomerEventAlias(params.getString("alias"));
689090208b5SStanisław Chmiela 
690090208b5SStanisław Chmiela         if (params.hasKey("customData")) {
691090208b5SStanisław Chmiela             ReadableMap customData = params.getMap("customData");
692090208b5SStanisław Chmiela             ReadableMapKeySetIterator it = customData.keySetIterator();
693090208b5SStanisław Chmiela             while (it.hasNextKey()) {
694090208b5SStanisław Chmiela                 String key = it.nextKey();
695090208b5SStanisław Chmiela                 event.addCustomDataProperty(key, customData.getString(key));
696090208b5SStanisław Chmiela             }
697090208b5SStanisław Chmiela         }
698090208b5SStanisław Chmiela 
699090208b5SStanisław Chmiela         return event;
700090208b5SStanisław Chmiela     }
701090208b5SStanisław Chmiela 
createLinkProperties(ReadableMap linkPropertiesMap, @Nullable ReadableMap controlParams)702090208b5SStanisław Chmiela     public static LinkProperties createLinkProperties(ReadableMap linkPropertiesMap, @Nullable ReadableMap controlParams){
703090208b5SStanisław Chmiela         LinkProperties linkProperties = new LinkProperties();
704090208b5SStanisław Chmiela         if (linkPropertiesMap.hasKey("alias")) linkProperties.setAlias(linkPropertiesMap.getString("alias"));
705090208b5SStanisław Chmiela         if (linkPropertiesMap.hasKey("campaign")) linkProperties.setCampaign(linkPropertiesMap.getString("campaign"));
706090208b5SStanisław Chmiela         if (linkPropertiesMap.hasKey("channel")) linkProperties.setChannel(linkPropertiesMap.getString("channel"));
707090208b5SStanisław Chmiela         if (linkPropertiesMap.hasKey("feature")) linkProperties.setFeature(linkPropertiesMap.getString("feature"));
708090208b5SStanisław Chmiela         if (linkPropertiesMap.hasKey("stage")) linkProperties.setStage(linkPropertiesMap.getString("stage"));
709090208b5SStanisław Chmiela 
710090208b5SStanisław Chmiela         if (linkPropertiesMap.hasKey("tags")) {
711090208b5SStanisław Chmiela             ReadableArray tags = linkPropertiesMap.getArray("tags");
712090208b5SStanisław Chmiela             for (int i=0; i<tags.size(); ++i) {
713090208b5SStanisław Chmiela                 String tag = tags.getString(i);
714090208b5SStanisław Chmiela                 linkProperties.addTag(tag);
715090208b5SStanisław Chmiela             }
716090208b5SStanisław Chmiela         }
717090208b5SStanisław Chmiela 
718090208b5SStanisław Chmiela         if (controlParams != null) {
719090208b5SStanisław Chmiela             ReadableMapKeySetIterator iterator = controlParams.keySetIterator();
720090208b5SStanisław Chmiela             while (iterator.hasNextKey()) {
721090208b5SStanisław Chmiela                 String key = iterator.nextKey();
722090208b5SStanisław Chmiela                 Object value = getReadableMapObjectForKey(controlParams, key);
723090208b5SStanisław Chmiela                 linkProperties.addControlParameter(key, value.toString());
724090208b5SStanisław Chmiela             }
725090208b5SStanisław Chmiela         }
726090208b5SStanisław Chmiela 
727090208b5SStanisław Chmiela         return linkProperties;
728090208b5SStanisław Chmiela     }
729090208b5SStanisław Chmiela 
setupBranch(Context context)730090208b5SStanisław Chmiela     private static Branch setupBranch(Context context) {
731090208b5SStanisław Chmiela         Branch branch = Branch.getInstance(context);
732090208b5SStanisław Chmiela 
733090208b5SStanisław Chmiela         if (!mInitialized) {
734090208b5SStanisław Chmiela             Log.i(REACT_CLASS, "Initializing Branch SDK v. " + BuildConfig.VERSION_NAME);
735090208b5SStanisław Chmiela 
736090208b5SStanisław Chmiela             RNBranchConfig config = new RNBranchConfig(context);
737090208b5SStanisław Chmiela 
738090208b5SStanisław Chmiela             if (mUseDebug || config.getDebugMode()) branch.setDebug();
739090208b5SStanisław Chmiela 
740ef35242dSStanisław Chmiela             if (config.getEnableFacebookLinkCheck()) branch.enableFacebookAppLinkCheck();
741ef35242dSStanisław Chmiela 
742090208b5SStanisław Chmiela             if (mRequestMetadata != null) {
743090208b5SStanisław Chmiela                 Iterator keys = mRequestMetadata.keys();
744090208b5SStanisław Chmiela                 while (keys.hasNext()) {
745090208b5SStanisław Chmiela                     String key = (String) keys.next();
746090208b5SStanisław Chmiela                     try {
747090208b5SStanisław Chmiela                         branch.setRequestMetadata(key, mRequestMetadata.getString(key));
748090208b5SStanisław Chmiela                     } catch (JSONException e) {
749090208b5SStanisław Chmiela                         // no-op
750090208b5SStanisław Chmiela                     }
751090208b5SStanisław Chmiela                 }
752090208b5SStanisław Chmiela             }
753090208b5SStanisław Chmiela 
754090208b5SStanisław Chmiela             mInitialized = true;
755090208b5SStanisław Chmiela         }
756090208b5SStanisław Chmiela 
757090208b5SStanisław Chmiela         return branch;
758090208b5SStanisław Chmiela     }
759090208b5SStanisław Chmiela 
findUniversalObjectOrReject(final String ident, final Promise promise)760090208b5SStanisław Chmiela     private BranchUniversalObject findUniversalObjectOrReject(final String ident, final Promise promise) {
761090208b5SStanisław Chmiela         BranchUniversalObject universalObject = mUniversalObjectMap.get(ident);
762090208b5SStanisław Chmiela 
763090208b5SStanisław Chmiela         if (universalObject == null) {
764090208b5SStanisław Chmiela             final String errorMessage = "BranchUniversalObject not found for ident " + ident + ".";
765090208b5SStanisław Chmiela             promise.reject(UNIVERSAL_OBJECT_NOT_FOUND_ERROR_CODE, errorMessage);
766090208b5SStanisław Chmiela         }
767090208b5SStanisław Chmiela 
768090208b5SStanisław Chmiela         return universalObject;
769090208b5SStanisław Chmiela     }
770090208b5SStanisław Chmiela 
createContentMetadata(ReadableMap map)771090208b5SStanisław Chmiela     public ContentMetadata createContentMetadata(ReadableMap map) {
772090208b5SStanisław Chmiela         ContentMetadata metadata = new ContentMetadata();
773090208b5SStanisław Chmiela 
774090208b5SStanisław Chmiela         if (map.hasKey("contentSchema")) {
775090208b5SStanisław Chmiela             BranchContentSchema schema = BranchContentSchema.valueOf(map.getString("contentSchema"));
776090208b5SStanisław Chmiela             metadata.setContentSchema(schema);
777090208b5SStanisław Chmiela         }
778090208b5SStanisław Chmiela 
779090208b5SStanisław Chmiela         if (map.hasKey("quantity")) {
780090208b5SStanisław Chmiela             metadata.setQuantity(map.getDouble("quantity"));
781090208b5SStanisław Chmiela         }
782090208b5SStanisław Chmiela 
783090208b5SStanisław Chmiela         if (map.hasKey("price")) {
784090208b5SStanisław Chmiela             double price = Double.parseDouble(map.getString("price"));
785090208b5SStanisław Chmiela             CurrencyType currency = null;
786090208b5SStanisław Chmiela             if (map.hasKey("currency")) currency = CurrencyType.valueOf(map.getString("currency"));
787090208b5SStanisław Chmiela             metadata.setPrice(price, currency);
788090208b5SStanisław Chmiela         }
789090208b5SStanisław Chmiela 
790090208b5SStanisław Chmiela         if (map.hasKey("sku")) {
791090208b5SStanisław Chmiela             metadata.setSku(map.getString("sku"));
792090208b5SStanisław Chmiela         }
793090208b5SStanisław Chmiela 
794090208b5SStanisław Chmiela         if (map.hasKey("productName")) {
795090208b5SStanisław Chmiela             metadata.setProductName(map.getString("productName"));
796090208b5SStanisław Chmiela         }
797090208b5SStanisław Chmiela 
798090208b5SStanisław Chmiela         if (map.hasKey("productBrand")) {
799090208b5SStanisław Chmiela             metadata.setProductBrand(map.getString("productBrand"));
800090208b5SStanisław Chmiela         }
801090208b5SStanisław Chmiela 
802090208b5SStanisław Chmiela         if (map.hasKey("productCategory")) {
803090208b5SStanisław Chmiela             ProductCategory category = getProductCategory(map.getString("productCategory"));
804090208b5SStanisław Chmiela             if (category != null) metadata.setProductCategory(category);
805090208b5SStanisław Chmiela         }
806090208b5SStanisław Chmiela 
807090208b5SStanisław Chmiela         if (map.hasKey("productVariant")) {
808090208b5SStanisław Chmiela             metadata.setProductVariant(map.getString("productVariant"));
809090208b5SStanisław Chmiela         }
810090208b5SStanisław Chmiela 
811090208b5SStanisław Chmiela         if (map.hasKey("condition")) {
812090208b5SStanisław Chmiela             ContentMetadata.CONDITION condition = ContentMetadata.CONDITION.valueOf(map.getString("condition"));
813090208b5SStanisław Chmiela             metadata.setProductCondition(condition);
814090208b5SStanisław Chmiela         }
815090208b5SStanisław Chmiela 
816090208b5SStanisław Chmiela         if (map.hasKey("ratingAverage") || map.hasKey("ratingMax") || map.hasKey("ratingCount")) {
817090208b5SStanisław Chmiela             Double average = null, max = null;
818090208b5SStanisław Chmiela             Integer count = null;
819090208b5SStanisław Chmiela             if (map.hasKey("ratingAverage")) average = map.getDouble("ratingAverage");
820090208b5SStanisław Chmiela             if (map.hasKey("ratingCount")) count = map.getInt("ratingCount");
821090208b5SStanisław Chmiela             if (map.hasKey("ratingMax")) max = map.getDouble("ratingMax");
822090208b5SStanisław Chmiela             metadata.setRating(average, max, count);
823090208b5SStanisław Chmiela         }
824090208b5SStanisław Chmiela 
825090208b5SStanisław Chmiela         if (map.hasKey("addressStreet") ||
826090208b5SStanisław Chmiela                 map.hasKey("addressCity") ||
827090208b5SStanisław Chmiela                 map.hasKey("addressRegion") ||
828090208b5SStanisław Chmiela                 map.hasKey("addressCountry") ||
829090208b5SStanisław Chmiela                 map.hasKey("addressPostalCode")) {
830090208b5SStanisław Chmiela             String street = null, city = null, region = null, country = null, postalCode = null;
831090208b5SStanisław Chmiela             if (map.hasKey("addressStreet")) street = map.getString("addressStreet");
832090208b5SStanisław Chmiela             if (map.hasKey("addressCity")) street = map.getString("addressCity");
833090208b5SStanisław Chmiela             if (map.hasKey("addressRegion")) street = map.getString("addressRegion");
834090208b5SStanisław Chmiela             if (map.hasKey("addressCountry")) street = map.getString("addressCountry");
835090208b5SStanisław Chmiela             if (map.hasKey("addressPostalCode")) street = map.getString("addressPostalCode");
836090208b5SStanisław Chmiela             metadata.setAddress(street, city, region, country, postalCode);
837090208b5SStanisław Chmiela         }
838090208b5SStanisław Chmiela 
839090208b5SStanisław Chmiela         if (map.hasKey("latitude") || map.hasKey("longitude")) {
840090208b5SStanisław Chmiela             Double latitude = null, longitude = null;
841090208b5SStanisław Chmiela             if (map.hasKey("latitude")) latitude = map.getDouble("latitude");
842090208b5SStanisław Chmiela             if (map.hasKey("longitude")) longitude = map.getDouble("longitude");
843090208b5SStanisław Chmiela             metadata.setLocation(latitude, longitude);
844090208b5SStanisław Chmiela         }
845090208b5SStanisław Chmiela 
846090208b5SStanisław Chmiela         if (map.hasKey("imageCaptions")) {
847090208b5SStanisław Chmiela             ReadableArray captions = map.getArray("imageCaptions");
848090208b5SStanisław Chmiela             for (int j=0; j < captions.size(); ++j) {
849090208b5SStanisław Chmiela                 metadata.addImageCaptions(captions.getString(j));
850090208b5SStanisław Chmiela             }
851090208b5SStanisław Chmiela         }
852090208b5SStanisław Chmiela 
853090208b5SStanisław Chmiela         if (map.hasKey("customMetadata")) {
854090208b5SStanisław Chmiela             ReadableMap customMetadata = map.getMap("customMetadata");
855090208b5SStanisław Chmiela             ReadableMapKeySetIterator it = customMetadata.keySetIterator();
856090208b5SStanisław Chmiela             while (it.hasNextKey()) {
857090208b5SStanisław Chmiela                 String key = it.nextKey();
858090208b5SStanisław Chmiela                 metadata.addCustomMetadata(key, customMetadata.getString(key));
859090208b5SStanisław Chmiela             }
860090208b5SStanisław Chmiela         }
861090208b5SStanisław Chmiela 
862090208b5SStanisław Chmiela         return metadata;
863090208b5SStanisław Chmiela     }
864090208b5SStanisław Chmiela 
createBranchUniversalObject(ReadableMap branchUniversalObjectMap)865090208b5SStanisław Chmiela     public BranchUniversalObject createBranchUniversalObject(ReadableMap branchUniversalObjectMap) {
866090208b5SStanisław Chmiela         BranchUniversalObject branchUniversalObject = new BranchUniversalObject()
867090208b5SStanisław Chmiela                 .setCanonicalIdentifier(branchUniversalObjectMap.getString("canonicalIdentifier"));
868090208b5SStanisław Chmiela 
869090208b5SStanisław Chmiela         if (branchUniversalObjectMap.hasKey("title")) branchUniversalObject.setTitle(branchUniversalObjectMap.getString("title"));
870090208b5SStanisław Chmiela         if (branchUniversalObjectMap.hasKey("canonicalUrl")) branchUniversalObject.setCanonicalUrl(branchUniversalObjectMap.getString("canonicalUrl"));
871090208b5SStanisław Chmiela         if (branchUniversalObjectMap.hasKey("contentDescription")) branchUniversalObject.setContentDescription(branchUniversalObjectMap.getString("contentDescription"));
872090208b5SStanisław Chmiela         if (branchUniversalObjectMap.hasKey("contentImageUrl")) branchUniversalObject.setContentImageUrl(branchUniversalObjectMap.getString("contentImageUrl"));
873090208b5SStanisław Chmiela 
874090208b5SStanisław Chmiela         if (branchUniversalObjectMap.hasKey("locallyIndex")) {
875090208b5SStanisław Chmiela             if (branchUniversalObjectMap.getBoolean("locallyIndex")) {
876090208b5SStanisław Chmiela                 branchUniversalObject.setLocalIndexMode(BranchUniversalObject.CONTENT_INDEX_MODE.PUBLIC);
877090208b5SStanisław Chmiela             }
878090208b5SStanisław Chmiela             else {
879090208b5SStanisław Chmiela                 branchUniversalObject.setLocalIndexMode(BranchUniversalObject.CONTENT_INDEX_MODE.PRIVATE);
880090208b5SStanisław Chmiela             }
881090208b5SStanisław Chmiela         }
882090208b5SStanisław Chmiela 
883090208b5SStanisław Chmiela         if (branchUniversalObjectMap.hasKey("publiclyIndex")) {
884090208b5SStanisław Chmiela             if (branchUniversalObjectMap.getBoolean("publiclyIndex")) {
885090208b5SStanisław Chmiela                 branchUniversalObject.setContentIndexingMode(BranchUniversalObject.CONTENT_INDEX_MODE.PUBLIC);
886090208b5SStanisław Chmiela             }
887090208b5SStanisław Chmiela             else {
888090208b5SStanisław Chmiela                 branchUniversalObject.setContentIndexingMode(BranchUniversalObject.CONTENT_INDEX_MODE.PRIVATE);
889090208b5SStanisław Chmiela             }
890090208b5SStanisław Chmiela         }
891090208b5SStanisław Chmiela 
892090208b5SStanisław Chmiela         if (branchUniversalObjectMap.hasKey("contentIndexingMode")) {
893090208b5SStanisław Chmiela             switch (branchUniversalObjectMap.getType("contentIndexingMode")) {
894090208b5SStanisław Chmiela                 case String:
895090208b5SStanisław Chmiela                     String mode = branchUniversalObjectMap.getString("contentIndexingMode");
896090208b5SStanisław Chmiela 
897090208b5SStanisław Chmiela                     if (mode.equals("private"))
898090208b5SStanisław Chmiela                         branchUniversalObject.setContentIndexingMode(BranchUniversalObject.CONTENT_INDEX_MODE.PRIVATE);
899090208b5SStanisław Chmiela                     else if (mode.equals("public"))
900090208b5SStanisław Chmiela                         branchUniversalObject.setContentIndexingMode(BranchUniversalObject.CONTENT_INDEX_MODE.PUBLIC);
901090208b5SStanisław Chmiela                     else
902090208b5SStanisław Chmiela                         Log.w(REACT_CLASS, "Unsupported value for contentIndexingMode: " + mode +
903090208b5SStanisław Chmiela                                 ". Supported values are \"public\" and \"private\"");
904090208b5SStanisław Chmiela                     break;
905090208b5SStanisław Chmiela                 default:
906090208b5SStanisław Chmiela                     Log.w(REACT_CLASS, "contentIndexingMode must be a String");
907090208b5SStanisław Chmiela                     break;
908090208b5SStanisław Chmiela             }
909090208b5SStanisław Chmiela         }
910090208b5SStanisław Chmiela 
911090208b5SStanisław Chmiela         if (branchUniversalObjectMap.hasKey("currency") && branchUniversalObjectMap.hasKey("price")) {
912090208b5SStanisław Chmiela             String currencyString = branchUniversalObjectMap.getString("currency");
913090208b5SStanisław Chmiela             CurrencyType currency = CurrencyType.valueOf(currencyString);
914090208b5SStanisław Chmiela             branchUniversalObject.setPrice(branchUniversalObjectMap.getDouble("price"), currency);
915090208b5SStanisław Chmiela         }
916090208b5SStanisław Chmiela 
917090208b5SStanisław Chmiela         if (branchUniversalObjectMap.hasKey("expirationDate")) {
918090208b5SStanisław Chmiela             String expirationString = branchUniversalObjectMap.getString("expirationDate");
919090208b5SStanisław Chmiela             SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
920090208b5SStanisław Chmiela             format.setTimeZone(TimeZone.getTimeZone("UTC"));
921090208b5SStanisław Chmiela             try {
922090208b5SStanisław Chmiela                 Date date = format.parse(expirationString);
923090208b5SStanisław Chmiela                 Log.d(REACT_CLASS, "Expiration date is " + date.toString());
924090208b5SStanisław Chmiela                 branchUniversalObject.setContentExpiration(date);
925090208b5SStanisław Chmiela             }
926090208b5SStanisław Chmiela             catch (ParseException e) {
927090208b5SStanisław Chmiela                 Log.w(REACT_CLASS, "Invalid expiration date format. Valid format is YYYY-mm-ddTHH:MM:SS, e.g. 2017-02-01T00:00:00. All times UTC.");
928090208b5SStanisław Chmiela             }
929090208b5SStanisław Chmiela         }
930090208b5SStanisław Chmiela 
931090208b5SStanisław Chmiela         if (branchUniversalObjectMap.hasKey("keywords")) {
932090208b5SStanisław Chmiela             ReadableArray keywords = branchUniversalObjectMap.getArray("keywords");
933090208b5SStanisław Chmiela             for (int i=0; i<keywords.size(); ++i) {
934090208b5SStanisław Chmiela                 branchUniversalObject.addKeyWord(keywords.getString(i));
935090208b5SStanisław Chmiela             }
936090208b5SStanisław Chmiela         }
937090208b5SStanisław Chmiela 
938090208b5SStanisław Chmiela         if(branchUniversalObjectMap.hasKey("metadata")) {
939090208b5SStanisław Chmiela             ReadableMap metadataMap = branchUniversalObjectMap.getMap("metadata");
940090208b5SStanisław Chmiela             ReadableMapKeySetIterator iterator = metadataMap.keySetIterator();
941090208b5SStanisław Chmiela             while (iterator.hasNextKey()) {
942090208b5SStanisław Chmiela                 String metadataKey = iterator.nextKey();
943090208b5SStanisław Chmiela                 Object metadataObject = getReadableMapObjectForKey(metadataMap, metadataKey);
944090208b5SStanisław Chmiela                 branchUniversalObject.addContentMetadata(metadataKey, metadataObject.toString());
945090208b5SStanisław Chmiela                 HashMap<String, String> metadata = branchUniversalObject.getMetadata();
946090208b5SStanisław Chmiela             }
947090208b5SStanisław Chmiela         }
948090208b5SStanisław Chmiela 
949090208b5SStanisław Chmiela         if (branchUniversalObjectMap.hasKey("type")) branchUniversalObject.setContentType(branchUniversalObjectMap.getString("type"));
950090208b5SStanisław Chmiela 
951090208b5SStanisław Chmiela         if (branchUniversalObjectMap.hasKey("contentMetadata")) {
952090208b5SStanisław Chmiela             branchUniversalObject.setContentMetadata(createContentMetadata(branchUniversalObjectMap.getMap("contentMetadata")));
953090208b5SStanisław Chmiela         }
954090208b5SStanisław Chmiela 
955090208b5SStanisław Chmiela         return branchUniversalObject;
956090208b5SStanisław Chmiela     }
957090208b5SStanisław Chmiela 
958090208b5SStanisław Chmiela     @Nullable
getProductCategory(final String stringValue)959090208b5SStanisław Chmiela     public ProductCategory getProductCategory(final String stringValue) {
960090208b5SStanisław Chmiela         ProductCategory[] possibleValues = ProductCategory.class.getEnumConstants();
961090208b5SStanisław Chmiela         for (ProductCategory value: possibleValues) {
962090208b5SStanisław Chmiela             if (stringValue.equals(value.getName())) {
963090208b5SStanisław Chmiela                 return value;
964090208b5SStanisław Chmiela             }
965090208b5SStanisław Chmiela         }
966090208b5SStanisław Chmiela         Log.w(REACT_CLASS, "Could not find product category " + stringValue);
967090208b5SStanisław Chmiela         return null;
968090208b5SStanisław Chmiela     }
969090208b5SStanisław Chmiela 
970090208b5SStanisław Chmiela     @ReactMethod
redeemRewards(int value, String bucket, Promise promise)971090208b5SStanisław Chmiela     public void redeemRewards(int value, String bucket, Promise promise)
972090208b5SStanisław Chmiela     {
973090208b5SStanisław Chmiela         if (bucket == null) {
974090208b5SStanisław Chmiela             Branch.getInstance().redeemRewards(value, new RedeemRewardsListener(promise));
975090208b5SStanisław Chmiela         } else {
976090208b5SStanisław Chmiela             Branch.getInstance().redeemRewards(bucket, value, new RedeemRewardsListener(promise));
977090208b5SStanisław Chmiela         }
978090208b5SStanisław Chmiela     }
979090208b5SStanisław Chmiela 
980090208b5SStanisław Chmiela     @ReactMethod
loadRewards(String bucket, Promise promise)981090208b5SStanisław Chmiela     public void loadRewards(String bucket, Promise promise)
982090208b5SStanisław Chmiela     {
983090208b5SStanisław Chmiela         Branch.getInstance().loadRewards(new LoadRewardsListener(bucket, promise));
984090208b5SStanisław Chmiela     }
985090208b5SStanisław Chmiela 
986090208b5SStanisław Chmiela     @ReactMethod
getCreditHistory(Promise promise)987090208b5SStanisław Chmiela     public void getCreditHistory(Promise promise)
988090208b5SStanisław Chmiela     {
989090208b5SStanisław Chmiela         Branch.getInstance().getCreditHistory(new CreditHistoryListener(promise));
990090208b5SStanisław Chmiela     }
991090208b5SStanisław Chmiela 
992090208b5SStanisław Chmiela     protected class CreditHistoryListener implements Branch.BranchListResponseListener
993090208b5SStanisław Chmiela     {
994090208b5SStanisław Chmiela         private Promise _promise;
995090208b5SStanisław Chmiela 
996090208b5SStanisław Chmiela         // Constructor that takes in a required callbackContext object
CreditHistoryListener(Promise promise)997090208b5SStanisław Chmiela         public CreditHistoryListener(Promise promise) {
998090208b5SStanisław Chmiela             this._promise = promise;
999090208b5SStanisław Chmiela         }
1000090208b5SStanisław Chmiela 
1001090208b5SStanisław Chmiela         // Listener that implements BranchListResponseListener for getCreditHistory()
1002090208b5SStanisław Chmiela         @Override
onReceivingResponse(JSONArray list, BranchError error)1003090208b5SStanisław Chmiela         public void onReceivingResponse(JSONArray list, BranchError error) {
1004090208b5SStanisław Chmiela             ArrayList<String> errors = new ArrayList<String>();
1005090208b5SStanisław Chmiela             if (error == null) {
1006090208b5SStanisław Chmiela                 try {
1007090208b5SStanisław Chmiela                     ReadableArray result = convertJsonToArray(list);
1008090208b5SStanisław Chmiela                     this._promise.resolve(result);
1009090208b5SStanisław Chmiela                 } catch (JSONException err) {
1010000da56bSStanisław Chmiela                     this._promise.reject(GENERIC_ERROR, err.getMessage());
1011090208b5SStanisław Chmiela                 }
1012090208b5SStanisław Chmiela             } else {
1013090208b5SStanisław Chmiela                 String errorMessage = error.getMessage();
1014090208b5SStanisław Chmiela                 Log.d(REACT_CLASS, errorMessage);
1015000da56bSStanisław Chmiela                 this._promise.reject(GENERIC_ERROR, errorMessage);
1016090208b5SStanisław Chmiela             }
1017090208b5SStanisław Chmiela         }
1018090208b5SStanisław Chmiela     }
1019090208b5SStanisław Chmiela 
1020090208b5SStanisław Chmiela     protected class RedeemRewardsListener implements Branch.BranchReferralStateChangedListener
1021090208b5SStanisław Chmiela     {
1022090208b5SStanisław Chmiela         private Promise _promise;
1023090208b5SStanisław Chmiela 
RedeemRewardsListener(Promise promise)1024090208b5SStanisław Chmiela         public RedeemRewardsListener(Promise promise) {
1025090208b5SStanisław Chmiela             this._promise = promise;
1026090208b5SStanisław Chmiela         }
1027090208b5SStanisław Chmiela 
1028090208b5SStanisław Chmiela         @Override
onStateChanged(boolean changed, BranchError error)1029090208b5SStanisław Chmiela         public void onStateChanged(boolean changed, BranchError error) {
1030090208b5SStanisław Chmiela             if (error == null) {
1031090208b5SStanisław Chmiela                 WritableMap map = new WritableNativeMap();
1032090208b5SStanisław Chmiela                 map.putBoolean("changed", changed);
1033090208b5SStanisław Chmiela                 this._promise.resolve(map);
1034090208b5SStanisław Chmiela             } else {
1035090208b5SStanisław Chmiela                 String errorMessage = error.getMessage();
1036090208b5SStanisław Chmiela                 Log.d(REACT_CLASS, errorMessage);
1037000da56bSStanisław Chmiela                 this._promise.reject(GENERIC_ERROR, errorMessage);
1038090208b5SStanisław Chmiela             }
1039090208b5SStanisław Chmiela         }
1040090208b5SStanisław Chmiela     }
1041090208b5SStanisław Chmiela 
1042090208b5SStanisław Chmiela     protected class LoadRewardsListener implements Branch.BranchReferralStateChangedListener
1043090208b5SStanisław Chmiela     {
1044090208b5SStanisław Chmiela         private String _bucket;
1045090208b5SStanisław Chmiela         private Promise _promise;
1046090208b5SStanisław Chmiela 
LoadRewardsListener(String bucket, Promise promise)1047090208b5SStanisław Chmiela         public LoadRewardsListener(String bucket, Promise promise) {
1048090208b5SStanisław Chmiela             this._bucket = bucket;
1049090208b5SStanisław Chmiela             this._promise = promise;
1050090208b5SStanisław Chmiela         }
1051090208b5SStanisław Chmiela 
1052090208b5SStanisław Chmiela         @Override
onStateChanged(boolean changed, BranchError error)1053090208b5SStanisław Chmiela         public void onStateChanged(boolean changed, BranchError error) {
1054090208b5SStanisław Chmiela             if (error == null) {
1055090208b5SStanisław Chmiela                 int credits = 0;
1056090208b5SStanisław Chmiela                 if (this._bucket == null) {
1057090208b5SStanisław Chmiela                   credits = Branch.getInstance().getCredits();
1058090208b5SStanisław Chmiela                 } else {
1059090208b5SStanisław Chmiela                   credits = Branch.getInstance().getCreditsForBucket(this._bucket);
1060090208b5SStanisław Chmiela                 }
1061090208b5SStanisław Chmiela                 WritableMap map = new WritableNativeMap();
1062090208b5SStanisław Chmiela                 map.putInt("credits", credits);
1063090208b5SStanisław Chmiela                 this._promise.resolve(map);
1064090208b5SStanisław Chmiela             } else {
1065090208b5SStanisław Chmiela                 String errorMessage = error.getMessage();
1066090208b5SStanisław Chmiela                 Log.d(REACT_CLASS, errorMessage);
1067000da56bSStanisław Chmiela                 this._promise.reject(GENERIC_ERROR, errorMessage);
1068090208b5SStanisław Chmiela             }
1069090208b5SStanisław Chmiela         }
1070090208b5SStanisław Chmiela     }
1071090208b5SStanisław Chmiela 
sendRNEvent(String eventName, @Nullable WritableMap params)1072090208b5SStanisław Chmiela     public void sendRNEvent(String eventName, @Nullable WritableMap params) {
1073090208b5SStanisław Chmiela         // This should avoid the crash in getJSModule() at startup
1074090208b5SStanisław Chmiela         // See also: https://github.com/walmartreact/react-native-orientation-listener/issues/8
1075090208b5SStanisław Chmiela 
1076090208b5SStanisław Chmiela         ReactApplicationContext context = getReactApplicationContext();
1077090208b5SStanisław Chmiela         Handler mainHandler = new Handler(context.getMainLooper());
1078090208b5SStanisław Chmiela 
1079090208b5SStanisław Chmiela         Runnable poller = new Runnable() {
1080090208b5SStanisław Chmiela 
1081090208b5SStanisław Chmiela             private Runnable init(ReactApplicationContext _context, Handler _mainHandler, String _eventName, WritableMap _params) {
1082090208b5SStanisław Chmiela                 mMainHandler = _mainHandler;
1083090208b5SStanisław Chmiela                 mEventName = _eventName;
1084090208b5SStanisław Chmiela                 mContext = _context;
1085090208b5SStanisław Chmiela                 mParams = _params;
1086090208b5SStanisław Chmiela                 return this;
1087090208b5SStanisław Chmiela             }
1088090208b5SStanisław Chmiela 
1089090208b5SStanisław Chmiela             final int pollDelayInMs = 100;
1090090208b5SStanisław Chmiela             final int maxTries = 300;
1091090208b5SStanisław Chmiela 
1092090208b5SStanisław Chmiela             int tries = 1;
1093090208b5SStanisław Chmiela             String mEventName;
1094090208b5SStanisław Chmiela             WritableMap mParams;
1095090208b5SStanisław Chmiela             Handler mMainHandler;
1096090208b5SStanisław Chmiela             ReactApplicationContext mContext;
1097090208b5SStanisław Chmiela 
1098090208b5SStanisław Chmiela             @Override
1099090208b5SStanisław Chmiela             public void run() {
1100090208b5SStanisław Chmiela                 try {
1101090208b5SStanisław Chmiela                     Log.d(REACT_CLASS, "Catalyst instance poller try " + Integer.toString(tries));
1102090208b5SStanisław Chmiela                     if (mContext.hasActiveCatalystInstance()) {
1103090208b5SStanisław Chmiela                         Log.d(REACT_CLASS, "Catalyst instance active");
1104090208b5SStanisław Chmiela                         mContext
1105090208b5SStanisław Chmiela                                 .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
1106090208b5SStanisław Chmiela                                 .emit(mEventName, mParams);
1107090208b5SStanisław Chmiela                     } else {
1108090208b5SStanisław Chmiela                         tries++;
1109090208b5SStanisław Chmiela                         if (tries <= maxTries) {
1110090208b5SStanisław Chmiela                             mMainHandler.postDelayed(this, pollDelayInMs);
1111090208b5SStanisław Chmiela                         } else {
1112090208b5SStanisław Chmiela                             Log.e(REACT_CLASS, "Could not get Catalyst instance");
1113090208b5SStanisław Chmiela                         }
1114090208b5SStanisław Chmiela                     }
1115090208b5SStanisław Chmiela                 }
1116090208b5SStanisław Chmiela                 catch (Exception e) {
1117090208b5SStanisław Chmiela                     e.printStackTrace();
1118090208b5SStanisław Chmiela                 }
1119090208b5SStanisław Chmiela             }
1120090208b5SStanisław Chmiela         }.init(context, mainHandler, eventName, params);
1121090208b5SStanisław Chmiela 
1122090208b5SStanisław Chmiela         Log.d(REACT_CLASS, "sendRNEvent");
1123090208b5SStanisław Chmiela 
1124090208b5SStanisław Chmiela         mainHandler.post(poller);
1125090208b5SStanisław Chmiela     }
1126090208b5SStanisław Chmiela 
getReadableMapObjectForKey(ReadableMap readableMap, String key)1127090208b5SStanisław Chmiela     private static Object getReadableMapObjectForKey(ReadableMap readableMap, String key) {
1128090208b5SStanisław Chmiela         switch (readableMap.getType(key)) {
1129090208b5SStanisław Chmiela             case Null:
1130090208b5SStanisław Chmiela                 return "Null";
1131090208b5SStanisław Chmiela             case Boolean:
1132090208b5SStanisław Chmiela                 return readableMap.getBoolean(key);
1133090208b5SStanisław Chmiela             case Number:
1134090208b5SStanisław Chmiela                 if (readableMap.getDouble(key) % 1 == 0) {
1135090208b5SStanisław Chmiela                     return readableMap.getInt(key);
1136090208b5SStanisław Chmiela                 } else {
1137090208b5SStanisław Chmiela                     return readableMap.getDouble(key);
1138090208b5SStanisław Chmiela                 }
1139090208b5SStanisław Chmiela             case String:
1140090208b5SStanisław Chmiela                 return readableMap.getString(key);
1141090208b5SStanisław Chmiela             default:
1142090208b5SStanisław Chmiela                 return "Unsupported Type";
1143090208b5SStanisław Chmiela         }
1144090208b5SStanisław Chmiela     }
1145090208b5SStanisław Chmiela 
convertMapToJson(ReadableMap readableMap)1146090208b5SStanisław Chmiela     private static JSONObject convertMapToJson(ReadableMap readableMap) throws JSONException {
1147090208b5SStanisław Chmiela         JSONObject object = new JSONObject();
1148090208b5SStanisław Chmiela         ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
1149090208b5SStanisław Chmiela         while (iterator.hasNextKey()) {
1150090208b5SStanisław Chmiela             String key = iterator.nextKey();
1151090208b5SStanisław Chmiela             switch (readableMap.getType(key)) {
1152090208b5SStanisław Chmiela                 case Null:
1153090208b5SStanisław Chmiela                     object.put(key, JSONObject.NULL);
1154090208b5SStanisław Chmiela                     break;
1155090208b5SStanisław Chmiela                 case Boolean:
1156090208b5SStanisław Chmiela                     object.put(key, readableMap.getBoolean(key));
1157090208b5SStanisław Chmiela                     break;
1158090208b5SStanisław Chmiela                 case Number:
1159090208b5SStanisław Chmiela                     object.put(key, readableMap.getDouble(key));
1160090208b5SStanisław Chmiela                     break;
1161090208b5SStanisław Chmiela                 case String:
1162090208b5SStanisław Chmiela                     object.put(key, readableMap.getString(key));
1163090208b5SStanisław Chmiela                     break;
1164090208b5SStanisław Chmiela                 case Map:
1165090208b5SStanisław Chmiela                     object.put(key, convertMapToJson(readableMap.getMap(key)));
1166090208b5SStanisław Chmiela                     break;
1167090208b5SStanisław Chmiela                 case Array:
1168090208b5SStanisław Chmiela                     object.put(key, convertArrayToJson(readableMap.getArray(key)));
1169090208b5SStanisław Chmiela                     break;
1170090208b5SStanisław Chmiela             }
1171090208b5SStanisław Chmiela         }
1172090208b5SStanisław Chmiela         return object;
1173090208b5SStanisław Chmiela     }
1174090208b5SStanisław Chmiela 
convertArrayToJson(ReadableArray readableArray)1175090208b5SStanisław Chmiela     private static JSONArray convertArrayToJson(ReadableArray readableArray) throws JSONException {
1176090208b5SStanisław Chmiela         JSONArray array = new JSONArray();
1177090208b5SStanisław Chmiela         for (int i = 0; i < readableArray.size(); i++) {
1178090208b5SStanisław Chmiela             switch (readableArray.getType(i)) {
1179090208b5SStanisław Chmiela                 case Null:
1180090208b5SStanisław Chmiela                     break;
1181090208b5SStanisław Chmiela                 case Boolean:
1182090208b5SStanisław Chmiela                     array.put(readableArray.getBoolean(i));
1183090208b5SStanisław Chmiela                     break;
1184090208b5SStanisław Chmiela                 case Number:
1185090208b5SStanisław Chmiela                     array.put(readableArray.getDouble(i));
1186090208b5SStanisław Chmiela                     break;
1187090208b5SStanisław Chmiela                 case String:
1188090208b5SStanisław Chmiela                     array.put(readableArray.getString(i));
1189090208b5SStanisław Chmiela                     break;
1190090208b5SStanisław Chmiela                 case Map:
1191090208b5SStanisław Chmiela                     array.put(convertMapToJson(readableArray.getMap(i)));
1192090208b5SStanisław Chmiela                     break;
1193090208b5SStanisław Chmiela                 case Array:
1194090208b5SStanisław Chmiela                     array.put(convertArrayToJson(readableArray.getArray(i)));
1195090208b5SStanisław Chmiela                     break;
1196090208b5SStanisław Chmiela             }
1197090208b5SStanisław Chmiela         }
1198090208b5SStanisław Chmiela         return array;
1199090208b5SStanisław Chmiela     }
1200090208b5SStanisław Chmiela 
convertJsonToMap(JSONObject jsonObject)1201090208b5SStanisław Chmiela     private static WritableMap convertJsonToMap(JSONObject jsonObject) {
1202090208b5SStanisław Chmiela         if(jsonObject == null) {
1203090208b5SStanisław Chmiela             return null;
1204090208b5SStanisław Chmiela         }
1205090208b5SStanisław Chmiela 
1206090208b5SStanisław Chmiela         WritableMap map = new WritableNativeMap();
1207090208b5SStanisław Chmiela 
1208090208b5SStanisław Chmiela         try {
1209090208b5SStanisław Chmiela             Iterator<String> iterator = jsonObject.keys();
1210090208b5SStanisław Chmiela             while (iterator.hasNext()) {
1211090208b5SStanisław Chmiela                 String key = iterator.next();
1212090208b5SStanisław Chmiela                 Object value = jsonObject.get(key);
1213090208b5SStanisław Chmiela                 if (value instanceof JSONObject) {
1214090208b5SStanisław Chmiela                     map.putMap(key, convertJsonToMap((JSONObject) value));
1215090208b5SStanisław Chmiela                 } else if (value instanceof  JSONArray) {
1216090208b5SStanisław Chmiela                     map.putArray(key, convertJsonToArray((JSONArray) value));
1217090208b5SStanisław Chmiela                 } else if (value instanceof  Boolean) {
1218090208b5SStanisław Chmiela                     map.putBoolean(key, (Boolean) value);
1219090208b5SStanisław Chmiela                 } else if (value instanceof  Integer) {
1220090208b5SStanisław Chmiela                     map.putInt(key, (Integer) value);
1221090208b5SStanisław Chmiela                 } else if (value instanceof  Double) {
1222090208b5SStanisław Chmiela                     map.putDouble(key, (Double) value);
1223090208b5SStanisław Chmiela                 } else if (value instanceof String)  {
1224090208b5SStanisław Chmiela                     map.putString(key, (String) value);
1225090208b5SStanisław Chmiela                 } else if (value == null || value == JSONObject.NULL) {
1226090208b5SStanisław Chmiela                     map.putNull(key);
1227090208b5SStanisław Chmiela                 } else {
1228090208b5SStanisław Chmiela                     map.putString(key, value.toString());
1229090208b5SStanisław Chmiela                 }
1230090208b5SStanisław Chmiela             }
1231090208b5SStanisław Chmiela         } catch(JSONException ex) {
1232090208b5SStanisław Chmiela             map.putString("error", "Failed to convert JSONObject to WriteableMap: " + ex.getMessage());
1233090208b5SStanisław Chmiela         }
1234090208b5SStanisław Chmiela 
1235090208b5SStanisław Chmiela         return map;
1236090208b5SStanisław Chmiela     }
1237090208b5SStanisław Chmiela 
convertJsonToArray(JSONArray jsonArray)1238090208b5SStanisław Chmiela     private static WritableArray convertJsonToArray(JSONArray jsonArray) throws JSONException {
1239090208b5SStanisław Chmiela         WritableArray array = new WritableNativeArray();
1240090208b5SStanisław Chmiela 
1241090208b5SStanisław Chmiela         for (int i = 0; i < jsonArray.length(); i++) {
1242090208b5SStanisław Chmiela             Object value = jsonArray.get(i);
1243090208b5SStanisław Chmiela             if (value instanceof JSONObject) {
1244090208b5SStanisław Chmiela                 array.pushMap(convertJsonToMap((JSONObject) value));
1245090208b5SStanisław Chmiela             } else if (value instanceof  JSONArray) {
1246090208b5SStanisław Chmiela                 array.pushArray(convertJsonToArray((JSONArray) value));
1247090208b5SStanisław Chmiela             } else if (value instanceof  Boolean) {
1248090208b5SStanisław Chmiela                 array.pushBoolean((Boolean) value);
1249090208b5SStanisław Chmiela             } else if (value instanceof  Integer) {
1250090208b5SStanisław Chmiela                 array.pushInt((Integer) value);
1251090208b5SStanisław Chmiela             } else if (value instanceof  Double) {
1252090208b5SStanisław Chmiela                 array.pushDouble((Double) value);
1253090208b5SStanisław Chmiela             } else if (value instanceof String)  {
1254090208b5SStanisław Chmiela                 array.pushString((String) value);
1255090208b5SStanisław Chmiela             } else {
1256090208b5SStanisław Chmiela                 array.pushString(value.toString());
1257090208b5SStanisław Chmiela             }
1258090208b5SStanisław Chmiela         }
1259090208b5SStanisław Chmiela         return array;
1260090208b5SStanisław Chmiela     }
1261090208b5SStanisław Chmiela 
1262090208b5SStanisław Chmiela     // Convert an arbitrary ReadableMap to a string-string hash of custom params for userCompletedAction.
convertMapToParams(ReadableMap map)1263090208b5SStanisław Chmiela     private static HashMap<String, String> convertMapToParams(ReadableMap map) {
1264090208b5SStanisław Chmiela         if (map == null) return null;
1265090208b5SStanisław Chmiela 
1266090208b5SStanisław Chmiela         HashMap<String, String> hash = new HashMap<>();
1267090208b5SStanisław Chmiela 
1268090208b5SStanisław Chmiela         ReadableMapKeySetIterator iterator = map.keySetIterator();
1269090208b5SStanisław Chmiela         while (iterator.hasNextKey()) {
1270090208b5SStanisław Chmiela             String key = iterator.nextKey();
1271090208b5SStanisław Chmiela             switch (map.getType(key)) {
1272090208b5SStanisław Chmiela                 case String:
1273090208b5SStanisław Chmiela                     hash.put(key, map.getString(key));
1274090208b5SStanisław Chmiela                 case Boolean:
1275090208b5SStanisław Chmiela                     hash.put(key, "" + map.getBoolean(key));
1276090208b5SStanisław Chmiela                 case Number:
1277090208b5SStanisław Chmiela                     hash.put(key, "" + map.getDouble(key));
1278090208b5SStanisław Chmiela                 default:
1279090208b5SStanisław Chmiela                     Log.w(REACT_CLASS, "Unsupported data type in params, ignoring");
1280090208b5SStanisław Chmiela             }
1281090208b5SStanisław Chmiela         }
1282090208b5SStanisław Chmiela 
1283090208b5SStanisław Chmiela         return hash;
1284090208b5SStanisław Chmiela     }
1285090208b5SStanisław Chmiela }
1286