package cn.jiguang.cordova.push;

import android.app.Activity;
import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;

import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaWebView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import cn.jpush.android.api.BasicPushNotificationBuilder;
import cn.jpush.android.api.JPushInterface;
import cn.jpush.android.api.TagAliasCallback;
import cn.jpush.android.data.JPushLocalNotification;
import cn.jiguang.api.utils.JCollectionAuth;

public class JPushPlugin extends CordovaPlugin {

    private static final String TAG = JPushPlugin.class.getSimpleName();

    private Context mContext;

    private static JPushPlugin instance;
    private static Activity cordovaActivity;

    static JSONObject notificationJson;
    static Map<String, Object> notificationExtras = new HashMap<String, Object>();

    static JSONObject openNotificationJson;
    static Map<String, Object> openNotificationExtras = new HashMap<String, Object>();

    static Map<Integer, CallbackContext> eventCallbackMap = new HashMap<Integer, CallbackContext>();

    /** getPushStatus 异步结果通过 JPushMessageReceiver.onCommandResult(cmd==2003) 回调，此处暂存 CallbackContext */
    static CallbackContext getPushStatusCallbackContext;

    public JPushPlugin() {
        instance = this;
    }

    @Override
    public void initialize(CordovaInterface cordova, CordovaWebView webView) {
        super.initialize(cordova, webView);
        mContext = cordova.getActivity().getApplicationContext();
        Log.d(TAG,"initialize plugin");

//        JPushInterface.init(mContext);

        cordovaActivity = cordova.getActivity();

        // 如果同时缓存了打开事件 openNotificationAlert 和 消息事件 notificationAlert，只向 UI 发打开事件。
        // 这样做是为了和 iOS 统一。
        if (openNotificationJson != null) {
            transmitNotificationOpen(openNotificationJson);
        }
        if (notificationJson != null) {
            transmitNotificationReceive(notificationJson);
        }
    }

    public void onResume(boolean multitasking) {
        if (openNotificationJson != null) {
            transmitNotificationOpen(openNotificationJson);
        }
        if (notificationJson != null) {
            transmitNotificationReceive(notificationJson);
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        cordovaActivity = null;
        instance = null;
    }

    private static JSONObject getMessageObject(String message, Map<String, Object> extras) {
        JSONObject data = new JSONObject();
        try {
            data.put("message", message);
            JSONObject jExtras = new JSONObject();
            for (Entry<String, Object> entry : extras.entrySet()) {
                if (entry.getKey().equals("cn.jpush.android.EXTRA")) {
                    JSONObject jo;
                    if (TextUtils.isEmpty((String) entry.getValue())) {
                        jo = new JSONObject();
                    } else {
                        jo = new JSONObject((String) entry.getValue());
                        String key;
                        Iterator keys = jo.keys();
                        while (keys.hasNext()) {
                            key = keys.next().toString();
                            jExtras.put(key, jo.getString(key));
                        }
                    }
                    jExtras.put("cn.jpush.android.EXTRA", jo);
                } else {
                    jExtras.put(entry.getKey(), entry.getValue());
                }
            }
            if (jExtras.length() > 0) {
                data.put("extras", jExtras);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return data;
    }

    private static JSONObject getNotificationObject(String title, String alert, Map<String, Object> extras) {
        JSONObject data = new JSONObject();
        try {
            data.put("title", title);
            data.put("alert", alert);
            JSONObject jExtras = new JSONObject();
            for (Entry<String, Object> entry : extras.entrySet()) {
                if (entry.getKey().equals("cn.jpush.android.EXTRA")) {
                    JSONObject jo;
                    if (TextUtils.isEmpty((String) entry.getValue())) {
                        jo = new JSONObject();
                    } else {
                        jo = new JSONObject((String) entry.getValue());
                        String key;
                        Iterator keys = jo.keys();
                        while (keys.hasNext()) {
                            key = keys.next().toString();
                            jExtras.put(key, jo.getString(key));
                        }
                    }
                    jExtras.put("cn.jpush.android.EXTRA", jo);
                } else {
                    jExtras.put(entry.getKey(), entry.getValue());
                }
            }
            if (jExtras.length() > 0) {
                data.put("extras", jExtras);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return data;
    }

    static void transmitMessageReceive(JSONObject data) {
        if (instance == null) {
            return;
        }
//        JSONObject data = getMessageObject(message, extras);
        String format = "window.plugins.jPushPlugin.receiveMessageInAndroidCallback(%s);";
        final String js = String.format(format, data.toString());
        cordovaActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                instance.webView.loadUrl("javascript:" + js);
            }
        });
    }
    static void transmitInAppMessageClick( JSONObject data) {
        if (instance == null) {
            return;
        }
        String format = "window.plugins.jPushPlugin.receiveInAppMessageClickCallback(%s);";
        final String js = String.format(format, data.toString());
        cordovaActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                instance.webView.loadUrl("javascript:" + js);
            }
        });
    }
    static void transmitInAppMessageShow( JSONObject data) {
        if (instance == null) {
            return;
        }
        String format = "window.plugins.jPushPlugin.receiveInAppMessageShowCallback(%s);";
        final String js = String.format(format, data.toString());
        cordovaActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                instance.webView.loadUrl("javascript:" + js);
            }
        });
    }
    
    static void transmitNotifyButtonClick( JSONObject data) {
        if (instance == null) {
            return;
        }
        String format = "window.plugins.jPushPlugin.receiveNotifyButtonClickCallback(%s);";
        final String js = String.format(format, data.toString());
        cordovaActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                instance.webView.loadUrl("javascript:" + js);
            }
        });
    }


    static void transmitNotificationOpen(JSONObject data) {
        if (instance == null) {
            return;
        }
//        JSONObject data = getNotificationObject(title, alert, extras);
        String format = "window.plugins.jPushPlugin.openNotificationInAndroidCallback(%s);";
        final String js = String.format(format, data.toString());
        cordovaActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                instance.webView.loadUrl("javascript:" + js);
            }
        });
        JPushPlugin.openNotificationJson = null;
    }

    static void transmitNotificationReceive(JSONObject data) {
        if (instance == null) {
            return;
        }
//        JSONObject data = getNotificationObject(title, alert, extras);
        String format = "window.plugins.jPushPlugin.receiveNotificationInAndroidCallback(%s);";
        final String js = String.format(format, data.toString());
        cordovaActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                instance.webView.loadUrl("javascript:" + js);
            }
        });
        JPushPlugin.notificationJson = null;
    }

    static void transmitReceiveRegistrationId(String rId) {
        if (instance == null) {
            return;
        }
        JSONObject data = new JSONObject();
        try {
            data.put("registrationId", rId);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        String format = "window.plugins.jPushPlugin.receiveRegistrationIdInAndroidCallback(%s);";
        final String js = String.format(format, data.toString());
        cordovaActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                instance.webView.loadUrl("javascript:" + js);
            }
        });
    }

    @Override
    public boolean execute(final String action, final JSONArray data, final CallbackContext callbackContext)
            throws JSONException {
        cordova.getThreadPool().execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Method method = JPushPlugin.class.getDeclaredMethod(action, JSONArray.class, CallbackContext.class);
                    method.invoke(JPushPlugin.this, data, callbackContext);
                } catch (Exception e) {
                    Log.e(TAG, e.toString());
                }
            }
        });
        return true;
    }

    void init(JSONArray data, CallbackContext callbackContext) {
        JPushInterface.setNotificationCallBackEnable(mContext, true);
        JPushInterface.init(mContext);
    }

    void setDebugMode(JSONArray data, CallbackContext callbackContext) {
        boolean mode;
        try {
            mode = data.getBoolean(0);
            JPushInterface.setDebugMode(mode);
            JLogger.setLoggerEnable(mode);
            callbackContext.success();
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    void setDataInsightsEnable(JSONArray data, CallbackContext callbackContext) {
        boolean mode;
        try {
            mode = data.getBoolean(0);
            JPushInterface.setDataInsightsEnable(mContext,mode);
            callbackContext.success();
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    void stopPush(JSONArray data, CallbackContext callbackContext) {
        JPushInterface.stopPush(mContext);
        callbackContext.success();
    }

    void resumePush(JSONArray data, CallbackContext callbackContext) {
        JPushInterface.resumePush(mContext);
        callbackContext.success();
    }

    void isPushStopped(JSONArray data, CallbackContext callbackContext) {
        boolean isStopped = JPushInterface.isPushStopped(mContext);
        if (isStopped) {
            callbackContext.success(1);
        } else {
            callbackContext.success(0);
        }
    }

    void getPushStatus(JSONArray data, CallbackContext callbackContext) {
        getPushStatusCallbackContext = callbackContext;
        JPushInterface.getPushStatus(mContext);
        // 结果在 JPushEventReceiver.onCommandResult 中当 cmd==2003 时回调
    }

    /**
     * 由 JPushEventReceiver.onCommandResult 在 cmd==2003 时调用，传递 getPushStatus 异步结果。
     * 与 iOS 对齐：code=0 表示成功，非 0 表示错误码；isStopped=0 未停止，1 已停止。
     * Android errorCode: 0=未停止, 1=已停止, 其他=异常。
     */
    static void onGetPushStatusResult(int errorCode) {
        if (getPushStatusCallbackContext == null) return;
        try {
            JSONObject result = new JSONObject();
            if (errorCode == 0 || errorCode == 1) {
                result.put("code", 0);
                result.put("isStopped", errorCode == 1 ? 1 : 0);
            } else {
                result.put("code", errorCode);
                result.put("isStopped", 0);
            }
            getPushStatusCallbackContext.success(result);
        } catch (JSONException e) {
            getPushStatusCallbackContext.error("getPushStatus result error");
        }
        getPushStatusCallbackContext = null;
    }

    void areNotificationEnabled(JSONArray data, final CallbackContext callback) {
        int isEnabled;
        if (hasPermission("OP_POST_NOTIFICATION")) {
            isEnabled = 1;
        } else {
            isEnabled = 0;
        }
        callback.success(isEnabled);
    }

    void setLatestNotificationNum(JSONArray data, CallbackContext callbackContext) {
        int num = -1;
        try {
            num = data.getInt(0);
        } catch (JSONException e) {
            e.printStackTrace();
            callbackContext.error("error reading num json");
            return;
        }
        if (num != -1) {
            JPushInterface.setLatestNotificationNumber(mContext, num);
        } else {
            callbackContext.error("error num");
        }
    }

    void setPushTime(JSONArray data, CallbackContext callbackContext) {
        Set<Integer> days = new HashSet<Integer>();
        JSONArray dayArray;
        int startHour = -1;
        int endHour = -1;
        try {
            dayArray = data.getJSONArray(0);
            for (int i = 0; i < dayArray.length(); i++) {
                days.add(dayArray.getInt(i));
            }
        } catch (JSONException e) {
            e.printStackTrace();
            callbackContext.error("error reading days json");
        }
        try {
            startHour = data.getInt(1);
            endHour = data.getInt(2);
        } catch (JSONException e) {
            callbackContext.error("error reading hour json");
        }
        Context context = mContext;
        JPushInterface.setPushTime(context, days, startHour, endHour);
        callbackContext.success();
    }

    void getRegistrationID(JSONArray data, CallbackContext callbackContext) {
        Context context = mContext;
        String regID = JPushInterface.getRegistrationID(context);
        callbackContext.success(regID);
    }

    void onResume(JSONArray data, CallbackContext callbackContext) {
        JPushInterface.onResume(this.cordova.getActivity());
    }

    void onPause(JSONArray data, CallbackContext callbackContext) {
        JPushInterface.onPause(this.cordova.getActivity());
    }

    void reportNotificationOpened(JSONArray data, CallbackContext callbackContext) {
        try {
            String msgID;
            msgID = data.getString(0);
            JPushInterface.reportNotificationOpened(this.cordova.getActivity(), msgID);
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    void setAlias(JSONArray data, CallbackContext callbackContext) {
        int sequence = -1;
        String alias = null;

        try {
            JSONObject params = data.getJSONObject(0);
            sequence = params.getInt("sequence");
            alias = params.getString("alias");
        } catch (JSONException e) {
            e.printStackTrace();
            callbackContext.error("Parameters error.");
            return;
        }

        JPushInterface.setAlias(mContext, sequence, alias);
        eventCallbackMap.put(sequence, callbackContext);
    }

    void deleteAlias(JSONArray data, CallbackContext callbackContext) {
        int sequence = -1;

        try {
            JSONObject params = data.getJSONObject(0);
            sequence = params.getInt("sequence");
        } catch (JSONException e) {
            e.printStackTrace();
            callbackContext.error("Parameters error.");
            return;
        }

        JPushInterface.deleteAlias(mContext, sequence);
        eventCallbackMap.put(sequence, callbackContext);
    }

    void getAlias(JSONArray data, CallbackContext callbackContext) {
        int sequence = -1;

        try {
            JSONObject params = data.getJSONObject(0);
            sequence = params.getInt("sequence");
        } catch (JSONException e) {
            e.printStackTrace();
            callbackContext.error("Parameters error.");
            return;
        }

        JPushInterface.getAlias(mContext, sequence);
        eventCallbackMap.put(sequence, callbackContext);
    }

    void setTags(JSONArray data, CallbackContext callbackContext) {
        int sequence = -1;
        Set<String> tags = new HashSet<String>();

        try {
            JSONObject params = data.getJSONObject(0);
            sequence = params.getInt("sequence");

            JSONArray tagsArr = params.getJSONArray("tags");
            for (int i = 0; i < tagsArr.length(); i++) {
                tags.add(tagsArr.getString(i));
            }

        } catch (JSONException e) {
            e.printStackTrace();
            callbackContext.error("Parameters error.");
            return;
        }

        JPushInterface.setTags(mContext, sequence, tags);
        eventCallbackMap.put(sequence, callbackContext);
    }

    void addTags(JSONArray data, CallbackContext callbackContext) {
        int sequence = -1;
        Set<String> tags = new HashSet<String>();

        try {
            JSONObject params = data.getJSONObject(0);
            sequence = params.getInt("sequence");

            JSONArray tagsArr = params.getJSONArray("tags");
            for (int i = 0; i < tagsArr.length(); i++) {
                tags.add(tagsArr.getString(i));
            }

        } catch (JSONException e) {
            e.printStackTrace();
            callbackContext.error("Parameters error.");
            return;
        }

        JPushInterface.addTags(mContext, sequence, tags);
        eventCallbackMap.put(sequence, callbackContext);
    }

    void deleteTags(JSONArray data, CallbackContext callbackContext) {
        int sequence = -1;
        Set<String> tags = new HashSet<String>();

        try {
            JSONObject params = data.getJSONObject(0);
            sequence = params.getInt("sequence");

            JSONArray tagsArr = params.getJSONArray("tags");
            for (int i = 0; i < tagsArr.length(); i++) {
                tags.add(tagsArr.getString(i));
            }

        } catch (JSONException e) {
            e.printStackTrace();
            callbackContext.error("Parameters error.");
            return;
        }

        JPushInterface.deleteTags(mContext, sequence, tags);
        eventCallbackMap.put(sequence, callbackContext);
    }

    void cleanTags(JSONArray data, CallbackContext callbackContext) {
        int sequence = -1;

        try {
            JSONObject params = data.getJSONObject(0);
            sequence = params.getInt("sequence");

        } catch (JSONException e) {
            e.printStackTrace();
            callbackContext.error("Parameters error.");
            return;
        }

        JPushInterface.cleanTags(mContext, sequence);
        eventCallbackMap.put(sequence, callbackContext);
    }

    void getAllTags(JSONArray data, CallbackContext callbackContext) {
        int sequence = -1;

        try {
            JSONObject params = data.getJSONObject(0);
            sequence = params.getInt("sequence");

        } catch (JSONException e) {
            e.printStackTrace();
            callbackContext.error("Parameters error.");
            return;
        }

        JPushInterface.getAllTags(mContext, sequence);
        eventCallbackMap.put(sequence, callbackContext);
    }

    void checkTagBindState(JSONArray data, CallbackContext callbackContext) {
        int sequence = -1;
        String tag = null;

        try {
            JSONObject params = data.getJSONObject(0);
            sequence = params.getInt("sequence");
            tag = params.getString("tag");

        } catch (JSONException e) {
            e.printStackTrace();
            callbackContext.error("Parameters error.");
            return;
        }

        JPushInterface.checkTagBindState(mContext, sequence, tag);
        eventCallbackMap.put(sequence, callbackContext);
    }

    void getConnectionState(JSONArray data, CallbackContext callback) {
        boolean isConnected = JPushInterface.getConnectionState(cordovaActivity.getApplicationContext());
        if (isConnected) {
            callback.success(1);
        } else {
            callback.success(0);
        }
    }

    /**
     * 自定义通知行为，声音、震动、呼吸灯等。
     */
    void setBasicPushNotificationBuilder(JSONArray data, CallbackContext callbackContext) {
        BasicPushNotificationBuilder builder = new BasicPushNotificationBuilder(this.cordova.getActivity());
        builder.developerArg0 = "Basic builder 1";
        JPushInterface.setPushNotificationBuilder(1, builder);
        JSONObject obj = new JSONObject();
        try {
            obj.put("id", 1);
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    /**
     * 自定义推送通知栏样式，需要自己实现具体代码。 http://docs.jiguang.cn/client/android_tutorials/#_11
     */
    void setCustomPushNotificationBuilder(JSONArray data, CallbackContext callbackContext) {
        // CustomPushNotificationBuilder builder = new CustomPushNotificationBuilder(
        // this.cordova.getActivity(), R.layout.test_notification_layout,
        // R.id.icon, R.id.title, R.id.text);
        // JPushInterface.setPushNotificationBuilder(2, builder);
        // JPushInterface.setDefaultPushNotificationBuilder(builder);
    }

    void clearAllNotification(JSONArray data, CallbackContext callbackContext) {
        JPushInterface.clearAllNotifications(this.cordova.getActivity());
    }

    void clearNotificationById(JSONArray data, CallbackContext callbackContext) {
        int notificationId = -1;
        try {
            notificationId = data.getInt(0);
        } catch (JSONException e) {
            e.printStackTrace();
            callbackContext.error("error reading id json");
            return;
        }
        if (notificationId != -1) {
            JPushInterface.clearNotificationById(this.cordova.getActivity(), notificationId);
        } else {
            callbackContext.error("error id");
        }
    }

    void addLocalNotification(JSONArray data, CallbackContext callbackContext) throws JSONException {
        int builderId = data.getInt(0);
        String content = data.getString(1);
        String title = data.getString(2);
        int notificationID = data.getInt(3);
        int broadcastTime = data.getInt(4);
        String extrasStr = data.isNull(5) ? "" : data.getString(5);
        JSONObject extras = new JSONObject();
        if (!extrasStr.isEmpty()) {
            extras = new JSONObject(extrasStr);
        }

        JPushLocalNotification ln = new JPushLocalNotification();
        ln.setBuilderId(builderId);
        ln.setContent(content);
        ln.setTitle(title);
        ln.setNotificationId(notificationID);
        ln.setBroadcastTime(System.currentTimeMillis() + broadcastTime);
        ln.setExtras(extras.toString());

        JPushInterface.addLocalNotification(this.cordova.getActivity(), ln);
    }

    void removeLocalNotification(JSONArray data, CallbackContext callbackContext) throws JSONException {
        int notificationID = data.getInt(0);
        JPushInterface.removeLocalNotification(this.cordova.getActivity(), notificationID);
    }

    void clearLocalNotifications(JSONArray data, CallbackContext callbackContext) {
        JPushInterface.clearLocalNotifications(this.cordova.getActivity());
    }

    /**
     * 设置通知静默时间 http://docs.jpush.io/client/android_api/#api_5
     */
    void setSilenceTime(JSONArray data, CallbackContext callbackContext) {
        try {
            int startHour = data.getInt(0);
            int startMinute = data.getInt(1);
            int endHour = data.getInt(2);
            int endMinute = data.getInt(3);
            if (!isValidHour(startHour) || !isValidMinute(startMinute)) {
                callbackContext.error("开始时间数值错误");
                return;
            }
            if (!isValidHour(endHour) || !isValidMinute(endMinute)) {
                callbackContext.error("结束时间数值错误");
                return;
            }
            JPushInterface.setSilenceTime(this.cordova.getActivity(), startHour, startMinute, endHour, endMinute);
        } catch (JSONException e) {
            e.printStackTrace();
            callbackContext.error("error: reading json data.");
        }
    }

    void setGeofenceInterval(JSONArray data, CallbackContext callbackContext) throws JSONException {
        long interval = data.getLong(0);
        JPushInterface.setGeofenceInterval(this.cordova.getActivity(), interval);
    }

    void setMaxGeofenceNumber(JSONArray data, CallbackContext callbackContext) throws JSONException {
        int maxNumber = data.getInt(0);
        JPushInterface.setMaxGeofenceNumber(mContext, maxNumber);
    }

    void setBadgeNumber(JSONArray data, CallbackContext callbackContext) throws JSONException {
        int badgeNumb = data.getInt(0);
        JPushInterface.setBadgeNumber(mContext, badgeNumb);
    }

    void setMobileNumber(JSONArray data, CallbackContext callbackContext) throws JSONException {
        int sequence = -1;
        String number = null;

        try {
            JSONObject params = data.getJSONObject(0);
            sequence = params.getInt("sequence");
            number = params.getString("mobileNumber");

        } catch (JSONException e) {
            e.printStackTrace();
            callbackContext.error("Parameters error.");
            return;
        }

        eventCallbackMap.put(sequence, callbackContext);
        JPushInterface.setMobileNumber(mContext,sequence, number);
    }

    void setAuth(JSONArray data, CallbackContext callbackContext) {
        boolean isAuth = false;
        try {
            isAuth = data.getBoolean(0);
            JCollectionAuth.setAuth(mContext, isAuth);
            callbackContext.success();
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    void setBackgroundEnable(JSONArray data, CallbackContext callbackContext) {
        try {
            boolean keep = data.getBoolean(0);
            JPushInterface.setKeepLongConnInBackground(mContext, keep);
            callbackContext.success();
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    static void transmitVoipMessage(JSONObject data) {
        if (instance == null) {
            return;
        }
        String format = "window.plugins.jPushPlugin.receiveVoipMessageInAndroidCallback(%s);";
        final String js = String.format(format, data.toString());
        cordovaActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                instance.webView.loadUrl("javascript:" + js);
            }
        });
    }

    private boolean isValidHour(int hour) {
        return !(hour < 0 || hour > 23);
    }

    private boolean isValidMinute(int minute) {
        return !(minute < 0 || minute > 59);
    }

    /**
     * 用于 Android 6.0 以上系统申请权限，具体可参考：
     * http://docs.Push.io/client/android_api/#android-60
     */
    void requestPermission(JSONArray data, CallbackContext callbackContext) {
        JPushInterface.requestPermission(this.cordova.getActivity());
    }

    private final TagAliasCallback mTagWithAliasCallback = new TagAliasCallback() {
        @Override
        public void gotResult(int code, String alias, Set<String> tags) {
            if (instance == null) {
                return;
            }
            JSONObject data = new JSONObject();
            try {
                data.put("resultCode", code);
                data.put("tags", tags);
                data.put("alias", alias);
                final String jsEvent = String.format("cordova.fireDocumentEvent('jpush.setTagsWithAlias',%s)",
                        data.toString());
                cordova.getActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        instance.webView.loadUrl("javascript:" + jsEvent);
                    }
                });
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    };

    private boolean hasPermission(String appOpsServiceId) {

        Context context = cordova.getActivity().getApplicationContext();
        if (Build.VERSION.SDK_INT >= 24) {
            NotificationManager mNotificationManager = (NotificationManager) context
                    .getSystemService(Context.NOTIFICATION_SERVICE);
            return mNotificationManager.areNotificationsEnabled();
        } else {
            AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
            ApplicationInfo appInfo = context.getApplicationInfo();

            String pkg = context.getPackageName();
            int uid = appInfo.uid;
            Class appOpsClazz;

            try {
                appOpsClazz = Class.forName(AppOpsManager.class.getName());
                Method checkOpNoThrowMethod = appOpsClazz.getMethod("checkOpNoThrow", Integer.TYPE, Integer.TYPE,
                        String.class);
                Field opValue = appOpsClazz.getDeclaredField(appOpsServiceId);
                int value = opValue.getInt(Integer.class);
                Object result = checkOpNoThrowMethod.invoke(mAppOps, value, uid, pkg);
                return Integer.parseInt(result.toString()) == AppOpsManager.MODE_ALLOWED;
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }

        return false;
    }

}
