it.near.sdk.reactnative.rnnearitsdk.RNNearItModule.java Source code

Java tutorial

Introduction

Here is the source code for it.near.sdk.reactnative.rnnearitsdk.RNNearItModule.java

Source

/*
 * Copyright (c) 2017 Mattia Panzeri <mattia.panzeri93@gmail.com>
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

package it.near.sdk.reactnative.rnnearitsdk;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Parcelable;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeArray;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import it.near.sdk.NearItManager;
import it.near.sdk.communication.OptOutNotifier;
import it.near.sdk.operation.NearItUserProfile;
import it.near.sdk.reactions.couponplugin.CouponListener;
import it.near.sdk.reactions.couponplugin.model.Coupon;
import it.near.sdk.reactions.feedbackplugin.FeedbackEvent;
import it.near.sdk.reactions.feedbackplugin.model.Feedback;
import it.near.sdk.recipes.NearITEventHandler;
import it.near.sdk.recipes.RecipeRefreshListener;
import it.near.sdk.recipes.foreground.ProximityListener;
import it.near.sdk.recipes.models.Recipe;
import it.near.sdk.trackings.TrackingInfo;
import it.near.sdk.utils.NearUtils;

public class RNNearItModule extends ReactContextBaseJavaModule
        implements LifecycleEventListener, ActivityEventListener, ProximityListener {

    // Module name
    static final String MODULE_NAME = "RNNearIt";

    // Event topic (used by JS to subscribe to generated events)
    static final String NATIVE_EVENTS_TOPIC = "RNNearItEvent";
    private static final String NATIVE_PERMISSIONS_TOPIC = "RNNearItPermissions";

    // Local Events topic (used by LocalBroadcastReceiver to handle foreground notifications)
    static final String LOCAL_EVENTS_TOPIC = "RNNearItLocalEvents";

    // Module Constants
    // Events type
    private static final String EVENT_TYPE_PERMISSIONS = "NearIt.Events.PermissionStatus";
    static final String EVENT_TYPE_SIMPLE = "NearIt.Events.SimpleNotification";
    static final String EVENT_TYPE_CUSTOM_JSON = "NearIt.Events.CustomJSON";
    static final String EVENT_TYPE_COUPON = "NearIt.Events.Coupon";
    static final String EVENT_TYPE_CONTENT = "NearIt.Events.Content";
    static final String EVENT_TYPE_FEEDBACK = "NearIt.Events.Feedback";

    // Events content
    static final String EVENT_TYPE = "type";
    static final String EVENT_TRACKING_INFO = "trackingInfo";
    static final String EVENT_CONTENT = "content";
    static final String EVENT_CONTENT_MESSAGE = "message";
    static final String EVENT_CONTENT_DATA = "data";
    static final String EVENT_CONTENT_COUPON = "coupon";
    static final String EVENT_CONTENT_TEXT = "text";
    static final String EVENT_CONTENT_TITLE = "title";
    static final String EVENT_CONTENT_IMAGE = "image";
    static final String EVENT_CONTENT_CTA = "cta";
    static final String EVENT_CONTENT_FEEDBACK = "feedbackId";
    static final String EVENT_CONTENT_QUESTION = "feedbackQuestion";
    static final String EVENT_FROM_USER_ACTION = "fromUserAction";
    private static final String EVENT_STATUS = "status";

    // Location permission status
    private static final String PERMISSION_LOCATION_GRANTED = "NearIt.Permissions.Location.Granted";
    private static final String PERMISSION_LOCATION_DENIED = "NearIt.Permissions.Location.Denied";

    // Error codes
    private static final String E_REFRESH_CONFIG_ERROR = "E_REFRESH_CONFIG_ERROR";
    private static final String E_START_RADAR_ERROR = "E_START_RADAR_ERROR";
    private static final String E_STOP_RADAR_ERROR = "E_STOP_RADAR_ERROR";
    private static final String E_SEND_TRACKING_ERROR = "E_SEND_TRACKING_ERROR";
    private static final String E_SEND_FEEDBACK_ERROR = "E_SEND_FEEDBACK_ERROR";
    private static final String E_USER_PROFILE_GET_ERROR = "E_USER_PROFILE_GET_ERROR";
    private static final String E_USER_PROFILE_SET_ERROR = "E_USER_PROFILE_SET_ERROR";
    private static final String E_USER_PROFILE_RESET_ERROR = "E_USER_PROFILE_RESET_ERROR";
    private static final String E_USER_PROFILE_DATA_ERROR = "E_USER_PROFILE_DATA_ERROR";
    private static final String E_COUPONS_PARSING_ERROR = "E_COUPONS_PARSING_ERROR";
    private static final String E_COUPONS_RETRIEVAL_ERROR = "E_COUPONS_RETRIEVAL_ERROR";
    private static final String E_OPT_OUT_ERROR = "E_OPT_OUT_ERROR";

    public RNNearItModule(ReactApplicationContext reactContext) {
        super(reactContext);

        // Listen for Resume, Pause, Destroy events
        reactContext.addLifecycleEventListener(this);
        reactContext.addActivityEventListener(this);

        // Register LocalBroadcastReceiver to be used for Foreground notification handling
        final LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(reactContext);
        localBroadcastManager.registerReceiver(new LocalBroadcastReceiver(), new IntentFilter(LOCAL_EVENTS_TOPIC));
    }

    // Module definition
    @Override
    public String getName() {
        return MODULE_NAME;
    }

    @Override
    public Map<String, Object> getConstants() {
        return Collections.unmodifiableMap(new HashMap<String, Object>() {
            {
                put("NativeEventsTopic", NATIVE_EVENTS_TOPIC);
                put("NativePermissionsTopic", NATIVE_PERMISSIONS_TOPIC);
                put("Events", getEventsConstants());
                put("EventContent", getEventContentConstants());
                put("Statuses", getStatusConstants());
                put("Permissions", getPermissionStatusConstants());
            }

            private Map<String, Object> getEventsConstants() {
                return Collections.unmodifiableMap(new HashMap<String, Object>() {
                    {
                        put("PermissionStatus", EVENT_TYPE_PERMISSIONS);
                        put("SimpleNotification", EVENT_TYPE_SIMPLE);
                        put("CustomJson", EVENT_TYPE_CUSTOM_JSON);
                        put("Coupon", EVENT_TYPE_COUPON);
                        put("Content", EVENT_TYPE_CONTENT);
                        put("Feedback", EVENT_TYPE_FEEDBACK);
                    }
                });
            }

            private Map<String, Object> getEventContentConstants() {
                return Collections.unmodifiableMap(new HashMap<String, Object>() {
                    {
                        put("type", EVENT_TYPE);
                        put("trackingInfo", EVENT_TRACKING_INFO);
                        put("content", EVENT_CONTENT);
                        put("message", EVENT_CONTENT_MESSAGE);
                        put("data", EVENT_CONTENT_DATA);
                        put("coupon", EVENT_CONTENT_COUPON);
                        put("title", EVENT_CONTENT_TITLE);
                        put("text", EVENT_CONTENT_TEXT);
                        put("image", EVENT_CONTENT_IMAGE);
                        put("cta", EVENT_CONTENT_CTA);
                        put("feedbackId", EVENT_CONTENT_FEEDBACK);
                        put("question", EVENT_CONTENT_QUESTION);
                        put("fromUserAction", EVENT_FROM_USER_ACTION);
                        put("status", EVENT_STATUS);
                    }
                });
            }

            private Map<String, Object> getStatusConstants() {
                return Collections.unmodifiableMap(new HashMap<String, Object>() {
                    {
                        put("notified", Recipe.NOTIFIED_STATUS);
                        put("engaged", Recipe.ENGAGED_STATUS);
                    }
                });
            }

            private Map<String, Object> getPermissionStatusConstants() {
                return Collections.unmodifiableMap(new HashMap<String, Object>() {
                    {
                        put("LocationGranted", PERMISSION_LOCATION_GRANTED);
                        put("LocationDenied", PERMISSION_LOCATION_DENIED);
                    }
                });
            }
        });
    }

    public static void onPostCreate(Context context, Intent intent) {
        if (NearUtils.carriesNearItContent(intent)) {
            NearUtils.parseContents(intent, new RNNearItCoreContentsListener(context, null, true));
        }
    }

    // ReactApp Lifecycle methods
    @Override
    public void onHostResume() {
        NearItManager.getInstance().addProximityListener(this);
        this.dispatchNotificationQueue();
    }

    @Override
    public void onHostPause() {
        NearItManager.getInstance().removeProximityListener(this);
    }

    @Override
    public void onHostDestroy() {
        RNNearItPersistedQueue.defaultQueue().resetListeners();
    }

    @Override
    public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
        // Nothing to do here
    }

    @Override
    public void onNewIntent(Intent intent) {
        if (NearUtils.carriesNearItContent(intent)) {
            NearUtils.parseContents(intent, new RNNearItCoreContentsListener(getReactApplicationContext(),
                    getRCTDeviceEventEmitter(), true));
        }
    }

    // ReactNative listeners management
    @ReactMethod
    public void listenerRegistered(final Promise promise) {
        final int listenersCount = RNNearItPersistedQueue.defaultQueue().registerListener();
        Log.i(MODULE_NAME, String.format("listenerRegistered (Registered listeners: %d)", listenersCount));
        this.dispatchNotificationQueue();
        promise.resolve(true);
    }

    @ReactMethod
    public void listenerUnregistered(final Promise promise) {
        final int listenersCount = RNNearItPersistedQueue.defaultQueue().unregisterListener();
        Log.d(MODULE_NAME, String.format("listenerUnregistered (Registered listeners: %d)", listenersCount));
        promise.resolve(true);
    }

    // NearIT SDK Listeners
    @Override
    public void foregroundEvent(Parcelable parcelable, TrackingInfo trackingInfo) {
        NearUtils.parseContents(parcelable, trackingInfo,
                new RNNearItCoreContentsListener(getReactApplicationContext(), getRCTDeviceEventEmitter(), false));
    }

    // NearIT Config
    @ReactMethod
    public void refreshConfig(final Promise promise) {
        NearItManager.getInstance().refreshConfigs(new RecipeRefreshListener() {
            @Override
            public void onRecipesRefresh() {
                promise.resolve(null);
            }

            @Override
            public void onRecipesRefreshFail() {
                promise.reject(E_REFRESH_CONFIG_ERROR, "refreshConfig failed");
            }
        });
    }

    // NearIT Radar
    @ReactMethod
    public void startRadar(final Promise promise) {
        try {
            NearItManager.getInstance().startRadar();
            promise.resolve(null);
        } catch (Exception e) {
            promise.reject(E_START_RADAR_ERROR, e);
        }
    }

    @ReactMethod
    public void stopRadar(final Promise promise) {
        try {
            NearItManager.getInstance().stopRadar();
            promise.resolve(null);
        } catch (Exception e) {
            promise.reject(E_STOP_RADAR_ERROR, e);
        }
    }

    // NearIT Trackings
    @ReactMethod
    public void sendTracking(final String trackinInfoData, final String status, final Promise promise) {
        try {
            NearItManager.getInstance().sendTracking(RNNearItUtils.trackingInfoFromBase64(trackinInfoData), status);

            promise.resolve(null);
        } catch (Exception e) {
            promise.reject(E_SEND_TRACKING_ERROR, e);
        }
    }

    // NearIT Feedback
    @ReactMethod
    public void sendFeedback(final String feedbackB64, final int rating, final String comment,
            final Promise promise) {
        final Feedback feedback;
        try {
            feedback = RNNearItUtils.feedbackFromBase64(feedbackB64);

            NearItManager.getInstance().sendEvent(new FeedbackEvent(feedback, rating, comment),
                    new NearITEventHandler() {
                        @Override
                        public void onSuccess() {
                            promise.resolve(null);
                        }

                        @Override
                        public void onFail(int errorCode, String errorMessage) {
                            promise.reject(E_SEND_FEEDBACK_ERROR, "Failed to send feedback to NearIT");
                        }
                    });
        } catch (Exception e) {
            promise.reject(E_SEND_FEEDBACK_ERROR, "Failed to encode feedback to be sent", e);
        }
    }

    // NearIT UserProfiling
    @ReactMethod
    public void getUserProfileId(final Promise promise) {
        NearItManager.getInstance().getProfileId(new NearItUserProfile.ProfileFetchListener() {
            @Override
            public void onProfileId(String profileId) {
                promise.resolve(profileId);
            }

            @Override
            public void onError(String error) {
                promise.reject(E_USER_PROFILE_GET_ERROR, error);
            }
        });
    }

    @ReactMethod
    public void setUserProfileId(final String profileId, final Promise promise) {
        try {
            NearItManager.getInstance().setProfileId(profileId);
            promise.resolve(profileId);
        } catch (Exception e) {
            promise.reject(E_USER_PROFILE_SET_ERROR, e);
        }
    }

    @ReactMethod
    public void resetUserProfile(final Promise promise) {
        NearItManager.getInstance().resetProfileId(new NearItUserProfile.ProfileFetchListener() {
            @Override
            public void onProfileId(String profileId) {
                promise.resolve(profileId);
            }

            @Override
            public void onError(String error) {
                promise.reject(E_USER_PROFILE_RESET_ERROR, error);
            }
        });
    }

    @ReactMethod
    public void setUserData(final ReadableMap userData, final Promise promise) {
        try {
            for (HashMap.Entry<String, Object> ud : userData.toHashMap().entrySet()) {
                NearItManager.getInstance().setUserData(ud.getKey(), String.valueOf(ud.getValue()));
            }
            promise.resolve(null);
        } catch (Exception e) {
            promise.reject(E_USER_PROFILE_DATA_ERROR, "Error while setting UserData");
        }
    }

    @ReactMethod
    public void optOut(final Promise promise) {
        NearItManager.getInstance().optOut(new OptOutNotifier() {
            @Override
            public void onSuccess() {
                promise.resolve(null);
            }

            @Override
            public void onFailure(String error) {
                promise.reject(E_OPT_OUT_ERROR, error);
            }
        });
    }

    // NearIT Permissions request

    @ReactMethod
    public void checkNotificationPermission(final Promise promise) {
        promise.resolve(true);
    }

    @ReactMethod
    public void requestNotificationPermission(final Promise promise) {
        promise.resolve(true);
    }

    @ReactMethod
    public void checkLocationPermission(final Promise promise) {
        promise.resolve(true);
    }

    @ReactMethod
    public void requestLocationPermission(final Promise promise) {
        promise.resolve(true);
    }

    // NearIT Custom Trigger

    @ReactMethod
    public void triggerEvent(final String eventKey, final Promise promise) {
        // Trigger Custom Event Key
        NearItManager.getInstance().processCustomTrigger(eventKey);
        // Resolve null, if a Recipe is triggered then the normal notification flow will run
        promise.resolve(null);
    }

    // NearIT Coupons

    @ReactMethod
    public void getCoupons(final Promise promise) {
        NearItManager.getInstance().getCoupons(new CouponListener() {
            @Override
            public void onCouponsDownloaded(List<Coupon> list) {
                try {
                    final WritableArray coupons = new WritableNativeArray();
                    for (Coupon c : list) {
                        coupons.pushMap(RNNearItUtils.bundleCoupon(c));
                    }
                    promise.resolve(coupons);
                } catch (Exception e) {
                    promise.reject(E_COUPONS_PARSING_ERROR, e);
                }
            }

            @Override
            public void onCouponDownloadError(String errorMessage) {
                promise.reject(E_COUPONS_RETRIEVAL_ERROR, errorMessage);
            }
        });
    }

    // Private methods
    private void dispatchNotificationQueue() {
        // Try to flush background notifications when a listener is added
        RNNearItPersistedQueue.dispatchNotificationsQueue(getReactApplicationContext(),
                new RNNearItPersistedQueue.NotificationDispatcher() {
                    @Override
                    public void onNotification(WritableMap notification) {
                        Log.i(MODULE_NAME, "Dispatching background notification");
                        try {
                            getRCTDeviceEventEmitter().emit(NATIVE_EVENTS_TOPIC, notification);
                        } catch (Exception e) {
                            Log.i(MODULE_NAME, "Error dispatching backgrounded notification");
                        }
                    }
                });
    }

    private DeviceEventManagerModule.RCTDeviceEventEmitter getRCTDeviceEventEmitter() {
        return this.getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class);
    }

    private void sendEventWithLocationPermissionStatus(final String permissionStatus) {
        try {
            // Create Event map to send to JS
            final WritableMap eventMap = new WritableNativeMap();
            eventMap.putString(EVENT_TYPE, EVENT_TYPE_PERMISSIONS);
            eventMap.putString(EVENT_STATUS, permissionStatus);

            // Send event to JS
            getRCTDeviceEventEmitter().emit(NATIVE_PERMISSIONS_TOPIC, eventMap);
        } catch (Exception e) {
            Log.e(MODULE_NAME, "Error while sending permissions to JS");
        }
    }

    // LocalBroadcastReceiver
    public class LocalBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent != null && NearUtils.carriesNearItContent(intent)) {
                NearUtils.parseContents(intent, new RNNearItCoreContentsListener(getReactApplicationContext(),
                        getRCTDeviceEventEmitter(), false));
            }
        }
    }
}