支持荣耀推送
This commit is contained in:
6
config/honor.properties
Normal file
6
config/honor.properties
Normal file
@@ -0,0 +1,6 @@
|
||||
honor.appSecret=a4e5e6a0c8a5d8424aba5a8f0aae3d0c
|
||||
honor.appId=100221325
|
||||
|
||||
#应用入口Activity类全路径,一定要根据你们的实际情况修改
|
||||
#样例:com.example.test.MainActivity
|
||||
honor.badgeClass=cn.wildfire.chat.app.main.SplashActivity
|
||||
@@ -6,6 +6,7 @@ import cn.wildfirechat.push.Utility;
|
||||
import cn.wildfirechat.push.android.fcm.FCMPush;
|
||||
import cn.wildfirechat.push.android.getui.GetuiPush;
|
||||
import cn.wildfirechat.push.android.hms.HMSPush;
|
||||
import cn.wildfirechat.push.android.honor.HonorPush;
|
||||
import cn.wildfirechat.push.android.meizu.MeiZuPush;
|
||||
import cn.wildfirechat.push.android.oppo.OppoPush;
|
||||
import cn.wildfirechat.push.android.vivo.VivoPush;
|
||||
@@ -45,6 +46,9 @@ public class AndroidPushServiceImpl implements AndroidPushService {
|
||||
@Autowired
|
||||
private GetuiPush getuiPush;
|
||||
|
||||
@Autowired
|
||||
private HonorPush honorPush;
|
||||
|
||||
private ExecutorService executorService = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), Runtime.getRuntime().availableProcessors() * 100,
|
||||
60L, TimeUnit.SECONDS,
|
||||
new SynchronousQueue<Runnable>());
|
||||
@@ -56,7 +60,7 @@ public class AndroidPushServiceImpl implements AndroidPushService {
|
||||
LOG.info("canceled");
|
||||
return "Canceled";
|
||||
}
|
||||
if(pushMessage.line == 1) {
|
||||
if (pushMessage.line == 1) {
|
||||
LOG.info("ignore moments messages");
|
||||
return "Canceled";
|
||||
}
|
||||
@@ -91,6 +95,9 @@ public class AndroidPushServiceImpl implements AndroidPushService {
|
||||
case AndroidPushType.ANDROID_PUSH_TYPE_GETUI:
|
||||
getuiPush.push(pushMessage, true);
|
||||
break;
|
||||
case AndroidPushType.ANDROID_PUSH_TYPE_HONOR:
|
||||
honorPush.push(pushMessage);
|
||||
break;
|
||||
default:
|
||||
LOG.info("unknown push type");
|
||||
break;
|
||||
|
||||
@@ -9,4 +9,6 @@ public interface AndroidPushType {
|
||||
int ANDROID_PUSH_TYPE_FCM = 6;
|
||||
|
||||
int ANDROID_PUSH_TYPE_GETUI = 7;
|
||||
int ANDROID_PUSH_TYPE_JPUSH = 8;
|
||||
int ANDROID_PUSH_TYPE_HONOR = 9;
|
||||
}
|
||||
|
||||
@@ -2,12 +2,8 @@ package cn.wildfirechat.push.android.hms;
|
||||
|
||||
|
||||
import cn.wildfirechat.push.PushMessage;
|
||||
import cn.wildfirechat.push.PushMessageType;
|
||||
import cn.wildfirechat.push.hm.payload.AlertPayload;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.google.gson.Gson;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
1
src/main/java/cn/wildfirechat/push/android/hms/readme.md
Normal file
1
src/main/java/cn/wildfirechat/push/android/hms/readme.md
Normal file
@@ -0,0 +1 @@
|
||||
Android 鸿蒙
|
||||
@@ -0,0 +1,38 @@
|
||||
package cn.wildfirechat.push.android.honor;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "honor")
|
||||
@PropertySource(value = "file:config/honor.properties")
|
||||
public class HonorConfig {
|
||||
private String appSecret;
|
||||
private String appId;
|
||||
private String badgeClass;
|
||||
|
||||
public String getAppSecret() {
|
||||
return appSecret;
|
||||
}
|
||||
|
||||
public void setAppSecret(String appSecret) {
|
||||
this.appSecret = appSecret;
|
||||
}
|
||||
|
||||
public String getAppId() {
|
||||
return appId;
|
||||
}
|
||||
|
||||
public void setAppId(String appId) {
|
||||
this.appId = appId;
|
||||
}
|
||||
|
||||
public String getBadgeClass() {
|
||||
return badgeClass;
|
||||
}
|
||||
|
||||
public void setBadgeClass(String badgeClass) {
|
||||
this.badgeClass = badgeClass;
|
||||
}
|
||||
}
|
||||
199
src/main/java/cn/wildfirechat/push/android/honor/HonorPush.java
Normal file
199
src/main/java/cn/wildfirechat/push/android/honor/HonorPush.java
Normal file
@@ -0,0 +1,199 @@
|
||||
package cn.wildfirechat.push.android.honor;
|
||||
|
||||
|
||||
import cn.wildfirechat.push.PushMessage;
|
||||
import cn.wildfirechat.push.android.honor.internal.RequestBody;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class HonorPush {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HonorPush.class);
|
||||
private static final String tokenUrl = "https://iam.developer.honor.com/auth/token"; //获取认证Token的URL
|
||||
private static final String apiUrl = "https://push-api.cloud.honor.com/api/v1/%s/sendMessage"; //应用级消息下发API
|
||||
private String accessToken;//下发通知消息的认证Token
|
||||
private long tokenExpiredTime = 0; //accessToken的过期时间,初始化为0
|
||||
|
||||
@Autowired
|
||||
private HonorConfig mConfig;
|
||||
|
||||
/**
|
||||
* 检查token是否有效
|
||||
*
|
||||
* @return true if token is valid and not expired
|
||||
*/
|
||||
private boolean isTokenValid() {
|
||||
if (StringUtils.isEmpty(accessToken)) {
|
||||
LOG.debug("Honor token is empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if (tokenExpiredTime <= currentTime) {
|
||||
LOG.debug("Honor token expired. Current: {}, Expired: {}", currentTime, tokenExpiredTime);
|
||||
return false;
|
||||
}
|
||||
|
||||
long remainingTime = (tokenExpiredTime - currentTime) / 1000;
|
||||
LOG.debug("Honor token is valid, remaining {} seconds", remainingTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
//获取下发通知消息的认证Token
|
||||
private void refreshToken() throws IOException {
|
||||
LOG.info("Honor refresh token");
|
||||
String msgBody = MessageFormat.format(
|
||||
"grant_type=client_credentials&client_secret={0}&client_id={1}",
|
||||
URLEncoder.encode(mConfig.getAppSecret(), "UTF-8"), mConfig.getAppId());
|
||||
String response = httpPost(tokenUrl, "", msgBody, 5000, 5000);
|
||||
JSONObject obj = JSONObject.parseObject(response);
|
||||
|
||||
if (obj.containsKey("access_token")) {
|
||||
accessToken = obj.getString("access_token");
|
||||
// 设置过期时间,提前5分钟刷新
|
||||
long expiresIn = obj.getLong("expires_in") * 1000; // 转换为毫秒
|
||||
tokenExpiredTime = System.currentTimeMillis() + expiresIn - 5 * 60 * 1000;
|
||||
LOG.info("Honor token refreshed successfully, expires in {} seconds", obj.getLong("expires_in"));
|
||||
} else {
|
||||
LOG.error("Failed to get access_token from response: {}", response);
|
||||
throw new IOException("Failed to get access_token from Honor auth response");
|
||||
}
|
||||
}
|
||||
|
||||
//发送Push消息
|
||||
public void push(PushMessage pushMessage) {
|
||||
// 检查token是否有效,无效则刷新
|
||||
if (!isTokenValid()) {
|
||||
try {
|
||||
refreshToken();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to refresh Honor token", e);
|
||||
return; // token刷新失败,直接返回
|
||||
}
|
||||
}
|
||||
/*PushManager.requestToken为客户端申请token的方法,可以调用多次以防止申请token失败*/
|
||||
/*PushToken不支持手动编写,需使用客户端的onToken方法获取*/
|
||||
// JSONArray deviceTokens = new JSONArray();//目标设备Token
|
||||
// deviceTokens.add(pushMessage.getDeviceToken());
|
||||
//
|
||||
// JSONObject param = new JSONObject();
|
||||
// param.put("appPkgName", pushMessage.packageName);//定义需要打开的appPkgName
|
||||
// JSONObject action = new JSONObject();
|
||||
// action.put("type", 3);//类型3为打开APP,其他行为请参考接口文档设置
|
||||
// action.put("param", param);//消息点击动作参数
|
||||
//
|
||||
//
|
||||
// JSONObject msg = new JSONObject();
|
||||
// // 透传消息
|
||||
// msg.put("type", 3);//3: 通知栏消息,异步透传消息请根据接口文档设置
|
||||
// msg.put("action", action);//消息点击动作 add by liguangyu
|
||||
//
|
||||
// String token = pushMessage.getDeviceToken();
|
||||
// pushMessage.deviceToken = null;
|
||||
//// msg.put("body", new Gson().toJson(pushMessage));//通知栏消息body内容
|
||||
//
|
||||
// JSONObject body = new JSONObject();//仅通知栏消息需要设置标题和内容,透传消息key和value为用户自定义
|
||||
// body.put("title", pushMessage.senderName);//消息标题
|
||||
// body.put("content", pushMessage.pushContent);//消息内容体
|
||||
// //body.put("info", new Gson().toJson(pushMessage));//消息内容体
|
||||
// msg.put("body", body);//通知栏消息body内容示例代码
|
||||
// LOG.info("liguangyu test body: {} pushMessage{}",body,new Gson().toJson(pushMessage) );
|
||||
//
|
||||
// // 华为消息分类
|
||||
// msg.put("importance", "NORMAL");
|
||||
// msg.put("category", "IM");
|
||||
//
|
||||
// JSONObject hps = new JSONObject();//华为PUSH消息总结构体
|
||||
// hps.put("msg", msg);
|
||||
//
|
||||
// JSONObject payload = new JSONObject();
|
||||
// payload.put("hps", hps);
|
||||
//
|
||||
// LOG.info("send push to Honor {}", payload);
|
||||
|
||||
try {
|
||||
// String postBody = MessageFormat.format(
|
||||
// "access_token={0}&nsp_svc={1}&nsp_ts={2}&device_token_list={3}&payload={4}",
|
||||
// URLEncoder.encode(accessToken,"UTF-8"),
|
||||
// URLEncoder.encode("openpush.message.api.send","UTF-8"),
|
||||
// URLEncoder.encode(String.valueOf(System.currentTimeMillis() / 1000),"UTF-8"),
|
||||
// URLEncoder.encode(deviceTokens.toString(),"UTF-8"),
|
||||
// URLEncoder.encode(payload.toString(),"UTF-8"));
|
||||
RequestBody alertPayload = RequestBody.buildRequestBody(pushMessage, mConfig.getAppId());
|
||||
LOG.info("Push message {}", alertPayload);
|
||||
//String postUrl = apiUrl + "?nsp_ctx=" + URLEncoder.encode("{\"ver\":\"1\", \"appId\":\"" + mConfig.getAppId() + "\"}", "UTF-8");
|
||||
String postUrl = String.format(apiUrl, mConfig.getAppId());
|
||||
String response = httpPost(postUrl, accessToken, alertPayload.toString(), 8000, 8000);
|
||||
LOG.info("Push to {} response {}", pushMessage.getDeviceToken(), response);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
LOG.info("Push to {} with exception", pushMessage.getDeviceToken(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public String httpPost(String httpUrl, String jwt, String data, int connectTimeout, int readTimeout) throws IOException {
|
||||
OutputStream outPut = null;
|
||||
HttpURLConnection urlConnection = null;
|
||||
InputStream in = null;
|
||||
|
||||
try {
|
||||
URL url = new URL(httpUrl);
|
||||
urlConnection = (HttpURLConnection) url.openConnection();
|
||||
urlConnection.setRequestMethod("POST");
|
||||
urlConnection.setDoOutput(true);
|
||||
urlConnection.setDoInput(true);
|
||||
if (StringUtils.isNotEmpty(jwt)) {
|
||||
urlConnection.setRequestProperty("Content-Type", "application/json");
|
||||
urlConnection.setRequestProperty("Authorization", "Bearer " + jwt);
|
||||
urlConnection.setRequestProperty("timestamp", "" + System.currentTimeMillis());
|
||||
//urlConnection.setRequestProperty("push-type", pushType + "");
|
||||
} else {
|
||||
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
|
||||
}
|
||||
|
||||
urlConnection.setConnectTimeout(connectTimeout);
|
||||
urlConnection.setReadTimeout(readTimeout);
|
||||
urlConnection.connect();
|
||||
|
||||
// POST data
|
||||
outPut = urlConnection.getOutputStream();
|
||||
outPut.write(data.getBytes("UTF-8"));
|
||||
outPut.flush();
|
||||
|
||||
// read response
|
||||
if (urlConnection.getResponseCode() < 400) {
|
||||
in = urlConnection.getInputStream();
|
||||
} else {
|
||||
in = urlConnection.getErrorStream();
|
||||
}
|
||||
|
||||
List<String> lines = IOUtils.readLines(in, urlConnection.getContentEncoding());
|
||||
StringBuffer strBuf = new StringBuffer();
|
||||
for (String line : lines) {
|
||||
strBuf.append(line);
|
||||
}
|
||||
LOG.info(strBuf.toString());
|
||||
return strBuf.toString();
|
||||
} finally {
|
||||
IOUtils.closeQuietly(outPut);
|
||||
IOUtils.closeQuietly(in);
|
||||
if (urlConnection != null) {
|
||||
urlConnection.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package cn.wildfirechat.push.android.honor.internal;
|
||||
|
||||
public class AndroidConfig {
|
||||
public String ttl = "86400s";
|
||||
public String biTag;
|
||||
public String data;
|
||||
public AndroidNotification notification;
|
||||
|
||||
// 0,普通消息;1,测试消息
|
||||
public int targetUserType = 0;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package cn.wildfirechat.push.android.honor.internal;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class AndroidNotification {
|
||||
public String title;
|
||||
public String body;
|
||||
public ClickAction clickAction;
|
||||
public String image;
|
||||
/**
|
||||
* 0:默认样式
|
||||
* 1:大文本样式
|
||||
*/
|
||||
public Integer style = 0;
|
||||
public String bigTitle;
|
||||
public String bigBody;
|
||||
public String importance;
|
||||
public String when;
|
||||
public List<Button> buttons;
|
||||
public BadgeNotification badge;
|
||||
public Integer notifyId;
|
||||
public String tag;
|
||||
public String group;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package cn.wildfirechat.push.android.honor.internal;
|
||||
|
||||
public class BadgeNotification {
|
||||
public Integer addNum;
|
||||
/**
|
||||
* 必选参数
|
||||
* 应用入口Activity类全路径。
|
||||
* 样例:com.example.test.MainActivity
|
||||
*/
|
||||
public String badgeClass;
|
||||
public Integer setNum;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package cn.wildfirechat.push.android.honor.internal;
|
||||
|
||||
public class Button {
|
||||
public String name;
|
||||
public Integer actionType;
|
||||
public Integer intentType;
|
||||
public String intent;
|
||||
public String data;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package cn.wildfirechat.push.android.honor.internal;
|
||||
|
||||
public class ClickAction {
|
||||
/**
|
||||
* 消息点击行为类型,取值如下:
|
||||
* <p>
|
||||
* 1:打开应用自定义页面
|
||||
* 2:点击后打开特定URL
|
||||
* 3:点击后打开应用
|
||||
*/
|
||||
|
||||
public Integer type = 3;
|
||||
|
||||
public String intent;
|
||||
public String url;
|
||||
public String action;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package cn.wildfirechat.push.android.honor.internal;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class HonorMessageNotification {
|
||||
public String importance = "NORMAL";
|
||||
@SerializedName("click_action")
|
||||
public ClickAction clickAction;
|
||||
// public String ticker;
|
||||
// public String notify_summary;
|
||||
//
|
||||
public String channel_id;
|
||||
// public int style = 0;
|
||||
// public int notify_id;
|
||||
// public String visibility;
|
||||
// public String when;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package cn.wildfirechat.push.android.honor.internal;
|
||||
|
||||
public class Notification {
|
||||
public String title;
|
||||
public String body;
|
||||
public String image;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package cn.wildfirechat.push.android.honor.internal;
|
||||
|
||||
import cn.wildfirechat.push.PushMessage;
|
||||
import cn.wildfirechat.push.Utility;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class RequestBody {
|
||||
|
||||
// 荣耀推送长度限制:title最大40字符,body最大256字符
|
||||
private static final int HONOR_PUSH_MAX_TITLE = 40;
|
||||
private static final int HONOR_PUSH_MAX_BODY = 256;
|
||||
|
||||
// 自定义消息负载
|
||||
public String data;
|
||||
public Notification notification;
|
||||
public AndroidConfig android;
|
||||
public List<String> token;
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new Gson().toJson(this);
|
||||
}
|
||||
|
||||
public static RequestBody buildRequestBody(PushMessage pushMessage, String appId) {
|
||||
RequestBody requestBody = new RequestBody();
|
||||
|
||||
requestBody.notification = new Notification();
|
||||
String[] titleAndBody = Utility.getPushTitleAndContent(pushMessage);
|
||||
String title = titleAndBody[0];
|
||||
String body = titleAndBody[1];
|
||||
|
||||
// 处理华为推送的长度限制
|
||||
if (title != null && title.length() > HONOR_PUSH_MAX_TITLE) {
|
||||
title = title.substring(0, HONOR_PUSH_MAX_TITLE - 3) + "...";
|
||||
}
|
||||
|
||||
if (body != null && body.length() > HONOR_PUSH_MAX_BODY) {
|
||||
body = body.substring(0, HONOR_PUSH_MAX_BODY - 3) + "...";
|
||||
}
|
||||
|
||||
requestBody.notification.title = title;
|
||||
requestBody.notification.body = body;
|
||||
|
||||
List<String> tokens = new ArrayList<>();
|
||||
tokens.add(pushMessage.deviceToken);
|
||||
requestBody.token = tokens;
|
||||
|
||||
requestBody.android = new AndroidConfig();
|
||||
requestBody.android.notification = new AndroidNotification();
|
||||
requestBody.android.notification.title = title;
|
||||
requestBody.android.notification.body = body;
|
||||
requestBody.android.notification.importance = "NORMAL";
|
||||
|
||||
requestBody.android.notification.clickAction = new ClickAction();
|
||||
requestBody.android.notification.clickAction.type = 3;
|
||||
requestBody.android.notification.badge = new BadgeNotification();
|
||||
|
||||
int badgeNum = pushMessage.getUnReceivedMsg() + pushMessage.getExistBadgeNumber();
|
||||
if (badgeNum <= 0) {
|
||||
badgeNum = 1;
|
||||
}
|
||||
requestBody.android.notification.badge.setNum = badgeNum;
|
||||
return requestBody;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Android 鸿蒙
|
||||
@@ -1,3 +1,5 @@
|
||||
原生鸿蒙
|
||||
|
||||
文档在这儿:
|
||||
|
||||
https://developer.huawei.com/consumer/cn/doc/harmonyos-references/push-rest-api
|
||||
Reference in New Issue
Block a user