commit ee29969ddcee3e6e4d73d1dca81bd0872275016f Author: 范大德 Date: Fri Jul 28 18:28:45 2023 +0800 完成初始版本(暂不支持华为的厂商通道) diff --git a/package.json b/package.json new file mode 100644 index 0000000..ae70a5d --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "cordova-plugin-tpns", + "version": "0.0.1", + "description": "Tencent Push Notification Service", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://m.shuto.cn:8681/public/cordova-plugin-tpns.git" + }, + "keywords": [ + "cordova", + "tpns", + "android", + "push" + ], + "author": "fandd@shuto.cn" +} diff --git a/plugin.xml b/plugin.xml new file mode 100644 index 0000000..b5b2d1c --- /dev/null +++ b/plugin.xml @@ -0,0 +1,59 @@ + + + tpns + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/android/CordovaEventKit.java b/src/android/CordovaEventKit.java new file mode 100644 index 0000000..4eba993 --- /dev/null +++ b/src/android/CordovaEventKit.java @@ -0,0 +1,52 @@ +package com.shuto.plugin.tpns; + +import android.util.Log; + +import org.apache.cordova.CordovaPlugin; +import org.json.JSONArray; +import org.json.JSONObject; + +public class CordovaEventKit { + public static CordovaEventKit kit = null; + private static final String TAG = CordovaEventKit.class.getSimpleName(); + private CordovaPlugin plugin; + + + private CordovaEventKit(CordovaPlugin plugin) { + this.plugin = plugin; + } + + public static void init(CordovaPlugin plugin){ + Log.d(TAG, "init"); + if(CordovaEventKit.kit == null){ + CordovaEventKit.kit = new CordovaEventKit(plugin); + } + } + + + public void fireEvent(String event, JSONArray obj){ + this.fireEvent(event,obj == null ? null : obj.toString()); + } + + public void fireEvent(String event, JSONObject obj){ + this.fireEvent(event,obj == null ? null :obj.toString()); + } + + public void fireEvent(String event, String obj){ + Log.d(TAG,"fireEvent --- event:"+event+",obj:"+obj); + if (plugin == null || event == null || obj == null) { + Log.w(TAG,"fireEvent --- plugin:"+plugin); + return; + } + event = event.replace('\'','_'); + String format = "window.Tpns.fireEvent('%s',%s);"; + final String js = String.format(format,event, obj); + plugin.cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + Log.d(TAG,"fireEvent --- code:"+js); + plugin.webView.loadUrl("javascript:" + js); + } + }); + } +} diff --git a/src/android/Events.java b/src/android/Events.java new file mode 100644 index 0000000..4bbf9e8 --- /dev/null +++ b/src/android/Events.java @@ -0,0 +1,59 @@ +package com.shuto.plugin.tpns; + +import android.os.Bundle; +import android.util.Log; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Events { + private static final String TAG = Events.class.getSimpleName(); + + private static final String PREFIX = "com.tencent.trtc.event"; + private static final Map> events = new HashMap(); + public static void fireEvent(String event){ + fireEvent(event,null); + } + public static void fireEvent(String event, Extra extra){ + Log.d(TAG,"fireEvent --- event:"+event+",extra:"+ extra); + if(events.containsKey(PREFIX+event)){ + for(Listener listener : events.get(PREFIX+event)){ + Bundle bundle = extra == null ? Bundle.EMPTY: new Bundle(); + if(extra != null){ + extra.extra(bundle); + } + Log.d(TAG,"fireEvent --- event:"+event+",bundle:"+ bundle); + listener.on(extra == null ? Bundle.EMPTY : bundle); + } + } + } + + public static void addListener(String eventName,Listener listener){ + Log.d(TAG,"addListener --- event:"+eventName+",listener:"+listener.hashCode()); + if(!events.containsKey(PREFIX+eventName)){ + events.put(PREFIX+eventName,new ArrayList<>()); + } + List list = events.get(PREFIX+eventName); + if(!list.contains(listener)){ + list.add(listener); + } + } + + public static void removeListener(String eventName,Listener listener){ + Log.d(TAG,"removeListener --- event:"+eventName+",listener:"+listener.hashCode()); + if(!events.containsKey(PREFIX+eventName)){ + events.put(PREFIX+eventName,new ArrayList<>()); + } + List list = events.get(PREFIX+eventName); + if(list.contains(listener)){ + list.remove(listener); + } + } + + + public interface Extra{ + public void extra(Bundle extra); + } +} diff --git a/src/android/Listener.java b/src/android/Listener.java new file mode 100644 index 0000000..e4e5d6d --- /dev/null +++ b/src/android/Listener.java @@ -0,0 +1,7 @@ +package com.shuto.plugin.tpns; + +import android.os.Bundle; + +public interface Listener { + void on(Bundle extra); +} diff --git a/src/android/TpnsPlugin.java b/src/android/TpnsPlugin.java new file mode 100644 index 0000000..d94311d --- /dev/null +++ b/src/android/TpnsPlugin.java @@ -0,0 +1,653 @@ +package com.shuto.plugin.tpns; + +import android.content.Context; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; + +import com.tencent.android.tpush.XGIOperateCallback; +import com.tencent.android.tpush.XGLocalMessage; +import com.tencent.android.tpush.XGPushConfig; +import com.tencent.android.tpush.XGPushManager; + +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.CordovaInterface; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.lang.reflect.Method; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * This class echoes a string called from JavaScript. + */ +public class TpnsPlugin extends CordovaPlugin { + + private static final String TAG = TpnsPlugin.class.getSimpleName(); + + private Context mContext; + @Override + public void initialize(CordovaInterface cordova, CordovaWebView webView) { + super.initialize(cordova, webView); + mContext = cordova.getActivity().getApplicationContext(); + CordovaEventKit.init(this); + Log.d(TAG,"initialize plugin"); + } + @Override + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { + cordova.getThreadPool().execute(new Runnable() { + @Override + public void run() { + try { + Method method = TpnsPlugin.class.getDeclaredMethod(action, JSONArray.class, CallbackContext.class); + method.invoke(TpnsPlugin.this, args, callbackContext); + } catch (Exception e) { + Log.e(TAG, e.toString()); + callbackContext.error(e.getLocalizedMessage()); + } + } + }); + return true; + } + + private void enableDebug(JSONArray data, CallbackContext callbackContext) { + boolean mode; + try { + mode = data.getBoolean(0); + XGPushConfig.enableDebug(mContext,mode); + callbackContext.success(); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + /** + * 关闭联合保活能力(1.1.6.1+) + * @param data + * @param callbackContext + */ + private void enablePullUpOtherApp(JSONArray data, CallbackContext callbackContext) { + boolean mode; + try { + mode = data.getBoolean(0); + XGPushConfig.enablePullUpOtherApp(mContext,mode); + callbackContext.success(); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + /** + * 获取设备 Token + * @param data + * @param callbackContext + */ + private void getToken(JSONArray data, CallbackContext callbackContext) { + callbackContext.success(XGPushConfig.getToken(mContext)); + } + /** + * 获取第三方厂商 Token + * @param data + * @param callbackContext + */ + private void getOtherPushToken(JSONArray data, CallbackContext callbackContext) { + callbackContext.success(XGPushConfig.getOtherPushToken(mContext)); + } + + private void registerPush(JSONArray data, CallbackContext callbackContext) { + try{ + JSONObject params = data.optJSONObject(0); + if(params !=null){ + XGPushConfig.enableOtherPush(mContext, true); + Iterator keys = params.keys(); + while (keys.hasNext()) { + String type = keys.next(); + JSONObject value = params.getJSONObject(type); + switch (type){ + case "MZ": + XGPushConfig.setMzPushAppId(mContext, value.getString("APP_ID")); + XGPushConfig.setMzPushAppKey(mContext, value.getString("APP_KEY")); + break; + case "OPPO": + XGPushConfig.setOppoPushAppId(mContext, value.getString("appKey")); + XGPushConfig.setOppoPushAppKey(mContext, value.getString("appSecret")); + break; + case "MI": + XGPushConfig.setMiPushAppId(mContext, value.getString("APPID")); + XGPushConfig.setMiPushAppKey(mContext, value.getString("APPKEY")); + break; + case "HW": + break; + case "RY": + break; + } + } + + } + } catch (JSONException e){ + Log.e(TAG,e.getLocalizedMessage()); + } + + XGPushManager.registerPush(mContext, new XGIOperateCallback() { + @Override + public void onSuccess(Object data, int flag) { + Log.d(TAG, "注册成功,设备token为:" + data); + callbackContext.success((String)data); + } + @Override + public void onFail(Object data, int errCode, String msg) { + Log.d(TAG, "注册失败,错误码:" + errCode + ",错误信息:" + msg); + callbackContext.error(msg(errCode,msg)); + } + }); + } + + private void unregisterPush(JSONArray data, CallbackContext callbackContext) { + XGPushManager.unregisterPush(mContext, new XGIOperateCallback() { + @Override + public void onSuccess(Object data, int i) { + Log.d(TAG, "反注册成功"); + callbackContext.success(); + } + @Override + public void onFail(Object data, int errCode, String msg) { + Log.d(TAG, "反注册失败,错误码:" + errCode + ",错误信息:" + msg); + + callbackContext.error(msg(errCode,msg)); + } + }); + + } + + + private void createNotificationChannel(JSONArray data, CallbackContext callbackContext){ + JSONObject param; + try { + param = data.getJSONObject(0); + XGPushManager.createNotificationChannel(mContext, + param.getString("channelId"), param.getString("channelName"), + param.optBoolean("enableVibration"), param.optBoolean("enableLights"), + param.optBoolean("enableSound"), Uri.parse(param.optString("soundUri"))); + callbackContext.success(); + } catch (JSONException e) { + e.printStackTrace(); + callbackContext.error(e.getLocalizedMessage()); + } + } + + private void addLocalNotification(JSONArray data, CallbackContext callbackContext){ + try{ + JSONObject param = data.getJSONObject(0); + //新建本地通知 + XGLocalMessage local_msg = new XGLocalMessage(); + //设置本地消息类型,1:通知,2:消息 + local_msg.setType(param.optInt("type",1)); + // 设置消息标题 + local_msg.setTitle(param.getString("title")); + //设置消息内容 + local_msg.setContent(param.getString("content")); + //设置消息日期,格式为:20140502 + local_msg.setDate(param.optString("date",new SimpleDateFormat("yyyyMMdd ").format(new Date()))); + //设置消息触发的小时(24小时制),例如:22代表晚上10点 + local_msg.setHour(param.optString("hour",new SimpleDateFormat("HH").format(new Date()))); + //获取消息触发的分钟,例如:05代表05分 + local_msg.setMin(param.optString("min",new SimpleDateFormat("mm").format(new Date()))); + //设置消息样式,默认为0或不设置 + local_msg.setBuilderId(param.optInt("builderId",0)); + //设置动作类型:1打开activity或App本身,2打开浏览器,3打开Intent ,4通过包名打开应用 + int actionType = param.getInt("actionType"); + local_msg.setAction_type(actionType); + switch (actionType){ + case 1: + //设置拉起应用页面 + local_msg.setActivity( param.getString("activity")); + break; + case 2: + // 设置URL + local_msg.setUrl(param.getString("url")); + break; + case 3: + // 设置Intent + local_msg.setIntent(param.getString("intent")); + break; + } + + // 是否覆盖原先build_id的保存设置。1覆盖,0不覆盖 + local_msg.setStyle_id(param.optInt("styleId",1)); + // 设置音频资源 + local_msg.setRing_raw(param.optString("ringRaw")); + + JSONObject body = param.optJSONObject("data"); + // 设置key,value + HashMap map = new HashMap(); + if(data!=null){ + Iterator keys = body.keys(); + while (keys.hasNext()){ + String key = keys.next(); + map.put(key,body.opt(key)); + } + } + local_msg.setCustomContent(map); + //添加通知到本地 + XGPushManager.addLocalNotification(mContext,local_msg); + callbackContext.success(); + } catch (JSONException e){ + e.printStackTrace(); + callbackContext.error(e.getLocalizedMessage()); + } + } + + private void clearLocalNotifications(JSONArray data, CallbackContext callbackContext){ + XGPushManager.clearLocalNotifications(mContext); + callbackContext.success(); + } + + + /** + * 对一个或多个账号类型的账号进行解绑。(SDK 1.2.3.0+) + * eg:[1001,1002] + * @param data + */ + private void delAccounts(JSONArray data, CallbackContext callbackContext) { + Log.d(TAG, "delAccounts | params: " + data); + try { + Set accountInfos = new HashSet<>(); + JSONArray accInfoArr = data.getJSONArray(0); + for(int i=0; i < accInfoArr.length(); i++) { + int accountType = accInfoArr.optInt(i,0); + accountInfos.add(accountType); + } + XGPushManager.delAccounts(mContext, accountInfos,new XGIOperateCallback() { + @Override + public void onSuccess(Object data, int i) { + Log.d(TAG, "对一个或多个账号类型的账号进行解绑成功"); + callbackContext.success(); + } + @Override + public void onFail(Object data, int errCode, String msg) { + Log.d(TAG, "对一个或多个账号类型的账号进行解绑失败,错误码:" + errCode + ",错误信息:" + msg); + callbackContext.error(msg(errCode,msg)); + } + }); + } catch (JSONException e) { + Log.d(TAG, "setAccount exception : " + e.getMessage()); + callbackContext.error(e.getLocalizedMessage()); + } + } + + /** + * 对的所有已绑定账号进行解绑。 + * @param data + */ + private void clearAccounts(JSONArray data, CallbackContext callbackContext) { + Log.d(TAG, "clearAccounts | params: " + data); + XGPushManager.clearAccounts(mContext,new XGIOperateCallback() { + @Override + public void onSuccess(Object data, int i) { + Log.d(TAG, "对的所有已绑定账号进行解绑成功"); + callbackContext.success(); + } + @Override + public void onFail(Object data, int errCode, String msg) { + Log.d(TAG, "对的所有已绑定账号进行解绑失败,错误码:" + errCode + ",错误信息:" + msg); + callbackContext.error(msg(errCode,msg)); + } + }); + } + + /** + * 添加或更新账号。若原来没有该类型账号,则添加;若原来有,则覆盖。可以同时添加多个账号,一个账号对应一个账号类型。 + * eg:[{"account":elva, "accountType":1001}, {"account":jay, "accountType":1002}] + * @param data + */ + private void upsertAccounts(JSONArray data, CallbackContext callbackContext) { + Log.d(TAG, "upsertAccounts | params: " + data); + try { + List accountInfos = new ArrayList<>(); + JSONArray accInfoArr = data.getJSONArray(0); + for(int i=0; i < accInfoArr.length(); i++) { + JSONObject accInfoJo = accInfoArr.optJSONObject(i); + if(accInfoJo == null) { + continue; + } + String account = accInfoJo.optString("account",""); + int accountType = accInfoJo.optInt("accountType", 0); + XGPushManager.AccountInfo info = new XGPushManager.AccountInfo(accountType, account); + accountInfos.add(info); + } + XGPushManager.upsertAccounts(mContext, accountInfos,new XGIOperateCallback() { + @Override + public void onSuccess(Object data, int i) { + Log.d(TAG, "添加或更新账号成功"); + callbackContext.success(); + } + @Override + public void onFail(Object data, int errCode, String msg) { + Log.d(TAG, "添加或更新账号失败,错误码:" + errCode + ",错误信息:" + msg); + callbackContext.error(msg(errCode,msg)); + } + }); + } catch (JSONException e) { + Log.d(TAG, "setAccount exception : " + e.getMessage()); + callbackContext.error(e.getLocalizedMessage()); + } + } + + /** + * 覆盖多个标签 一次设置多个标签,会覆盖这个设备之前设置的标签。 + */ + private void clearAndAppendTags(JSONArray data, CallbackContext callbackContext) { + Log.d(TAG, "clearAndAppendTags | params: " + data); + try { + Set tags = new HashSet<>(); + String operateName = data.getString(0); + JSONArray accInfoArr = data.getJSONArray(1); + for(int i=0; i < accInfoArr.length(); i++) { + String tag = accInfoArr.optString(i); + tags.add(tag); + } + XGPushManager.clearAndAppendTags(mContext,operateName, tags,new XGIOperateCallback() { + @Override + public void onSuccess(Object data, int i) { + Log.d(TAG, "覆盖多个标签成功"); + callbackContext.success(); + } + @Override + public void onFail(Object data, int errCode, String msg) { + Log.d(TAG, "覆盖多个标签失败,错误码:" + errCode + ",错误信息:" + msg); + callbackContext.error(msg(errCode,msg)); + } + }); + } catch (JSONException e) { + Log.d(TAG, "setAccount exception : " + e.getMessage()); + callbackContext.error(e.getLocalizedMessage()); + } + } + + /** + * 新增多个标签 + * 如果新覆盖的标签都带有 : 号,例如 test:2, level:2,则会删除这个设备已绑定的所有 test:* 和 level:* 标签,再新增 test:2 和 level:2。 + * 如果新增的标签有部分不带 : 号,例如 test:2 level,则会删除这个设备的全部历史标签,再新增 test:2 和 level 标签。 + */ + private void appendTags(JSONArray data, CallbackContext callbackContext) { + Log.d(TAG, "appendTags | params: " + data); + try { + Set tags = new HashSet<>(); + String operateName = data.getString(0); + JSONArray accInfoArr = data.getJSONArray(1); + for(int i=0; i < accInfoArr.length(); i++) { + String tag = accInfoArr.optString(i); + tags.add(tag); + } + XGPushManager.appendTags(mContext,operateName, tags,new XGIOperateCallback() { + @Override + public void onSuccess(Object data, int i) { + Log.d(TAG, "新增多个标签成功"); + callbackContext.success(); + } + @Override + public void onFail(Object data, int errCode, String msg) { + Log.d(TAG, "新增多个标签失败,错误码:" + errCode + ",错误信息:" + msg); + callbackContext.error(msg(errCode,msg)); + } + }); + } catch (JSONException e) { + Log.d(TAG, "setAccount exception : " + e.getMessage()); + callbackContext.error(e.getLocalizedMessage()); + } + } + /** + * 一次删除多个标签。 + */ + private void delTags(JSONArray data, CallbackContext callbackContext) { + Log.d(TAG, "delTags | params: " + data); + try { + Set tags = new HashSet<>(); + String operateName = data.getString(0); + JSONArray accInfoArr = data.getJSONArray(1); + for(int i=0; i < accInfoArr.length(); i++) { + String tag = accInfoArr.optString(i); + tags.add(tag); + } + XGPushManager.delTags(mContext,operateName, tags,new XGIOperateCallback() { + @Override + public void onSuccess(Object data, int i) { + Log.d(TAG, "一次删除多个标签成功"); + callbackContext.success(); + } + @Override + public void onFail(Object data, int errCode, String msg) { + Log.d(TAG, "一次删除多个标签失败,错误码:" + errCode + ",错误信息:" + msg); + callbackContext.error(msg(errCode,msg)); + } + }); + } catch (JSONException e) { + Log.d(TAG, "setAccount exception : " + e.getMessage()); + callbackContext.error(e.getLocalizedMessage()); + } + } + + /** + * 清除这个设备的所有标签。 + */ + private void clearTags(JSONArray data, CallbackContext callbackContext) { + Log.d(TAG, "clearTags | params: " + data); + try { + String operateName = data.getString(0); + XGPushManager.clearTags(mContext,operateName,new XGIOperateCallback() { + @Override + public void onSuccess(Object data, int i) { + Log.d(TAG, "清除这个设备的所有标签成功"); + callbackContext.success(); + } + @Override + public void onFail(Object data, int errCode, String msg) { + Log.d(TAG, "清除这个设备的所有标签失败,错误码:" + errCode + ",错误信息:" + msg); + callbackContext.error(msg(errCode,msg)); + } + }); + } catch (JSONException e) { + Log.d(TAG, "setAccount exception : " + e.getMessage()); + callbackContext.error(e.getLocalizedMessage()); + } + } + + /** + * 获取这个设备的标签 + */ + private void queryTags(JSONArray data, CallbackContext callbackContext) { + Log.d(TAG, "queryTags | params: " + data); + try { + String operateName = data.getString(0); + int start = data.optInt(1,0); + int size = data.optInt(2,100); + Events.addListener("queryTags." + operateName, new Listener() { + @Override + public void on(Bundle extra) { + Log.d(TAG,"接收到查询标签结果"); + callbackContext.success(msg(extra.getInt("code"),extra.getString("data"))); + Events.removeListener("queryTags." + operateName,this); + } + }); + XGPushManager.queryTags(mContext,operateName,start,size,new XGIOperateCallback() { + @Override + public void onSuccess(Object data, int i) { + Log.d(TAG, "清除这个设备的所有标签成功"); + } + @Override + public void onFail(Object data, int errCode, String msg) { + Log.d(TAG, "清除这个设备的所有标签失败,错误码:" + errCode + ",错误信息:" + msg); + callbackContext.error(msg(errCode,msg)); + } + }); + } catch (JSONException e) { + Log.d(TAG, "setAccount exception : " + e.getMessage()); + callbackContext.error(e.getLocalizedMessage()); + } + } + + /** + * 添加属性(带回调):有则覆盖,无则添加。 + */ + private void upsertAttributes(JSONArray data, CallbackContext callbackContext) { + Log.d(TAG, "upsertAttributes | params: " + data); + try { + String operateName = data.getString(0); + JSONObject body = data.optJSONObject(1); + HashMap map = new HashMap<>(); + if(data!=null){ + Iterator keys = body.keys(); + while (keys.hasNext()){ + String key = keys.next(); + map.put(key,body.optString(key)); + } + } + XGPushManager.upsertAttributes(mContext,operateName,map,new XGIOperateCallback() { + @Override + public void onSuccess(Object data, int i) { + Log.d(TAG, "添加属性成功"); + } + @Override + public void onFail(Object data, int errCode, String msg) { + Log.d(TAG, "添加属性失败,错误码:" + errCode + ",错误信息:" + msg); + callbackContext.error(msg(errCode,msg)); + } + }); + } catch (JSONException e) { + Log.d(TAG, "setAccount exception : " + e.getMessage()); + callbackContext.error(e.getLocalizedMessage()); + } + } + + /** + * 更新用户属性 + */ + private void clearAndAppendAttributes(JSONArray data, CallbackContext callbackContext) { + Log.d(TAG, "clearAndAppendAttributes | params: " + data); + try { + String operateName = data.getString(0); + JSONObject body = data.optJSONObject(1); + HashMap map = new HashMap<>(); + if(data!=null){ + Iterator keys = body.keys(); + while (keys.hasNext()){ + String key = keys.next(); + map.put(key,body.optString(key)); + } + } + XGPushManager.clearAndAppendAttributes(mContext,operateName,map,new XGIOperateCallback() { + @Override + public void onSuccess(Object data, int i) { + Log.d(TAG, "更新用户属性成功"); + } + @Override + public void onFail(Object data, int errCode, String msg) { + Log.d(TAG, "更新用户属性失败,错误码:" + errCode + ",错误信息:" + msg); + callbackContext.error(msg(errCode,msg)); + } + }); + } catch (JSONException e) { + Log.d(TAG, "setAccount exception : " + e.getMessage()); + callbackContext.error(e.getLocalizedMessage()); + } + } + + /** + * 删除指定的属性。 + * + */ + private void delAttributes(JSONArray data, CallbackContext callbackContext) { + Log.d(TAG, "delAttributes | params: " + data); + try { + String operateName = data.getString(0); + Set attrs = new HashSet<>(); + JSONArray accInfoArr = data.getJSONArray(1); + for(int i=0; i < accInfoArr.length(); i++) { + String tag = accInfoArr.optString(i); + attrs.add(tag); + } + XGPushManager.delAttributes(mContext,operateName, attrs,new XGIOperateCallback() { + @Override + public void onSuccess(Object data, int i) { + Log.d(TAG, "删除指定的属性成功"); + callbackContext.success(); + } + @Override + public void onFail(Object data, int errCode, String msg) { + Log.d(TAG, "删除指定的属性失败,错误码:" + errCode + ",错误信息:" + msg); + callbackContext.error(msg(errCode,msg)); + } + }); + } catch (JSONException e) { + Log.d(TAG, "setAccount exception : " + e.getMessage()); + callbackContext.error(e.getLocalizedMessage()); + } + } + + /** + * 删除已设置的所有属性。 + */ + private void clearAttributes(JSONArray data, CallbackContext callbackContext) { + Log.d(TAG, "clearAttributes | params: " + data); + try { + String operateName = data.getString(0); + XGPushManager.clearAttributes(mContext,operateName,new XGIOperateCallback() { + @Override + public void onSuccess(Object data, int i) { + Log.d(TAG, "删除已设置的所有属性成功"); + callbackContext.success(); + } + @Override + public void onFail(Object data, int errCode, String msg) { + Log.d(TAG, "删除已设置的所有属性失败,错误码:" + errCode + ",错误信息:" + msg); + callbackContext.error(msg(errCode,msg)); + } + }); + } catch (JSONException e) { + Log.d(TAG, "setAccount exception : " + e.getMessage()); + callbackContext.error(e.getLocalizedMessage()); + } + } + + /** + * + * @param data + * @param callbackContext + */ + + private void cancelAllNotifaction(JSONArray data, CallbackContext callbackContext){ + XGPushManager.cancelAllNotifaction(mContext); + callbackContext.success(); + } + + + + + + + + + private JSONObject msg(int code, String msg){ + JSONObject obj = new JSONObject(); + try { + obj.put("code",code); + obj.put("message",msg); + }catch (JSONException e){ + Log.e(TAG,e.getMessage()); + }finally { + return obj; + } + } + +} diff --git a/src/android/TpnsReceiver.java b/src/android/TpnsReceiver.java new file mode 100644 index 0000000..5ee7d3e --- /dev/null +++ b/src/android/TpnsReceiver.java @@ -0,0 +1,125 @@ +// ~ Copyright (c) 2023 Beijing Shuto Technology Co,. Ltd. All rights reserved. + +package com.shuto.plugin.tpns; + + +import android.content.Context; +import android.os.Bundle; +import android.util.Log; + +import com.tencent.android.tpush.NotificationAction; +import com.tencent.android.tpush.XGPushBaseReceiver; +import com.tencent.android.tpush.XGPushClickedResult; +import com.tencent.android.tpush.XGPushRegisterResult; +import com.tencent.android.tpush.XGPushShowedResult; +import com.tencent.android.tpush.XGPushTextMessage; + +import org.json.JSONException; +import org.json.JSONObject; + +public abstract class TpnsReceiver extends XGPushBaseReceiver { + private static final String TAG = TpnsPlugin.class.getSimpleName(); + + /** + * 消息透传处理 + * @param context + * @param message 解析自定义的 JSON + */ + @Override + public void onTextMessage(Context context, XGPushTextMessage message) { + String text = "收到消息:" + message.toString(); + // APP自主处理消息的过程... + JSONObject obj = null; + if(message !=null){ + try{ + obj = new JSONObject(); + obj.put("content",message.getContent()); + obj.put("customContent",message.getCustomContent()); + obj.put("msgId",message.getMsgId()); + obj.put("channel",message.getPushChannel()); + obj.put("templateId",message.getTemplateId()); + obj.put("title",message.getTitle()); + obj.put("traceId",message.getTraceId()); + }catch (JSONException e){ + Log.e(TAG,e.getLocalizedMessage()); + } + } + CordovaEventKit.kit.fireEvent("onTextMessage",obj); + Log.d(TAG, text); + } + @Override + public void onNotificationShowedResult(Context context, XGPushShowedResult notifiShowedRlt){ + JSONObject obj = null; + if(notifiShowedRlt !=null){ + try{ + obj = new JSONObject(); + obj.put("activity",notifiShowedRlt.getActivity()); + obj.put("content",notifiShowedRlt.getContent()); + obj.put("customContent",notifiShowedRlt.getCustomContent()); + obj.put("msgId",notifiShowedRlt.getMsgId()); + obj.put("notifactionId",notifiShowedRlt.getNotifactionId()); + obj.put("actionType",notifiShowedRlt.getNotificationActionType()); + obj.put("channel",notifiShowedRlt.getPushChannel()); + obj.put("templateId",notifiShowedRlt.getTemplateId()); + obj.put("title",notifiShowedRlt.getTitle()); + obj.put("traceId",notifiShowedRlt.getTraceId()); + }catch (JSONException e){ + Log.e(TAG,e.getLocalizedMessage()); + } + } + CordovaEventKit.kit.fireEvent("onNotificationShowedResult",obj); + } + + + // 通知点击回调,actionType=0 为该消息被点击,actionType=2 为该消息被清除 + @Override + public void onNotificationClickedResult(Context context, XGPushClickedResult message) { + if (context == null || message == null) { + return; + } + String text = ""; + if (message.getActionType() == NotificationAction.clicked.getType()) { + // 通知在通知栏被点击 + // APP自己处理点击的相关动作 + text = "通知被打开 :" + message; + } else if (message.getActionType() == NotificationAction.delete.getType()) { + // 通知被清除 + // APP自己处理通知被清除后的相关动作 + text = "通知被清除 :" + message; + } + JSONObject obj = null; + if(message !=null){ + try{ + obj = new JSONObject(); + obj.put("activity",message.getActivityName()); + obj.put("content",message.getContent()); + obj.put("customContent",message.getCustomContent()); + obj.put("msgId",message.getMsgId()); + obj.put("actionType",message.getNotificationActionType()); + obj.put("channel",message.getPushChannel()); + obj.put("templateId",message.getTemplateId()); + obj.put("title",message.getTitle()); + obj.put("traceId",message.getTraceId()); + }catch (JSONException e){ + Log.e(TAG,e.getLocalizedMessage()); + } + } + CordovaEventKit.kit.fireEvent("onNotificationClickedResult",obj); + // APP自主处理的过程。 + + Log.d(TAG, "广播接收到通知:" + text); + } + + @Override + public void onQueryTagsResult(Context context, int errorCode, String data, String operateName) { + Log.i(TAG, "action - onQueryTagsResult, errorCode:" + errorCode + ", operateName:" + operateName + ", data: " + data); + Events.fireEvent("queryTags." + operateName, new Events.Extra() { + @Override + public void extra(Bundle extra) { + extra.putInt("code",errorCode); + extra.putString("data",data); + } + }); + + } +} diff --git a/src/android/build-tpns.gradle b/src/android/build-tpns.gradle new file mode 100644 index 0000000..0f44521 --- /dev/null +++ b/src/android/build-tpns.gradle @@ -0,0 +1,53 @@ +def getConfigPreference(name) { + name = name.toLowerCase() + def xml = file("src/main/res/xml/config.xml").getText() + // Disable namespace awareness since Cordova doesn't use them properly + def root = new XmlParser(false, false).parseText(xml) + + def ret, defaultValue + root.feature.each { it -> + def attrName = it.attribute("name") + if (attrName && attrName.toLowerCase() == "tpns") { + it.param.each { p -> + def pName = p.attribute("name") + if (p.attribute('default') != null) { + defaultValue = p.attribute('default'); + } else { + ret = p.attribute("value") + } + + } + } + } + return ret ? ret : defaultValue +} + +android { + defaultConfig { + applicationId getConfigPreference("PACKAGE_NAME") + ndk { + abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a' + } + manifestPlaceholders = [ + XG_ACCESS_ID : getConfigPreference("XG_ACCESS_ID"), + XG_ACCESS_KEY : getConfigPreference("XG_ACCESS_KEY"), + ] + } +} +dependencies { + //添加以下依赖 + implementation 'com.tencent.tpns:tpns:1.4.0.1-release' + // TPNS 推送 [VERSION] 为最新 SDK 版本号,即为上述步骤2获取的版本号 + implementation 'com.tencent.tpns:honor:1.4.0.1-release' //荣耀 + implementation 'com.tencent.tpns:meizu:1.4.0.1-release' //魅族 + implementation 'com.tencent.tpns:xiaomi:1.4.0.1-release'//小米推送 [VERSION] 为当前 SDK 版本号,版本号可在 Android SDK 发布动态查看 + implementation 'com.tencent.tpns:vivo:1.4.0.1-release' // vivo 推送 [VERSION] 为当前 SDK 版本号,版本号可在 Android SDK 发布动态查看 + + // OPPO 推送 SDK,[VERSION] 为当前 SDK 版本号,版本号可在 Android SDK 发布动态查看 + implementation 'com.tencent.tpns:oppo:1.4.0.1-release' + // 自 SDK 1.3.2.0 起,需一并加入以下依赖语句,否则可能导致 OPPO 推送注册失败 + implementation 'com.google.code.gson:gson:2.6.2' + implementation 'commons-codec:commons-codec:1.15' + + +} \ No newline at end of file diff --git a/www/tpns.js b/www/tpns.js new file mode 100644 index 0000000..e7265b1 --- /dev/null +++ b/www/tpns.js @@ -0,0 +1,293 @@ +var exec = require('cordova/exec'); + +var TpnsPlugin = function () { }; + +// private plugin function + +TpnsPlugin.prototype.receiveMessage = {}; +TpnsPlugin.prototype.openNotification = {}; +TpnsPlugin.prototype.receiveNotification = {}; + +TpnsPlugin.prototype.isPlatformIOS = function () { + return ( + device.platform === "iPhone" || + device.platform === "iPad" || + device.platform === "iPod touch" || + device.platform === "iOS" + ); +}; + +TpnsPlugin.prototype.errorCallback = function (msg) { + console.log("JPush Callback Error: " + msg); +}; + +TpnsPlugin.prototype.fireEvent = function(event,extra) { + cordova.fireDocumentEvent("tpns."+event, extra); +}; + +TpnsPlugin.prototype.Constant = { + EVENT_ON_TEXT_MESSAGE :'tpns.onTextMessage', + EVENT_ON_NOTIFICATION_SHOWED_RESULT :'tpns.onNotificationShowedResult', + EVENT_ON_NOTIFICATION_CLICKED_RESULT :'tpns.onNotificationClickedResult', +}; + +TpnsPlugin.prototype.callNative = function ( + name, + args, + successCallback, + errorCallback +) { + if (this.isPlatformIOS()) { + console.log("tnps android plugin skip ios platform."); + if (successCallback) { + successCallback(); + } + return; + } + if (errorCallback) { + cordova.exec(successCallback, errorCallback, "TpnsPlugin", name, args); + } else { + cordova.exec( + successCallback, + this.errorCallback, + "JPushPlugin", + name, + args + ); + } +}; +/** + * 启用调试 + */ +TpnsPlugin.prototype.enableDebug = function(enable) { + this.callNative("enableDebug", [enable], null); +}; +/** + * 关闭联合保活能力(1.1.6.1+) + * */ +TpnsPlugin.prototype.enablePullUpOtherApp = function(enable) { + this.callNative("enablePullUpOtherApp", [enable], null); +}; +/** + * 获取设备 Token + */ +TpnsPlugin.prototype.getToken = function(successCallback,errorCallback){ + this.callNative("getToken", [], successCallback,errorCallback); +}; +/** + * 获取第三方厂商 Token + */ +TpnsPlugin.prototype.getOtherPushToken = function(successCallback,errorCallback){ + this.callNative("getOtherPushToken", [], successCallback,errorCallback); +}; +/** + * 注册消息推送 + * @param {*} other 厂商通道? 为空时不开启 格式如下: + * { + * "MZ":{"APP_ID":魅族APP_ID,"APP_KEY":魅族APP_KEY}, + * "OPPO":{"appKey":OPPO appKey,"appSecret": OPPO appSecret}, + * "MI":{"APPID":小米APPID,"APPKEY":小米APPKEY}, + * "HW":{},"RY":{} + * } + * @param {*} successCallback + * @param {*} errorCallback + */ +TpnsPlugin.prototype.registerPush = function(other,successCallback,errorCallback){ + this.callNative("registerPush", other?[other]:[],successCallback,errorCallback); +} +/** + * 取消注册消息推送 + * @param {*} successCallback + * @param {*} errorCallback + */ +TpnsPlugin.prototype.unregisterPush = function(successCallback,errorCallback){ + this.callNative("unregisterPush", [],successCallback,errorCallback); +} +/** + * 添加本地消息通知 + * @param {*} data 消息通知内容 + * { + * channelId:通道id,channelName:通道名称,enableVibration:是否震动,enableLights:是否闪光,enableSound:是否铃声,soundUri:铃声文件地址 + * } + * @param {*} successCallback + * @param {*} errorCallback + */ +TpnsPlugin.prototype.createNotificationChannel =function(data,successCallback,errorCallback){ + this.callNative("createNotificationChannel", [data],successCallback,errorCallback); +} + +/** + * 创建消息通道 + * @param {*} data 消息通道信息 + * { + * type?: 本地消息类型,1:通知,2:消息, + * title: 消息标题, + * content: 消息内容, + * date?: 消息日期,格式为:20140502, + * hour?: 消息触发的小时(24小时制),例如:22代表晚上10点, + * min? :消息触发的分钟,例如:05代表05分, + * builderId?: 消息样式,默认为0或不设置, + * actionType: 设置动作类型:1打开activity或App本身,2打开浏览器,3打开Intent ,4通过包名打开应用, + * activity?:拉起应用页面, + * url?: URL, + * intent?: intent + * styleId?: 是否覆盖原先build_id的保存设置。1覆盖,0不覆盖, + * ringRaw?: 音频资源 + * data?: {} 自定义内容列表 + * } + * @param {*} successCallback + * @param {*} errorCallback + */ +TpnsPlugin.prototype.addLocalNotification =function(data,successCallback,errorCallback){ + this.callNative("addLocalNotification", [data],successCallback,errorCallback); +} + +/** + * 清除本地消息通知 + * @param {*} successCallback + * @param {*} errorCallback + */ +TpnsPlugin.prototype.clearLocalNotifications = function(successCallback,errorCallback){ + this.callNative("clearLocalNotifications", [],successCallback,errorCallback); +} +/** + * 对一个或多个账号类型的账号进行解绑。(SDK 1.2.3.0+) + * @param {*} params 账户类型数组 [1001,1002] + * @param {*} successCallback + * @param {*} errorCallback + */ +TpnsPlugin.prototype.delAccounts = function(params,successCallback,errorCallback){ + this.callNative("delAccounts", [params],successCallback,errorCallback); +} +/** + * 对的所有已绑定账号进行解绑 + * @param {*} successCallback + * @param {*} errorCallback + */ +TpnsPlugin.prototype.clearAccounts = function(successCallback,errorCallback){ + this.callNative("clearAccounts", [],successCallback,errorCallback); +} + +/** + * 添加或更新账号。若原来没有该类型账号,则添加;若原来有,则覆盖。可以同时添加多个账号,一个账号对应一个账号类型。 + * @param {*} params 账户数组[{"account":elva, "accountType":1001}, {"account":jay, "accountType":1002}] + * @param {*} successCallback + * @param {*} errorCallback + */ +TpnsPlugin.prototype.upsertAccounts = function(params,successCallback,errorCallback){ + this.callNative("upsertAccounts", [params],successCallback,errorCallback); +} + +/** + * 覆盖多个标签 一次设置多个标签,会覆盖这个设备之前设置的标签。 + * @param {*} operateName 操作名称 全局唯一,类似于全局序列 + * @param {*} tags 标签数组["tag1","tag2"] + * @param {*} successCallback + * @param {*} errorCallback + */ +TpnsPlugin.prototype.clearAndAppendTags = function(operateName,tags,successCallback,errorCallback){ + this.callNative("clearAndAppendTags", [operateName,tags],successCallback,errorCallback); +} + +/** + * 新增多个标签 + * 如果新覆盖的标签都带有 : 号,例如 test:2, level:2,则会删除这个设备已绑定的所有 test:* 和 level:* 标签,再新增 test:2 和 level:2。 + * 如果新增的标签有部分不带 : 号,例如 test:2 level,则会删除这个设备的全部历史标签,再新增 test:2 和 level 标签。 + * @param {*} operateName 操作名称 全局唯一,类似于全局序列 + * @param {*} tags 标签数组["tag1","tag2"] + * @param {*} successCallback + * @param {*} errorCallback + */ +TpnsPlugin.prototype.appendTags = function(operateName,tags,successCallback,errorCallback){ + this.callNative("appendTags", [operateName,tags],successCallback,errorCallback); +} + +/** + * 一次删除多个标签。 + * @param {*} operateName 操作名称 全局唯一,类似于全局序列 + * @param {*} tags 标签数组["tag1","tag2"] + * @param {*} successCallback + * @param {*} errorCallback + */ +TpnsPlugin.prototype.delTags = function(operateName,tags,successCallback,errorCallback){ + this.callNative("delTags", [operateName,tags],successCallback,errorCallback); +} + +/** + * 清除这个设备的所有标签。 + * @param {*} operateName 操作名称 全局唯一,类似于全局序列 + * @param {*} successCallback + * @param {*} errorCallback + */ +TpnsPlugin.prototype.clearTags = function(operateName,successCallback,errorCallback){ + this.callNative("clearTags", [operateName],successCallback,errorCallback); +} + +/** + * 获取这个设备的标签 + * @param {*} operateName 操作名称 全局唯一,类似于全局序列 + * @param {*} offset 开始位置 + * @param {*} limit 取值数量 最大100 + * @param {*} successCallback 返回标签数组字符串 + * @param {*} errorCallback + */ +TpnsPlugin.prototype.queryTags = function(operateName,offset,limit,successCallback,errorCallback){ + this.callNative("queryTags", [operateName,offset,limit],successCallback,errorCallback); +} + + +/** + * 添加属性(带回调):有则覆盖,无则添加。 + * @param {*} operateName 操作名称 全局唯一,类似于全局序列 + * @param {*} attrs 属性对象 {"属性名1":"属性值1","属性名2":"属性值2"} + * @param {*} successCallback + * @param {*} errorCallback + */ +TpnsPlugin.prototype.upsertAttributes = function(operateName,attrs,successCallback,errorCallback){ + this.callNative("upsertAttributes", [operateName,attrs],successCallback,errorCallback); +} + +/** + * 更新用户属性 + * @param {*} operateName 操作名称 全局唯一,类似于全局序列 + * @param {*} attrs 属性对象 {"属性名1":"属性值1","属性名2":"属性值2"} + * @param {*} successCallback + * @param {*} errorCallback + */ +TpnsPlugin.prototype.clearAndAppendAttributes = function(operateName,attrs,successCallback,errorCallback){ + this.callNative("clearAndAppendAttributes", [operateName,attrs],successCallback,errorCallback); +} + +/** + * 删除指定的属性。 + * @param {*} operateName 操作名称 全局唯一,类似于全局序列 + * @param {*} attrs 属性名数组 ["属性名1","属性名2"] + * @param {*} successCallback + * @param {*} errorCallback + */ +TpnsPlugin.prototype.delAttributes = function(operateName,attrs,successCallback,errorCallback){ + this.callNative("delAttributes", [operateName,attrs],successCallback,errorCallback); +} + +/** + * 删除已设置的所有属性 + * @param {*} operateName 操作名称 全局唯一,类似于全局序列 + * @param {*} successCallback + * @param {*} errorCallback + */ +TpnsPlugin.prototype.clearAttributes = function(operateName,successCallback,errorCallback){ + this.callNative("clearAttributes", [operateName],successCallback,errorCallback); +} + + + +/** + * 取消全部消息通知 + * @param {*} successCallback + * @param {*} errorCallback + */ +TpnsPlugin.prototype.cancelAllNotifaction = function(successCallback,errorCallback){ + this.callNative("cancelAllNotifaction", [],successCallback,errorCallback); +} + +module.exports = new TpnsPlugin(); \ No newline at end of file