admin
2022-01-12 4a7367a869ef12375ea6678ca44e102b8919c624
极光推送完善(推送通知与透传消息)
5 文件已重命名
11个文件已修改
69个文件已添加
8577 ■■■■■ 已修改文件
facade-push/src/main/java/com/ks/push/pojo/DO/BPushMessage.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
facade-push/src/main/java/com/ks/push/pojo/DO/PushPlatform.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/pom.xml 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/JPushClient.java 1295 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/JPushConfig.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/admin/AdminClient.java 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/admin/AppResult.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/admin/CreateAppResult.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/device/AliasDeviceListResult.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/device/DeviceClient.java 296 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/device/OnlineStatus.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/device/TagAliasResult.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/device/TagListResult.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/device/package-info.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/file/FileClient.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/file/model/FileModel.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/file/model/FileModelPage.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/file/model/FileType.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/file/model/FileUploadResult.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/image/ImageClient.java 178 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/image/model/ImageFilePayload.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/image/model/ImageSource.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/image/model/ImageType.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/image/model/ImageUploadResult.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/image/model/ImageUrlPayload.java 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/CIDResult.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/GroupPushClient.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/GroupPushResult.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/PushClient.java 380 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/PushResult.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/model/BatchPushResult.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/model/EncryptKeys.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/model/EncryptPushPayload.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/model/InappMessage.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/model/Message.java 233 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/model/Notification3rd.java 270 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/model/Options.java 291 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/model/Platform.java 171 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/model/PushModel.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/model/PushPayload.java 368 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/model/SMS.java 220 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/model/audience/Audience.java 194 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/model/audience/AudienceTarget.java 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/model/audience/AudienceType.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/model/notification/AndroidNotification.java 448 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/model/notification/InterfaceAdapter.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/model/notification/IosAlert.java 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/model/notification/IosNotification.java 230 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/model/notification/Notification.java 272 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/model/notification/PlatformNotification.java 249 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/model/notification/QuickappNotification.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/model/notification/WinphoneNotification.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/push/package-info.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/report/GroupMessageDetailResult.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/report/GroupUsersResult.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/report/MessageDetailResult.java 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/report/MessageStatus.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/report/MessagesResult.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/report/ReceivedsResult.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/report/ReportClient.java 231 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/report/UsersResult.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/report/model/CheckMessagePayload.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/report/package-info.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/schedule/ScheduleClient.java 169 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/schedule/ScheduleListResult.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/schedule/ScheduleMsgIdsResult.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/schedule/ScheduleResult.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/schedule/model/IModel.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/schedule/model/SchedulePayload.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/schedule/model/TriggerPayload.java 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/cn/jpush/api/schedule/package-info.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/com/ks/push/PushApplication.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/com/ks/push/manager/CMQManager.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/com/ks/push/manager/PushManager.java 103 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/com/ks/push/utils/PushUtil.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/com/ks/push/utils/push/HuaWeiPushUtil.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/com/ks/push/utils/push/JpushUtil.java 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/com/ks/push/utils/push/MeiZuPushUtil.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/com/ks/push/utils/push/OppoPushUtil.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/com/ks/push/utils/push/VIVOPushUtil.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/java/com/ks/push/utils/push/XiaoMiPushUtil.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/resources/logback.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/resources/static/pushplatform-appinfo-add.html 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service-push/src/main/resources/static/pushtask-add.html 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
facade-push/src/main/java/com/ks/push/pojo/DO/BPushMessage.java
@@ -21,6 +21,11 @@
    private Map<String, String> extras;
    /**
     * 是否为透传消息
     */
    private boolean message;
    public String getTitle() {
@@ -71,4 +76,11 @@
        this.androidHostPath = androidHostPath;
    }
    public boolean isMessage() {
        return message;
    }
    public void setMessage(boolean message) {
        this.message = message;
    }
}
facade-push/src/main/java/com/ks/push/pojo/DO/PushPlatform.java
@@ -1,7 +1,7 @@
package com.ks.push.pojo.DO;
public enum PushPlatform {
    xm("小米"), hw("华为"), oppo("OPPO"), vivo("VIVO"), mz("魅族");
    jpush("极光"), xm("小米"), hw("华为"), oppo("OPPO"), vivo("VIVO"), mz("魅族");
    private String name;
pom.xml
@@ -55,6 +55,7 @@
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
service-push/pom.xml
@@ -262,6 +262,88 @@
            <version>1.2.8.20190114_release</version>
        </dependency>
        <!-- 极光推送 -->
        <dependency>
            <groupId>cn.jpush.api</groupId>
            <artifactId>jiguang-common</artifactId>
            <version>1.1.11</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.6.Final</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.6</version>
            <scope>provided</scope>
        </dependency>
        <!--<dependency>-->
            <!--<groupId>com.google.code.gson</groupId>-->
            <!--<artifactId>gson</artifactId>-->
            <!--<version>2.3</version>-->
        <!--</dependency>-->
        <!--<dependency>-->
            <!--<groupId>org.slf4j</groupId>-->
            <!--<artifactId>slf4j-api</artifactId>-->
            <!--<version>1.7.7</version>-->
        <!--</dependency>-->
        <!--&lt;!&ndash; For log4j &ndash;&gt;-->
        <!--<dependency>-->
            <!--<groupId>org.slf4j</groupId>-->
            <!--<artifactId>slf4j-log4j12</artifactId>-->
            <!--<version>1.7.7</version>-->
        <!--</dependency>-->
        <!--<dependency>-->
            <!--<groupId>log4j</groupId>-->
            <!--<artifactId>log4j</artifactId>-->
            <!--<version>1.2.17</version>-->
        <!--</dependency>-->
        <!-- 极光推送 -->
        <dependency>
            <groupId>cn.jpush.api</groupId>
            <artifactId>jiguang-common</artifactId>
            <version>1.1.11</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.6.Final</version>
            <scope>compile</scope>
        </dependency>
        <!--<dependency>-->
            <!--<groupId>com.google.code.gson</groupId>-->
            <!--<artifactId>gson</artifactId>-->
            <!--<version>2.3</version>-->
        <!--</dependency>-->
        <!--<dependency>-->
            <!--<groupId>org.slf4j</groupId>-->
            <!--<artifactId>slf4j-api</artifactId>-->
            <!--<version>1.7.7</version>-->
        <!--</dependency>-->
        <!--&lt;!&ndash; For log4j &ndash;&gt;-->
        <!--<dependency>-->
            <!--<groupId>org.slf4j</groupId>-->
            <!--<artifactId>slf4j-log4j12</artifactId>-->
            <!--<version>1.7.7</version>-->
        <!--</dependency>-->
        <!--<dependency>-->
            <!--<groupId>log4j</groupId>-->
            <!--<artifactId>log4j</artifactId>-->
            <!--<version>1.2.17</version>-->
        <!--</dependency>-->
    </dependencies>
service-push/src/main/java/cn/jpush/api/JPushClient.java
New file
@@ -0,0 +1,1295 @@
package cn.jpush.api;
import java.util.List;
import java.util.Map;
import java.util.Set;
import cn.jiguang.common.resp.*;
import cn.jpush.api.push.CIDResult;
import cn.jpush.api.push.model.*;
import cn.jpush.api.report.*;
import cn.jpush.api.report.model.CheckMessagePayload;
import com.google.gson.JsonObject;
import cn.jiguang.common.ClientConfig;
import cn.jiguang.common.TimeUnit;
import cn.jiguang.common.Week;
import cn.jiguang.common.utils.Preconditions;
import cn.jiguang.common.connection.HttpProxy;
import cn.jpush.api.device.AliasDeviceListResult;
import cn.jpush.api.device.DeviceClient;
import cn.jpush.api.device.OnlineStatus;
import cn.jpush.api.device.TagAliasResult;
import cn.jpush.api.device.TagListResult;
import cn.jpush.api.push.PushClient;
import cn.jpush.api.push.PushResult;
import cn.jpush.api.push.model.audience.Audience;
import cn.jpush.api.push.model.notification.IosAlert;
import cn.jpush.api.push.model.notification.Notification;
import cn.jpush.api.schedule.ScheduleClient;
import cn.jpush.api.schedule.ScheduleListResult;
import cn.jpush.api.schedule.ScheduleMsgIdsResult;
import cn.jpush.api.schedule.ScheduleResult;
import cn.jpush.api.schedule.model.SchedulePayload;
import cn.jpush.api.schedule.model.TriggerPayload;
/**
 * The global entrance of JPush API library.
 */
public class JPushClient {
    private final PushClient _pushClient;
    private final ReportClient _reportClient;
    private final DeviceClient _deviceClient;
    private final ScheduleClient _scheduleClient;
    /**
     * Create a JPush Client.
     *
     * @param masterSecret API access secret of the appKey.
     * @param appKey The KEY of one application on JPush.
     */
    public JPushClient(String masterSecret, String appKey) {
        _pushClient = new PushClient(masterSecret, appKey);
        _reportClient = new ReportClient(masterSecret, appKey);
        _deviceClient = new DeviceClient(masterSecret, appKey);
        _scheduleClient = new ScheduleClient(masterSecret, appKey);
    }
    /**
     * Create a JPush Client by custom Client configuration.
     *
     * @param masterSecret API access secret of the appKey.
     * @param appKey The KEY of one application on JPush.
     * @param proxy The proxy, if there is no proxy, should be null.
     * @param conf The client configuration. Can use ClientConfig.getInstance() as default.
     */
    public JPushClient(String masterSecret, String appKey, HttpProxy proxy, ClientConfig conf) {
        _pushClient = new PushClient(masterSecret, appKey, proxy, conf);
        _reportClient = new ReportClient(masterSecret, appKey, proxy, conf);
        _deviceClient = new DeviceClient(masterSecret, appKey, proxy, conf);
        _scheduleClient = new ScheduleClient(masterSecret, appKey, proxy, conf);
    }
    /**
     * This will be removed in the future. Please use ClientConfig{jiguang-common cn.jiguang.common.ClientConfig#setMaxRetryTimes} instead of this constructor.
     * @param masterSecret API access secret of the appKey.
     * @param appKey The KEY of one application on JPush.
     * @param maxRetryTimes The max retry times.
     */
    @Deprecated
    public JPushClient(String masterSecret, String appKey, int maxRetryTimes) {
        _pushClient = new PushClient(masterSecret, appKey, maxRetryTimes);
        _reportClient = new ReportClient(masterSecret, appKey, maxRetryTimes);
        _deviceClient = new DeviceClient(masterSecret, appKey, maxRetryTimes);
        _scheduleClient = new ScheduleClient(masterSecret, appKey, maxRetryTimes);
    }
    /**
     * This will be removed in the future. Please use ClientConfig{jiguang-common cn.jiguang.common.ClientConfig#setMaxRetryTimes} instead of this constructor.
     * @param masterSecret API access secret of the appKey.
     * @param appKey The KEY of one application on JPush.
     * @param maxRetryTimes The max retry times.
     * @param proxy The proxy, if there is no proxy, should be null.
     */
    @Deprecated
    public JPushClient(String masterSecret, String appKey, int maxRetryTimes, HttpProxy proxy) {
        _pushClient = new PushClient(masterSecret, appKey, maxRetryTimes, proxy);
        _reportClient = new ReportClient(masterSecret, appKey, maxRetryTimes, proxy);
        _deviceClient = new DeviceClient(masterSecret, appKey, maxRetryTimes, proxy);
        _scheduleClient = new ScheduleClient(masterSecret, appKey, maxRetryTimes, proxy);
    }
    /**
     * Create a JPush Client by custom Client configuration.
     *
     * If you are using JPush privacy cloud, maybe this constructor is what you needed.
     * This will be removed in the future. Please use ClientConfig{jiguang-common cn.jiguang.common.ClientConfig#setMaxRetryTimes} instead of this constructor.
     *
     * @param masterSecret API access secret of the appKey.
     * @param appKey The KEY of one application on JPush.
     * @param maxRetryTimes Client request retry times.
     * @param proxy The proxy, if there is no proxy, should be null.
     * @param conf The client configuration. Can use ClientConfig.getInstance() as default.
     */
    @Deprecated
    public JPushClient(String masterSecret, String appKey, int maxRetryTimes, HttpProxy proxy, ClientConfig conf) {
        conf.setMaxRetryTimes(maxRetryTimes);
        _pushClient = new PushClient(masterSecret, appKey, proxy, conf);
        _reportClient = new ReportClient(masterSecret, appKey, proxy, conf);
        _deviceClient = new DeviceClient(masterSecret, appKey, proxy, conf);
        _scheduleClient = new ScheduleClient(masterSecret, appKey, proxy, conf);
    }
    /**
     * Create a JPush Client by custom Client configuration with global settings.
     *
     * If you are using JPush privacy cloud, and you want different settings from default globally,
     * maybe this constructor is what you needed.
     * This will be removed in the future. Please use ClientConfig{jiguang-common cn.jiguang.common.ClientConfig#setGlobalPushSetting} instead of this constructor.
     *
     * @param masterSecret API access secret of the appKey.
     * @param appKey The KEY of one application on JPush.
     * @param maxRetryTimes Client request retry times.
     * @param proxy The proxy, if there is no proxy, should be null.
     * @param conf The client configuration. Can use ClientConfig.getInstance() as default.
     * @param apnsProduction Global APNs environment setting. It will override PushPayload Options.
     * @param timeToLive Global time_to_live setting. It will override PushPayload Options.
     */
    @Deprecated
    public JPushClient(String masterSecret, String appKey, int maxRetryTimes, HttpProxy proxy, ClientConfig conf,
                       boolean apnsProduction, long timeToLive) {
        conf.setMaxRetryTimes(maxRetryTimes);
        conf.setApnsProduction(apnsProduction);
        conf.setTimeToLive(timeToLive);
        _pushClient = new PushClient(masterSecret, appKey, proxy, conf);
        _reportClient = new ReportClient(masterSecret, appKey, proxy, conf);
        _deviceClient = new DeviceClient(masterSecret, appKey, proxy, conf);
        _scheduleClient = new ScheduleClient(masterSecret, appKey, proxy, conf);
    }
    /**
     * Create a JPush Client with global settings.
     *
     * If you want different settings from default globally, this constructor is what you needed.
     * This will be removed in the future. Please use ClientConfig{jiguang-common cn.jiguang.common.ClientConfig#setGlobalPushSetting} instead of this constructor.
     *
     * @param masterSecret API access secret of the appKey.
     * @param appKey The KEY of one application on JPush.
     * @param apnsProduction Global APNs environment setting. It will override PushPayload Options.
     * @param timeToLive Global time_to_live setting. It will override PushPayload Options.
     */
    @Deprecated
    public JPushClient(String masterSecret, String appKey, boolean apnsProduction, long timeToLive) {
        ClientConfig conf = ClientConfig.getInstance();
        conf.setApnsProduction(apnsProduction);
        conf.setTimeToLive(timeToLive);
        _pushClient = new PushClient(masterSecret, appKey);
        _reportClient = new ReportClient(masterSecret, appKey);
        _deviceClient = new DeviceClient(masterSecret, appKey);
        _scheduleClient = new ScheduleClient(masterSecret, appKey);
    }
    public PushClient getPushClient() {
        return _pushClient;
    }
    // ----------------------------- Push API
    /**
     * Send a push with PushPayload object.
     *
     * @param pushPayload payload object of a push.
     * @return PushResult The result object of a Push. Can be printed to a JSON.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendPush(PushPayload pushPayload) throws APIConnectionException, APIRequestException {
        return _pushClient.sendPush(pushPayload);
    }
    /**
     * Send a push with JSON string.
     *
     * You can send a push JSON string directly with this method.
     *
     * Attention: globally settings cannot be affect this type of Push.
     *
     * @param  payloadString payload of a push.
     * @return PushResult. Can be printed to a JSON.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendPush(String payloadString) throws APIConnectionException, APIRequestException {
        return _pushClient.sendPush(payloadString);
    }
    /**
     * Send a file push with PushPayload object.
     *
     * @param pushPayload payload object of a push.
     * @return PushResult The result object of a Push. Can be printed to a JSON.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendFilePush(PushPayload pushPayload) throws APIConnectionException, APIRequestException {
        return _pushClient.sendFilePush(pushPayload);
    }
    /**
     * Validate a push action, but do NOT send it actually.
     *
     * @param payload payload of a push.
     * @return PushResult. Can be printed to a JSON.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendPushValidate(PushPayload payload) throws APIConnectionException, APIRequestException {
        return _pushClient.sendPushValidate(payload);
    }
    public PushResult sendPushValidate(String payloadString) throws APIConnectionException, APIRequestException {
        return _pushClient.sendPushValidate(payloadString);
    }
    public BatchPushResult batchSendPushByRegId(List<PushPayload> pushPayloadList) throws APIConnectionException, APIRequestException {
        return _pushClient.batchSendPushByRegId(pushPayloadList);
    }
    public BatchPushResult batchSendPushByAlias(List<PushPayload> pushPayloadList) throws APIConnectionException, APIRequestException {
        return _pushClient.batchSendPushByAlias(pushPayloadList);
    }
    /**
     * Get cid list, the data form of cid is appKey-uuid.
     * @param count the count of cid list, from 1 to 1000. default is 1.
     * @param type default is push, option: schedule
     * @return CIDResult, an array of cid
     * @throws APIConnectionException connect exception
     * @throws APIRequestException request exception
     */
    public CIDResult getCidList(int count, String type) throws APIConnectionException, APIRequestException {
        return _pushClient.getCidList(count, type);
    }
    // ------------------------------- Report API
    /**
     * Get received report.
     *
     * @param msgIds 100 msgids to batch getting is supported.
     * @return ReceivedResult. Can be printed to JSON.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public ReceivedsResult getReportReceiveds(String msgIds) throws APIConnectionException, APIRequestException {
        return _reportClient.getReceiveds(msgIds);
    }
    public UsersResult getReportUsers(TimeUnit timeUnit, String start, int duration) throws APIConnectionException, APIRequestException {
        return _reportClient.getUsers(timeUnit, start, duration);
    }
    public MessagesResult getReportMessages(String msgIds) throws APIConnectionException, APIRequestException {
        return _reportClient.getMessages(msgIds);
    }
    public Map<String, MessageStatus> getMessageStatus(CheckMessagePayload payload)
            throws APIConnectionException, APIRequestException {
        return _reportClient.getMessagesStatus(payload);
    }
    public ReceivedsResult getReceivedsDetail(String msgIds)
            throws APIConnectionException, APIRequestException {
        return _reportClient.getReceivedsDetail(msgIds);
    }
    public MessageDetailResult getMessagesDetail(String msgIds)
            throws APIConnectionException, APIRequestException {
        return _reportClient.getMessagesDetail(msgIds);
    }
    public GroupMessageDetailResult getGroupMessagesDetail(String groupMsgIds)
            throws APIConnectionException, APIRequestException {
        return _reportClient.getGroupMessagesDetail(groupMsgIds);
    }
    public GroupUsersResult getGroupReportUsers(TimeUnit timeUnit, String start, int duration) throws APIConnectionException,
            APIRequestException {
        return _reportClient.getGroupUsers(timeUnit, start, duration);
    }
    // ------------------------------ Shortcuts - notification
    public PushResult sendNotificationAll(String alert) throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.alertAll(alert);
        return _pushClient.sendPush(payload);
    }
    /**
     * Send a notification to all.
     * If it doesn't received within the delay time,JPush will send a SMS to the corresponding users.
     *
     * @param alert The notification content.
     * @param sms The SMS content and delay time. If null, sms doesn't work, no effect on Push feature.
     * @return push result
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendNotificationAll(String alert, SMS sms) throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.alertAll(alert, sms);
        return _pushClient.sendPush(payload);
    }
    public PushResult sendAndroidNotificationWithAlias(String title, String alert,
            Map<String, String> extras, String... alias)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.android())
                .setAudience(Audience.alias(alias))
                .setNotification(Notification.android(alert, title, extras))
                .build();
        return _pushClient.sendPush(payload);
    }
    /**
     * Send a notification to Android with alias.
     * If it doesn't received within the delay time,JPush will send a SMS to the corresponding users.
     *
     * @param title The notification title.
     * @param alert The notification content.
     * @param sms The SMS content and delay time. If null, sms doesn't work, no effect on Push feature.
     * @param extras The extra parameter.
     * @param alias The users' alias.
     * @return push result.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendAndroidNotificationWithAlias(String title, String alert, SMS sms,
                                                       Map<String, String> extras, String... alias)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.android())
                .setAudience(Audience.alias(alias))
                .setNotification(Notification.android(alert, title, extras))
                .setSMS(sms)
                .build();
        return _pushClient.sendPush(payload);
    }
    public PushResult sendAndroidNotificationWithRegistrationID(String title, String alert,
            Map<String, String> extras, String... registrationID)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.android())
                .setAudience(Audience.registrationId(registrationID))
                .setNotification(Notification.android(alert, title, extras))
                .build();
        return _pushClient.sendPush(payload);
    }
    /**
     * Send a notification to Android with RegistrationID.
     * If it doesn't received within the delay time,JPush will send a SMS to the corresponding users.
     *
     * @param title The notification title.
     * @param alert The notification content.
     * @param sms The SMS content and delay time. If null, sms doesn't work, no effect on Push feature.
     * @param extras The extra parameter.
     * @param registrationID The registration id generated by JPush.
     * @return push result.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendAndroidNotificationWithRegistrationID(String title, String alert, SMS sms,
                                                                Map<String, String> extras, String... registrationID)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.android())
                .setAudience(Audience.registrationId(registrationID))
                .setNotification(Notification.android(alert, title, extras))
                .setSMS(sms)
                .build();
        return _pushClient.sendPush(payload);
    }
    public PushResult sendIosNotificationWithAlias(String alert,
            Map<String, String> extras, String... alias)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.ios())
                .setAudience(Audience.alias(alias))
                .setNotification(Notification.ios(alert, extras))
                .build();
        return _pushClient.sendPush(payload);
    }
    /**
     * Send a notification to iOS with alias.
     * If it doesn't received within the delay time,JPush will send a SMS to the corresponding users.
     * @param alert The notification content.
     * @param sms The SMS content and delay time. If null, sms doesn't work, no effect on Push feature.
     * @param extras The extra parameter.
     * @param alias The users' alias.
     * @return push result.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendIosNotificationWithAlias(String alert, SMS sms,
                                                   Map<String, String> extras, String... alias)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.ios())
                .setAudience(Audience.alias(alias))
                .setNotification(Notification.ios(alert, extras))
                .setSMS(sms)
                .build();
        return _pushClient.sendPush(payload);
    }
    /**
     * Send an iOS notification with alias.
     * If you want to send alert as a Json object, maybe this method is what you needed.
     *
     * @param alert The wrapper of APNs alert.
     * @param extras The extra params.
     * @param alias The alias list.
     * @return push result.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendIosNotificationWithAlias(IosAlert alert,
                                                   Map<String, String> extras, String... alias)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.ios())
                .setAudience(Audience.alias(alias))
                .setNotification(Notification.ios(alert, extras))
                .build();
        return _pushClient.sendPush(payload);
    }
    /**
     * Send an iOS notification with alias.
     * If you want to send alert as a Json object, maybe this method is what you needed.
     * If it doesn't received within the delay time,JPush will send a SMS to the corresponding users.
     *
     * @param alert The wrapper of APNs alert.
     * @param sms The SMS content and delay time. If null, sms doesn't work, no effect on Push feature.
     * @param extras The extra params.
     * @param alias The alias list.
     * @return push result.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendIosNotificationWithAlias(IosAlert alert, SMS sms,
                                                   Map<String, String> extras, String... alias)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.ios())
                .setAudience(Audience.alias(alias))
                .setNotification(Notification.ios(alert, extras))
                .setSMS(sms)
                .build();
        return _pushClient.sendPush(payload);
    }
    /**
     * Send an iOS notification with alias.
     * If you want to send alert as a Json object, maybe this method is what you needed.
     *
     * @param alert The JSON object of APNs alert.
     * @param extras The extra params.
     * @param alias The alias list.
     * @return push result.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendIosNotificationWithAlias(JsonObject alert,
                                                   Map<String, String> extras, String... alias)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.ios())
                .setAudience(Audience.alias(alias))
                .setNotification(Notification.ios(alert, extras))
                .build();
        return _pushClient.sendPush(payload);
    }
    /**
     * Send an iOS notification with alias.
     * If you want to send alert as a Json object, maybe this method is what you needed.
     * If it doesn't received within the delay time,JPush will send a SMS to the corresponding users.
     *
     * @param alert The JSON object of APNs alert.
     * @param sms The SMS content and delay time. If null, sms doesn't work, no effect on Push feature.
     * @param extras The extra params.
     * @param alias The alias list.
     * @return push result.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendIosNotificationWithAlias(JsonObject alert, SMS sms,
                                                   Map<String, String> extras, String... alias)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.ios())
                .setAudience(Audience.alias(alias))
                .setNotification(Notification.ios(alert, extras))
                .setSMS(sms)
                .build();
        return _pushClient.sendPush(payload);
    }
    public PushResult sendIosNotificationWithRegistrationID(String alert,
            Map<String, String> extras, String... registrationID)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.ios())
                .setAudience(Audience.registrationId(registrationID))
                .setNotification(Notification.ios(alert, extras))
                .build();
        return _pushClient.sendPush(payload);
    }
    /**
     * Send an iOS notification with registrationIds.
     * If it doesn't received within the delay time,JPush will send a SMS to the corresponding users.
     *
     * @param alert The notification content.
     * @param sms The SMS content and delay time. If null, sms doesn't work, no effect on Push feature.
     * @param extras The extra params.
     * @param registrationID The alias list.
     * @return push result.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendIosNotificationWithRegistrationID(String alert, SMS sms,
                                                            Map<String, String> extras, String... registrationID)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.ios())
                .setAudience(Audience.registrationId(registrationID))
                .setNotification(Notification.ios(alert, extras))
                .setSMS(sms)
                .build();
        return _pushClient.sendPush(payload);
    }
    /**
     * Send an iOS notification with registrationIds.
     * If you want to send alert as a Json object, maybe this method is what you needed.
     *
     * @param alert The wrapper of APNs alert.
     * @param extras The extra params.
     * @param registrationID The registration ids.
     * @return push result.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendIosNotificationWithRegistrationID(IosAlert alert,
                                                            Map<String, String> extras, String... registrationID)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.ios())
                .setAudience(Audience.registrationId(registrationID))
                .setNotification(Notification.ios(alert, extras))
                .build();
        return _pushClient.sendPush(payload);
    }
    /**
     * Send an iOS notification with registrationIds.
     * If you want to send alert as a Json object, maybe this method is what you needed.
     * If it doesn't received within the delay time,JPush will send a SMS to the corresponding users.
     *
     * @param alert The wrapper of APNs alert.
     * @param sms The SMS content and delay time. If null, sms doesn't work, no effect on Push feature.
     * @param extras The extra params.
     * @param registrationID The registration ids.
     * @return push result.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendIosNotificationWithRegistrationID(IosAlert alert, SMS sms,
                                                            Map<String, String> extras, String... registrationID)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.ios())
                .setAudience(Audience.registrationId(registrationID))
                .setNotification(Notification.ios(alert, extras))
                .setSMS(sms)
                .build();
        return _pushClient.sendPush(payload);
    }
    /**
     * Send an iOS notification with registrationIds.
     * If you want to send alert as a Json object, maybe this method is what you needed.
     *
     * @param alert The wrapper of APNs alert.
     * @param extras The extra params.
     * @param registrationID The registration ids.
     * @return push result.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendIosNotificationWithRegistrationID(JsonObject alert,
                                                            Map<String, String> extras, String... registrationID)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.ios())
                .setAudience(Audience.registrationId(registrationID))
                .setNotification(Notification.ios(alert, extras))
                .build();
        return _pushClient.sendPush(payload);
    }
    /**
     * Send an iOS notification with registrationIds.
     * If you want to send alert as a Json object, maybe this method is what you needed.
     * If it doesn't received within the delay time,JPush will send a SMS to the corresponding users.
     *
     * @param alert The JSON object of APNs alert.
     * @param sms The SMS content and delay time. If null, sms doesn't work, no effect on Push feature.
     * @param extras The extra params.
     * @param registrationID The registration ids.
     * @return push result.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendIosNotificationWithRegistrationID(JsonObject alert, SMS sms,
                                                            Map<String, String> extras, String... registrationID)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.ios())
                .setAudience(Audience.registrationId(registrationID))
                .setNotification(Notification.ios(alert, extras))
                .setSMS(sms)
                .build();
        return _pushClient.sendPush(payload);
    }
    // ---------------------- shortcuts - message
    public PushResult sendMessageAll(String msgContent) throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.messageAll(msgContent);
        return _pushClient.sendPush(payload);
    }
    /**
     * Send a message to all
     * If it doesn't received within the delay time,JPush will send a SMS to the corresponding users.
     *
     * @param msgContent The message content.
     * @param sms The SMS content and delay time. If null, sms doesn't work, no effect on Push feature.
     * @return push result.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendMessageAll(String msgContent, SMS sms) throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.messageAll(msgContent, sms);
        return _pushClient.sendPush(payload);
    }
    public PushResult sendAndroidMessageWithAlias(String title, String msgContent, String... alias)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.android())
                .setAudience(Audience.alias(alias))
                .setMessage(Message.newBuilder()
                        .setTitle(title)
                        .setMsgContent(msgContent)
                        .build())
                .build();
        return _pushClient.sendPush(payload);
    }
    /**
     * Send an Android message with alias.
     * If it doesn't received within the delay time,JPush will send a SMS to the corresponding users.
     *
     * @param title The message title.
     * @param msgContent The message content.
     * @param sms The SMS content and delay time. If null, sms doesn't work, no effect on Push feature.
     * @param alias The alias list.
     * @return push result.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendAndroidMessageWithAlias(String title, String msgContent, SMS sms, String... alias)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.android())
                .setAudience(Audience.alias(alias))
                .setMessage(Message.newBuilder()
                        .setTitle(title)
                        .setMsgContent(msgContent)
                        .build())
                .setSMS(sms)
                .build();
        return _pushClient.sendPush(payload);
    }
    public PushResult sendAndroidMessageWithRegistrationID(String title, String msgContent, String... registrationID)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.android())
                .setAudience(Audience.registrationId(registrationID))
                .setMessage(Message.newBuilder()
                        .setTitle(title)
                        .setMsgContent(msgContent)
                        .build())
                .build();
        return _pushClient.sendPush(payload);
    }
    /**
     * Send an Android message with registration id.
     * If it doesn't received within the delay time,JPush will send a SMS to the corresponding users.
     *
     * @param title The message title.
     * @param msgContent The message content.
     * @param sms The SMS content and delay time. If null, sms doesn't work, no effect on Push feature.
     * @param registrationID The registration id list.
     * @return push result.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendAndroidMessageWithRegistrationID(String title, String msgContent, SMS sms, String... registrationID)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.android())
                .setAudience(Audience.registrationId(registrationID))
                .setMessage(Message.newBuilder()
                        .setTitle(title)
                        .setMsgContent(msgContent)
                        .build())
                .setSMS(sms)
                .build();
        return _pushClient.sendPush(payload);
    }
    public PushResult sendIosMessageWithAlias(String title, String msgContent, String... alias)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.ios())
                .setAudience(Audience.alias(alias))
                .setMessage(Message.newBuilder()
                        .setTitle(title)
                        .setMsgContent(msgContent)
                        .build())
                .build();
        return _pushClient.sendPush(payload);
    }
    /**
     * Send an iOS message with alias.
     * If it doesn't received within the delay time,JPush will send a SMS to the corresponding users.
     *
     * @param title The message title.
     * @param msgContent The message content.
     * @param sms The SMS content and delay time. If null, sms doesn't work, no effect on Push feature.
     * @param alias The alias list.
     * @return push result.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendIosMessageWithAlias(String title, String msgContent, SMS sms, String... alias)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.ios())
                .setAudience(Audience.alias(alias))
                .setMessage(Message.newBuilder()
                        .setTitle(title)
                        .setMsgContent(msgContent)
                        .build())
                .setSMS(sms)
                .build();
        return _pushClient.sendPush(payload);
    }
    public PushResult sendIosMessageWithRegistrationID(String title, String msgContent, String... registrationID)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.ios())
                .setAudience(Audience.registrationId(registrationID))
                .setMessage(Message.newBuilder()
                        .setTitle(title)
                        .setMsgContent(msgContent)
                        .build())
                .build();
        return _pushClient.sendPush(payload);
    }
    /**
     * Send an iOS message with registration id.
     * If it doesn't received within the delay time,JPush will send a SMS to the corresponding users.
     *
     * @param title The message title.
     * @param msgContent The message content.
     * @param sms The SMS content and delay time. If null, sms doesn't work, no effect on Push feature.
     * @param registrationID The registrationIds generated by JPush.
     * @return push result.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendIosMessageWithRegistrationID(String title, String msgContent, SMS sms, String... registrationID)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.ios())
                .setAudience(Audience.registrationId(registrationID))
                .setMessage(Message.newBuilder()
                        .setTitle(title)
                        .setMsgContent(msgContent)
                        .build())
                .setSMS(sms)
                .build();
        return _pushClient.sendPush(payload);
    }
    public PushResult sendMessageWithRegistrationID(String title, String msgContent, String... registrationID)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.all())
                .setAudience(Audience.registrationId(registrationID))
                .setMessage(Message.newBuilder()
                        .setTitle(title)
                        .setMsgContent(msgContent)
                        .build())
                .build();
        return _pushClient.sendPush(payload);
    }
    /**
     * Send a message with registrationIds.
     * If it doesn't received within the delay time,JPush will send a SMS to the corresponding users.
     *
     * @param title The message title.
     * @param msgContent The message content.
     * @param sms The SMS content and delay time. If null, sms doesn't work, no effect on Push feature.
     * @param registrationID The registrationIds generated by JPush.
     * @return push result.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public PushResult sendMessageWithRegistrationID(String title, String msgContent, SMS sms, String... registrationID)
            throws APIConnectionException, APIRequestException {
        PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.all())
                .setAudience(Audience.registrationId(registrationID))
                .setMessage(Message.newBuilder()
                        .setTitle(title)
                        .setMsgContent(msgContent)
                        .build())
                .setSMS(sms)
                .build();
        return _pushClient.sendPush(payload);
    }
    /**
     * Delete a push by msgId.
     * @param msgId  The message id
     * @return delete result
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException    if a request exception occurs
     */
    public DefaultResult deletePush(String msgId) throws APIConnectionException, APIRequestException {
        return _pushClient.deletePush(msgId);
    }
    // ----------------------- Device
    public TagAliasResult getDeviceTagAlias(String registrationId)
            throws APIConnectionException, APIRequestException {
        return _deviceClient.getDeviceTagAlias(registrationId);
    }
    public DefaultResult updateDeviceTagAlias(String registrationId, boolean clearAlias, boolean clearTag)
            throws APIConnectionException, APIRequestException {
        return _deviceClient.updateDeviceTagAlias(registrationId, clearAlias, clearTag);
    }
    public DefaultResult updateDeviceTagAlias(String registrationId, String alias,
                Set<String> tagsToAdd, Set<String> tagsToRemove)
            throws APIConnectionException, APIRequestException {
        return _deviceClient.updateDeviceTagAlias(registrationId, alias, tagsToAdd, tagsToRemove);
    }
    public TagListResult getTagList()
            throws APIConnectionException, APIRequestException {
        return _deviceClient.getTagList();
    }
    public BooleanResult isDeviceInTag(String theTag, String registrationID)
            throws APIConnectionException, APIRequestException {
        return _deviceClient.isDeviceInTag(theTag, registrationID);
    }
    public DefaultResult addRemoveDevicesFromTag(String theTag,
                Set<String> toAddUsers, Set<String> toRemoveUsers)
            throws APIConnectionException, APIRequestException {
        return _deviceClient.addRemoveDevicesFromTag(theTag, toAddUsers,
                toRemoveUsers);
    }
    public DefaultResult deleteTag(String theTag, String platform)
            throws APIConnectionException, APIRequestException {
        return _deviceClient.deleteTag(theTag, platform);
    }
    public AliasDeviceListResult getAliasDeviceList(String alias,
            String platform) throws APIConnectionException, APIRequestException {
        return _deviceClient.getAliasDeviceList(alias, platform);
    }
    public DefaultResult deleteAlias(String alias, String platform)
            throws APIConnectionException, APIRequestException {
        return _deviceClient.deleteAlias(alias, platform);
    }
    public DefaultResult removeDevicesFromAlias(String alias, Set<String> toRemoveDevice)
            throws APIConnectionException, APIRequestException {
        return _deviceClient.removeDevicesFromAlias(alias, toRemoveDevice);
    }
    public Map<String, OnlineStatus> getUserOnlineStatus(String... registrationIds)
            throws APIConnectionException, APIRequestException
    {
        return _deviceClient.getUserOnlineStatus(registrationIds);
    }
    public DefaultResult bindMobile(String registrationId, String mobile)
            throws APIConnectionException, APIRequestException
    {
        return _deviceClient.bindMobile(registrationId, mobile);
    }
    // ----------------------- Schedule
    /**
     * Create a single schedule.
     * @param name The schedule name.
     * @param time The push time, format is 'yyyy-MM-dd HH:mm:ss'
     * @param push The push payload.
     * @param masterSecret master secret
     * @param appKey app key
     * @return The created scheduleResult instance.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public ScheduleResult createSingleSchedule(String name, String time, PushPayload push, String masterSecret,
                                               String appKey)
            throws APIConnectionException, APIRequestException {
        TriggerPayload trigger = TriggerPayload.newBuilder()
                .setSingleTime(time)
                .buildSingle();
        SchedulePayload payload = SchedulePayload.newBuilder()
                .setName(name)
                .setEnabled(true)
                .setTrigger(trigger)
                .setPush(push)
                .build();
        return _scheduleClient.createSchedule(payload, masterSecret, appKey);
    }
    /**
     * Create a daily schedule push everyday.
     * @param name The schedule name.
     * @param start The schedule comes into effect date, format 'yyyy-MM-dd HH:mm:ss'.
     * @param end The schedule expiration date, format 'yyyy-MM-dd HH:mm:ss'.
     * @param time The push time, format 'HH:mm:ss'
     * @param push The push payload.
     * @param masterSecret master secret
     * @param appKey app key
     * @return The created scheduleResult instance.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public ScheduleResult createDailySchedule(String name, String start, String end, String time, PushPayload push,
                                              String masterSecret, String appKey)
            throws APIConnectionException, APIRequestException {
        return createPeriodicalSchedule(name, start, end, time, TimeUnit.DAY, 1, null, push,
                masterSecret, appKey);
    }
    /**
     * Create a daily schedule push with a custom frequency.
     * @param name The schedule name.
     * @param start The schedule comes into effect date, format 'yyyy-MM-dd HH:mm:ss'.
     * @param end The schedule expiration date, format 'yyyy-MM-dd HH:mm:ss'.
     * @param time The push time, format 'HH:mm:ss'
     * @param frequency The custom frequency.
     * @param push The push payload.
     * @param masterSecret master secret
     * @param appKey app key
     * @return The created scheduleResult instance.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public ScheduleResult createDailySchedule(String name, String start, String end, String time, int frequency,
                                              PushPayload push, String masterSecret, String appKey)
            throws APIConnectionException, APIRequestException {
        return createPeriodicalSchedule(name, start, end, time, TimeUnit.DAY, frequency, null, push,
                masterSecret, appKey);
    }
    /**
     * Create a weekly schedule push every week at the appointed days.
     * @param name The schedule name.
     * @param start The schedule comes into effect date, format 'yyyy-MM-dd HH:mm:ss'.
     * @param end The schedule expiration date, format 'yyyy-MM-dd HH:mm:ss'.
     * @param time The push time, format 'HH:mm:ss'
     * @param days The appointed days.
     * @param push The push payload.
     * @param masterSecret master secret
     * @param appKey app key
     * @return The created scheduleResult instance.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public ScheduleResult createWeeklySchedule(String name, String start, String end, String time, Week[] days,
                                               PushPayload push, String masterSecret, String appKey)
            throws APIConnectionException, APIRequestException {
        Preconditions.checkArgument(null != days && days.length > 0, "The days must not be empty.");
        String[] points = new String[days.length];
        for(int i = 0 ; i < days.length; i++) {
            points[i] = days[i].name();
        }
        return createPeriodicalSchedule(name, start, end, time, TimeUnit.WEEK, 1, points, push,
                masterSecret, appKey);
    }
    /**
     * Create a weekly schedule push with a custom frequency at the appointed days.
     * @param name The schedule name.
     * @param start The schedule comes into effect date, format 'yyyy-MM-dd HH:mm:ss'.
     * @param end The schedule expiration date, format 'yyyy-MM-dd HH:mm:ss'.
     * @param time The push time, format 'HH:mm:ss'.
     * @param frequency The custom frequency.
     * @param days The appointed days.
     * @param push The push payload.
     * @param masterSecret master secret
     * @param appKey app key
     * @return The created scheduleResult instance.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public ScheduleResult createWeeklySchedule(String name, String start, String end, String time, int frequency,
                                               Week[] days, PushPayload push, String masterSecret, String appKey)
            throws APIConnectionException, APIRequestException {
        Preconditions.checkArgument(null != days && days.length > 0, "The days must not be empty.");
        String[] points = new String[days.length];
        for(int i = 0 ; i < days.length; i++) {
            points[i] = days[i].name();
        }
        return createPeriodicalSchedule(name, start, end, time, TimeUnit.WEEK, frequency, points, push, masterSecret, appKey);
    }
    /**
     * Create a monthly schedule push every month at the appointed days.
     * @param name The schedule name.
     * @param start The schedule comes into effect date, format 'yyyy-MM-dd HH:mm:ss'.
     * @param end The schedule expiration date, format 'yyyy-MM-dd HH:mm:ss'.
     * @param time The push time, format 'HH:mm:ss'.
     * @param points The appointed days.
     * @param push The push payload.
     * @param masterSecret master secret
     * @param appKey app key
     * @return The created scheduleResult instance.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public ScheduleResult createMonthlySchedule(String name, String start, String end, String time, String[] points,
                                                PushPayload push, String masterSecret, String appKey)
            throws APIConnectionException, APIRequestException {
        Preconditions.checkArgument(null != points && points.length > 0, "The points must not be empty.");
        return createPeriodicalSchedule(name, start, end, time, TimeUnit.MONTH, 1, points, push, masterSecret, appKey);
    }
    /**
     * Create a monthly schedule push with a custom frequency at the appointed days.
     * @param name The schedule name.
     * @param start The schedule comes into effect date, format 'yyyy-MM-dd HH:mm:ss'.
     * @param end The schedule expiration date, format 'yyyy-MM-dd HH:mm:ss'.
     * @param time The push time, format 'HH:mm:ss'.
     * @param frequency The custom frequency.
     * @param points The appointed days.
     * @param push The push payload.
     * @param masterSecret master secret
     * @param appKey app key
     * @return The created scheduleResult instance.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public ScheduleResult createMonthlySchedule(String name, String start, String end, String time, int frequency, String[] points,
                                                PushPayload push, String masterSecret, String appKey)
            throws APIConnectionException, APIRequestException {
        Preconditions.checkArgument(null != points && points.length > 0, "The points must not be empty.");
        return createPeriodicalSchedule(name, start, end, time, TimeUnit.MONTH, frequency, points, push, masterSecret, appKey);
    }
    /**
     * Get the schedule information by the schedule id.
     * @param scheduleId The schedule id.
     * @return The schedule information.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public ScheduleResult getSchedule(String scheduleId)
            throws APIConnectionException, APIRequestException {
        return  _scheduleClient.getSchedule(scheduleId);
    }
    /**
     * Get the message id by the schedule id.
     * @param scheduleId The schedule id.
     * @return The message id list.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public ScheduleMsgIdsResult getScheduleMsgIds(String scheduleId)
            throws APIConnectionException, APIRequestException {
        return  _scheduleClient.getScheduleMsgIds(scheduleId);
    }
    /**
     * Get the schedule list size and the first page.
     * @return The schedule list size and the first page.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public ScheduleListResult getScheduleList()
            throws APIConnectionException, APIRequestException {
        return _scheduleClient.getScheduleList(1);
    }
    /**
     * Get the schedule list by the page.
     * @param page The page to search.
     * @return The schedule list of the appointed page.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public ScheduleListResult getScheduleList(int page)
            throws APIConnectionException, APIRequestException {
        return _scheduleClient.getScheduleList(page);
    }
    /**
     * Update the schedule name
     * @param scheduleId The schedule id.
     * @param name The new name.
     * @return The schedule information after updated.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public ScheduleResult updateScheduleName(String scheduleId, String name)
            throws APIConnectionException, APIRequestException {
        SchedulePayload payload = SchedulePayload.newBuilder()
                .setName(name)
                .build();
        return updateSchedule(scheduleId, payload);
    }
    /**
     * Enable the schedule.
     * @param scheduleId The schedule id.
     * @return The schedule information after updated.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public ScheduleResult enableSchedule(String scheduleId)
            throws APIConnectionException, APIRequestException {
        SchedulePayload payload = SchedulePayload.newBuilder()
                .setEnabled(true)
                .build();
        return updateSchedule(scheduleId, payload);
    }
    /**
     * Disable the schedule.
     * @param scheduleId The schedule id.
     * @return The schedule information after updated.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public ScheduleResult disableSchedule(String scheduleId)
            throws APIConnectionException, APIRequestException {
        SchedulePayload payload = SchedulePayload.newBuilder()
                .setEnabled(false)
                .build();
        return updateSchedule(scheduleId, payload);
    }
    /**
     * Update the trigger of the schedule.
     * @param scheduleId The schedule id.
     * @param trigger The new trigger.
     * @return The schedule information after updated.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public ScheduleResult updateScheduleTrigger(String scheduleId, TriggerPayload trigger)
            throws APIConnectionException, APIRequestException {
        SchedulePayload payload = SchedulePayload.newBuilder()
                .setTrigger(trigger)
                .build();
        return updateSchedule(scheduleId, payload);
    }
    /**
     * Update the push content of the schedule.
     * @param scheduleId The schedule id.
     * @param push The new push payload.
     * @return The schedule information after updated.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public ScheduleResult updateSchedulePush(String scheduleId, PushPayload push)
            throws APIConnectionException, APIRequestException {
        SchedulePayload payload = SchedulePayload.newBuilder()
                .setPush(push)
                .build();
        return updateSchedule(scheduleId, payload);
    }
    /**
     * Update a schedule by the id.
     * @param scheduleId The schedule id to update.
     * @param payload The new schedule payload.
     * @return The new schedule information.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public ScheduleResult updateSchedule(String scheduleId, SchedulePayload payload)
            throws APIConnectionException, APIRequestException {
        return _scheduleClient.updateSchedule(scheduleId, payload);
    }
    /**
     * Delete a schedule by id.
     * @param scheduleId The schedule id.
     * @throws APIConnectionException if a remote or network exception occurs.
     * @throws APIRequestException if a request exception occurs.
     */
    public void deleteSchedule(String scheduleId)
            throws APIConnectionException, APIRequestException {
        _scheduleClient.deleteSchedule(scheduleId);
    }
    private ScheduleResult createPeriodicalSchedule(String name, String start, String end, String time,
                                                    TimeUnit timeUnit, int frequency, String[] point, PushPayload push,
                                                    String masterSecret, String appKey)
            throws APIConnectionException, APIRequestException {
        TriggerPayload trigger = TriggerPayload.newBuilder()
                .setPeriodTime(start, end, time)
                .setTimeFrequency(timeUnit, frequency, point )
                .buildPeriodical();
        SchedulePayload payload = SchedulePayload.newBuilder()
                .setName(name)
                .setEnabled(true)
                .setTrigger(trigger)
                .setPush(push)
                .build();
        return _scheduleClient.createSchedule(payload, masterSecret, appKey);
    }
    public void close() {
        _pushClient.close();
    }
}
service-push/src/main/java/cn/jpush/api/JPushConfig.java
New file
@@ -0,0 +1,39 @@
package cn.jpush.api;
import cn.jiguang.common.ClientConfig;
public class JPushConfig {
    private static ClientConfig clientConfig = ClientConfig.getInstance();
    private static JPushConfig instance = new JPushConfig();
    public static final String ADMIN_HOST_NAME = "api.admin.host.name";
    public static final String V1_APP_PATH = "jpush.v1.app.path";
    private JPushConfig() {
        clientConfig.put(ADMIN_HOST_NAME, "https://admin.jpush.cn");
        clientConfig.put(V1_APP_PATH, "/v1/app");
    }
    public static JPushConfig getInstance() {
        return instance;
    }
    public ClientConfig getClientConfig() {
        return clientConfig;
    }
    public JPushConfig setAdminHostName(String hostName) {
        clientConfig.put(ADMIN_HOST_NAME, hostName);
        return this;
    }
    public void put(String key, Object value) {
        clientConfig.put(key, value);
    }
    public Object get(String key) {
        return clientConfig.get(key);
    }
}
service-push/src/main/java/cn/jpush/api/admin/AdminClient.java
New file
@@ -0,0 +1,134 @@
package cn.jpush.api.admin;
import cn.jiguang.common.ServiceHelper;
import cn.jiguang.common.connection.HttpProxy;
import cn.jiguang.common.connection.IHttpClient;
import cn.jiguang.common.connection.NativeHttpClient;
import cn.jiguang.common.resp.APIConnectionException;
import cn.jiguang.common.resp.APIRequestException;
import cn.jiguang.common.resp.DefaultResult;
import cn.jiguang.common.resp.ResponseWrapper;
import cn.jiguang.common.utils.Preconditions;
import cn.jpush.api.JPushConfig;
import cn.jpush.api.file.FileClient;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
/**
 * Admin APIs
 * https://docs.jiguang.cn/jpush/server/push/rest_api_admin_api_v1/
 */
public class AdminClient {
    protected static final Logger LOG = LoggerFactory.getLogger(FileClient.class);
    private IHttpClient mHttpClient;
    private String mBasePath;
    private String mV1AppPath;
    private Gson mGson = new Gson();
    /**
     * Create a Push Client.
     *
     * @param appKey The KEY of one application on JPush.
     * @param masterSecret API access secret of the appKey.
     */
    public AdminClient(String appKey, String masterSecret) {
        this(appKey, masterSecret, null, JPushConfig.getInstance());
    }
    public AdminClient(String appKey, String masterSecret, HttpProxy proxy) {
        this(appKey, masterSecret, proxy, JPushConfig.getInstance());
    }
    public AdminClient(String appKey, String masterSecret, HttpProxy proxy, JPushConfig conf) {
        ServiceHelper.checkBasic(appKey, masterSecret);
        mBasePath = (String) conf.get(JPushConfig.ADMIN_HOST_NAME);
        mV1AppPath = (String) conf.get(JPushConfig.V1_APP_PATH);
        String authCode = ServiceHelper.getBasicAuthorization(appKey, masterSecret);
        this.mHttpClient = new NativeHttpClient(authCode, proxy, conf.getClientConfig());
    }
    public void setHttpClient(IHttpClient client) {
        this.mHttpClient = client;
    }
    /**
     * Create an app under developer account
     * @param appName app name
     * @param packageName android package name
     * @param groupName developer app group name
     * @return {@link CreateAppResult}
     * @throws APIConnectionException connect exception
     * @throws APIRequestException request exception
     */
    public CreateAppResult createApp(String appName, String packageName, String groupName)
            throws APIConnectionException, APIRequestException {
        Preconditions.checkArgument(null != appName, "app name should not be null");
        Preconditions.checkArgument(null != packageName, "package name should not be null");
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("app_name", appName);
        jsonObject.addProperty("android_package", packageName);
        if (null != groupName) {
            jsonObject.addProperty("group_name", groupName);
        }
        ResponseWrapper responseWrapper = mHttpClient.sendPost(mBasePath + mV1AppPath, mGson.toJson(jsonObject));
        return CreateAppResult.fromResponse(responseWrapper, CreateAppResult.class);
    }
    /**
     * Delete app by app key
     * @param appKey app key
     * @return {@link AppResult}
     * @throws APIConnectionException connect exception
     * @throws APIRequestException request exception
     */
    public AppResult deleteApp(String appKey) throws APIConnectionException, APIRequestException {
        ResponseWrapper responseWrapper = mHttpClient.sendDelete(mBasePath + mV1AppPath + "/" + appKey + "/delete");
        return DefaultResult.fromResponse(responseWrapper, AppResult.class);
    }
    /**
     * Upload certificate
     * @param appKey app key
     * @param devCertificatePassword dev certificate password
     * @param proCertificatePassword pro certificate password
     * @param devCertificateFile dev certificate file
     * @param proCertificateFile pro certificate file
     * @throws APIConnectionException connect exception
     * @throws APIRequestException request exception
     */
    public void uploadCertificate(String appKey, String devCertificateFile, String devCertificatePassword,
                                  String proCertificateFile, String proCertificatePassword)
            throws APIConnectionException, APIRequestException {
        Preconditions.checkArgument(devCertificateFile != null || proCertificateFile != null,
                "dev certificate file or pro certificate file should not be null");
        NativeHttpClient client = (NativeHttpClient) mHttpClient;
        String url = mBasePath + mV1AppPath + "/" + appKey + "/certificate";
        Map<String, String> fileMap = new HashMap<>();
        Map<String, String> textMap = new HashMap<>();
        if(devCertificateFile != null) {
            textMap.put("devCertificatePassword", devCertificatePassword);
            fileMap.put("devCertificateFile", devCertificateFile);
        }
        if(proCertificateFile != null) {
            textMap.put("proCertificatePassword", proCertificatePassword);
            fileMap.put("proCertificateFile", proCertificateFile);
        }
        String response = client.formUploadByPost(url, textMap, fileMap, null);
        LOG.info("uploadFile:{}", response);
    }
}
service-push/src/main/java/cn/jpush/api/admin/AppResult.java
New file
@@ -0,0 +1,13 @@
package cn.jpush.api.admin;
import cn.jiguang.common.resp.BaseResult;
import com.google.gson.annotations.Expose;
public class AppResult extends BaseResult {
    @Expose private String success;
    public String getSuccess() {
        return success;
    }
}
service-push/src/main/java/cn/jpush/api/admin/CreateAppResult.java
New file
@@ -0,0 +1,22 @@
package cn.jpush.api.admin;
import cn.jiguang.common.resp.BaseResult;
import com.google.gson.annotations.Expose;
public class CreateAppResult extends BaseResult {
    @Expose private String app_key;
    @Expose private boolean is_new_created;
    @Expose private String android_package;
    public String getApp_key() {
        return app_key;
    }
    public boolean is_new_created() {
        return is_new_created;
    }
    public String getAndroid_package() {
        return android_package;
    }
}
service-push/src/main/java/cn/jpush/api/device/AliasDeviceListResult.java
New file
@@ -0,0 +1,15 @@
package cn.jpush.api.device;
import java.util.ArrayList;
import java.util.List;
import com.google.gson.annotations.Expose;
import cn.jiguang.common.resp.BaseResult;
public class AliasDeviceListResult extends BaseResult {
    @Expose public List<String> registration_ids = new ArrayList<String>();
}
service-push/src/main/java/cn/jpush/api/device/DeviceClient.java
New file
@@ -0,0 +1,296 @@
package cn.jpush.api.device;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.Set;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.reflect.TypeToken;
import cn.jiguang.common.ClientConfig;
import cn.jiguang.common.ServiceHelper;
import cn.jiguang.common.utils.Preconditions;
import cn.jiguang.common.utils.StringUtils;
import cn.jiguang.common.connection.HttpProxy;
import cn.jiguang.common.connection.NativeHttpClient;
import cn.jiguang.common.resp.APIConnectionException;
import cn.jiguang.common.resp.APIRequestException;
import cn.jiguang.common.resp.BaseResult;
import cn.jiguang.common.resp.BooleanResult;
import cn.jiguang.common.resp.DefaultResult;
import cn.jiguang.common.resp.ResponseWrapper;
public class DeviceClient {
    private final NativeHttpClient _httpClient;
    private String hostName;
    private String devicesPath;
    private String tagsPath;
    private String aliasesPath;
    public DeviceClient(String masterSecret, String appKey) {
        this(masterSecret, appKey, null, ClientConfig.getInstance());
    }
    /**
     * This will be removed in the future. Please use ClientConfig{jiguang-common cn.jiguang.common.ClientConfig#setMaxRetryTimes} instead of this constructor.
     * @param masterSecret API access secret of the appKey.
     * @param appKey The KEY of one application on JPush.
     * @param maxRetryTimes The max retry times.
     *
     */
    @Deprecated
    public DeviceClient(String masterSecret, String appKey, int maxRetryTimes) {
        this(masterSecret, appKey, maxRetryTimes, null);
    }
    /**
     * This will be removed in the future. Please use ClientConfig{jiguang-common cn.jiguang.common.ClientConfig#setMaxRetryTimes} instead of this constructor.
     * @param masterSecret API access secret of the appKey.
     * @param appKey The KEY of one application on JPush.
     * @param maxRetryTimes The max retry times.
     * @param proxy The HTTP proxy.
     *
     */
    @Deprecated
    public DeviceClient(String masterSecret, String appKey, int maxRetryTimes, HttpProxy proxy) {
        ClientConfig conf = ClientConfig.getInstance();
        conf.setMaxRetryTimes(maxRetryTimes);
        ServiceHelper.checkBasic(appKey, masterSecret);
        hostName = (String) conf.get(ClientConfig.DEVICE_HOST_NAME);
        devicesPath = (String) conf.get(ClientConfig.DEVICES_PATH);
        tagsPath = (String) conf.get(ClientConfig.TAGS_PATH);
        aliasesPath = (String) conf.get(ClientConfig.ALIASES_PATH);
        String authCode = ServiceHelper.getBasicAuthorization(appKey, masterSecret);
        _httpClient = new NativeHttpClient(authCode, proxy, conf);
    }
    /**
     * Create a Device Client by client configuration.
     *
     * @param masterSecret API access secret of the appKey.
     * @param appKey The KEY of one application on JPush.
     * @param proxy The proxy, if there is no proxy, should be null.
     * @param conf The client configuration. Can use ClientConfig.getInstance() as default.
     */
    public DeviceClient(String masterSecret, String appKey, HttpProxy proxy, ClientConfig conf) {
        ServiceHelper.checkBasic(appKey, masterSecret);
        hostName = (String) conf.get(ClientConfig.DEVICE_HOST_NAME);
        devicesPath = (String) conf.get(ClientConfig.DEVICES_PATH);
        tagsPath = (String) conf.get(ClientConfig.TAGS_PATH);
        aliasesPath = (String) conf.get(ClientConfig.ALIASES_PATH);
        String authCode = ServiceHelper.getBasicAuthorization(appKey, masterSecret);
        _httpClient = new NativeHttpClient(authCode, proxy, conf);
    }
    // -------------- device
    public TagAliasResult getDeviceTagAlias(String registrationId) throws APIConnectionException, APIRequestException {
        String url = hostName + devicesPath + "/" + registrationId;
        ResponseWrapper response = _httpClient.sendGet(url);
        return BaseResult.fromResponse(response, TagAliasResult.class);
    }
    public DefaultResult updateDeviceTagAlias(String registrationId, boolean clearAlias, boolean clearTag) throws APIConnectionException, APIRequestException {
        Preconditions.checkArgument(clearAlias || clearTag, "It is not meaningful to do nothing.");
        String url = hostName + devicesPath + "/" + registrationId;
        JsonObject top = new JsonObject();
        if (clearAlias) {
            top.addProperty("alias", "");
        }
        if (clearTag) {
            top.addProperty("tags", "");
        }
        ResponseWrapper response = _httpClient.sendPost(url, top.toString());
        return DefaultResult.fromResponse(response);
    }
    public DefaultResult updateDeviceTagAlias(String registrationId, String alias,
            Set<String> tagsToAdd, Set<String> tagsToRemove) throws APIConnectionException, APIRequestException {
        String url = hostName + devicesPath + "/" + registrationId;
        JsonObject top = new JsonObject();
        if (null != alias) {
            top.addProperty("alias", alias);
        }
        JsonObject tagObject = new JsonObject();
        JsonArray tagsAdd = ServiceHelper.fromSet(tagsToAdd);
        if (tagsAdd.size() > 0) {
            tagObject.add("add", tagsAdd);
        }
        JsonArray tagsRemove = ServiceHelper.fromSet(tagsToRemove);
        if (tagsRemove.size() > 0) {
            tagObject.add("remove", tagsRemove);
        }
        if (tagObject.entrySet().size() > 0) {
            top.add("tags", tagObject);
        }
        ResponseWrapper response = _httpClient.sendPost(url, top.toString());
        return DefaultResult.fromResponse(response);
    }
    public DefaultResult bindMobile(String registrationId, String mobile)
            throws APIConnectionException, APIRequestException {
        if ( StringUtils.isEmpty(mobile) ) {
            // delete bind while mobile is empty.
            mobile = "";
        } else {
//            Preconditions.checkArgument(StringUtils.isMobileNumber(mobile), "The mobile format is incorrect. " + mobile);
        }
        String url = hostName + devicesPath + "/" + registrationId;
        JsonObject top = new JsonObject();
        top.addProperty("mobile", mobile);
        ResponseWrapper response = _httpClient.sendPost(url, top.toString());
        return DefaultResult.fromResponse(response);
    }
    // ------------- tags
    public TagListResult getTagList() throws APIConnectionException, APIRequestException {
        String url = hostName + tagsPath + "/";
        ResponseWrapper response = _httpClient.sendGet(url);
        return TagListResult.fromResponse(response, TagListResult.class);
    }
    public BooleanResult isDeviceInTag(String theTag, String registrationID) throws APIConnectionException, APIRequestException {
        String url = hostName + tagsPath + "/" + theTag + "/registration_ids/" + registrationID;
        ResponseWrapper response = _httpClient.sendGet(url);
        return BaseResult.fromResponse(response, BooleanResult.class);
    }
    public DefaultResult addRemoveDevicesFromTag(String theTag, Set<String> toAddUsers, Set<String> toRemoveUsers) throws APIConnectionException, APIRequestException {
        String url = hostName + tagsPath + "/" + theTag;
        JsonObject top = new JsonObject();
        JsonObject registrationIds = new JsonObject();
        if (null != toAddUsers && toAddUsers.size() > 0) {
            JsonArray array = new JsonArray();
            for (String user : toAddUsers) {
                array.add(new JsonPrimitive(user));
            }
            registrationIds.add("add", array);
        }
        if (null != toRemoveUsers && toRemoveUsers.size() > 0) {
            JsonArray array = new JsonArray();
            for (String user : toRemoveUsers) {
                array.add(new JsonPrimitive(user));
            }
            registrationIds.add("remove", array);
        }
        top.add("registration_ids", registrationIds);
        ResponseWrapper response = _httpClient.sendPost(url, top.toString());
        return DefaultResult.fromResponse(response);
    }
    public DefaultResult deleteTag(String theTag, String platform) throws APIConnectionException, APIRequestException {
        String url = hostName + tagsPath + "/" + theTag;
        if (null != platform) {
            url += "?platform=" + platform;
        }
        ResponseWrapper response = _httpClient.sendDelete(url);
        return DefaultResult.fromResponse(response);
    }
    // ------------- alias
    public AliasDeviceListResult getAliasDeviceList(String alias, String platform) throws APIConnectionException, APIRequestException {
        String url = hostName + aliasesPath + "/" + alias;
        if (null != platform) {
            url += "?platform=" + platform;
        }
        ResponseWrapper response = _httpClient.sendGet(url);
        return BaseResult.fromResponse(response, AliasDeviceListResult.class);
    }
    public DefaultResult deleteAlias(String alias, String platform) throws APIConnectionException, APIRequestException {
        String url = hostName + aliasesPath + "/" + alias;
        if (null != platform) {
            url += "?platform=" + platform;
        }
        ResponseWrapper response = _httpClient.sendDelete(url);
        return DefaultResult.fromResponse(response);
    }
    public DefaultResult removeDevicesFromAlias(String alias, Set<String> toRemoveDevice) throws APIConnectionException, APIRequestException {
        String url = hostName + aliasesPath + "/" + alias;
        JsonObject top = new JsonObject();
        JsonObject registrationIds = new JsonObject();
        if (null != toRemoveDevice && toRemoveDevice.size() > 0) {
            JsonArray array = new JsonArray();
            for (String device : toRemoveDevice) {
                array.add(new JsonPrimitive(device));
            }
            registrationIds.add("remove", array);
        }
        top.add("registration_ids", registrationIds);
        ResponseWrapper response = _httpClient.sendPost(url, top.toString());
        return DefaultResult.fromResponse(response);
    }
    // -------------- devices status
    public Map<String, OnlineStatus> getUserOnlineStatus(String... registrationIds)
            throws APIConnectionException, APIRequestException
    {
        Preconditions.checkArgument((null != registrationIds ),
                "The registration id list should not be null.");
        Preconditions.checkArgument(registrationIds.length > 0 && registrationIds.length <= 1000,
                "The length of registration id list should between 1 and 1000.");
        String url = hostName + devicesPath + "/status";
        JsonObject json = new JsonObject();
        JsonArray array = new JsonArray();
        for(int i = 0; i < registrationIds.length; i++) {
            array.add(new JsonPrimitive(registrationIds[i]));
        }
        json.add("registration_ids", array);
        Type type = new TypeToken<Map<String, OnlineStatus>>(){}.getType();
        ResponseWrapper response = _httpClient.sendPost(url, json.toString());
        Map<String, OnlineStatus> map = new Gson().fromJson(response.responseContent, type);
        return map;
    }
}
service-push/src/main/java/cn/jpush/api/device/OnlineStatus.java
New file
@@ -0,0 +1,35 @@
package cn.jpush.api.device;
import java.io.Serializable;
public class OnlineStatus implements Serializable {
    private static final long serialVersionUID = -5436655826293828109L;
    private Boolean online;
    private String last_online_time;
    public Boolean getOnline() {
        return online;
    }
    public void setOnline(Boolean online) {
        this.online = online;
    }
    public String getLast_online_time() {
        return last_online_time;
    }
    public void setLast_online_time(String last_online_time) {
        this.last_online_time = last_online_time;
    }
    @Override
    public String toString() {
        if(null == last_online_time) {
            return "status: " + online;
        }
        return "status: " + online + " , last_online_time: " + last_online_time;
    }
}
service-push/src/main/java/cn/jpush/api/device/TagAliasResult.java
New file
@@ -0,0 +1,16 @@
package cn.jpush.api.device;
import java.util.List;
import com.google.gson.annotations.Expose;
import cn.jiguang.common.resp.BaseResult;
public class TagAliasResult extends BaseResult {
    private static final long serialVersionUID = -4765083329495728276L;
    @Expose public List<String> tags;
    @Expose public String alias;
}
service-push/src/main/java/cn/jpush/api/device/TagListResult.java
New file
@@ -0,0 +1,16 @@
package cn.jpush.api.device;
import java.util.ArrayList;
import java.util.List;
import com.google.gson.annotations.Expose;
import cn.jiguang.common.resp.BaseResult;
public class TagListResult extends BaseResult {
    private static final long serialVersionUID = -5395153728332839175L;
    @Expose public List<String> tags = new ArrayList<String>();
}
service-push/src/main/java/cn/jpush/api/device/package-info.java
New file
@@ -0,0 +1,6 @@
/**
 * Devices API features.
 *
 * Url: https://device.jpush.cn/v3/devices
 */
package cn.jpush.api.device;
service-push/src/main/java/cn/jpush/api/file/FileClient.java
New file
@@ -0,0 +1,91 @@
package cn.jpush.api.file;
import cn.jiguang.common.ClientConfig;
import cn.jiguang.common.ServiceHelper;
import cn.jiguang.common.connection.HttpProxy;
import cn.jiguang.common.connection.IHttpClient;
import cn.jiguang.common.connection.NativeHttpClient;
import cn.jiguang.common.resp.APIConnectionException;
import cn.jiguang.common.resp.APIRequestException;
import cn.jiguang.common.resp.ResponseWrapper;
import cn.jiguang.common.utils.Preconditions;
import cn.jiguang.common.utils.StringUtils;
import cn.jpush.api.file.model.FileModel;
import cn.jpush.api.file.model.FileModelPage;
import cn.jpush.api.file.model.FileType;
import cn.jpush.api.file.model.FileUploadResult;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
/**
 * @author daixuan
 * @version 2020/2/23 19:38
 */
public class FileClient {
    protected static final Logger LOG = LoggerFactory.getLogger(FileClient.class);
    private IHttpClient _httpClient;
    private String _baseUrl;
    private String _filesPath;
    private Gson _gson = new Gson();
    public FileClient(String masterSecret, String appKey) {
        this(masterSecret, appKey, null, ClientConfig.getInstance());
    }
    public FileClient(String masterSecret, String appKey, HttpProxy proxy, ClientConfig conf) {
        _baseUrl = (String) conf.get(ClientConfig.PUSH_HOST_NAME);
        _filesPath = (String) conf.get(ClientConfig.V3_FILES_PATH);
        String authCode = ServiceHelper.getBasicAuthorization(appKey, masterSecret);
        this._httpClient = new NativeHttpClient(authCode, proxy, conf);
    }
    public FileUploadResult uploadFile(FileType type, String filename)
            throws APIConnectionException, APIRequestException {
        Preconditions.checkArgument(type != null, "type should not be null");
        Preconditions.checkArgument(StringUtils.isNotEmpty(filename), "filename should not be null");
        NativeHttpClient client = (NativeHttpClient) _httpClient;
        String typeStr = type.value();
        String url = _baseUrl + _filesPath + "/" + typeStr;
        Map<String, String> fileMap = new HashMap<>();
        fileMap.put("filename", filename);
        String response = client.formUploadByPost(url, null, fileMap, null);
        LOG.info("uploadFile:{}", response);
        return _gson.fromJson(response,
                new TypeToken<FileUploadResult>() {
                }.getType());
    }
    public FileModelPage queryEffectFiles() throws APIConnectionException, APIRequestException {
        String url = _baseUrl + _filesPath + "/";
        ResponseWrapper response = _httpClient.sendGet(url);
        LOG.info("queryEffFiles:{}", response);
        return _gson.fromJson(response.responseContent,
                new TypeToken<FileModelPage>() {
                }.getType());
    }
    public FileModel queryFile(String fileId) throws APIConnectionException, APIRequestException {
        Preconditions.checkArgument(StringUtils.isNotEmpty(fileId), "fileId should not be null");
        String url = _baseUrl + _filesPath + "/" + fileId;
        ResponseWrapper response = _httpClient.sendGet(url);
        LOG.info("queryFile:{}", response);
        return _gson.fromJson(response.responseContent,
                new TypeToken<FileModel>() {
                }.getType());
    }
    public ResponseWrapper deleteFile(String fileId) throws APIConnectionException, APIRequestException {
        Preconditions.checkArgument(StringUtils.isNotEmpty(fileId), "fileId should not be null");
        String url = _baseUrl + _filesPath + "/" + fileId;
        ResponseWrapper response = _httpClient.sendDelete(url);
        LOG.info("deleteFile:{}", response);
        return response;
    }
}
service-push/src/main/java/cn/jpush/api/file/model/FileModel.java
New file
@@ -0,0 +1,21 @@
package cn.jpush.api.file.model;
import com.google.gson.annotations.Expose;
import lombok.Data;
import java.util.Date;
/**
 * @author daixuan
 * @version 2020/2/23 20:18
 */
@Data
public class FileModel {
    @Expose
    private String file_id;
    @Expose
    private String type;
    @Expose
    private Date create_time;
}
service-push/src/main/java/cn/jpush/api/file/model/FileModelPage.java
New file
@@ -0,0 +1,21 @@
package cn.jpush.api.file.model;
import com.google.gson.annotations.Expose;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
 * @author daixuan
 * @version 2020/2/23 20:18
 */
@Data
public class FileModelPage {
    @Expose
    private int total_count;
    @Expose
    private List<FileModel> files;
}
service-push/src/main/java/cn/jpush/api/file/model/FileType.java
New file
@@ -0,0 +1,21 @@
package cn.jpush.api.file.model;
/**
 * @author daixuan
 * @version 2020/2/23 20:14
 */
public enum FileType {
    ALIAS("alias"),
    REGISTRATION_ID("registration_id");
    private final String value;
    private FileType(final String value) {
        this.value = value;
    }
    public String value() {
        return this.value;
    }
}
service-push/src/main/java/cn/jpush/api/file/model/FileUploadResult.java
New file
@@ -0,0 +1,16 @@
package cn.jpush.api.file.model;
import lombok.Data;
@Data
public class FileUploadResult {
    private String file_id;
    private Error error;
    @Data
    public static class Error {
        private String message;
        private int code;
    }
}
service-push/src/main/java/cn/jpush/api/image/ImageClient.java
New file
@@ -0,0 +1,178 @@
package cn.jpush.api.image;
import cn.jiguang.common.ClientConfig;
import cn.jiguang.common.ServiceHelper;
import cn.jiguang.common.connection.HttpProxy;
import cn.jiguang.common.connection.IHttpClient;
import cn.jiguang.common.connection.NativeHttpClient;
import cn.jiguang.common.resp.APIConnectionException;
import cn.jiguang.common.resp.APIRequestException;
import cn.jiguang.common.resp.ResponseWrapper;
import cn.jiguang.common.utils.Preconditions;
import cn.jiguang.common.utils.StringUtils;
import cn.jpush.api.image.model.ImageFilePayload;
import cn.jpush.api.image.model.ImageSource;
import cn.jpush.api.image.model.ImageUploadResult;
import cn.jpush.api.image.model.ImageUrlPayload;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonSyntaxException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
/**
 * Provide the ability to upload images to the Jiguang server. Only images in JPG, JPEG and PNG format are supported.
 *
 * @author fuyx
 * @version 2020-12-14
 */
public class ImageClient {
    protected static final Logger LOG = LoggerFactory.getLogger(ImageClient.class);
    private IHttpClient _httpClient;
    private String _baseUrl;
    private String _imagesPath;
    private Gson _gson = new Gson();
    public ImageClient(String masterSecret, String appKey) {
        this(masterSecret, appKey, null, ClientConfig.getInstance());
    }
    public ImageClient(String masterSecret, String appKey, HttpProxy proxy, ClientConfig conf) {
        _baseUrl = (String) conf.get(ClientConfig.PUSH_HOST_NAME);
        _imagesPath = (String) conf.get(ClientConfig.V3_IMAGES_PATH);
        String authCode = ServiceHelper.getBasicAuthorization(appKey, masterSecret);
        this._httpClient = new NativeHttpClient(authCode, proxy, conf);
    }
    /**
     * Upload image by url. Require at least one non-null url.
     * @param imageUrlPayload image url payload
     * @return {@link ImageUploadResult}
     * @throws APIConnectionException connect exception
     * @throws APIRequestException request exception
     */
    public ImageUploadResult uploadImage(ImageUrlPayload imageUrlPayload)
            throws APIConnectionException, APIRequestException {
        Preconditions.checkArgument(imageUrlPayload.getImageType() != null, "Image type should not be null");
        checkImageUrlPayload(imageUrlPayload);
        NativeHttpClient client = (NativeHttpClient) _httpClient;
        String url = _baseUrl + _imagesPath + "/" + ImageSource.URL.value();
        JsonElement jsonElement = imageUrlPayload.toJSON();
        String content = _gson.toJson(jsonElement);
        ResponseWrapper responseWrapper = client.sendPost(url, content);
        if (responseWrapper.responseCode != 200) {
            LOG.error("upload image failed: {}", responseWrapper);
        }
        ImageUploadResult imageUploadResult = _gson.fromJson(responseWrapper.responseContent, ImageUploadResult.class);
        LOG.info("upload image result:{}", imageUploadResult);
        return imageUploadResult;
    }
    /**
     * Upload image by file. Require at least 1 non-null fileName. Currently only support Xiaomi and OPPO
     * @param imageFilePayload image file payload
     * @return {@link ImageUploadResult}
     */
    public ImageUploadResult uploadImage(ImageFilePayload imageFilePayload) {
        Preconditions.checkArgument(imageFilePayload.getImageType() != null, "Image type should not be null");
        checkImageFilePayload(imageFilePayload);
        NativeHttpClient client = (NativeHttpClient) _httpClient;
        String url = _baseUrl + _imagesPath + "/" + ImageSource.FILE.value();
        Map<String, String> textMap = new HashMap<>();
        textMap.put("image_type", String.valueOf(imageFilePayload.getImageType().value()));
        Map<String, String> fileMap = imageFilePayload.toFileMap();
        LOG.debug("upload fileMap: {}", fileMap);
        String response = client.formUploadByPost(url, textMap, fileMap, null);
        LOG.debug("upload image result: {}", response);
        ImageUploadResult imageUploadResult;
        try {
            imageUploadResult = _gson.fromJson(response, ImageUploadResult.class);
        } catch (JsonSyntaxException e) {
            LOG.error("could not parse response: {}", response);
            throw new IllegalStateException("could not parse response", e);
        }
        LOG.info("upload image result:{}", imageUploadResult);
        return imageUploadResult;
    }
    /**
     * Modify image by url. Require at least one non-null url.
     * @param mediaId media id
     * @param imageUrlPayload image url payload
     * @return {@link ImageUploadResult}
     * @throws APIConnectionException connection exception
     * @throws APIRequestException request exception
     */
    public ImageUploadResult modifyImage(String mediaId, ImageUrlPayload imageUrlPayload)
            throws APIConnectionException, APIRequestException {
        Preconditions.checkArgument(StringUtils.isNotEmpty(mediaId), "mediaId should not be empty");
        checkImageUrlPayload(imageUrlPayload);
        NativeHttpClient client = (NativeHttpClient) _httpClient;
        String url = _baseUrl + _imagesPath + "/" + ImageSource.URL.value() + "/" + mediaId;
        JsonElement jsonElement = imageUrlPayload.toJSON();
        String content = _gson.toJson(jsonElement);
        ResponseWrapper responseWrapper = client.sendPut(url, content);
        if (responseWrapper.responseCode != 200) {
            LOG.error("upload image failed: {}", responseWrapper);
        }
        ImageUploadResult imageUploadResult = _gson.fromJson(responseWrapper.responseContent, ImageUploadResult.class);
        LOG.info("upload image result:{}", imageUploadResult);
        return imageUploadResult;
    }
    /**
     * Modify image by file. Require at least 1 non-null fileName. Currently only support Xiaomi and OPPO
     * @param mediaId media id
     * @param imageFilePayload image file payload
     * @return {@link ImageUploadResult}
     */
    public ImageUploadResult modifyImage(String mediaId, ImageFilePayload imageFilePayload) {
        Preconditions.checkArgument(StringUtils.isNotEmpty(mediaId), "mediaId should not be empty");
        checkImageFilePayload(imageFilePayload);
        NativeHttpClient client = (NativeHttpClient) _httpClient;
        String url = _baseUrl + _imagesPath + "/" + ImageSource.FILE.value() + "/" + mediaId;
        Map<String, String> fileMap = imageFilePayload.toFileMap();
        LOG.debug("upload image fileMap: {}", fileMap);
        String response = client.formUploadByPut(url, null, fileMap, null);
        LOG.debug("upload image result: {}", response);
        ImageUploadResult imageUploadResult;
        try {
            imageUploadResult = _gson.fromJson(response, ImageUploadResult.class);
        } catch (JsonSyntaxException e) {
            LOG.error("could not parse response: {}", response);
            throw new IllegalStateException("could not parse response", e);
        }
        LOG.info("upload image result:{}", imageUploadResult);
        return imageUploadResult;
    }
    private void checkImageUrlPayload(ImageUrlPayload imageUrlPayload) {
        boolean anyUrlNotEmpty = StringUtils.isNotEmpty(imageUrlPayload.getImageUrl())
                || StringUtils.isNotEmpty(imageUrlPayload.getFcmImageUrl())
                || StringUtils.isNotEmpty(imageUrlPayload.getHuaweiImageUrl())
                || StringUtils.isNotEmpty(imageUrlPayload.getOppoImageUrl())
                || StringUtils.isNotEmpty(imageUrlPayload.getXiaomiImageUrl())
                || StringUtils.isNotEmpty(imageUrlPayload.getJiguangImageUrl()) ;
        Preconditions.checkArgument(anyUrlNotEmpty, "Require at least 1 non-empty url");
    }
    private void checkImageFilePayload(ImageFilePayload imageFilePayload) {
        boolean anyFileNotEmpty = StringUtils.isNotEmpty(imageFilePayload.getOppoFileName() )
                || StringUtils.isNotEmpty(imageFilePayload.getXiaomiFileName() );
        Preconditions.checkArgument(anyFileNotEmpty, "Require at least 1 non-empty fileName. Currently only support Xiaomi and OPPO");
    }
}
service-push/src/main/java/cn/jpush/api/image/model/ImageFilePayload.java
New file
@@ -0,0 +1,105 @@
package cn.jpush.api.image.model;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
@Data
public class ImageFilePayload {
    private static final String IMAGE_TYPE = "image_type";
    private static final String OPPO_IMAGE_FILE = "oppo_file";
    private static final String XIAOMI_IMAGE_FILE = "xiaomi_file";
    private static final String HUAWEI_IMAGE_FILE = "huawei_file";
    private static final String FCM_IMAGE_FILE = "fcm_file";
    private static final String JIGUANG_IMAGE_FILE = "jiguang_file";
    private ImageType imageType;
    private String oppoFileName;
    private String xiaomiFileName;
    private String huaweiFileName;
    private String fcmFileName;
    private String jiguangFileName;
    public Map<String, String> toFileMap() {
        HashMap<String, String> fileMap = new HashMap<>();
        if (null != oppoFileName) {
            fileMap.put(OPPO_IMAGE_FILE, oppoFileName);
        }
        if (null != xiaomiFileName) {
            fileMap.put(XIAOMI_IMAGE_FILE, xiaomiFileName);
        }
        if (null != huaweiFileName) {
            fileMap.put(HUAWEI_IMAGE_FILE, huaweiFileName);
        }
        if (null != fcmFileName) {
            fileMap.put(FCM_IMAGE_FILE, fcmFileName);
        }
        if (null != jiguangFileName) {
            fileMap.put(JIGUANG_IMAGE_FILE, jiguangFileName);
        }
        return fileMap;
    }
    public static Builder newBuilder() {
        return new Builder();
    }
    public static class Builder {
        private ImageType imageType;
        private String oppoFileName;
        private String xiaomiFileName;
        private String huaweiFileName;
        private String fcmFileName;
        private String jiguangFileName;
        private Builder() {
        }
        public static Builder builder() {
            return new Builder();
        }
        public Builder setImageType(ImageType imageType) {
            this.imageType = imageType;
            return this;
        }
        public Builder setOppoFileName(String oppoFileName) {
            this.oppoFileName = oppoFileName;
            return this;
        }
        public Builder setXiaomiFileName(String xiaomiFileName) {
            this.xiaomiFileName = xiaomiFileName;
            return this;
        }
        public Builder setHuaweiFileName(String huaweiFileName) {
            this.huaweiFileName = huaweiFileName;
            return this;
        }
        public Builder setFcmFileName(String fcmFileName) {
            this.fcmFileName = fcmFileName;
            return this;
        }
        public Builder setJiguangFileName(String jiguangFileName) {
            this.jiguangFileName = jiguangFileName;
            return this;
        }
        public ImageFilePayload build() {
            ImageFilePayload imageFilePayload = new ImageFilePayload();
            imageFilePayload.setImageType(imageType);
            imageFilePayload.setOppoFileName(oppoFileName);
            imageFilePayload.setXiaomiFileName(xiaomiFileName);
            imageFilePayload.setHuaweiFileName(huaweiFileName);
            imageFilePayload.setFcmFileName(fcmFileName);
            imageFilePayload.setJiguangFileName(jiguangFileName);
            return imageFilePayload;
        }
    }
}
service-push/src/main/java/cn/jpush/api/image/model/ImageSource.java
New file
@@ -0,0 +1,27 @@
package cn.jpush.api.image.model;
/**
 * @author fuyx
 * @version  2020-12-14
 */
public enum ImageSource {
    /**
     * Network Resource
     */
    URL("byurls"),
    /**
     * File Resource
     */
    FILE("byfiles");
    private final String value;
    ImageSource(final String value) {
        this.value = value;
    }
    public String value() {
        return this.value;
    }
}
service-push/src/main/java/cn/jpush/api/image/model/ImageType.java
New file
@@ -0,0 +1,27 @@
package cn.jpush.api.image.model;
import com.google.gson.annotations.SerializedName;
/**
 * @author fuyx
 * @version 2020-12-14
 */
public enum ImageType {
    @SerializedName("1")
    BIG_PICTURE(1),
    @SerializedName("2")
    LARGE_ICON(2),
    @SerializedName("3")
    SMALL_ICON(3);
    private final int value;
    ImageType(final int value) {
        this.value = value;
    }
    public int value() {
        return this.value;
    }
}
service-push/src/main/java/cn/jpush/api/image/model/ImageUploadResult.java
New file
@@ -0,0 +1,31 @@
package cn.jpush.api.image.model;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
@Data
public class ImageUploadResult {
    @SerializedName("media_id")
    private String mediaId;
    @SerializedName("oppo_image_url")
    private String oppoImageUrl;
    @SerializedName("xiaomi_image_url")
    private String xiaomiImageUrl;
    @SerializedName("huawei_image_url")
    private String huaweiImageUrl;
    @SerializedName("fcm_image_url")
    private String fcmImageUrl;
    @SerializedName("jiguang_image_url")
    private String jiguangImageUrl;
    @SerializedName("error")
    private Error error;
    @Data
    public static class Error {
        @SerializedName("message")
        private String message;
        @SerializedName("code")
        private int code;
    }
}
service-push/src/main/java/cn/jpush/api/image/model/ImageUrlPayload.java
New file
@@ -0,0 +1,119 @@
package cn.jpush.api.image.model;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import lombok.Data;
@Data
public class ImageUrlPayload {
    private static final String IMAGE_TYPE = "image_type";
    private static final String IMAGE_URL = "image_url";
    private static final String OPPO_IMAGE_URL = "oppo_image_url";
    private static final String XIAOMI_IMAGE_URL = "xiaomi_image_url";
    private static final String HUAWEI_IMAGE_URL = "huawei_image_url";
    private static final String FCM_IMAGE_URL = "fcm_image_url";
    private static final String JIGUANG_IMAGE_URL = "jiguang_image_url";
    private ImageType imageType;
    private String imageUrl;
    private String oppoImageUrl;
    private String xiaomiImageUrl;
    private String huaweiImageUrl;
    private String fcmImageUrl;
    private String jiguangImageUrl;
    public JsonElement toJSON() {
        JsonObject json = new JsonObject();
        if (null != imageType) {
            json.addProperty(IMAGE_TYPE, imageType.value());
        }
        if (null != imageUrl) {
            json.addProperty(IMAGE_URL, imageUrl);
        }
        if (null != oppoImageUrl) {
            json.addProperty(OPPO_IMAGE_URL, oppoImageUrl);
        }
        if (null != xiaomiImageUrl) {
            json.addProperty(XIAOMI_IMAGE_URL, xiaomiImageUrl);
        }
        if (null != huaweiImageUrl) {
            json.addProperty(HUAWEI_IMAGE_URL, huaweiImageUrl);
        }
        if (null != fcmImageUrl) {
            json.addProperty(FCM_IMAGE_URL, fcmImageUrl);
        }
        if (null != jiguangImageUrl) {
            json.addProperty(JIGUANG_IMAGE_URL, jiguangImageUrl);
        }
        return json;
    }
    public static Builder newBuilder() {
        return new Builder();
    }
    public static class Builder {
        private ImageType imageType;
        private String imageUrl;
        private String oppoImageUrl;
        private String xiaomiImageUrl;
        private String huaweiImageUrl;
        private String fcmImageUrl;
        private String jiguangImageUrl;
        private Builder() {
        }
        public static Builder builder() {
            return new Builder();
        }
        public Builder setImageType(ImageType imageType) {
            this.imageType = imageType;
            return this;
        }
        public Builder setImageUrl(String imageUrl) {
            this.imageUrl = imageUrl;
            return this;
        }
        public Builder setOppoImageUrl(String oppoImageUrl) {
            this.oppoImageUrl = oppoImageUrl;
            return this;
        }
        public Builder setXiaomiImageUrl(String xiaomiImageUrl) {
            this.xiaomiImageUrl = xiaomiImageUrl;
            return this;
        }
        public Builder setHuaweiImageUrl(String huaweiImageUrl) {
            this.huaweiImageUrl = huaweiImageUrl;
            return this;
        }
        public Builder setFcmImageUrl(String fcmImageUrl) {
            this.fcmImageUrl = fcmImageUrl;
            return this;
        }
        public Builder setJiguangImageUrl(String jiguangImageUrl) {
            this.jiguangImageUrl = jiguangImageUrl;
            return this;
        }
        public ImageUrlPayload build() {
            ImageUrlPayload imageUrlPayload = new ImageUrlPayload();
            imageUrlPayload.setImageType(imageType);
            imageUrlPayload.setImageUrl(imageUrl);
            imageUrlPayload.setOppoImageUrl(oppoImageUrl);
            imageUrlPayload.setXiaomiImageUrl(xiaomiImageUrl);
            imageUrlPayload.setHuaweiImageUrl(huaweiImageUrl);
            imageUrlPayload.setFcmImageUrl(fcmImageUrl);
            imageUrlPayload.setJiguangImageUrl(jiguangImageUrl);
            return imageUrlPayload;
        }
    }
}
service-push/src/main/java/cn/jpush/api/push/CIDResult.java
New file
@@ -0,0 +1,12 @@
package cn.jpush.api.push;
import cn.jiguang.common.resp.BaseResult;
import com.google.gson.annotations.Expose;
import java.util.ArrayList;
import java.util.List;
public class CIDResult extends BaseResult {
    @Expose public List<String> cidlist = new ArrayList<String>();
}
service-push/src/main/java/cn/jpush/api/push/GroupPushClient.java
New file
@@ -0,0 +1,87 @@
package cn.jpush.api.push;
import cn.jiguang.common.ClientConfig;
import cn.jiguang.common.ServiceHelper;
import cn.jiguang.common.connection.HttpProxy;
import cn.jiguang.common.connection.IHttpClient;
import cn.jiguang.common.connection.NativeHttpClient;
import cn.jiguang.common.resp.APIConnectionException;
import cn.jiguang.common.resp.APIRequestException;
import cn.jiguang.common.resp.ResponseWrapper;
import cn.jiguang.common.utils.Base64;
import cn.jiguang.common.utils.Preconditions;
import cn.jiguang.common.utils.sm2.SM2Util;
import cn.jpush.api.push.model.EncryptKeys;
import cn.jpush.api.push.model.EncryptPushPayload;
import cn.jpush.api.push.model.PushPayload;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import java.util.Map;
/**
 * Created by caiyaoguan on 2017/7/4.
 */
public class GroupPushClient {
    private IHttpClient _httpClient;
    private String _baseUrl;
    private String _groupPushPath;
    private String _encryptType;
    private Gson _gson = new Gson();
    private JsonParser _jsonParser = new JsonParser();
    public GroupPushClient(String groupMasterSecret, String groupKey) {
        this(groupMasterSecret, groupKey, null, ClientConfig.getInstance());
    }
    public GroupPushClient(String groupMasterSecret, String groupKey, HttpProxy proxy, ClientConfig conf) {
        ServiceHelper.checkBasic(groupKey, groupMasterSecret);
        this._baseUrl = (String) conf.get(ClientConfig.PUSH_HOST_NAME);
        this._groupPushPath = (String) conf.get(ClientConfig.GROUP_PUSH_PATH);
        this._encryptType = (String) conf.get(ClientConfig.ENCRYPT_TYPE);
        String authCode = ServiceHelper.getBasicAuthorization("group-" + groupKey, groupMasterSecret);
        this._httpClient = new NativeHttpClient(authCode, proxy, conf);
    }
    public GroupPushResult sendGroupPush(PushPayload pushPayload) throws APIConnectionException, APIRequestException {
        Preconditions.checkArgument(! (null == pushPayload), "pushPayload should not be null");
        ResponseWrapper response = _httpClient.sendPost(_baseUrl + _groupPushPath, getEncryptData(pushPayload));
        // 2020-8-6 兼容分组推送新返回的group_msgid结构
        JsonObject responseJson = _jsonParser.parse(response.responseContent).getAsJsonObject();
        String groupMsgIdKey = "group_msgid";
        JsonElement _groupMsgId = responseJson.get(groupMsgIdKey);
        String groupMsgId = null != _groupMsgId ? _groupMsgId.getAsString() : "";
        responseJson.remove(groupMsgIdKey);
        Map<String, PushResult> result = _gson.fromJson(responseJson, new TypeToken<Map<String, PushResult>>(){}.getType());
        return new GroupPushResult(groupMsgId, result);
    }
    /**
     * 获取加密的payload数据
     * @param pushPayload
     * @return
     */
    private String getEncryptData(PushPayload pushPayload) {
        if (_encryptType.isEmpty()) {
            return pushPayload.toString();
        }
        if (EncryptKeys.ENCRYPT_SMS2_TYPE.equals(_encryptType)) {
            EncryptPushPayload encryptPushPayload = new EncryptPushPayload();
            try {
                encryptPushPayload.setPayload(String.valueOf(Base64.encode(SM2Util.encrypt(pushPayload.toString(), EncryptKeys.DEFAULT_SM2_ENCRYPT_KEY))));
            } catch (Exception e) {
                throw new RuntimeException("encrypt word exception", e);
            }
            encryptPushPayload.setAudience(pushPayload.getAudience());
            return encryptPushPayload.toString();
        }
        // 不支持的加密默认不加密
        return pushPayload.toString();
    }
}
service-push/src/main/java/cn/jpush/api/push/GroupPushResult.java
New file
@@ -0,0 +1,38 @@
package cn.jpush.api.push;
import java.util.Map;
/**
 * @author tp
 * @since 2020/8/6
 */
public class GroupPushResult {
    private Map<String, PushResult> appResultMap;
    private String groupMsgId;
    public GroupPushResult() {
    }
    public GroupPushResult(String groupMsgId, Map<String, PushResult> appResultMap) {
        this.groupMsgId = groupMsgId;
        this.appResultMap = appResultMap;
    }
    public Map<String, PushResult> getAppResultMap() {
        return appResultMap;
    }
    public void setAppResultMap(Map<String, PushResult> appResultMap) {
        this.appResultMap = appResultMap;
    }
    public String getGroupMsgId() {
        return groupMsgId;
    }
    public void setGroupMsgId(String groupMsgId) {
        this.groupMsgId = groupMsgId;
    }
}
service-push/src/main/java/cn/jpush/api/push/PushClient.java
New file
@@ -0,0 +1,380 @@
package cn.jpush.api.push;
import cn.jiguang.common.ClientConfig;
import cn.jiguang.common.ServiceHelper;
import cn.jiguang.common.connection.*;
import cn.jiguang.common.resp.*;
import cn.jiguang.common.utils.Base64;
import cn.jiguang.common.utils.Preconditions;
import cn.jiguang.common.utils.StringUtils;
import cn.jiguang.common.utils.sm2.SM2Util;
import cn.jpush.api.push.model.*;
import cn.jpush.api.push.model.audience.Audience;
import com.google.gson.*;
import java.util.List;
/**
 * Entrance for sending Push.
 * <p>
 * For the following parameters, you can set them by instance creation.
 * This action will override setting in PushPayload Optional.
 * * apnsProduction If not present, the default is true.
 * * timeToLive If not present, the default is 86400(s) (one day).
 * <p>
 * Can be used directly.
 */
public class PushClient {
    private IHttpClient _httpClient;
    private String _baseUrl;
    private String _pushPath;
    private String _pushValidatePath;
    private String batchRegidPushPath;
    private String batchAliasPushPath;
    private String _filePushPath;
    private JsonParser _jsonParser = new JsonParser();
    // If not present, true by default.
    private int _apnsProduction;
    // If not present, the default value is 86400(s) (one day)
    private long _timeToLive;
    // encrypt type, the default value is empty
    private String _encryptType;
    public PushClient() {}
    /**
     * Create a Push Client.
     *
     * @param masterSecret API access secret of the appKey.
     * @param appKey       The KEY of one application on JPush.
     */
    public PushClient(String masterSecret, String appKey) {
        this(masterSecret, appKey, null, ClientConfig.getInstance());
    }
    /**
     * This will be removed in the future. Please use ClientConfig{jiguang-common cn.jiguang.common.ClientConfig} instead of this constructor.
     *
     * @param masterSecret  API access secret of the appKey.
     * @param appKey        The KEY of one application on JPush.
     * @param maxRetryTimes The max retry times.
     */
    @Deprecated
    public PushClient(String masterSecret, String appKey, int maxRetryTimes) {
        this(masterSecret, appKey, maxRetryTimes, null);
    }
    /**
     * Create a Push Client with max retry times.
     * This will be removed in the future. Please use ClientConfig{jiguang-common cn.jiguang.common.ClientConfig} instead of this constructor.
     *
     * @param masterSecret  API access secret of the appKey.
     * @param appKey        The KEY of one application on JPush.
     * @param maxRetryTimes max retry times
     * @param proxy         The max retry times.
     */
    @Deprecated
    public PushClient(String masterSecret, String appKey, int maxRetryTimes, HttpProxy proxy) {
        ServiceHelper.checkBasic(appKey, masterSecret);
        ClientConfig conf = ClientConfig.getInstance();
        conf.setMaxRetryTimes(maxRetryTimes);
        this._baseUrl = (String) conf.get(ClientConfig.PUSH_HOST_NAME);
        this._pushPath = (String) conf.get(ClientConfig.PUSH_PATH);
        this._pushValidatePath = (String) conf.get(ClientConfig.PUSH_VALIDATE_PATH);
        this._filePushPath = (String) conf.get(ClientConfig.FILE_PUSH_PATH);
        this._apnsProduction = (Integer) conf.get(ClientConfig.APNS_PRODUCTION);
        this._timeToLive = (Long) conf.get(ClientConfig.TIME_TO_LIVE);
        String authCode = ServiceHelper.getBasicAuthorization(appKey, masterSecret);
        this._httpClient = new NativeHttpClient(authCode, proxy, conf);
    }
    public PushClient(String masterSecret, String appKey, HttpProxy proxy, ClientConfig conf) {
        ServiceHelper.checkBasic(appKey, masterSecret);
        this._baseUrl = (String) conf.get(ClientConfig.PUSH_HOST_NAME);
        this._pushPath = (String) conf.get(ClientConfig.PUSH_PATH);
        this._pushValidatePath = (String) conf.get(ClientConfig.PUSH_VALIDATE_PATH);
        this._filePushPath = (String) conf.get(ClientConfig.FILE_PUSH_PATH);
        this.batchAliasPushPath = (String) conf.get(ClientConfig.BATCH_ALIAS_PUSH_PATH);
        this.batchRegidPushPath = (String) conf.get(ClientConfig.BATCH_REGID_PUSH_PATH);
        this._apnsProduction = (Integer) conf.get(ClientConfig.APNS_PRODUCTION);
        this._timeToLive = (Long) conf.get(ClientConfig.TIME_TO_LIVE);
        this._encryptType = (String) conf.get(ClientConfig.ENCRYPT_TYPE);
        String authCode = ServiceHelper.getBasicAuthorization(appKey, masterSecret);
        this._httpClient = new NativeHttpClient(authCode, proxy, conf);
    }
    /**
     * Create a Push Client with global settings.
     * <p>
     * If you want different settings from default globally, this constructor is what you needed.
     * This will be removed in the future. Please use ClientConfig{jiguang-common cn.jiguang.common.ClientConfig} instead of this constructor.
     *
     * @param masterSecret   API access secret of the appKey.
     * @param appKey         The KEY of one application on JPush.
     * @param apnsProduction Global APNs environment setting. It will override PushPayload Options.
     * @param timeToLive     Global time_to_live setting. It will override PushPayload Options.
     */
    @Deprecated
    public PushClient(String masterSecret, String appKey, boolean apnsProduction, long timeToLive) {
        this(masterSecret, appKey);
        if (apnsProduction) {
            _apnsProduction = 1;
        } else {
            _apnsProduction = 0;
        }
        this._timeToLive = timeToLive;
    }
    /**
     * This will be removed in the future. Please use ClientConfig{jiguang-common cn.jiguang.common.ClientConfig#setGlobalPushSetting} instead of this method.
     *
     * @param apnsProduction Global APNs environment setting. It will override PushPayload Options.
     * @param timeToLive     Global time_to_live setting. It will override PushPayload Options.
     */
    @Deprecated
    public void setDefaults(boolean apnsProduction, long timeToLive) {
        if (apnsProduction) {
            _apnsProduction = 1;
        } else {
            _apnsProduction = 0;
        }
        this._timeToLive = timeToLive;
    }
    public void setBaseUrl(String baseUrl) {
        this._baseUrl = baseUrl;
    }
    public PushResult sendPush(PushPayload pushPayload) throws APIConnectionException, APIRequestException {
        checkPushPayload(pushPayload);
        ResponseWrapper response = _httpClient.sendPost(_baseUrl + _pushPath, getEncryptData(pushPayload));
        return BaseResult.fromResponse(response, PushResult.class);
    }
    public PushResult sendPushValidate(PushPayload pushPayload) throws APIConnectionException, APIRequestException {
        checkPushPayload(pushPayload);
        ResponseWrapper response = _httpClient.sendPost(_baseUrl + _pushValidatePath, getEncryptData(pushPayload));
        return BaseResult.fromResponse(response, PushResult.class);
    }
    public PushResult sendPush(String payloadString) throws APIConnectionException, APIRequestException {
        Preconditions.checkArgument(StringUtils.isNotEmpty(payloadString), "pushPayload should not be empty");
        try {
            _jsonParser.parse(payloadString);
        } catch (JsonParseException e) {
            Preconditions.checkArgument(false, "payloadString should be a valid JSON string.");
        }
        ResponseWrapper response = _httpClient.sendPost(_baseUrl + _pushPath, getEncryptData(payloadString));
        return BaseResult.fromResponse(response, PushResult.class);
    }
    public PushResult sendFilePush(PushPayload pushPayload) throws APIConnectionException, APIRequestException {
        checkPushPayload(pushPayload);
        ResponseWrapper response = _httpClient.sendPost(_baseUrl + _filePushPath, getEncryptData(pushPayload));
        return BaseResult.fromResponse(response, PushResult.class);
    }
    public PushResult sendPushValidate(String payloadString) throws APIConnectionException, APIRequestException {
        Preconditions.checkArgument(StringUtils.isNotEmpty(payloadString), "pushPayload should not be empty");
        try {
            _jsonParser.parse(payloadString);
        } catch (JsonParseException e) {
            Preconditions.checkArgument(false, "payloadString should be a valid JSON string.");
        }
        ResponseWrapper response = _httpClient.sendPost(_baseUrl + _pushValidatePath, getEncryptData(payloadString));
        return BaseResult.fromResponse(response, PushResult.class);
    }
    public BatchPushResult batchSendPushByRegId(List<PushPayload> pushPayloadList) throws APIConnectionException, APIRequestException {
        return batchSendPush(_baseUrl + batchRegidPushPath, pushPayloadList);
    }
    public BatchPushResult batchSendPushByAlias(List<PushPayload> pushPayloadList) throws APIConnectionException, APIRequestException {
        return batchSendPush(_baseUrl + batchAliasPushPath, pushPayloadList);
    }
    public BatchPushResult batchSendPush(String url, List<PushPayload> pushPayloadList) throws APIConnectionException, APIRequestException {
        Preconditions.checkArgument((null != pushPayloadList), "param should not be null");
        Preconditions.checkArgument((!pushPayloadList.isEmpty()), "pushPayloadList should not be empty");
        Gson gson = new Gson();
        JsonObject contentJson = new JsonObject();
        CIDResult cidResult = getCidList(pushPayloadList.size(), "push");
        int i = 0;
        JsonObject pushPayLoadList = new JsonObject();
        // setting cid
        for (PushPayload payload : pushPayloadList) {
            String cid = payload.getCid();
            if (cid != null && !cid.trim().isEmpty()) {
                payload.setCid(null);
            } else {
                cid = cidResult.cidlist.get(i++);
            }
            pushPayLoadList.add(cid, payload.toJSON());
        }
        contentJson.add("pushlist", pushPayLoadList);
        ResponseWrapper response = _httpClient.sendPost(url, getEncryptData(gson.toJson(contentJson)));
        return BatchPushResult.fromResponse(response);
    }
    /**
     * Get cid list, the data form of cid is appKey-uuid.
     *
     * @param count the count of cid list, from 1 to 1000. default is 1.
     * @param type  default is "push", option: "schedule"
     * @return CIDResult, an array of cid
     * @throws APIConnectionException connect exception
     * @throws APIRequestException    request exception
     */
    public CIDResult getCidList(int count, String type) throws APIConnectionException, APIRequestException {
        Preconditions.checkArgument(count >= 1 && count <= 1000, "count should not less than 1 or larger than 1000");
        Preconditions.checkArgument(type == null || type.equals("push") || type.equals("schedule"), "type should be \"push\" or \"schedule\"");
        ResponseWrapper responseWrapper;
        if (type != null) {
            responseWrapper = _httpClient.sendGet(_baseUrl + _pushPath + "/cid?count=" + count + "&type=" + type);
        } else {
            responseWrapper = _httpClient.sendGet(_baseUrl + _pushPath + "/cid?count=" + count);
        }
        return BaseResult.fromResponse(responseWrapper, CIDResult.class);
    }
    /**
     * Delete a push by msgId.
     *
     * @param msgId The message id
     * @return delete result
     * @throws APIConnectionException connect exception
     * @throws APIRequestException    request exception
     */
    public DefaultResult deletePush(String msgId) throws APIConnectionException, APIRequestException {
        Preconditions.checkArgument(StringUtils.isNotEmpty(msgId), "msgId should not be empty");
        ResponseWrapper responseWrapper = _httpClient.sendDelete(_baseUrl + _pushPath + "/" + msgId);
        return DefaultResult.fromResponse(responseWrapper);
    }
    public void setHttpClient(IHttpClient client) {
        this._httpClient = client;
    }
    // 如果使用 NettyHttpClient,在发送请求后需要手动调用 close 方法
    public void close() {
        if (_httpClient != null && _httpClient instanceof NettyHttpClient) {
            ((NettyHttpClient) _httpClient).close();
        } else if (_httpClient != null && _httpClient instanceof ApacheHttpClient) {
            ((ApacheHttpClient) _httpClient).close();
        }
    }
    /**
     * 获取加密的payload数据
     *
     * @param payloadData
     * @return
     */
    private String getEncryptData(String payloadData) {
        JsonElement payloadElement = _jsonParser.parse(payloadData);
        JsonObject jsonObject = payloadElement.getAsJsonObject();
        Audience audience = Audience.fromJsonElement(jsonObject.get("audience"));
        return getEncryptData(payloadData, audience);
    }
    /**
     * 获取加密的payload数据
     *
     * @param pushPayload
     * @return
     */
    private String getEncryptData(PushPayload pushPayload) {
        if (StringUtils.isEmpty(_encryptType)) {
            return pushPayload.toString();
        }
        if (EncryptKeys.ENCRYPT_SMS2_TYPE.equals(_encryptType)) {
            EncryptPushPayload encryptPushPayload = new EncryptPushPayload();
            try {
                encryptPushPayload.setPayload(String.valueOf(Base64.encode(SM2Util.encrypt(pushPayload.toString(), EncryptKeys.DEFAULT_SM2_ENCRYPT_KEY))));
            } catch (Exception e) {
                throw new RuntimeException("encrypt word exception", e);
            }
            encryptPushPayload.setAudience(pushPayload.getAudience());
            return encryptPushPayload.toString();
        }
        // 不支持的加密默认不加密
        return pushPayload.toString();
    }
    /**
     * 获取加密的payload数据
     *
     * @param pushPayload
     * @return
     */
    private String getEncryptData(String pushPayload, Audience audience) {
        if (StringUtils.isEmpty(_encryptType)) {
            return pushPayload;
        }
        if (EncryptKeys.ENCRYPT_SMS2_TYPE.equals(_encryptType)) {
            EncryptPushPayload encryptPushPayload = new EncryptPushPayload();
            try {
                encryptPushPayload.setPayload(String.valueOf(Base64.encode(SM2Util.encrypt(pushPayload, EncryptKeys.DEFAULT_SM2_ENCRYPT_KEY))));
            } catch (Exception e) {
                throw new RuntimeException("encrypt word exception", e);
            }
            encryptPushPayload.setAudience(audience);
            return encryptPushPayload.toString();
        }
        // 不支持的加密默认不加密
        return pushPayload;
    }
    private void checkPushPayload(PushPayload pushPayload) {
        Preconditions.checkArgument(!(null == pushPayload), "pushPayload should not be null");
        if (_apnsProduction > 0) {
            pushPayload.resetOptionsApnsProduction(true);
        } else if (_apnsProduction == 0) {
            pushPayload.resetOptionsApnsProduction(false);
        }
        if (_timeToLive >= 0) {
            pushPayload.resetOptionsTimeToLive(_timeToLive);
        }
    }
}
service-push/src/main/java/cn/jpush/api/push/PushResult.java
New file
@@ -0,0 +1,29 @@
package cn.jpush.api.push;
import com.google.gson.annotations.Expose;
import cn.jiguang.common.resp.BaseResult;
public class PushResult extends BaseResult {
    private static final long serialVersionUID = 93783137655776743L;
    @Expose public long msg_id;
    @Expose public int sendno;
    @Expose public int statusCode;
    @Expose public Error error;
    public class Error {
        @Expose String message;
        @Expose int code;
        public String getMessage() {
            return this.message;
        }
        public int getCode() {
            return this.code;
        }
    }
}
service-push/src/main/java/cn/jpush/api/push/model/BatchPushResult.java
New file
@@ -0,0 +1,49 @@
package cn.jpush.api.push.model;
import cn.jiguang.common.resp.BaseResult;
import cn.jiguang.common.resp.ResponseWrapper;
import com.google.gson.annotations.Expose;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.Map;
public class BatchPushResult extends BaseResult {
    private static final Type RESULT_TYPE = new TypeToken<Map<String, PushResult>>() {}.getType();
    private Map<String, PushResult> batchPushResult;
    public class PushResult {
        @Expose public long msg_id;
        @Expose public Error error;
    }
    public class Error {
        @Expose String message;
        @Expose int code;
        public String getMessage() {
            return this.message;
        }
        public int getCode() {
            return this.code;
        }
    }
    public static BatchPushResult fromResponse(ResponseWrapper responseWrapper) {
        BatchPushResult result = new BatchPushResult();
        if (responseWrapper.isServerResponse()) {
            result.batchPushResult = _gson.fromJson(responseWrapper.responseContent, RESULT_TYPE);
        }
        result.setResponseWrapper(responseWrapper);
        return result;
    }
    public Map<String, PushResult> getBatchPushResult() {
        return batchPushResult;
    }
}
service-push/src/main/java/cn/jpush/api/push/model/EncryptKeys.java
New file
@@ -0,0 +1,8 @@
package cn.jpush.api.push.model;
public class EncryptKeys {
    public static String ENCRYPT_SMS2_TYPE = "SM2";
    public static String DEFAULT_SM2_ENCRYPT_KEY = "BPj6Mj/T444gxPaHc6CDCizMRp4pEl14WI2lvIbdEK2c+5XiSqmQt2TQc8hMMZqfxcDqUNQW95puAfQx1asv3rU=";
}
service-push/src/main/java/cn/jpush/api/push/model/EncryptPushPayload.java
New file
@@ -0,0 +1,36 @@
package cn.jpush.api.push.model;
import cn.jpush.api.push.model.audience.Audience;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class EncryptPushPayload {
    private static Gson _gson = new GsonBuilder().disableHtmlEscaping().create();
    private String payload;
    private Audience audience;
    public Audience getAudience() {
        return audience;
    }
    public void setAudience(Audience audience) {
        this.audience = audience;
    }
    public String getPayload() {
        return payload;
    }
    public void setPayload(String payload) {
        this.payload = payload;
    }
    @Override
    public String toString() {
        return _gson.toJson(this);
    }
}
service-push/src/main/java/cn/jpush/api/push/model/InappMessage.java
New file
@@ -0,0 +1,53 @@
package cn.jpush.api.push.model;
import cn.jiguang.common.utils.Preconditions;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
/**
 * inapp_message 此功能生效需Android push SDK≥V3.9.0、iOS push SDK≥V3.4.0,若低于此版本按照原流程执行。
 *
 * 默认值为false
 */
public class InappMessage implements PushModel{
    private boolean inappMessage;
    private InappMessage(boolean inappMessage) {
        this.inappMessage = inappMessage;
    }
    /**
     * the entrance for building a inappMessage object
     * @return inappMessage builder
     */
    public static Builder newBuilder() { return new Builder(); }
    public boolean getInappMessage() { return inappMessage; }
    @Override
    public JsonElement toJSON() {
        JsonObject json = new JsonObject();
        json.addProperty("inapp_message", inappMessage);
        return json;
    }
    public static class Builder {
        private boolean inappMessage;
        public Builder setInappMessage(boolean inappMessage) {
            this.inappMessage = inappMessage;
            return this;
        }
        public InappMessage build() {
            return new InappMessage(inappMessage);
        }
    }
}
service-push/src/main/java/cn/jpush/api/push/model/Message.java
New file
@@ -0,0 +1,233 @@
package cn.jpush.api.push.model;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import cn.jiguang.common.utils.Preconditions;
/**
 * Android 1.6.2 及以下版本 接收 notification 与 message 并存(即本次 api 调用同时推送通知和消息)的离线推送, 只能收到通知部分,message 部分没有透传给 App。
 *
 * Android 1.6.3 及以上 SDK 版本已做相应调整,能正常接收同时推送通知和消息的离线记录。
 *
 * iOS 1.7.3 及以上的版本才能正确解析 v3 的 message,但是无法解析 v2 推送通知同时下发的应用内消息。
 */
public class Message implements PushModel {
    private static final String MSG_CONTENT = "msg_content";
    private static final String TITLE = "title";
    private static final String CONTENT_TYPE = "content_type";
    private static final String EXTRAS = "extras";
    private final String msgContent;
    private final String title;
    private final String contentType;
    private final Map<String, String> extras;
    private final Map<String, Number> numberExtras;
    private final Map<String, Boolean> booleanExtras;
    private final Map<String, JsonObject> jsonExtras;
    private final Map<String, JsonPrimitive> customData;
    private Message(String title, String msgContent, String contentType,
                    Map<String, String> extras,
                    Map<String, Number> numberExtras,
                    Map<String, Boolean> booleanExtras,
                    Map<String, JsonObject> jsonExtras,
                    Map<String, JsonPrimitive> customData) {
        this.title = title;
        this.msgContent = msgContent;
        this.contentType = contentType;
        this.extras = extras;
        this.numberExtras = numberExtras;
        this.booleanExtras = booleanExtras;
        this.jsonExtras = jsonExtras;
        this.customData = customData;
    }
    public static Builder newBuilder() {
        return new Builder();
    }
    public static Message content(String msgContent) {
        return new Builder().setMsgContent(msgContent).build();
    }
    @Override
    public JsonElement toJSON() {
        JsonObject json = new JsonObject();
        if (null != title) {
            json.add(TITLE, new JsonPrimitive(title));
        }
        if (null != msgContent) {
            json.add(MSG_CONTENT, new JsonPrimitive(msgContent));
        }
        if (null != contentType) {
            json.add(CONTENT_TYPE, new JsonPrimitive(contentType));
        }
        JsonObject extrasObject = null;
        if (null != extras || null != numberExtras || null != booleanExtras || null != jsonExtras) {
            extrasObject = new JsonObject();
        }
        if (null != extras) {
            for (String key : extras.keySet()) {
                if (extras.get(key) != null) {
                    extrasObject.add(key, new JsonPrimitive(extras.get(key)));
                } else {
                    extrasObject.add(key, JsonNull.INSTANCE);
                }
            }
        }
        if (null != numberExtras) {
            for (String key : numberExtras.keySet()) {
                extrasObject.add(key, new JsonPrimitive(numberExtras.get(key)));
            }
        }
        if (null != booleanExtras) {
            for (String key : booleanExtras.keySet()) {
                extrasObject.add(key, new JsonPrimitive(booleanExtras.get(key)));
            }
        }
        if (null != jsonExtras) {
            for (String key : jsonExtras.keySet()) {
                extrasObject.add(key, jsonExtras.get(key));
            }
        }
        if (null != extras || null != numberExtras || null != booleanExtras || null != jsonExtras) {
            json.add(EXTRAS, extrasObject);
        }
        if (null != customData) {
            for (Map.Entry<String, JsonPrimitive> entry : customData.entrySet()) {
                json.add(entry.getKey(), entry.getValue());
            }
        }
        return json;
    }
    public static class Builder {
        private String title;
        private String msgContent;
        private String contentType;
        private Map<String, String> extrasBuilder;
        private Map<String, Number> numberExtrasBuilder;
        private Map<String, Boolean> booleanExtrasBuilder;
        protected Map<String, JsonObject> jsonExtrasBuilder;
        private Map<String, JsonPrimitive> customData;
        public Builder setTitle(String title) {
            this.title = title;
            return this;
        }
        public Builder setMsgContent(String msgContent) {
            this.msgContent = msgContent;
            return this;
        }
        public Builder setContentType(String contentType) {
            this.contentType = contentType;
            return this;
        }
        public Builder addExtra(String key, String value) {
            Preconditions.checkArgument(!(null == key || null == value), "Key/Value should not be null.");
            if (null == extrasBuilder) {
                extrasBuilder = new HashMap<String, String>();
            }
            extrasBuilder.put(key, value);
            return this;
        }
        public Builder addExtras(Map<String, String> extras) {
            Preconditions.checkArgument(!(null == extras), "extras should not be null.");
            if (null == extrasBuilder) {
                extrasBuilder = new HashMap<String, String>();
            }
            for (String key : extras.keySet()) {
                extrasBuilder.put(key, extras.get(key));
            }
            return this;
        }
        public Builder addExtra(String key, Number value) {
            Preconditions.checkArgument(!(null == key || null == value), "Key/Value should not be null.");
            if (null == numberExtrasBuilder) {
                numberExtrasBuilder = new HashMap<String, Number>();
            }
            numberExtrasBuilder.put(key, value);
            return this;
        }
        public Builder addExtra(String key, Boolean value) {
            Preconditions.checkArgument(!(null == key || null == value), "Key/Value should not be null.");
            if (null == booleanExtrasBuilder) {
                booleanExtrasBuilder = new HashMap<String, Boolean>();
            }
            booleanExtrasBuilder.put(key, value);
            return this;
        }
        public Builder addExtra(String key, JsonObject value) {
            Preconditions.checkArgument(!(null == key || null == value), "Key/Value should not be null.");
            if (null == jsonExtrasBuilder) {
                jsonExtrasBuilder = new HashMap<String, JsonObject>();
            }
            jsonExtrasBuilder.put(key, value);
            return this;
        }
        public Builder addCustom(Map<String, String> extras) {
            if (customData == null) {
                customData = new LinkedHashMap<>();
            }
            for (Map.Entry<String, String> entry : extras.entrySet()) {
                customData.put(entry.getKey(), new JsonPrimitive(entry.getValue()));
            }
            return this;
        }
        public Builder addCustom(String key, Number value) {
            Preconditions.checkArgument(!(null == key), "Key should not be null.");
            if (customData == null) {
                customData = new LinkedHashMap<>();
            }
            customData.put(key, new JsonPrimitive(value));
            return this;
        }
        public Builder addCustom(String key, String value) {
            Preconditions.checkArgument(!(null == key), "Key should not be null.");
            if (customData == null) {
                customData = new LinkedHashMap<>();
            }
            customData.put(key, new JsonPrimitive(value));
            return this;
        }
        public Builder addCustom(String key, Boolean value) {
            Preconditions.checkArgument(!(null == key), "Key should not be null.");
            if (customData == null) {
                customData = new LinkedHashMap<>();
            }
            customData.put(key, new JsonPrimitive(value));
            return this;
        }
        public Message build() {
            Preconditions.checkArgument(!(null == msgContent),
                    "msgContent should be set");
            return new Message(title, msgContent, contentType,
                    extrasBuilder, numberExtrasBuilder, booleanExtrasBuilder, jsonExtrasBuilder, customData);
        }
    }
}
service-push/src/main/java/cn/jpush/api/push/model/Notification3rd.java
New file
@@ -0,0 +1,270 @@
package cn.jpush.api.push.model;
import java.util.HashMap;
import java.util.Map;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import cn.jiguang.common.utils.Preconditions;
/**
 * 使用说明
 * notification_3rd 只针对开通了厂商通道的用户生效;
 * notification 和 notification_3rd 不能同时有内容,如果这两块同时有内容,则会返回错误提示;
 * notification_3rd 的内容对 iOS 和 WinPhone 平台无效,只针对 Android 平台生效;
 * notification_3rd 是用作补发厂商通知的内容,只有当 message 部分有内容,才允许传递此字段,且要两者都不为空时,才会对离线的厂商设备转发厂商通道的通知。
 */
public class Notification3rd implements PushModel{
    private static final String TITLE = "title";
    private static final String CONTENT = "content";
    private static final String CHANNEL_ID = "channel_id";
    private static final String URI_ACTIVITY = "uri_activity";
    private static final String URI_ACTION = "uri_action";
    private static final String BADGE_ADD_NUM = "badge_add_num";
    private static final String BADGE_CLASS = "badge_class";
    private static final String SOUND = "sound";
    private static final String EXTRAS = "extras";
    private final String title;
    private final String content;
    private final String channel_id;
    private final String uri_activity;
    private final String uri_action;
    private final int badge_add_num;
    private final String badge_class;
    private final String sound;
    private final Map<String, String> extras;
    private final Map<String, Number> numberExtras;
    private final Map<String, Boolean> booleanExtras;
    private final Map<String, JsonObject> jsonExtras;
    private Notification3rd(String title, String content, String channel_id,
                            String uri_activity, String uri_action, int badge_add_num,
                            String badge_class, String sound,
                            Map<String, String> extras,
                            Map<String, Number> numberExtras,
                            Map<String, Boolean> booleanExtras,
                            Map<String, JsonObject> jsonExtras) {
        this.title = title;
        this.content = content;
        this.channel_id = channel_id;
        this.uri_activity = uri_activity;
        this.uri_action = uri_action;
        this.badge_add_num = badge_add_num;
        this.badge_class = badge_class;
        this.sound = sound;
        this.extras = extras;
        this.numberExtras = numberExtras;
        this.booleanExtras = booleanExtras;
        this.jsonExtras = jsonExtras;
    }
    /**
     * The entrance for building a Notification3rd object.
     * @return Notification3rd builder
     */
    public static Builder newBuilder() { return new Builder(); }
    @Override
    public JsonElement toJSON() {
        JsonObject json = new JsonObject();
        if (null != title) {
            json.addProperty(TITLE, title);
        }
        // 必填
        json.addProperty(CONTENT, content);
        if (null != channel_id) {
            json.addProperty(CHANNEL_ID, channel_id);
        }
        if (null != uri_activity) {
            json.addProperty(URI_ACTIVITY, uri_activity);
        }
        if (null != uri_action) {
            json.addProperty(URI_ACTION, uri_action);
        }
        if (0 != badge_add_num) {
            json.addProperty(BADGE_ADD_NUM, badge_add_num);
        }
        if (null != badge_class) {
            json.addProperty(BADGE_CLASS, badge_class);
        }
        if (null != sound) {
            json.addProperty(SOUND, sound);
        }
        /**
         * for adding extras into json
         */
        JsonObject extrasObject = null;
        if (null != extras || null != numberExtras || null != booleanExtras || null != jsonExtras) {
            extrasObject = new JsonObject();
        }
        if (null != extras) {
            String value = null;
            for (String key : extras.keySet()) {
                value = extras.get(key);
                if (null != value) {
                    extrasObject.add(key, new JsonPrimitive(value));
                }
            }
        }
        if (null != numberExtras) {
            Number value = null;
            for (String key : numberExtras.keySet()) {
                value = numberExtras.get(key);
                if (null != value) {
                    extrasObject.add(key, new JsonPrimitive(value));
                }
            }
        }
        if (null != booleanExtras) {
            Boolean value = null;
            for (String key : booleanExtras.keySet()) {
                value = booleanExtras.get(key);
                if (null != value) {
                    extrasObject.add(key, new JsonPrimitive(value));
                }
            }
        }
        if (null != jsonExtras) {
            JsonObject value = null;
            for (String key : jsonExtras.keySet()) {
                value = jsonExtras.get(key);
                if (null != value) {
                    extrasObject.add(key, value);
                }
            }
        }
        if (null != extras || null != numberExtras || null != booleanExtras || null != jsonExtras) {
            json.add(EXTRAS, extrasObject);
        }
        return json;
    }
    public static class Builder{
        private String title;
        private String content;
        private String channel_id;
        private String uri_activity;
        private String uri_action;
        private int badge_add_num;
        private String badge_class;
        private String sound;
        protected Map<String, String> extrasBuilder;
        protected Map<String, Number> numberExtrasBuilder;
        protected Map<String, Boolean> booleanExtrasBuilder;
        protected Map<String, JsonObject> jsonExtrasBuilder;
        public Builder setTitle(String title) {
            this.title = title;
            return this;
        }
        public Builder setContent(String content) {
            this.content = content;
            return this;
        }
        public Builder setChannelId(String channel_id) {
            this.channel_id = channel_id;
            return this;
        }
        public Builder setUriActivity(String uri_activity) {
            this.uri_activity = uri_activity;
            return this;
        }
        public Builder setUriAction(String uri_action) {
            this.uri_action = uri_action;
            return this;
        }
        public Builder setBadgeAddNum(int badge_add_num) {
            this.badge_add_num = badge_add_num;
            return this;
        }
        public Builder setBadgeClass(String badge_class) {
            this.badge_class = badge_class;
            return this;
        }
        public Builder setSound(String sound) {
            this.sound = sound;
            return this;
        }
        // addExtra 一次加入一对
        public Builder addExtra(String key, String value) {
            Preconditions.checkArgument(! (null == key || null == value), "Key/Value should not be null.");
            if (null == extrasBuilder) {
                extrasBuilder = new HashMap<String, String>();
            }
            extrasBuilder.put(key, value);
            return this;
        }
        // addExtras 可以一次加入多对
        public Builder addExtras(Map<String, String> extras) {
            Preconditions.checkArgument(! (null == extras), "extras should not be null.");
            if (null == extrasBuilder) {
                extrasBuilder = new HashMap<String, String>();
            }
            for (String key : extras.keySet()) {
                extrasBuilder.put(key, extras.get(key));
            }
            return this;
        }
        public Builder addExtra(String key, Number value) {
            Preconditions.checkArgument(! (null == key || null == value), "Key/Value should not be null.");
            if (null == numberExtrasBuilder) {
                numberExtrasBuilder = new HashMap<String, Number>();
            }
            numberExtrasBuilder.put(key, value);
            return this;
        }
        public Builder addExtra(String key, Boolean value) {
            Preconditions.checkArgument(! (null == key || null == value), "Key/Value should not be null.");
            if (null == booleanExtrasBuilder) {
                booleanExtrasBuilder = new HashMap<String, Boolean>();
            }
            booleanExtrasBuilder.put(key, value);
            return this;
        }
        public Builder addExtra(String key, JsonObject value) {
            Preconditions.checkArgument(! (null == key || null == value), "Key/Value should not be null.");
            if (null == jsonExtrasBuilder) {
                jsonExtrasBuilder = new HashMap<String, JsonObject>();
            }
            jsonExtrasBuilder.put(key, value);
            return this;
        }
        public Notification3rd build() {
            Preconditions.checkArgument(content != null && content != "", "content should not be null or empty");
            return new Notification3rd(title, content, channel_id, uri_activity, uri_action, badge_add_num,
                    badge_class, sound, extrasBuilder, numberExtrasBuilder, booleanExtrasBuilder, jsonExtrasBuilder);
        }
    }
}
service-push/src/main/java/cn/jpush/api/push/model/Options.java
New file
@@ -0,0 +1,291 @@
package cn.jpush.api.push.model;
import com.google.gson.*;
import cn.jiguang.common.ServiceHelper;
import cn.jiguang.common.utils.Preconditions;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
public class Options implements PushModel {
    private static final String SENDNO = "sendno";
    private static final String OVERRIDE_MSG_ID = "override_msg_id";
    private static final String TIME_TO_LIVE = "time_to_live";
    private static final String APNS_PRODUCTION = "apns_production";
    private static final String BIG_PUSH_DURATION = "big_push_duration";
    private static final String APNS_COLLAPSE_ID = "apns_collapse_id";
    private static final String THIRD_PARTH_CHANNEl = "third_party_channel";
    private static final long NONE_TIME_TO_LIVE = -1;
    private final int sendno;
    private final long overrideMsgId;
    private long timeToLive;
    private boolean apnsProduction;
    // minutes
    private int bigPushDuration;
    private String apnsCollapseId;
    private final Map<String, JsonPrimitive> customData;
    /**
     * {
     *     "third_party_channel":{
     *         "xiaomi":{
     *             "distribution":"ospush",
     *             "channel_id":"*******"
     *         },
     *         "huawei":{
     *             "distribution":"jpush"
     *         },
     *         "meizu":{
     *             "distribution":"jpush"
     *         },
     *         "fcm":{
     *             "distribution":"ospush"
     *         },
     *         "oppo":{
     *             "distribution":"ospush",
     *             "channel_id":"*******"
     *         },
     *         "vivo":{
     *             "distribution":"ospush",
     *             "classification":0 // 2020/06 新增,和vivo官方字段含义一致 0 代表运营消息,1 代表系统消息,不填vivo官方默认为0
     *                                // 使用此字段时,需使用setThirdPartyChannelV2方法,因为此值只能为整数形式
     *         }
     *     }
     * }
     */
    private Map<String, JsonObject> thirdPartyChannel;
    private Options(int sendno,
                    long overrideMsgId,
                    long timeToLive,
                    boolean apnsProduction,
                    int bigPushDuration,
                    String apnsCollapseId,
                    Map<String, JsonObject> thirdPartyChannel,
                    Map<String, JsonPrimitive> customData) {
        this.sendno = sendno;
        this.overrideMsgId = overrideMsgId;
        this.timeToLive = timeToLive;
        this.apnsProduction = apnsProduction;
        this.bigPushDuration = bigPushDuration;
        this.apnsCollapseId = apnsCollapseId;
        this.thirdPartyChannel = thirdPartyChannel;
        this.customData = customData;
    }
    public static Builder newBuilder() {
        return new Builder();
    }
    public static Options sendno() {
        return newBuilder().setSendno(ServiceHelper.generateSendno()).build();
    }
    public static Options sendno(int sendno) {
        return newBuilder().setSendno(sendno).build();
    }
    public void setApnsProduction(boolean apnsProduction) {
        this.apnsProduction = apnsProduction;
    }
    public void setTimeToLive(long timeToLive) {
        this.timeToLive = timeToLive;
    }
    public void setBigPushDuration(int bigPushDuration) {
        this.bigPushDuration = bigPushDuration;
    }
    public int getSendno() {
        return this.sendno;
    }
    @Override
    public JsonElement toJSON() {
        JsonObject json = new JsonObject();
        if (sendno > 0) {
            json.add(SENDNO, new JsonPrimitive(sendno));
        }
        if (overrideMsgId > 0) {
            json.add(OVERRIDE_MSG_ID, new JsonPrimitive(overrideMsgId));
        }
        if (timeToLive >= 0) {
            json.add(TIME_TO_LIVE, new JsonPrimitive(timeToLive));
        }
        json.add(APNS_PRODUCTION, new JsonPrimitive(apnsProduction));
        if (bigPushDuration > 0) {
            json.add(BIG_PUSH_DURATION, new JsonPrimitive(bigPushDuration));
        }
        if (apnsCollapseId != null) {
            json.add(APNS_COLLAPSE_ID, new JsonPrimitive(apnsCollapseId));
        }
        if (null != thirdPartyChannel && thirdPartyChannel.size() > 0) {
            JsonObject partyChannel = new JsonObject();
            for (Map.Entry<String, JsonObject> entry : thirdPartyChannel.entrySet()) {
                JsonObject channel = entry.getValue();
                partyChannel.add(entry.getKey(), channel);
            }
            json.add(THIRD_PARTH_CHANNEl, partyChannel);
        }
        if (null != customData) {
            for (Map.Entry<String, JsonPrimitive> entry : customData.entrySet()) {
                json.add(entry.getKey(), entry.getValue());
            }
        }
        return json;
    }
    public static class Builder {
        private int sendno = 0;
        private long overrideMsgId = 0;
        private long timeToLive = NONE_TIME_TO_LIVE;
        private boolean apnsProduction = false;
        private int bigPushDuration = 0;
        private String apnsCollapseId;
        private Map<String, JsonObject> thirdPartyChannel;
        private Map<String, JsonPrimitive> customData;
        public Builder setSendno(int sendno) {
            this.sendno = sendno;
            return this;
        }
        public Builder setOverrideMsgId(long overrideMsgId) {
            this.overrideMsgId = overrideMsgId;
            return this;
        }
        public Builder setTimeToLive(long timeToLive) {
            this.timeToLive = timeToLive;
            return this;
        }
        public Builder setApnsProduction(boolean apnsProduction) {
            this.apnsProduction = apnsProduction;
            return this;
        }
        public Builder setApnsCollapseId(String id) {
            this.apnsCollapseId = id;
            return this;
        }
        public Builder setBigPushDuration(int bigPushDuration) {
            this.bigPushDuration = bigPushDuration;
            return this;
        }
        @Deprecated
        public Map<String, Map<String, String>> getThirdPartyChannel() {
            if (null != thirdPartyChannel) {
                Map<String, Map<String, String>> thirdPartyChannelRsp = new HashMap<>();
                Set<Map.Entry<String, JsonObject>> entrySet = thirdPartyChannel.entrySet();
                for (Map.Entry<String, JsonObject> entry : entrySet) {
                    JsonObject entryValue = entry.getValue();
                    Set<Map.Entry<String, JsonElement>> valueEntrySet = entryValue.entrySet();
                    Map<String, String> valueMap = new HashMap<>();
                    for (Map.Entry<String, JsonElement> valueEntry : valueEntrySet) {
                        valueMap.put(valueEntry.getKey(), null == valueEntry.getValue() ? null : valueEntry.getValue().getAsString());
                    }
                    thirdPartyChannelRsp.put(entry.getKey(), valueMap);
                }
                return thirdPartyChannelRsp;
            }
            return null;
        }
        @Deprecated
        public Builder setThirdPartyChannel(Map<String, Map<String, String>> thirdPartyChannel) {
            this.thirdPartyChannel = new HashMap<>();
            if (null != thirdPartyChannel) {
                Set<Map.Entry<String, Map<String, String>>> entrySet = thirdPartyChannel.entrySet();
                for (Map.Entry<String, Map<String, String>> entry : entrySet) {
                    String key = entry.getKey();
                    Map<String, String> valueMap = entry.getValue();
                    JsonObject valueObj = new JsonObject();
                    if (null != valueMap) {
                        Set<Map.Entry<String, String>> valueEntrySet = valueMap.entrySet();
                        for (Map.Entry<String, String> valueEntry : valueEntrySet) {
                            valueObj.addProperty(valueEntry.getKey(), valueEntry.getValue());
                        }
                        this.thirdPartyChannel.put(key, valueObj);
                    }
                }
            }
            return this;
        }
        public Builder setThirdPartyChannelV2(Map<String, JsonObject> thirdPartyChannel) {
            this.thirdPartyChannel = thirdPartyChannel;
            return this;
        }
        public Builder addCustom(Map<String, String> extras) {
            if (customData == null) {
                customData = new LinkedHashMap<>();
            }
            for (Map.Entry<String, String> entry : extras.entrySet()) {
                customData.put(entry.getKey(), new JsonPrimitive(entry.getValue()));
            }
            return this;
        }
        public Builder addCustom(String key, Number value) {
            Preconditions.checkArgument(! (null == key), "Key should not be null.");
            if (customData == null) {
                customData = new LinkedHashMap<>();
            }
            customData.put(key, new JsonPrimitive(value));
            return this;
        }
        public Builder addCustom(String key, String value) {
            Preconditions.checkArgument(! (null == key), "Key should not be null.");
            if (customData == null) {
                customData = new LinkedHashMap<>();
            }
            customData.put(key, new JsonPrimitive(value));
            return this;
        }
        public Builder addCustom(String key, Boolean value) {
            Preconditions.checkArgument(! (null == key), "Key should not be null.");
            if (customData == null) {
                customData = new LinkedHashMap<>();
            }
            customData.put(key, new JsonPrimitive(value));
            return this;
        }
        public Options build() {
            Preconditions.checkArgument(sendno >= 0, "sendno should be greater than 0.");
            Preconditions.checkArgument(overrideMsgId >= 0, "override_msg_id should be greater than 0.");
            Preconditions.checkArgument(timeToLive >= NONE_TIME_TO_LIVE, "time_to_live should be greater than 0.");
            Preconditions.checkArgument(bigPushDuration >= 0, "bigPushDuration should be greater than 0.");
            if (sendno <= 0) {
                sendno = ServiceHelper.generateSendno();
            }
            return new Options(sendno, overrideMsgId, timeToLive, apnsProduction, bigPushDuration, apnsCollapseId, thirdPartyChannel, customData);
        }
    }
}
service-push/src/main/java/cn/jpush/api/push/model/Platform.java
New file
@@ -0,0 +1,171 @@
package cn.jpush.api.push.model;
import java.util.HashSet;
import java.util.Set;
import cn.jiguang.common.DeviceType;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import cn.jiguang.common.utils.Preconditions;
/**
 * https://docs.jiguang.cn/jpush/server/push/rest_api_v3_push/#platform
 *
 * JPush 当前支持 Android, iOS, QuickApp,Windows Phone 四个平台的推送。其关键字分别为:"android", "ios", "quickapp","winphone"。
 * 使用方法,只需要在PushPayload中调用setPlatform方法。如:setPlatform(Platform.android_ios())
 * 如需要全平台推送,只需要setPlatform(Platform.all())
 */
public class Platform implements PushModel {
    private static final String ALL = "all";
    private final boolean all;
    private final Set<DeviceType> deviceTypes;
    private Platform(boolean all, Set<DeviceType> deviceTypes) {
        this.all = all;
        this.deviceTypes = deviceTypes;
    }
    public static Builder newBuilder() {
        return new Builder();
    }
    public static Platform all() {
        return newBuilder().setAll(true).build();
    }
    public static Platform android() {
        return newBuilder().addDeviceType(DeviceType.Android).build();
    }
    public static Platform ios() {
        return newBuilder().addDeviceType(DeviceType.IOS).build();
    }
    public static Platform quickapp() {
        return newBuilder().addDeviceType(DeviceType.QuickApp).build();
    }
    public static Platform winphone() {
        return newBuilder().addDeviceType(DeviceType.WinPhone).build();
    }
    public static Platform android_ios() {
        return newBuilder()
                .addDeviceType(DeviceType.Android)
                .addDeviceType(DeviceType.IOS)
                .build();
    }
    public static Platform android_winphone() {
        return newBuilder()
                .addDeviceType(DeviceType.Android)
                .addDeviceType(DeviceType.WinPhone)
                .build();
    }
    public static Platform ios_winphone() {
        return newBuilder()
                .addDeviceType(DeviceType.IOS)
                .addDeviceType(DeviceType.WinPhone)
                .build();
    }
    public static Platform android_quickapp() {
        return newBuilder()
                .addDeviceType(DeviceType.Android)
                .addDeviceType(DeviceType.QuickApp)
                .build();
    }
    public static Platform ios_quickapp() {
        return newBuilder()
                .addDeviceType(DeviceType.IOS)
                .addDeviceType(DeviceType.QuickApp)
                .build();
    }
    public static Platform quickapp_winphone() {
        return newBuilder()
                .addDeviceType(DeviceType.QuickApp)
                .addDeviceType(DeviceType.WinPhone)
                .build();
    }
    public static Platform android_ios_quickapp() {
        return newBuilder()
                .addDeviceType(DeviceType.Android)
                .addDeviceType(DeviceType.IOS)
                .addDeviceType(DeviceType.QuickApp)
                .build();
    }
    public static Platform android_ios_winphone() {
        return newBuilder()
                .addDeviceType(DeviceType.Android)
                .addDeviceType(DeviceType.IOS)
                .addDeviceType(DeviceType.WinPhone)
                .build();
    }
    public static Platform android_quickapp_winphone() {
        return newBuilder()
                .addDeviceType(DeviceType.Android)
                .addDeviceType(DeviceType.QuickApp)
                .addDeviceType(DeviceType.WinPhone)
                .build();
    }
    public static Platform ios_quickapp_winphone() {
        return newBuilder()
                .addDeviceType(DeviceType.IOS)
                .addDeviceType(DeviceType.QuickApp)
                .addDeviceType(DeviceType.WinPhone)
                .build();
    }
    public boolean isAll() {
        return all;
    }
    @Override
    public JsonElement toJSON() {
        if (all) {
            return new JsonPrimitive(ALL);
        }
        JsonArray json = new JsonArray();
        for (DeviceType deviceType : deviceTypes) {
            json.add(new JsonPrimitive(deviceType.value()));
        }
        return json;
    }
    public static class Builder {
        private boolean all;
        private Set<DeviceType> deviceTypes;
        public Builder setAll(boolean all) {
            this.all = all;
            return this;
        }
        public Builder addDeviceType(DeviceType deviceType) {
            if (null == deviceTypes) {
                deviceTypes = new HashSet<DeviceType>();
            }
            deviceTypes.add(deviceType);
            return this;
        }
        public Platform build() {
            Preconditions.checkArgument(! (all && null != deviceTypes), "Since all is enabled, any platform should not be set.");
            Preconditions.checkArgument(! (!all && null == deviceTypes), "No any deviceType is set.");
            return new Platform(all, deviceTypes);
        }
    }
}
service-push/src/main/java/cn/jpush/api/push/model/PushModel.java
New file
@@ -0,0 +1,11 @@
package cn.jpush.api.push.model;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
public interface PushModel {
    public static Gson gson = new Gson();
    public JsonElement toJSON();
}
service-push/src/main/java/cn/jpush/api/push/model/PushPayload.java
New file
@@ -0,0 +1,368 @@
package cn.jpush.api.push.model;
import cn.jiguang.common.utils.StringUtils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import cn.jiguang.common.utils.Preconditions;
import cn.jpush.api.push.model.audience.Audience;
import cn.jpush.api.push.model.notification.AndroidNotification;
import cn.jpush.api.push.model.notification.IosNotification;
import cn.jpush.api.push.model.notification.Notification;
import cn.jpush.api.push.model.notification.PlatformNotification;
import java.util.LinkedHashMap;
import java.util.Map;
/**
 * The object you should build for sending a push.
 *
 * Basically start with newBuilder() method to build a PushPayload object.
 *
 * alertAll() is a shortcut for quickly build payload of alert to all platform and all audience;
 * mesageAll() is a shortcut for quickly build payload of message to all platform and all audience.
 *
 */
public class PushPayload implements PushModel {
    private static final String PLATFORM = "platform";
    private static final String AUDIENCE = "audience";
    private static final String NOTIFICATION = "notification";
    private static final String INAPPMESSAGE = "inapp_message";
    private static final String MESSAGE = "message";
    private static final String OPTIONS = "options";
    private static final String SMS = "sms_message";
    private static final String NOTIFICATION3RD = "notification_3rd";
    private static final String CID = "cid";
    private static final String TARGET = "target";
    /**
     * Definition acording to JPush Docs
     */
    private static final int MAX_GLOBAL_ENTITY_LENGTH = 4000;
    /**
     * Definition acording to JPush Docs
     */
    private static final int MAX_IOS_PAYLOAD_LENGTH = 2000;
    private static Gson _gson = new GsonBuilder().disableHtmlEscaping().create();
    private final Platform platform;
    private final Audience audience;
    private final Notification notification;
    private final InappMessage inappMessage;
    private final Message message;
    private Options options;
    private SMS sms;
    private final Notification3rd notification3rd;
    private String cid;
    private String target;
    protected Map<String, JsonObject> custom;
    private PushPayload(Platform platform, Audience audience,
            Notification notification, InappMessage inappMessage, Message message, Options options,
                        SMS sms, Notification3rd notification3rd, String cid, String target, Map<String, JsonObject> custom) {
        this.platform = platform;
        this.audience = audience;
        this.notification = notification;
        this.inappMessage = inappMessage;
        this.message = message;
        this.options = options;
        this.sms = sms;
        this.notification3rd = notification3rd;
        this.cid = cid;
        this.target = target;
        this.custom = custom;
    }
    public PushPayload setCid(String cid) {
        this.cid = cid;
        return this;
    }
    public Platform getPlatform() {
        return platform;
    }
    public String getTarget() {
        return target;
    }
    public String getCid() {
        return cid;
    }
    /**
     * The entrance for building a PushPayload object.
     * @return PushPayload builder
     */
    public static Builder newBuilder() {
        return new Builder();
    }
    /**
     * The shortcut of building a simple alert notification object to all platforms and all audiences
     * @param alert The alert message.
     * @return PushPayload
     */
    public static PushPayload alertAll(String alert) {
        return new Builder()
            .setPlatform(Platform.all())
            .setAudience(Audience.all())
            .setNotification(Notification.alert(alert)).build();
    }
    public static PushPayload alertAll(String alert, SMS sms) {
        return new Builder()
                .setPlatform(Platform.all())
                .setAudience(Audience.all())
                .setNotification(Notification.alert(alert))
                .setSMS(sms)
                .build();
    }
    /**
     * The shortcut of building a simple message object to all platforms and all audiences
     * @param msgContent The message content.
     * @return PushPayload
     */
    public static PushPayload messageAll(String msgContent) {
        return new Builder()
            .setPlatform(Platform.all())
            .setAudience(Audience.all())
            .setMessage(Message.content(msgContent)).build();
    }
    public static PushPayload messageAll(String msgContent, SMS sms) {
        return new Builder()
                .setPlatform(Platform.all())
                .setAudience(Audience.all())
                .setMessage(Message.content(msgContent))
                .setSMS(sms)
                .build();
    }
    public static PushPayload fromJSON(String payloadString) {
        return _gson.fromJson(payloadString, PushPayload.class);
    }
    public void resetOptionsApnsProduction(boolean apnsProduction) {
        if (null == options) {
            options = Options.newBuilder().setApnsProduction(apnsProduction).build();
        } else {
            options.setApnsProduction(apnsProduction);
        }
    }
    public void resetOptionsTimeToLive(long timeToLive) {
        if (null == options) {
            options = Options.newBuilder().setTimeToLive(timeToLive).build();
        } else {
            options.setTimeToLive(timeToLive);
        }
    }
    public int getSendno() {
        if (null != options) {
            return options.getSendno();
        }
        return 0;
    }
    public Audience getAudience() {
        return audience;
    }
    public Options getOptions() {
        return options;
    }
    @Override
    public JsonElement toJSON() {
        JsonObject json = new JsonObject();
        if (null != platform) {
            json.add(PLATFORM, platform.toJSON());
        }
        if (null != audience) {
            json.add(AUDIENCE, audience.toJSON());
        }
        if (null != notification) {
            json.add(NOTIFICATION, notification.toJSON());
        }
        if (null != inappMessage) {
            json.add(INAPPMESSAGE, inappMessage.toJSON());
        }
        if (null != message) {
            json.add(MESSAGE, message.toJSON());
        }
        if (null != options) {
            json.add(OPTIONS, options.toJSON());
        }
        if (null != sms) {
            json.add(SMS, sms.toJSON());
        }
        if (null != notification3rd) {
            json.add(NOTIFICATION3RD, notification3rd.toJSON());
        }
        if (null != cid) {
            json.addProperty(CID, cid);
        }
        if (null != target) {
            json.addProperty(TARGET, target);
        }
        if (null != custom) {
            for (Map.Entry<String, JsonObject> entry : custom.entrySet()) {
                json.add(entry.getKey(), entry.getValue());
            }
        }
        return json;
    }
    public boolean isGlobalExceedLength() {
        int messageLength = 0;
        JsonObject payload = (JsonObject) this.toJSON();
        if (payload.has(MESSAGE)) {
            JsonObject message = payload.getAsJsonObject(MESSAGE);
            messageLength = message.toString().getBytes().length;
        }
        if (!payload.has(NOTIFICATION)) {
            // only mesage
            return messageLength > MAX_GLOBAL_ENTITY_LENGTH;
        } else {
            JsonObject notification = payload.getAsJsonObject(NOTIFICATION);
            if (notification.has(AndroidNotification.NOTIFICATION_ANDROID)) {
                JsonObject android = notification.getAsJsonObject(AndroidNotification.NOTIFICATION_ANDROID);
                int androidLength = android.toString().getBytes().length;
                return (androidLength + messageLength) > MAX_GLOBAL_ENTITY_LENGTH;
            }
        }
        return false;
    }
    public boolean isIosExceedLength() {
        JsonObject payload = (JsonObject) this.toJSON();
        if (payload.has(NOTIFICATION)) {
            JsonObject notification = payload.getAsJsonObject(NOTIFICATION);
            if (notification.has(IosNotification.NOTIFICATION_IOS)) {
                JsonObject ios = notification.getAsJsonObject(IosNotification.NOTIFICATION_IOS);
                return ios.toString().getBytes().length > MAX_IOS_PAYLOAD_LENGTH;
            } else {
                if (notification.has(PlatformNotification.ALERT)) {
                    String alert = notification.get(PlatformNotification.ALERT).getAsString();
                    JsonObject ios = new JsonObject();
                    ios.add("alert", new JsonPrimitive(alert));
                    return ios.toString().getBytes().length > MAX_IOS_PAYLOAD_LENGTH;
                } else {
                    // No iOS Payload
                }
            }
        }
        return false;
    }
    @Override
    public String toString() {
        return _gson.toJson(toJSON());
    }
    public static class Builder {
        private Platform platform = null;
        private Audience audience = null;
        private Notification notification = null;
        private InappMessage inappMessage = null;
        private Message message = null;
        private Options options = null;
        private SMS sms = null;
        private Notification3rd notification3rd = null;
        private String cid;
        private String target;
        private Map<String, JsonObject> custom;
        public Builder() {
            this.custom = new LinkedHashMap<>();
        }
        public Builder setTarget(String target) {
            this.target = target;
            return this;
        }
        public Builder setPlatform(Platform platform) {
            this.platform = platform;
            return this;
        }
        public Builder setAudience(Audience audience) {
            this.audience = audience;
            return this;
        }
        public Builder setNotification(Notification notification) {
            this.notification = notification;
            return this;
        }
        public Builder setInappMessage(InappMessage inappMessage) {
            this.inappMessage = inappMessage;
            return this;
        }
        public Builder setMessage(Message message) {
            this.message = message;
            return this;
        }
        public Builder setOptions(Options options) {
            this.options = options;
            return this;
        }
        public Builder setSMS(SMS sms) {
            this.sms = sms;
            return this;
        }
        public Builder setNotification3rd(Notification3rd notification3rd) {
            this.notification3rd = notification3rd;
            return this;
        }
        public Builder setCid(String cid) {
            this.cid = cid;
            return this;
        }
        public Builder addCustom(String field, JsonObject jsonObject) {
            this.custom.put(field, jsonObject);
            return this;
        }
        public PushPayload build() {
            if (StringUtils.isTrimedEmpty(target)) {
                Preconditions.checkArgument(!(null == audience || null == platform),
                        "audience and platform both should be set.");
            }
            if (!StringUtils.isTrimedEmpty(target)) {
                Preconditions.checkArgument(!StringUtils.isTrimedEmpty(target) && null != platform,
                        "target and platform should be set.");
            }
            Preconditions.checkArgument(! (null == notification && null == message),
                    "notification or message should be set at least one.");
            // if options is not set, a sendno will be generated for tracing easily
            if (null == options) {
                options = Options.sendno();
            }
            return new PushPayload(platform, audience, notification, inappMessage, message, options, sms, notification3rd, cid, target, custom);
        }
    }
}
service-push/src/main/java/cn/jpush/api/push/model/SMS.java
New file
@@ -0,0 +1,220 @@
package cn.jpush.api.push.model;
import java.util.HashMap;
import java.util.Map;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import cn.jiguang.common.utils.Preconditions;
/**
 * sms_message 用于设置短信推送内容以及短信发送的延迟时间。
 */
public class SMS implements PushModel {
    private final String content;
    private final int delay_time;
    private final long temp_id;
    // default is true
    private boolean active_filter;
    // this flag is used to indicate if the active_filter being set
    private boolean is_set_active_filter = false;
    private final Map<String, String> extras;
    private final Map<String, Number> numberExtras;
    private final Map<String, Boolean> booleanExtras;
    private final Map<String, JsonObject> jsonExtras;
    private SMS(String content, int delay_time, long temp_id, boolean active_filter,
            Map<String, String> extras,
            Map<String, Number> numberExtras,
            Map<String, Boolean> booleanExtras,
            Map<String, JsonObject> jsonExtras) {
        this.content = content;
        this.delay_time = delay_time;
        this.temp_id = temp_id;
        this.active_filter = active_filter;
        this.extras = extras;
        this.numberExtras = numberExtras;
        this.booleanExtras = booleanExtras;
        this.jsonExtras = jsonExtras;
    }
    public static Builder newBuilder() {
        return new Builder();
    }
    /**
     * This will be removed in the future. Please use content(long tempId, int delayTime)  this constructor.
     * Create a SMS content with a delay time.
     * JPush will send a SMS if the message doesn't received within the delay time. If the delay time is 0, the SMS will be sent immediately.
     * Please note the delay time only works on Android.
     * If you are pushing to iOS, the SMS will be sent immediately, whether or not the delay time is 0.
     *
     * @param content The SMS content.
     * @param delayTime The seconds you want to delay, should be greater than or equal to 0.
     * @return SMS payload.
     */
    @Deprecated
    public static SMS content(String content, int delayTime) {
        return new Builder()
                .setContent(content)
                .setDelayTime(delayTime)
                .build();
    }
    public static SMS content(long tempId, int delayTime) {
        return new Builder()
                .setTempID(tempId)
                .setDelayTime(delayTime)
                .build();
    }
    @Override
    public JsonElement toJSON() {
        JsonObject json = new JsonObject();
        json.addProperty("delay_time", delay_time);
        if (temp_id > 0) {
            json.addProperty("temp_id", temp_id);
        }
        if (null != content) {
            json.addProperty("content", content);
        }
        json.addProperty("active_filter", active_filter);
        JsonObject extrasObject = null;
        if (null != extras || null != numberExtras || null != booleanExtras || null != jsonExtras) {
            extrasObject = new JsonObject();
        }
        if (null != extras) {
            for (String key : extras.keySet()) {
                if (extras.get(key) != null) {
                    extrasObject.add(key, new JsonPrimitive(extras.get(key)));
                } else {
                    extrasObject.add(key, JsonNull.INSTANCE);
                }
            }
        }
        if (null != numberExtras) {
            for (String key : numberExtras.keySet()) {
                extrasObject.add(key, new JsonPrimitive(numberExtras.get(key)));
            }
        }
        if (null != booleanExtras) {
            for (String key : booleanExtras.keySet()) {
                extrasObject.add(key, new JsonPrimitive(booleanExtras.get(key)));
            }
        }
        if (null != jsonExtras) {
            for (String key : jsonExtras.keySet()) {
                extrasObject.add(key, jsonExtras.get(key));
            }
        }
        if (null != extras || null != numberExtras || null != booleanExtras || null != jsonExtras) {
            json.add("temp_para", extrasObject);
        }
        return json;
    }
    public static class Builder {
        private String content;
        private int delay_time;
        private long temp_id;
        private boolean active_filter;
        private boolean is_set_active_filter;
        private Map<String, String> extrasBuilder;
        private Map<String, Number> numberExtrasBuilder;
        private Map<String, Boolean> booleanExtrasBuilder;
        protected Map<String, JsonObject> jsonExtrasBuilder;
        public Builder setContent(String content) {
            this.content = content;
            return this;
        }
        public Builder setDelayTime(int delayTime) {
            this.delay_time = delayTime;
            return this;
        }
        public Builder setTempID(long tempID) {
            this.temp_id = tempID;
            return this;
        }
        public Builder setActiveFilter(boolean activeFilter) {
            this.active_filter = activeFilter;
            this.is_set_active_filter = true;
            return this;
        }
        public Builder addPara(String key, String value) {
            Preconditions.checkArgument(! (null == key || null == value), "Key/Value should not be null.");
            if (null == extrasBuilder) {
                extrasBuilder = new HashMap<String, String>();
            }
            extrasBuilder.put(key, value);
            return this;
        }
        public Builder addParas(Map<String, String> extras) {
            Preconditions.checkArgument(! (null == extras), "extras should not be null.");
            if (null == extrasBuilder) {
                extrasBuilder = new HashMap<String, String>();
            }
            for (String key : extras.keySet()) {
                extrasBuilder.put(key, extras.get(key));
            }
            return this;
        }
        public Builder addPara(String key, Number value) {
            Preconditions.checkArgument(! (null == key || null == value), "Key/Value should not be null.");
            if (null == numberExtrasBuilder) {
                numberExtrasBuilder = new HashMap<String, Number>();
            }
            numberExtrasBuilder.put(key, value);
            return this;
        }
        public Builder addPara(String key, Boolean value) {
            Preconditions.checkArgument(! (null == key || null == value), "Key/Value should not be null.");
            if (null == booleanExtrasBuilder) {
                booleanExtrasBuilder = new HashMap<String, Boolean>();
            }
            booleanExtrasBuilder.put(key, value);
            return this;
        }
        public Builder addPara(String key, JsonObject value) {
            Preconditions.checkArgument(! (null == key || null == value), "Key/Value should not be null.");
            if (null == jsonExtrasBuilder) {
                jsonExtrasBuilder = new HashMap<String, JsonObject>();
            }
            jsonExtrasBuilder.put(key, value);
            return this;
        }
        public SMS build() {
            Preconditions.checkArgument(delay_time >= 0, "The delay time must be greater than or equal to 0");
            // if active filter not being set, will default set it to true.
            if (is_set_active_filter == false) { active_filter = true; }
            return new SMS(content, delay_time, temp_id, active_filter,
                    extrasBuilder, numberExtrasBuilder, booleanExtrasBuilder,jsonExtrasBuilder);
        }
    }
}
service-push/src/main/java/cn/jpush/api/push/model/audience/Audience.java
New file
@@ -0,0 +1,194 @@
package cn.jpush.api.push.model.audience;
import cn.jiguang.common.utils.Preconditions;
import cn.jpush.api.push.model.PushModel;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
public class Audience implements PushModel {
    private static final String ALL = "all";
    private final boolean all;
    private final boolean file;
    private final Set<AudienceTarget> targets;
    private Audience(boolean all, boolean file, Set<AudienceTarget> targets) {
        this.all = all;
        this.file = file;
        this.targets = targets;
    }
    public static Builder newBuilder() {
        return new Builder();
    }
    public static Audience all() {
        return newBuilder().setAll(true).build();
    }
    public static Audience tag(String... tagValue) {
        AudienceTarget target = AudienceTarget.tag(tagValue);
        return newBuilder().addAudienceTarget(target).build();
    }
    public static Audience tag(Collection<String> tagValues) {
        AudienceTarget target = AudienceTarget.tag(tagValues);
        return newBuilder().addAudienceTarget(target).build();
    }
    public static Audience tag_and(String... tagValue) {
        AudienceTarget target = AudienceTarget.tag_and(tagValue);
        return newBuilder().addAudienceTarget(target).build();
    }
    public static Audience tag_and(Collection<String> tagValues) {
        AudienceTarget target = AudienceTarget.tag_and(tagValues);
        return newBuilder().addAudienceTarget(target).build();
    }
    public static Audience tag_not(String...tagValue) {
        AudienceTarget target = AudienceTarget.tag_not(tagValue);
        return newBuilder().addAudienceTarget(target).build();
    }
    public static Audience tag_not(Collection<String> tagValues) {
        AudienceTarget target = AudienceTarget.tag_not(tagValues);
        return newBuilder().addAudienceTarget(target).build();
    }
    public static Audience alias(String... alias) {
        AudienceTarget target = AudienceTarget.alias(alias);
        return newBuilder().addAudienceTarget(target).build();
    }
    public static Audience alias(Collection<String> aliases) {
        AudienceTarget target = AudienceTarget.alias(aliases);
        return newBuilder().addAudienceTarget(target).build();
    }
    public static Audience segment(String... segment) {
        AudienceTarget target = AudienceTarget.segment(segment);
        return newBuilder().addAudienceTarget(target).build();
    }
    public static Audience segment(Collection<String> segments) {
        AudienceTarget target = AudienceTarget.segment(segments);
        return newBuilder().addAudienceTarget(target).build();
    }
    public static Audience registrationId(String... registrationId) {
        AudienceTarget target = AudienceTarget.registrationId(registrationId);
        return newBuilder().addAudienceTarget(target).build();
    }
    public static Audience registrationId(Collection<String> registrationIds) {
        AudienceTarget target = AudienceTarget.registrationId(registrationIds);
        return newBuilder().addAudienceTarget(target).build();
    }
    public static Audience abTest(String... abTestId) {
        AudienceTarget target = AudienceTarget.abTest(abTestId);
        return newBuilder().addAudienceTarget(target).build();
    }
    public static Audience abTest(Collection<String> abTestIds) {
        AudienceTarget target = AudienceTarget.abTest(abTestIds);
        return newBuilder().addAudienceTarget(target).build();
    }
    public static Audience file(String fileId) {
        AudienceTarget target = AudienceTarget.file(fileId);
        return newBuilder().setFile(Boolean.TRUE).addAudienceTarget(target).build();
    }
    public boolean isAll() {
        return this.all;
    }
    public JsonElement toJSON() {
        if (all) {
            return new JsonPrimitive(ALL);
        }
        // if not all, there will be target be set.
        JsonObject json = new JsonObject();
        if (file) {
            for (AudienceTarget target : targets) {
                if (AudienceType.FILE == target.getAudienceType()) {
                    json.add(target.getAudienceTypeValue(), target.toFileJSON());
                }
            }
            return json;
        }
        if (null != targets) {
            for (AudienceTarget target : targets) {
                json.add(target.getAudienceTypeValue(), target.toJSON());
            }
        }
        return json;
    }
    public static Audience fromJsonElement(JsonElement jsonElement) {
        if (jsonElement == null) {
            return null;
        }
        boolean all = !jsonElement.isJsonObject();
        if (all) {
            return new Audience(true, false,null);
        }
        boolean file = false;
        JsonObject jsonObject = jsonElement.getAsJsonObject();
        Set<AudienceTarget> audienceTargetSet = new HashSet<>();
        for (AudienceType type : AudienceType.values()) {
            JsonArray jsonArray = jsonObject.getAsJsonArray(type.value());
            if (jsonArray == null) {
                continue;
            }
            if (AudienceType.FILE == type) {
                file = true;
            }
            audienceTargetSet.add(AudienceTarget.fromJsonElement(jsonArray, type));
        }
        return new Audience(false, file, audienceTargetSet);
    }
    public static class Builder {
        private boolean all = false;
        private boolean file = false;
        private Set<AudienceTarget> audienceBuilder = null;
        public Builder setAll(boolean all) {
            this.all = all;
            return this;
        }
        public Builder setFile(boolean file) {
            this.file = file;
            return this;
        }
        public Builder addAudienceTarget(AudienceTarget target) {
            if (null == audienceBuilder) {
                audienceBuilder = new HashSet<AudienceTarget>();
            }
            audienceBuilder.add(target);
            return this;
        }
        public Audience build() {
            Preconditions.checkArgument(! (all && null != audienceBuilder), "If audience is all, no any other audience may be set.");
            Preconditions.checkArgument(! (all == false && null == audienceBuilder), "No any audience target is set.");
            return new Audience(all, file, audienceBuilder);
        }
    }
}
service-push/src/main/java/cn/jpush/api/push/model/audience/AudienceTarget.java
New file
@@ -0,0 +1,180 @@
package cn.jpush.api.push.model.audience;
import cn.jiguang.common.utils.Preconditions;
import cn.jpush.api.push.model.PushModel;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
public class AudienceTarget implements PushModel {
    private final AudienceType audienceType;
    private final Set<String> values;
    private AudienceTarget(AudienceType audienceType, Set<String> values) {
        this.audienceType = audienceType;
        this.values = values;
    }
    public static Builder newBuilder() {
        return new Builder();
    }
    public static AudienceTarget tag(String... tag) {
        return newBuilder().setAudienceType(AudienceType.TAG).addAudienceTargetValues(tag).build();
    }
    public static AudienceTarget tag(Collection<String> tags) {
        return newBuilder().setAudienceType(AudienceType.TAG).addAudienceTargetValues(tags).build();
    }
    public static AudienceTarget tag_and(String... tag) {
        return newBuilder().setAudienceType(AudienceType.TAG_AND).addAudienceTargetValues(tag).build();
    }
    public static AudienceTarget tag_and(Collection<String> tags) {
        return newBuilder().setAudienceType(AudienceType.TAG_AND).addAudienceTargetValues(tags).build();
    }
    public static AudienceTarget tag_not(String...tag) {
        return newBuilder().setAudienceType(AudienceType.TAG_NOT).addAudienceTargetValues(tag).build();
    }
    public static AudienceTarget tag_not(Collection<String> tags) {
        return newBuilder().setAudienceType(AudienceType.TAG_NOT).addAudienceTargetValues(tags).build();
    }
    public static AudienceTarget alias(String... alias) {
        return newBuilder().setAudienceType(AudienceType.ALIAS).addAudienceTargetValues(alias).build();
    }
    public static AudienceTarget alias(Collection<String> aliases) {
        return newBuilder().setAudienceType(AudienceType.ALIAS).addAudienceTargetValues(aliases).build();
    }
    public static AudienceTarget registrationId(String... registrationId) {
        return newBuilder().setAudienceType(AudienceType.REGISTRATION_ID).addAudienceTargetValues(registrationId).build();
    }
    public static AudienceTarget registrationId(Collection<String> registrationIds) {
        return newBuilder().setAudienceType(AudienceType.REGISTRATION_ID).addAudienceTargetValues(registrationIds).build();
    }
    public static AudienceTarget segment(String... segment) {
        return newBuilder().setAudienceType(AudienceType.SEGMENT).addAudienceTargetValues(segment).build();
    }
    public static AudienceTarget segment(Collection<String> segments) {
        return newBuilder().setAudienceType(AudienceType.SEGMENT).addAudienceTargetValues(segments).build();
    }
    public static AudienceTarget abTest(String... abTestId) {
        return newBuilder().setAudienceType(AudienceType.ABTEST).addAudienceTargetValues(abTestId).build();
    }
    public static AudienceTarget abTest(Collection<String> abTestIds) {
        return newBuilder().setAudienceType(AudienceType.ABTEST).addAudienceTargetValues(abTestIds).build();
    }
    public static AudienceTarget file(String fileId) {
        return newBuilder().setAudienceType(AudienceType.FILE).addFileId(fileId).build();
    }
    public AudienceType getAudienceType() {
        return this.audienceType;
    }
    public String getAudienceTypeValue() {
        return this.audienceType.value();
    }
    public JsonElement toJSON() {
        JsonArray array = new JsonArray();
        if (null != values) {
            for (String value : values) {
                array.add(new JsonPrimitive(value));
            }
        }
        return array;
    }
    public JsonElement toFileJSON() {
        JsonObject jsonObject = new JsonObject();
        if (null != values) {
            for (String value : values) {
                jsonObject.add("file_id", new JsonPrimitive(value));
            }
        }
        return jsonObject;
    }
    public static AudienceTarget fromJsonElement(JsonArray jsonArray, AudienceType type) {
        Set<String> stringSet = new HashSet<>();
        if (jsonArray != null) {
            for (int i=0; i<jsonArray.size(); i++) {
                stringSet.add(jsonArray.get(i).getAsString());
            }
        }
        return new AudienceTarget(type, stringSet);
    }
    public static class Builder {
        private AudienceType audienceType = null;
        private Set<String> valueBuilder = null;
        public Builder setAudienceType(AudienceType audienceType) {
            this.audienceType = audienceType;
            return this;
        }
        public Builder addAudienceTargetValue(String value) {
            if (null == valueBuilder) {
                valueBuilder = new HashSet<String>();
            }
            valueBuilder.add(value);
            return this;
        }
        public Builder addAudienceTargetValues(Collection<String> values) {
            if (null == valueBuilder) {
                valueBuilder = new HashSet<String>();
            }
            for (String value : values) {
                valueBuilder.add(value);
            }
            return this;
        }
        public Builder addAudienceTargetValues(String... values) {
            if (null == valueBuilder) {
                valueBuilder = new HashSet<String>();
            }
            for (String value : values) {
                valueBuilder.add(value);
            }
            return this;
        }
        public Builder addFileId(String fileId) {
            if (null == valueBuilder) {
                valueBuilder = new HashSet<String>();
            }
            valueBuilder.add(fileId);
            return this;
        }
        public AudienceTarget build() {
            Preconditions.checkArgument(null != audienceType, "AudienceType should be set.");
            Preconditions.checkArgument(null != valueBuilder, "Target values should be set one at least.");
            return new AudienceTarget(audienceType, valueBuilder);
        }
    }
}
service-push/src/main/java/cn/jpush/api/push/model/audience/AudienceType.java
New file
@@ -0,0 +1,33 @@
package cn.jpush.api.push.model.audience;
public enum AudienceType {
    TAG("tag"),
    TAG_AND("tag_and"),
    TAG_NOT("tag_not"),
    ALIAS("alias"),
    SEGMENT("segment"),
    ABTEST("abtest"),
    REGISTRATION_ID("registration_id"),
    FILE("file"),
    ;
    private final String value;
    private AudienceType(final String value) {
        this.value = value;
    }
    public String value() {
        return this.value;
    }
    public static AudienceType getType(String value) {
        for (AudienceType type: AudienceType.values()) {
            if (type.value.equals(value)) {
                return type;
            }
        }
        return null;
    }
}
service-push/src/main/java/cn/jpush/api/push/model/notification/AndroidNotification.java
New file
@@ -0,0 +1,448 @@
package cn.jpush.api.push.model.notification;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import java.util.Map;
/**
 * <p><b>Android 通知类</b></p>
 * <br>
 * 具体使用方法请参考官方文档
 * 支持 Android Notification 的参数:
 * <ul>
 * <li>alert: 继承自父类 PlatformNotification 的 alert 属性;本类设置则覆盖。</li>
 * <li>title: 支持 setTitle(string) 方法来设置;可替换展示App名称的地方。 </li>
 * <li>builder_id: 支持 setBuilderId(int) 方法来设置。android 8.0 开始建议采用NotificationChannel配置。 </li>
 * <li>channel_id: 支持 setChannelId(string) 方法来设置;不超过1000字节。</li>
 * <li>priority: 支持 setPriority(int) 方法来设置。可改变通知栏展示优先级</li>
 * <li>category: 支持 setCategory(string) 方法来设置。</li>
 * <li>style: 支持 setStyle(int) 方法来设置;可改变通知栏样式类型。</li>
 * <li>alert_type: 支持 setAlertType(int) 方法来设置;可改变通知方式。</li>
 * <li>big_text: 支持 setBigText(string) 方法来设置;可改变大文本通知栏样式。 </li>
 * <li>inbox: 支持 setInbox(JSONObject) 方法来设置;可改变文本条目通知栏样式。</li>
 * <li>big_pic_path: 支持 setBigPicPath(string) 方法来设置;可改变大图片通知栏样式。 </li>
 * <li>extras: 继承自父类 PlatformNotification 的 extras 属性;支持通过 addExtra(key, value) 来添加自定义字段,具体看代码。 </li>
 * <li>large_icon: 支持 setLargeIcon(string) 方法来设置;可设置通知栏大图标。</li>
 * <li>small_icon_uri: 支持 setSmallIconUri(string) 方法来设置;可设置通知栏小图标。</li>
 * <li>intent: 支持 setIntent(JSONObject) 方法来设置; 可指定跳转页面。</li>
 * <li>uri_activity: 支持 setUriActivity(string) 方法来设置; 可指定跳转页面。</li>
 * <li>uri_action: 支持 setUriAction(string) 方法来设置;可指定跳转页面。</li>
 * <li>badge_add_num: 支持 setBadgeAddNum(int) 方法来设置;可指定角标数字增加</li>
 * <li>badge_class: 支持 setBadgeClass(string) 方法来设置;配合badge_add_num使用,二者需要共存,缺一不可。</li>
 * <li>sound: 支持 setSound(string) 方法来设置声音文件;填写文件名称即可,无需文件名后缀。</li>
 * <li>show_begin_time: 支持 setShowBeginTime(string) 方法来设置;可定时展示开始时间。</li>
 * <li>show_end_time: 支持 setShowEndTime(string) 方法来设置;可定时展示结束时间。</li>
 * <li>display_foreground: 支持 setDisplayForeground(string) 方法来设置; 可设置app在前台,通知是否展示。</li>
 ** </ul>
 * <br>
 */
public class AndroidNotification extends PlatformNotification {
    public static final String NOTIFICATION_ANDROID = "android";
    private static final String TITLE = "title";
    private static final String BUILDER_ID = "builder_id";
    private static final String CHANNEL_ID = "channel_id";
    private static final String PRIORITY = "priority";
    private static final String CATEGORY = "category";
    private static final String STYLE = "style";
    private static final String ALERT_TYPE = "alert_type";
    private static final String BIG_TEXT = "big_text";
    private static final String INBOX = "inbox";
    private static final String BIG_PIC_PATH = "big_pic_path";
    private static final String LARGE_ICON = "large_icon";
    private static final String SMALL_ICON_URI = "small_icon_uri";
    private static final String INTENT = "intent";
    private static final String URI_ACTIVITY = "uri_activity";
    private static final String URI_ACTION = "uri_action";
    private static final String BADGE_ADD_NUM = "badge_add_num";
    private static final String BADGE_CLASS = "badge_class";
    private static final String SOUND = "sound";
    private static final String SHOW_BEGIN_TIME = "show_begin_time";
    private static final String SHOW_END_TIME = "show_end_time";
    private static final String DISPLAY_FOREGROUND = "display_foreground";
    private final String title;
    private final int builderId;
    private String channelId;
    // range from [-2 ~ 2], default is 0
    private int priority;
    private String category;
    // range from [0 ~ 3], default is 0. bigText=1, Inbox=2, bigPicture=3.
    private int style = 0;
    // range from [-1 ~ 7], default is -1
    private int alert_type;
    private String big_text;
    private Object inbox;
    private String big_pic_path;
    private String large_icon;
    private String small_icon_uri;
    private JsonObject intent;
    private String uri_activity;
    private String uri_action;
    // range from [1 ~ 99], suggest set to 1
    private int badge_add_num;
    private String badge_class;
    private String sound;
    private String show_begin_time;
    private String show_end_time;
    private String display_foreground;
    private AndroidNotification(Object alert,
                                String title,
                                int builderId,
                                String channelId,
                                int priority,
                                String category,
                                int style,
                                int alertType,
                                String bigText,
                                Object inbox,
                                String bigPicPath,
                                String large_icon,
                                String small_icon_uri,
                                JsonObject intent,
                                String uri_activity,
                                String uri_action,
                                int badge_add_num,
                                String badge_class,
                                String sound,
                                String show_begin_time,
                                String show_end_time,
                                String display_foreground,
                                Map<String, String> extras,
                                Map<String, Number> numberExtras,
                                Map<String, Boolean> booleanExtras,
                                Map<String, JsonObject> jsonExtras,
                                Map<String, JsonPrimitive> customData) {
        super(alert, extras, numberExtras, booleanExtras, jsonExtras, customData);
        this.title = title;
        this.builderId = builderId;
        this.channelId = channelId;
        this.priority = priority;
        this.category = category;
        this.style = style;
        this.alert_type = alertType;
        this.big_text = bigText;
        this.inbox = inbox;
        this.big_pic_path = bigPicPath;
        this.large_icon = large_icon;
        this.small_icon_uri = small_icon_uri;
        this.intent = intent;
        this.uri_activity = uri_activity;
        this.uri_action = uri_action;
        this.badge_add_num = badge_add_num;
        this.badge_class = badge_class;
        this.sound = sound;
        this.show_begin_time = show_begin_time;
        this.show_end_time = show_end_time;
        this.display_foreground = display_foreground;
    }
    public static Builder newBuilder() {
        return new Builder();
    }
    public static AndroidNotification alert(String alert) {
        return newBuilder().setAlert(alert).build();
    }
    @Override
    public String getPlatform() {
        return NOTIFICATION_ANDROID;
    }
    protected Object getInbox() {
        return this.inbox;
    }
    protected void setInbox(Object inbox) {
        this.inbox = inbox;
    }
    @Override
    public JsonElement toJSON() {
        JsonObject json = super.toJSON().getAsJsonObject();
        if (null != title) {
            json.add(TITLE, new JsonPrimitive(title));
        }
        if (builderId > 0) {
            json.add(BUILDER_ID, new JsonPrimitive(this.builderId));
        }
        if (null != channelId) {
            json.add(CHANNEL_ID, new JsonPrimitive(channelId));
        }
        // 默认为 0
        if (0 != priority) {
            json.add(PRIORITY, new JsonPrimitive(priority));
        }
        if (null != category) {
            json.add(CATEGORY, new JsonPrimitive(category));
        }
        // 默认是 0
        if (0 != style) {
            json.add(STYLE, new JsonPrimitive(this.style));
        }
        if (-1 != alert_type && alert_type <= 7) {
            json.add(ALERT_TYPE, new JsonPrimitive(this.alert_type));
        }
        if (null != big_text) {
            json.add(BIG_TEXT, new JsonPrimitive(this.big_text));
        }
        if (null != inbox) {
            if (inbox instanceof JsonObject) {
                json.add(INBOX, (JsonObject) inbox);
            }
        }
        if (null != big_pic_path) {
            json.add(BIG_PIC_PATH, new JsonPrimitive(this.big_pic_path));
        }
        if (null != large_icon) {
            json.add(LARGE_ICON, new JsonPrimitive(this.large_icon));
        }
        if (null != small_icon_uri) {
            json.add(SMALL_ICON_URI, new JsonPrimitive(this.small_icon_uri));
        }
        if (null != intent) {
            json.add(INTENT, intent);
        }
        if (null != uri_activity) {
            json.add(URI_ACTIVITY, new JsonPrimitive(this.uri_activity));
        }
        if (null != uri_action) {
            json.add(URI_ACTION, new JsonPrimitive(this.uri_action));
        }
        // 如果不填写,表示不改变角标数字
        if (0 != badge_add_num) {
            json.add(BADGE_ADD_NUM, new JsonPrimitive(this.badge_add_num));
        }
        if (null != badge_class) {
            json.add(BADGE_CLASS, new JsonPrimitive(this.badge_class));
        }
        if (null != sound) {
            json.add(SOUND, new JsonPrimitive(this.sound));
        }
        if (null != show_begin_time) {
            json.add(SHOW_BEGIN_TIME, new JsonPrimitive(this.show_begin_time));
        }
        if (null != show_end_time) {
            json.add(SHOW_END_TIME, new JsonPrimitive(this.show_end_time));
        }
        if (null != display_foreground) {
            json.add(DISPLAY_FOREGROUND, new JsonPrimitive(this.display_foreground));
        }
        return json;
    }
    public static class Builder extends PlatformNotification.Builder<AndroidNotification, Builder> {
        private String title;
        private int builderId;
        private String channelId;
        private int priority;
        private String category;
        private int style = 0;
        private int alert_type = -1;
        private String big_text;
        private Object inbox;
        private String big_pic_path;
        private String large_icon;
        private String small_icon_uri;
        private JsonObject intent;
        private String uri_activity;
        private String uri_action;
        private int badge_add_num;
        private String badge_class;
        private String sound;
        private String show_begin_time;
        private String show_end_time;
        private String display_foreground;
        @Override
        protected Builder getThis() {
            return this;
        }
        @Override
        public Builder setAlert(Object alert) {
            this.alert = alert;
            return this;
        }
        public Builder setTitle(String title) {
            this.title = title;
            return this;
        }
        public Builder setBuilderId(int builderId) {
            this.builderId = builderId;
            return this;
        }
        public String getChannelId() {
            return channelId;
        }
        public Builder setChannelId(String channelId) {
            this.channelId = channelId;
            return this;
        }
        public Builder setPriority(int priority) {
            this.priority = priority;
            return this;
        }
        public Builder setCategory(String category) {
            this.category = category;
            return this;
        }
        public Builder setStyle(int style) {
            this.style = style;
            return this;
        }
        public Builder setAlertType(int alertType) {
            this.alert_type = alertType;
            return this;
        }
        public Builder setBigText(String bigText) {
            this.big_text = bigText;
            return this;
        }
        public Builder setInbox(Object inbox) {
            if (null == inbox) {
                LOG.warn("Null inbox. Throw away it.");
                return this;
            }
            this.inbox = inbox;
            return this;
        }
        public Builder setBigPicPath(String bigPicPath) {
            this.big_pic_path = bigPicPath;
            return this;
        }
        public Builder setLargeIcon(String largeIcon) {
            this.large_icon = largeIcon;
            return this;
        }
        public Builder setSmallIconUri(String smallIconUri) {
            this.small_icon_uri = smallIconUri;
            return this;
        }
        public Builder setIntent(JsonObject intent) {
            if (null == intent) {
                LOG.warn("Null intent. Throw away it.");
                return this;
            }
            this.intent = intent;
            return this;
        }
        public Builder setUriActivity(String uriActivity) {
            this.uri_activity = uriActivity;
            return this;
        }
        public Builder setUriAction(String uriAction) {
            this.uri_action = uriAction;
            return this;
        }
        public Builder setBadgeAddNum(int badgeAddNum) {
            this.badge_add_num = badgeAddNum;
            return this;
        }
        public Builder setBadgeClass(String badgeClass) {
            this.badge_class = badgeClass;
            return this;
        }
        public Builder setSound(String sound) {
            this.sound = sound;
            return this;
        }
        public Builder setShowBeginTime(String showBeginTime) {
            this.show_begin_time = showBeginTime;
            return this;
        }
        public Builder setShowEndTime(String showEndTime) {
            this.show_end_time = showEndTime;
            return this;
        }
        public Builder setDisplayForeground(String displayForeground) {
            this.display_foreground = displayForeground;
            return this;
        }
        @Override
        public AndroidNotification build() {
            return new AndroidNotification(
                    alert,
                    title,
                    builderId,
                    channelId,
                    priority,
                    category,
                    style,
                    alert_type,
                    big_text,
                    inbox,
                    big_pic_path,
                    large_icon,
                    small_icon_uri,
                    intent,
                    uri_activity,
                    uri_action,
                    badge_add_num,
                    badge_class,
                    sound,
                    show_begin_time,
                    show_end_time,
                    display_foreground,
                    extrasBuilder,
                    numberExtrasBuilder,
                    booleanExtrasBuilder,
                    jsonExtrasBuilder,
                    super.customData
            );
        }
    }
}
service-push/src/main/java/cn/jpush/api/push/model/notification/InterfaceAdapter.java
New file
@@ -0,0 +1,33 @@
package cn.jpush.api.push.model.notification;
import com.google.gson.*;
import java.lang.reflect.Type;
/**
 * Created by Ken on 2016/10/25.
 */
public class InterfaceAdapter<T> implements JsonSerializer, JsonDeserializer<T> {
    @Override
    public T deserialize(JsonElement jsonElement, Type type,
                              JsonDeserializationContext jsonDeserializationContext)
            throws JsonParseException {
        JsonObject jsonObj = jsonElement.getAsJsonObject();
        String className = jsonObj.get("type").getAsString();
        try {
            Class<?> clz = Class.forName(className);
            return jsonDeserializationContext.deserialize(jsonElement, clz);
        } catch (ClassNotFoundException e) {
            throw new JsonParseException(e);
        }
    }
    @Override
    public JsonElement serialize(Object object, Type type,
                                 JsonSerializationContext jsonSerializationContext) {
        JsonElement jsonEle = jsonSerializationContext.serialize(object, object.getClass());
        jsonEle.getAsJsonObject().addProperty("type",
                object.getClass().getCanonicalName());
        return jsonEle;
    }
}
service-push/src/main/java/cn/jpush/api/push/model/notification/IosAlert.java
New file
@@ -0,0 +1,140 @@
package cn.jpush.api.push.model.notification;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import cn.jiguang.common.utils.StringUtils;
import cn.jpush.api.push.model.PushModel;
public class IosAlert implements PushModel {
    private final String title;
    private final String subtitle;
    private final String body;
    private final String title_loc_key;
    private final String[] title_loc_args;
    private final String action_loc_key;
    private final String loc_key;
    private final String[] loc_args;
    private final String launch_image;
    private IosAlert(String title, String subtitle, String body, String title_loc_key, String[] title_loc_args,
                    String action_loc_key, String loc_key, String[] loc_args, String launch_image) {
        this.title = title;
        this.subtitle = subtitle;
        this.body = body;
        this.title_loc_key = title_loc_key;
        this.title_loc_args = title_loc_args;
        this.action_loc_key = action_loc_key;
        this.loc_key = loc_key;
        this.loc_args = loc_args;
        this.launch_image = launch_image;
    }
    public static Builder newBuilder() {
        return new Builder();
    }
    @Override
    public JsonElement toJSON() {
        JsonObject json = new JsonObject();
        if( StringUtils.isNotEmpty(title) ) {
            json.addProperty("title", title);
        }
        if ( StringUtils.isNotEmpty(subtitle) ) {
            json.addProperty("subtitle", subtitle);
        }
        if( StringUtils.isNotEmpty(body) ) {
            json.addProperty("body", body);
        }
        if( StringUtils.isNotEmpty(title_loc_key) ) {
            json.addProperty("title-loc-key", title_loc_key);
            if( null != title_loc_args && title_loc_args.length > 0 ) {
                JsonArray args = new JsonArray();
                for(int i = 0; i < title_loc_args.length; i++) {
                    args.add(new JsonPrimitive(title_loc_args[i]));
                }
                json.add("title-loc-args", args);
            }
        }
        if( StringUtils.isNotEmpty(action_loc_key) ) {
            json.addProperty("action-loc-key", action_loc_key);
        }
        if( StringUtils.isNotEmpty(loc_key) ) {
            json.addProperty("loc-key", loc_key);
            if( null != loc_args && loc_args.length > 0 ) {
                JsonArray args = new JsonArray();
                for(int i = 0; i < loc_args.length; i++) {
                    args.add(new JsonPrimitive(loc_args[i]));
                }
                json.add("loc-args", args);
            }
        }
        if( StringUtils.isNotEmpty(launch_image) ) {
            json.addProperty("launch-image", launch_image);
        }
        return json;
    }
    @Override
    public String toString() {
        return gson.toJson(toJSON());
    }
    public static class Builder {
        private String title;
        private String subtitle;
        private String body;
        private String title_loc_key;
        private String[] title_loc_args;
        private String action_loc_key;
        private String loc_key;
        private String[] loc_args;
        private String launch_image;
        public Builder setTitleAndBody(String title, String subtitle, String body){
            this.title = title;
            this.subtitle = subtitle;
            this.body = body;
            return this;
        }
        public Builder setTitleLoc(String title_loc_key, String... title_loc_args) {
            this.title_loc_key = title_loc_key;
            this.title_loc_args = title_loc_args;
            return this;
        }
        public Builder setActionLocKey(String action_loc_key) {
            this.action_loc_key = action_loc_key;
            return this;
        }
        public Builder setLoc(String loc_key, String... loc_args) {
            this.loc_key = loc_key;
            this.loc_args = loc_args;
            return this;
        }
        public Builder setLaunchImage(String launch_image) {
            this.launch_image = launch_image;
            return this;
        }
        public IosAlert build() {
            return new IosAlert(title, subtitle, body, title_loc_key, title_loc_args, action_loc_key,
                    loc_key, loc_args, launch_image);
        }
    }
}
service-push/src/main/java/cn/jpush/api/push/model/notification/IosNotification.java
New file
@@ -0,0 +1,230 @@
package cn.jpush.api.push.model.notification;
import java.util.Map;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import cn.jiguang.common.ServiceHelper;
/**
 * <p><b>APNs 通知类</b></p>
 * <br>
 * 支持 APNs 默认的几个参数:
 * <ul>
 * <li>alert: 继承自父类 PlatformNotification 的 alert 属性;本类设置则覆盖。</li>
 * <li>badge: 支持 setBadge(int) 方法来设置;支持 incrBadge(int) 方法来增加。</li>
 * <li>sound: 支持 setSound(string) 方法来设置声音文件。或者 setSound(JSON object) 对应官方payload结构 </li>
 * <li>content-available: 用来支持后台推送。如果该值赋值为 1,表示开启后台推送。</li>
 * <li>mutable-content: 通知扩展</li>
 * <li>category: IOS 8 才支持。设置 APNs payload 中的 "category" 字段值</li>
 * <li>mutable-content: 通知扩展</li>
 * <li>extras: JSON object. 支持更多的自定义字段信息。</li>
 * </ul>
 * <br>
 * 需要特别留意的是,JPush SDK 会对以下几个值有特别的默认设置考虑:
 * <ul>
 * <li>badge: 默认为 "+1"。如果需要取消 badge 值,需要显式地调用 disableBadge()。</li>
 * <li>sound: 默认为 "",即默认的声音提示。如果需要取消 sound 值,即不要声音,需要显式地调用 disableSound()。</li>
 * </ul>
 */
public class IosNotification extends PlatformNotification {
    public static final String NOTIFICATION_IOS = "ios";
    private static final String DEFAULT_SOUND = "";
    private static final String DEFAULT_BADGE = "+1";
    private static final String BADGE = "badge";
    private static final String SOUND = "sound";
    private static final String CONTENT_AVAILABLE = "content-available";
    private static final String MUTABLE_CONTENT = "mutable-content";
    private static final String CATEGORY = "category";
    private static final String THREAD_ID = "thread-id";
    private static final String ALERT_VALID_BADGE = "Badge number should be 0~99999, "
            + "and can be prefixed with + to add, - to minus";
    private final boolean soundDisabled;
    private final boolean badgeDisabled;
    private final Object sound;
    private final String badge;
    private final boolean contentAvailable;
    private final String category;
    private final boolean mutableContent;
    private final String threadId;
    private IosNotification(Object alert, Object sound, String badge,
            boolean contentAvailable, boolean soundDisabled, boolean badgeDisabled,
            String category, boolean mutableContent,String threadId,
            Map<String, String> extras,
            Map<String, Number> numberExtras,
            Map<String, Boolean> booleanExtras,
            Map<String, JsonObject> jsonExtras,
            Map<String, JsonPrimitive> customData) {
        super(alert, extras, numberExtras, booleanExtras, jsonExtras, customData);
        this.sound = sound;
        this.badge = badge;
        this.contentAvailable = contentAvailable;
        this.soundDisabled = soundDisabled;
        this.badgeDisabled = badgeDisabled;
        this.category = category;
        this.mutableContent = mutableContent;
        this.threadId = threadId;
    }
    public static Builder newBuilder() {
        return new Builder();
    }
    public static IosNotification alert(String alert) {
        return newBuilder().setAlert(alert).build();
    }
    @Override
    public String getPlatform() {
        return NOTIFICATION_IOS;
    }
    @Override
    public JsonElement toJSON() {
        JsonObject json = super.toJSON().getAsJsonObject();
        if (!badgeDisabled) {
            if (null != badge) {
                json.add(BADGE, new JsonPrimitive(this.badge));
            } else {
                json.add(BADGE, new JsonPrimitive(DEFAULT_BADGE));
            }
        }
        if (!soundDisabled) {
            if (null != sound) {
                if(sound instanceof String){
                    json.add(SOUND, new JsonPrimitive((String)sound));
                }else if (sound instanceof JsonObject) {
                    json.add(SOUND,  (JsonObject) sound);
                }
            } else {
                json.add(SOUND, new JsonPrimitive(DEFAULT_SOUND));
            }
        }
        if (contentAvailable) {
            json.add(CONTENT_AVAILABLE, new JsonPrimitive(true));
        }
        if (null != category) {
            json.add(CATEGORY, new JsonPrimitive(category));
        }
        if (mutableContent) {
            json.add(MUTABLE_CONTENT, new JsonPrimitive(true));
        }
        if (null != threadId) {
            json.add(THREAD_ID, new JsonPrimitive(threadId));
        }
        return json;
    }
    public static class Builder extends PlatformNotification.Builder<IosNotification, Builder> {
        private Object sound;
        private String badge;
        private boolean contentAvailable = false;
        private boolean soundDisabled = false;
        private boolean badgeDisabled = false;
        private String category;
        private boolean mutableContent;
        private String threadId;
        @Override
        protected Builder getThis() {
            return this;
        }
        public Builder setSound(Object sound) {
            if (null == sound) {
                LOG.warn("Null sound. Throw away it.");
                return this;
            }
            this.sound = sound;
            return this;
        }
        public Builder disableSound() {
            this.soundDisabled = true;
            return this;
        }
        public Builder incrBadge(int badge) {
            if (!ServiceHelper.isValidIntBadge(Math.abs(badge))) {
                LOG.warn(ALERT_VALID_BADGE);
                return this;
            }
            if (badge >= 0) {
                this.badge = "+" + badge;
            } else {
                this.badge = "" + badge;
            }
            return this;
        }
        public Builder setBadge(int badge) {
            if (!ServiceHelper.isValidIntBadge(badge)) {
                LOG.warn(ALERT_VALID_BADGE);
                return this;
            }
            this.badge = "" + badge;
            return this;
        }
        /**
         * equals to: +1
         * @return IosNotification builder
         */
        public Builder autoBadge() {
            return incrBadge(1);
        }
        public Builder disableBadge() {
            this.badgeDisabled = true;
            return this;
        }
        public Builder setContentAvailable(boolean contentAvailable) {
            this.contentAvailable = contentAvailable;
            return this;
        }
        public Builder setCategory(String category) {
            this.category = category;
            return this;
        }
        @Override
        public Builder setAlert(Object alert) {
            this.alert = alert;
            return this;
        }
        public Builder setMutableContent(boolean mutableContent) {
            this.mutableContent = mutableContent;
            return this;
        }
        public Builder setThreadId(String threadId) {
            this.threadId = threadId;
            return this;
        }
        public IosNotification build() {
            return new IosNotification(alert, sound, badge, contentAvailable,
                    soundDisabled, badgeDisabled, category, mutableContent, threadId,
                    extrasBuilder, numberExtrasBuilder, booleanExtrasBuilder, jsonExtrasBuilder, super.customData);
        }
    }
}
service-push/src/main/java/cn/jpush/api/push/model/notification/Notification.java
New file
@@ -0,0 +1,272 @@
package cn.jpush.api.push.model.notification;
import java.util.*;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import cn.jiguang.common.utils.Preconditions;
import cn.jpush.api.push.model.PushModel;
public class Notification implements PushModel {
    private static final String AI_OPPORTUNITY = "ai_opportunity";
    private static final String VOIP = "voip";
    // default is false
    private boolean aiOpportunity;
    private final Object alert;
    private final Set<PlatformNotification> notifications;
    private final Map<String, String> voip;
    private final Map<String, Number> numberVoip;
    private final Map<String, Boolean> booleanVoip;
    private final Map<String, JsonObject> jsonVoip;
    private Notification(boolean aiOpportunity, Object alert, Set<PlatformNotification> notifications,
                         Map<String, String> voip, Map<String, Number> numberVoip, Map<String, Boolean> booleanVoip,
                         Map<String, JsonObject> jsonVoip) {
        this.aiOpportunity = aiOpportunity;
        this.alert = alert;
        this.notifications = notifications;
        this.voip = voip;
        this.numberVoip = numberVoip;
        this.booleanVoip = booleanVoip;
        this.jsonVoip = jsonVoip;
    }
    public static Builder newBuilder() {
        return new Builder();
    }
    public static Notification aiOpportunity(boolean ai_opportunity) {
        return newBuilder().setAiOpportunity(ai_opportunity).build();
    }
    /**
     * Quick set all platform alert.
     * Platform notification can override this alert.
     *
     * @param alert Notification alert
     * @return first level notification object
     */
    public static Notification alert(Object alert) {
        return newBuilder().setAlert(alert).build();
    }
    public static Notification android(String alert, String title, Map<String, String> extras) {
        return newBuilder()
                .addPlatformNotification(AndroidNotification.newBuilder()
                    .setAlert(alert)
                    .setTitle(title)
                    .addExtras(extras)
                    .build())
                .build();
    }
    public static Notification ios(Object alert, Map<String, String> extras) {
        return newBuilder()
                .addPlatformNotification(IosNotification.newBuilder()
                    .setAlert(alert)
                    .addExtras(extras)
                    .build())
                .build();
    }
    public static Notification ios_auto_badge() {
        return newBuilder()
                .addPlatformNotification(IosNotification.newBuilder()
                    .setAlert("")
                    .autoBadge()
                    .build())
                .build();
    }
    public static Notification ios_set_badge(int badge) {
        return newBuilder()
                .addPlatformNotification(IosNotification.newBuilder()
                    .setAlert("")
                    .setBadge(badge)
                    .build())
                .build();
    }
    public static Notification ios_incr_badge(int badge) {
        return newBuilder()
                .addPlatformNotification(IosNotification.newBuilder()
                    .setAlert("")
                    .incrBadge(badge)
                    .build())
                .build();
    }
    public static Notification winphone(String alert, Map<String, String> extras) {
        return newBuilder()
                .addPlatformNotification(WinphoneNotification.newBuilder()
                    .setAlert(alert)
                    .addExtras(extras)
                    .build())
                .build();
    }
    public JsonElement toJSON() {
        JsonObject json = new JsonObject();
        json.addProperty(AI_OPPORTUNITY, aiOpportunity);
        if (null != alert) {
            if(alert instanceof JsonObject) {
                json.add(PlatformNotification.ALERT, (JsonObject) alert);
            } else if (alert instanceof IosAlert) {
                json.add(PlatformNotification.ALERT, ((IosAlert) alert).toJSON());
            } else {
                json.add(PlatformNotification.ALERT, new JsonPrimitive(alert.toString()));
            }
        }
        if (null != notifications) {
            for (PlatformNotification pn : notifications) {
                if (this.alert != null && pn.getAlert() == null) {
                    pn.setAlert(this.alert);
                }
                Preconditions.checkArgument(! (null == pn.getAlert()),
                        "For any platform notification, alert field is needed. It can be empty string.");
                json.add(pn.getPlatform(), pn.toJSON());
            }
        }
        JsonObject extrasObject = null;
        if (null != voip || null != numberVoip || null != booleanVoip || null != jsonVoip) {
            extrasObject = new JsonObject();
        }
        if (null != voip) {
            String value = null;
            for (String key : voip.keySet()) {
                value = voip.get(key);
                if (null != value) {
                    extrasObject.add(key, new JsonPrimitive(value));
                }
            }
        }
        if (null != numberVoip) {
            Number value = null;
            for (String key : numberVoip.keySet()) {
                value = numberVoip.get(key);
                if (null != value) {
                    extrasObject.add(key, new JsonPrimitive(value));
                }
            }
        }
        if (null != booleanVoip) {
            Boolean value = null;
            for (String key : booleanVoip.keySet()) {
                value = booleanVoip.get(key);
                if (null != value) {
                    extrasObject.add(key, new JsonPrimitive(value));
                }
            }
        }
        if (null != jsonVoip) {
            JsonObject value = null;
            for (String key : jsonVoip.keySet()) {
                value = jsonVoip.get(key);
                if (null != value) {
                    extrasObject.add(key, value);
                }
            }
        }
        if (null != voip || null != numberVoip || null != booleanVoip || null != jsonVoip) {
            json.add(VOIP, extrasObject);
        }
        return json;
    }
    public static class Builder {
        private boolean aiOpportunity;
        private Object alert;
        private Set<PlatformNotification> builder;
        protected Map<String, String> voipBuilder;
        protected  Map<String, Number> numberVoipBuilder;
        protected  Map<String, Boolean> booleanVoipBuilder;
        protected  Map<String, JsonObject> jsonVoipBuilder;
        public Builder setAiOpportunity(boolean aiOpportunity) {
            this.aiOpportunity = aiOpportunity;
            return this;
        }
        public Builder setAlert(Object alert) {
            this.alert = alert;
            return this;
        }
        public Builder addPlatformNotification(PlatformNotification notification) {
            if (null == builder) {
                builder = new HashSet<PlatformNotification>();
            }
            builder.add(notification);
            return this;
        }
        public Builder addVoip(String key, String value) {
            Preconditions.checkArgument(! (null == key || null == value), "Key/Value should not be null.");
            if (null == voipBuilder) {
                voipBuilder = new HashMap<String, String>();
            }
            voipBuilder.put(key, value);
            return this;
        }
        public Builder addVoip(Map<String, String> voip) {
            Preconditions.checkArgument(! (null == voip), "extras should not be null.");
            if (null == voipBuilder) {
                voipBuilder = new HashMap<String, String>();
            }
            for (String key : voip.keySet()) {
                voipBuilder.put(key, voip.get(key));
            }
            return this;
        }
        public Builder addVoip(String key, Number value) {
            Preconditions.checkArgument(! (null == key || null == value), "Key/Value should not be null.");
            if (null == numberVoipBuilder) {
                numberVoipBuilder = new HashMap<String, Number>();
            }
            numberVoipBuilder.put(key, value);
            return this;
        }
        public Builder addVoip(String key, Boolean value) {
            Preconditions.checkArgument(! (null == key || null == value), "Key/Value should not be null.");
            if (null == booleanVoipBuilder) {
                booleanVoipBuilder = new HashMap<String, Boolean>();
            }
            booleanVoipBuilder.put(key, value);
            return this;
        }
        public Builder addVoip(String key, JsonObject value) {
            Preconditions.checkArgument(! (null == key || null == value), "Key/Value should not be null.");
            if (null == jsonVoipBuilder) {
                jsonVoipBuilder = new HashMap<String, JsonObject>();
            }
            jsonVoipBuilder.put(key, value);
            return this;
        }
        public Notification build() {
            Preconditions.checkArgument(! (null == builder && null == alert),
                    "No notification payload is set.");
            return new Notification(aiOpportunity, alert, builder, voipBuilder, numberVoipBuilder, booleanVoipBuilder
            , jsonVoipBuilder);
        }
    }
}
service-push/src/main/java/cn/jpush/api/push/model/notification/PlatformNotification.java
New file
@@ -0,0 +1,249 @@
package cn.jpush.api.push.model.notification;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import cn.jiguang.common.utils.Preconditions;
import cn.jpush.api.push.model.PushModel;
public abstract class PlatformNotification implements PushModel {
    public static final String ALERT = "alert";
    private static final String EXTRAS = "extras";
    protected static final Logger LOG = LoggerFactory.getLogger(PlatformNotification.class);
    private Object alert;
    private final Map<String, String> extras;
    private final Map<String, Number> numberExtras;
    private final Map<String, Boolean> booleanExtras;
    private final Map<String, JsonObject> jsonExtras;
    private final Map<String, JsonPrimitive> customData;
    public PlatformNotification(Object alert, Map<String, String> extras,
            Map<String, Number> numberExtras,
            Map<String, Boolean> booleanExtras,
            Map<String, JsonObject> jsonExtras) {
        this.alert = alert;
        this.extras = extras;
        this.numberExtras = numberExtras;
        this.booleanExtras = booleanExtras;
        this.jsonExtras = jsonExtras;
        customData = new LinkedHashMap<>();
    }
    public PlatformNotification(Object alert, Map<String, String> extras,
                                Map<String, Number> numberExtras,
                                Map<String, Boolean> booleanExtras,
                                Map<String, JsonObject> jsonExtras,
                                Map<String, JsonPrimitive> customData) {
        this.alert = alert;
        this.extras = extras;
        this.numberExtras = numberExtras;
        this.booleanExtras = booleanExtras;
        this.jsonExtras = jsonExtras;
        this.customData = customData;
    }
    @Override
    public JsonElement toJSON() {
        JsonObject json = new JsonObject();
        if (null != alert) {
            if ( alert instanceof JsonObject) {
                json.add(ALERT, (JsonObject) alert);
            } else if (alert instanceof IosAlert) {
                json.add(ALERT, ((IosAlert) alert).toJSON());
            } else {
                json.add(ALERT, new JsonPrimitive(alert.toString()));
            }
        }
        JsonObject extrasObject = null;
        if (null != extras || null != numberExtras || null != booleanExtras || null != jsonExtras) {
            extrasObject = new JsonObject();
        }
        if (null != extras) {
            String value = null;
            for (String key : extras.keySet()) {
                value = extras.get(key);
                if (null != value) {
                    extrasObject.add(key, new JsonPrimitive(value));
                }
            }
        }
        if (null != numberExtras) {
            Number value = null;
            for (String key : numberExtras.keySet()) {
                value = numberExtras.get(key);
                if (null != value) {
                    extrasObject.add(key, new JsonPrimitive(value));
                }
            }
        }
        if (null != booleanExtras) {
            Boolean value = null;
            for (String key : booleanExtras.keySet()) {
                value = booleanExtras.get(key);
                if (null != value) {
                    extrasObject.add(key, new JsonPrimitive(value));
                }
            }
        }
        if (null != jsonExtras) {
            JsonObject value = null;
            for (String key : jsonExtras.keySet()) {
                value = jsonExtras.get(key);
                if (null != value) {
                    extrasObject.add(key, value);
                }
            }
        }
        if (null != extras || null != numberExtras || null != booleanExtras || null != jsonExtras) {
            json.add(EXTRAS, extrasObject);
        }
        if (null != customData) {
            for (Map.Entry<String, JsonPrimitive> entry : customData.entrySet()) {
                json.add(entry.getKey(), entry.getValue());
            }
        }
        return json;
    }
    protected Object getAlert() {
        return this.alert;
    }
    protected void setAlert(Object alert) {
        this.alert = alert;
    }
    protected abstract String getPlatform();
    protected abstract static class Builder<T extends PlatformNotification, B extends Builder<T, B>> {
        private B theBuilder;
        protected Object alert;
        protected Map<String, String> extrasBuilder;
        protected Map<String, Number> numberExtrasBuilder;
        protected Map<String, Boolean> booleanExtrasBuilder;
        protected Map<String, JsonObject> jsonExtrasBuilder;
        protected Map<String, JsonPrimitive> customData;
        public Builder () {
            customData = new LinkedHashMap<>();
            theBuilder = getThis();
        }
        protected abstract B getThis();
        public abstract B setAlert(Object alert);
        public B addExtra(String key, String value) {
            Preconditions.checkArgument(! (null == key), "Key should not be null.");
            if (null == value) {
                LOG.debug("Extra value is null, throw away it.");
                return theBuilder;
            }
            if (null == extrasBuilder) {
                extrasBuilder = new HashMap<String, String>();
            }
            extrasBuilder.put(key, value);
            return theBuilder;
        }
        public B addExtras(Map<String, String> extras) {
            if (null == extras) {
                LOG.warn("Null extras param. Throw away it.");
                return theBuilder;
            }
            if (null == extrasBuilder) {
                extrasBuilder = new HashMap<String, String>();
            }
            for (String key : extras.keySet()) {
                extrasBuilder.put(key, extras.get(key));
            }
            return theBuilder;
        }
        public B addExtra(String key, Number value) {
            Preconditions.checkArgument(! (null == key), "Key should not be null.");
            if (null == value) {
                LOG.debug("Extra value is null, throw away it.");
                return theBuilder;
            }
            if (null == numberExtrasBuilder) {
                numberExtrasBuilder = new HashMap<String, Number>();
            }
            numberExtrasBuilder.put(key, value);
            return theBuilder;
        }
        public B addExtra(String key, Boolean value) {
            Preconditions.checkArgument(! (null == key), "Key should not be null.");
            if (null == value) {
                LOG.debug("Extra value is null, throw away it.");
                return theBuilder;
            }
            if (null == booleanExtrasBuilder) {
                booleanExtrasBuilder = new HashMap<String, Boolean>();
            }
            booleanExtrasBuilder.put(key, value);
            return theBuilder;
        }
        public B addExtra(String key, JsonObject value) {
            Preconditions.checkArgument(! (null == key), "Key should not be null.");
            if (null == value) {
                LOG.debug("Extra value is null, throw away it.");
                return theBuilder;
            }
            if (null == jsonExtrasBuilder) {
                jsonExtrasBuilder = new HashMap<String, JsonObject>();
            }
            jsonExtrasBuilder.put(key, value);
            return theBuilder;
        }
        public B addCustom(Map<String, String> extras) {
            for (Map.Entry<String, String> entry : extras.entrySet()) {
                customData.put(entry.getKey(), new JsonPrimitive(entry.getValue()));
            }
            return theBuilder;
        }
        public B addCustom(String key, Number value) {
            Preconditions.checkArgument(! (null == key), "Key should not be null.");
            customData.put(key, new JsonPrimitive(value));
            return theBuilder;
        }
        public B addCustom(String key, String value) {
            Preconditions.checkArgument(! (null == key), "Key should not be null.");
            customData.put(key, new JsonPrimitive(value));
            return theBuilder;
        }
        public B addCustom(String key, Boolean value) {
            Preconditions.checkArgument(! (null == key), "Key should not be null.");
            customData.put(key, new JsonPrimitive(value));
            return theBuilder;
        }
        public abstract T build();
    }
}
service-push/src/main/java/cn/jpush/api/push/model/notification/QuickappNotification.java
New file
@@ -0,0 +1,102 @@
package cn.jpush.api.push.model.notification;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import java.util.Map;
/**
 * <p><b>QuickApp 通知类</b></p>
 * <br>
 * 具体使用方法请参考官方文档 https://docs.jiguang.cn/jpush/server/push/rest_api_v3_push/#platform
 * 支持 QuickApp Notification 的参数:
 * <ul>
 * <li>alert: 继承自父类 PlatformNotification 的 alert 属性;本类设置则覆盖。</li>
 * <li>title: 支持 setTitle(string) 方法来设置;可替换快应用推送通知的标题。 </li>
 * <li>page: 支持 setPage(String) 方法来设置;可设置快应用跳转页面</li>
 * <li>extras: 继承自父类 PlatformNotification 的 extras 属性;支持通过 addExtra(key, value) 来添加自定义字段,具体看代码。 </li>
 * </ul>
 * <br>
 */
public class QuickappNotification extends PlatformNotification {
    private static final String NOTIFICATION_QUICKAPP = "quickapp";
    private static final String TITLE = "title";
    private static final String PAGE = "page";
    private final String title;
    private final String page;
    private QuickappNotification(Object alert, String title, String page,
                                 Map<String, String> extras,
                                 Map<String, Number> numberExtras,
                                 Map<String, Boolean> booleanExtras,
                                 Map<String, JsonObject> jsonExtras,
                                 Map<String, JsonPrimitive> customData) {
        super(alert, extras, numberExtras, booleanExtras, jsonExtras, customData);
        this.title = title;
        this.page = page;
    }
    public static Builder newBuilder() {
        return new Builder();
    }
    public static QuickappNotification alert(String alert) {
        return newBuilder().setAlert(alert).build();
    }
    @Override
    public String getPlatform() {
        return NOTIFICATION_QUICKAPP;
    }
    @Override
    public JsonElement toJSON() {
        JsonObject json = super.toJSON().getAsJsonObject();
        if (null != title) {
            json.add(TITLE, new JsonPrimitive(title));
        }
        if (null != page) {
            json.add(PAGE, new JsonPrimitive(page));
        }
        return json;
    }
    public static class Builder extends PlatformNotification.Builder<QuickappNotification, Builder> {
        private String title;
        private String page;
        @Override
        protected Builder getThis() {
            return this;
        }
        public Builder setTitle(String title) {
            this.title = title;
            return this;
        }
        public Builder setPage(String page) {
            this.page = page;
            return this;
        }
        @Override
        public Builder setAlert(Object alert) {
            this.alert = alert;
            return this;
        }
        public QuickappNotification build() {
            return new QuickappNotification(alert, title, page,
                    extrasBuilder, numberExtrasBuilder, booleanExtrasBuilder, jsonExtrasBuilder, super.customData);
        }
    }
}
service-push/src/main/java/cn/jpush/api/push/model/notification/WinphoneNotification.java
New file
@@ -0,0 +1,103 @@
package cn.jpush.api.push.model.notification;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import java.util.Map;
/**
 * <p><b>Windows Phone 通知类</b></p>
 * <br>
 * 具体使用方法请参考官方文档 https://docs.jiguang.cn/jpush/server/push/rest_api_v3_push/#platform
 * 支持 Winphone Notification 的参数:
 * <ul>
 * <li>alert: 继承自父类 PlatformNotification 的 alert 属性;本类设置则覆盖。</li>
 * <li>title: 支持 setTitle(string) 方法来设置;可替换通知标题。 </li>
 * <li>_open_page: 支持 setOpenPage(String) 方法来设置;可设置点击打开的页面名称</li>
 * <li>extras: 继承自父类 PlatformNotification 的 extras 属性;支持通过 addExtra(key, value) 来添加自定义字段,具体看代码。 </li>
 * </ul>
 * <br>
 */
public class WinphoneNotification extends PlatformNotification {
    private static final String NOTIFICATION_WINPHONE = "winphone";
    private static final String TITLE = "title";
    private static final String _OPEN_PAGE = "_open_page";
    private final String title;
    private final String openPage;
    private WinphoneNotification(Object alert, String title, String openPage,
            Map<String, String> extras,
            Map<String, Number> numberExtras,
            Map<String, Boolean> booleanExtras,
            Map<String, JsonObject> jsonExtras,
            Map<String, JsonPrimitive> customData) {
        super(alert, extras, numberExtras, booleanExtras, jsonExtras, customData);
        this.title = title;
        this.openPage = openPage;
    }
    public static Builder newBuilder() {
        return new Builder();
    }
    public static WinphoneNotification alert(String alert) {
        return newBuilder().setAlert(alert).build();
    }
    @Override
    public String getPlatform() {
        return NOTIFICATION_WINPHONE;
    }
    @Override
    public JsonElement toJSON() {
        JsonObject json = super.toJSON().getAsJsonObject();
        if (null != title) {
            json.add(TITLE, new JsonPrimitive(title));
        }
        if (null != openPage) {
            json.add(_OPEN_PAGE, new JsonPrimitive(openPage));
        }
        return json;
    }
    public static class Builder extends PlatformNotification.Builder<WinphoneNotification, Builder> {
        private String title;
        private String openPage;
        @Override
        protected Builder getThis() {
            return this;
        }
        public Builder setTitle(String title) {
            this.title = title;
            return this;
        }
        public Builder setOpenPage(String openPage) {
            this.openPage = openPage;
            return this;
        }
        @Override
        public Builder setAlert(Object alert) {
            this.alert = alert;
            return this;
        }
        public WinphoneNotification build() {
            return new WinphoneNotification(alert, title, openPage,
                    extrasBuilder, numberExtrasBuilder, booleanExtrasBuilder, jsonExtrasBuilder, super.customData);
        }
    }
}
service-push/src/main/java/cn/jpush/api/push/package-info.java
New file
@@ -0,0 +1,6 @@
/**
 * Push API features.
 *
 * Url: https://api.jpush.cn/v3/push
 */
package cn.jpush.api.push;
service-push/src/main/java/cn/jpush/api/report/GroupMessageDetailResult.java
New file
@@ -0,0 +1,91 @@
package cn.jpush.api.report;
import cn.jiguang.common.resp.BaseResult;
import cn.jiguang.common.resp.ResponseWrapper;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.annotations.Expose;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
public class GroupMessageDetailResult extends BaseResult {
    // what is this?
    private static final Type JSON_OBJECT_TYPE = new TypeToken<List<JsonObject>>() {}.getType();
    private static final Type RECEIVED_TYPE = new TypeToken<List<Received>>() {}.getType();
    @Expose
    public List<Received> received_list = new ArrayList<>();
    public static class Received {
        @Expose
        public String group_msgids;
        @Expose
        public JpushDetail jpush;
        @Expose
        public JsonObject android_pns;
        @Expose
        public IosDetail ios;
        @Expose
        public WinphoeDetail winphone;
    }
    public static class JpushDetail {
        @Expose
        public long target;
        @Expose
        public int online_push;
        @Expose
        public int received;
        @Expose
        public int click;
        @Expose
        public int msg_click;
    }
    public static class WinphoeDetail {
        @Expose
        public long mpns_target;
        @Expose
        public int mpns_sent;
        @Expose
        public int click;
    }
    public static class IosDetail {
        @Expose
        public long apns_target;
        @Expose
        public int apns_sent;
        @Expose
        public int apns_received;
        @Expose
        public int apns_click;
        @Expose
        public int msg_target;
        @Expose
        public int msg_received;
    }
    static GroupMessageDetailResult fromResponse(ResponseWrapper responseWrapper) {
        GroupMessageDetailResult result = new GroupMessageDetailResult();
        if (responseWrapper.isServerResponse()) {
            List<JsonObject> tempList = _gson.fromJson(responseWrapper.responseContent, JSON_OBJECT_TYPE);
            for (JsonObject jsonObject : tempList) {
                if (jsonObject.isJsonNull() || jsonObject.get("android_pns").isJsonNull()) {
                    continue;
                }
                Received received = _gson.fromJson(jsonObject, Received.class);
                result.received_list.add(received);
            }
        }
        result.setResponseWrapper(responseWrapper);
        return result;
    }
}
service-push/src/main/java/cn/jpush/api/report/GroupUsersResult.java
New file
@@ -0,0 +1,40 @@
package cn.jpush.api.report;
import java.util.ArrayList;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import cn.jiguang.common.TimeUnit;
import cn.jiguang.common.resp.BaseResult;
public class GroupUsersResult extends BaseResult {
    @Expose public TimeUnit time_unit;
    @Expose public String start;
    @Expose public int duration;
    @Expose public List<User> items = new ArrayList<User>();
    public static class User {
        @Expose public String time;
        @Expose public Android android;
        @Expose public Ios ios;
    }
    public static class Android {
        @SerializedName("new") @Expose public long add;
        @Expose public int online;
        @Expose public int active;
    }
    public static class Ios {
        @SerializedName("new") @Expose public long add;
        @Expose public int online;
        @Expose public int active;
    }
}
service-push/src/main/java/cn/jpush/api/report/MessageDetailResult.java
New file
@@ -0,0 +1,86 @@
package cn.jpush.api.report;
import cn.jiguang.common.resp.BaseResult;
import cn.jiguang.common.resp.ResponseWrapper;
import com.google.gson.JsonObject;
import com.google.gson.annotations.Expose;
import com.google.gson.reflect.TypeToken;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
public class MessageDetailResult extends BaseResult {
    private static final Type RECEIVED_TYPE = new TypeToken<List<Received>>() {}.getType();
    private static final long serialVersionUID = 156439166846147394L;
    @Expose
    public List<Received> received_list = new ArrayList<Received>();
    public static class Received {
        @Expose
        public long msg_id;
        @Expose
        public JpushDetail jpush;
        @Expose
        public JsonObject android_pns;
        @Expose
        public JsonObject details;
        @Expose
        public IosDetail ios;
        @Expose
        public WinphoeDetail winphone;
    }
    public static class JpushDetail {
        @Expose
        public long target;
        @Expose
        public int online_push;
        @Expose
        public int received;
        @Expose
        public int click;
        @Expose
        public int msg_click;
    }
    public static class WinphoeDetail {
        @Expose
        public long mpns_target;
        @Expose
        public int mpns_sent;
        @Expose
        public int click;
    }
    public static class IosDetail {
        @Expose
        public long apns_target;
        @Expose
        public int apns_sent;
        @Expose
        public int apns_received;
        @Expose
        public int apns_click;
        @Expose
        public int msg_target;
        @Expose
        public int msg_received;
    }
    static MessageDetailResult fromResponse(ResponseWrapper responseWrapper) {
        MessageDetailResult result = new MessageDetailResult();
        if (responseWrapper.isServerResponse()) {
            result.received_list = _gson.fromJson(responseWrapper.responseContent, RECEIVED_TYPE);
        }
        result.setResponseWrapper(responseWrapper);
        return result;
    }
}
service-push/src/main/java/cn/jpush/api/report/MessageStatus.java
New file
@@ -0,0 +1,28 @@
package cn.jpush.api.report;
import java.io.Serializable;
public class MessageStatus implements Serializable {
    /**
     * 0  送达
     * 1  未送达
     * 2  用户不属于该 app
     * 3  用户属于该 app,但不属于该 msgid
     * 4  系统异常
     */
    private int status;
    public void setStatus(int status) {
        this.status = status;
    }
    public int getStatus() {
        return status;
    }
    @Override
    public String toString() {
        return "status : " + status;
    }
}
service-push/src/main/java/cn/jpush/api/report/MessagesResult.java
New file
@@ -0,0 +1,78 @@
package cn.jpush.api.report;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.reflect.TypeToken;
import cn.jiguang.common.resp.BaseResult;
import cn.jiguang.common.resp.ResponseWrapper;
public class MessagesResult extends BaseResult {
    private static final Type MESSAGE_TYPE = new TypeToken<List<Message>>() {}.getType();
    private static final long serialVersionUID = -1582895355000647292L;
    @Expose
    public List<Message> messages = new ArrayList<Message>();
    public static class Message {
        @Expose
        public String msg_id;
        @Expose
        public Android android;
        @Expose
        public Ios ios;
        @Expose
        public Winphone winphone;
    }
    public static class Android {
        @Expose
        public int received;
        @Expose
        public int target;
        @Expose
        public int online_push;
        @Expose
        public int click;
        @Expose
        public int msg_click;
    }
    public static class Ios {
        @Expose
        public int apns_sent;
        @Expose
        public int apns_target;
        @Expose
        public int apns_received;
        @Expose
        public int click;
        @Expose
        public int target;
        @Expose
        public int received;
    }
    public static class Winphone {
        @Expose
        public int mpns_target;
        @Expose
        public int mpns_sent;
        @Expose
        public int click;
    }
    static MessagesResult fromResponse(ResponseWrapper responseWrapper) {
        MessagesResult result = new MessagesResult();
        if (responseWrapper.isServerResponse()) {
            result.messages = _gson.fromJson(responseWrapper.responseContent, MESSAGE_TYPE);
        }
        result.setResponseWrapper(responseWrapper);
        return result;
    }
}
service-push/src/main/java/cn/jpush/api/report/ReceivedsResult.java
New file
@@ -0,0 +1,56 @@
package cn.jpush.api.report;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.reflect.TypeToken;
import cn.jiguang.common.resp.BaseResult;
import cn.jiguang.common.resp.ResponseWrapper;
public class ReceivedsResult extends BaseResult {
    private static final Type RECEIVED_TYPE = new TypeToken<List<Received>>() {}.getType();
    private static final long serialVersionUID = 1761456104618847304L;
    @Expose
    public List<Received> received_list = new ArrayList<Received>();
    public static class Received {
        @Expose
        public long msg_id;
        @Expose
        public int android_pns_sent;
        @Expose
        public int android_pns_received;
        @Expose
        public int quickapp_jpush_received;
        @Expose
        public int quickapp_pns_sent;
        @Expose
        public int android_received;
        @Expose
        public int ios_apns_sent;
        @Expose
        public int ios_apns_received;
        @Expose
        public int ios_msg_received;
        @Expose
        public int wp_mpns_sent;
        @Expose
        public int jpush_received;
    }
    static ReceivedsResult fromResponse(ResponseWrapper responseWrapper) {
        ReceivedsResult result = new ReceivedsResult();
        if (responseWrapper.isServerResponse()) {
            result.received_list = _gson.fromJson(responseWrapper.responseContent, RECEIVED_TYPE);
        }
        result.setResponseWrapper(responseWrapper);
        return result;
    }
}
service-push/src/main/java/cn/jpush/api/report/ReportClient.java
New file
@@ -0,0 +1,231 @@
package cn.jpush.api.report;
import cn.jiguang.common.ClientConfig;
import cn.jiguang.common.ServiceHelper;
import cn.jiguang.common.TimeUnit;
import cn.jiguang.common.connection.HttpProxy;
import cn.jiguang.common.connection.NativeHttpClient;
import cn.jiguang.common.resp.APIConnectionException;
import cn.jiguang.common.resp.APIRequestException;
import cn.jiguang.common.resp.BaseResult;
import cn.jiguang.common.resp.ResponseWrapper;
import cn.jiguang.common.utils.StringUtils;
import cn.jpush.api.report.model.CheckMessagePayload;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.net.URLEncoder;
import java.util.Map;
import java.util.regex.Pattern;
public class ReportClient {
    private final NativeHttpClient _httpClient;
    private String _hostName;
    private String _receivePath;
    private String _userPath;
    private String _messagePath;
    private String _statusPath;
    private String messageDetailPath;
    private String receiveDetailPath;
    private String groupMessageDetailPath;
    private String groupUserPath;
    public ReportClient(String masterSecret, String appKey) {
        this(masterSecret, appKey, null, ClientConfig.getInstance());
    }
    /**
     * This will be removed in the future. Please use ClientConfig{jiguang-common cn.jiguang.common.ClientConfig#setMaxRetryTimes} instead of this constructor.
     * @param masterSecret  API access secret of the appKey.
     * @param appKey The KEY of one application on JPush.
     * @param maxRetryTimes max retry times.
     *
     */
    @Deprecated
    public ReportClient(String masterSecret, String appKey, int maxRetryTimes) {
        this(masterSecret, appKey, maxRetryTimes, null);
    }
    /**
     * This will be removed in the future. Please use ClientConfig{jiguang-common cn.jiguang.common.ClientConfig#setMaxRetryTimes} instead of this constructor.
     * @param masterSecret  API access secret of the appKey.
     * @param appKey The KEY of one application on JPush.
     * @param maxRetryTimes max retry times
     * @param proxy The max retry times.
     *
     */
    @Deprecated
    public ReportClient(String masterSecret, String appKey, int maxRetryTimes, HttpProxy proxy) {
        ServiceHelper.checkBasic(appKey, masterSecret);
        ClientConfig conf = ClientConfig.getInstance();
        conf.setMaxRetryTimes(maxRetryTimes);
        _hostName = (String) conf.get(ClientConfig.REPORT_HOST_NAME);
        _receivePath = (String) conf.get(ClientConfig.REPORT_RECEIVE_PATH);
        _userPath = (String) conf.get(ClientConfig.REPORT_USER_PATH);
        _messagePath = (String) conf.get(ClientConfig.REPORT_MESSAGE_PATH);
        _statusPath = (String) conf.get(ClientConfig.REPORT_STATUS_PATH);
        messageDetailPath = (String) conf.get(ClientConfig.REPORT_MESSAGE_DETAIL_PATH);
        receiveDetailPath = (String) conf.get(ClientConfig.REPORT_RECEIVE_DETAIL_PATH);
        groupMessageDetailPath = (String) conf.get(ClientConfig.REPORT_GROUP_MESSAGE_DETAIL_PATH);
        groupUserPath = (String) conf.get(ClientConfig.REPORT_GROUP_USER_PATH);
        String authCode = ServiceHelper.getBasicAuthorization(appKey, masterSecret);
        _httpClient = new NativeHttpClient(authCode, proxy, conf);
    }
    public ReportClient(String masterSecret, String appKey, HttpProxy proxy, ClientConfig conf) {
        ServiceHelper.checkBasic(appKey, masterSecret);
        _hostName = (String) conf.get(ClientConfig.REPORT_HOST_NAME);
        _receivePath = (String) conf.get(ClientConfig.REPORT_RECEIVE_PATH);
        _userPath = (String) conf.get(ClientConfig.REPORT_USER_PATH);
        _messagePath = (String) conf.get(ClientConfig.REPORT_MESSAGE_PATH);
        _statusPath = (String) conf.get(ClientConfig.REPORT_STATUS_PATH);
        messageDetailPath = (String) conf.get(ClientConfig.REPORT_MESSAGE_DETAIL_PATH);
        receiveDetailPath = (String) conf.get(ClientConfig.REPORT_RECEIVE_DETAIL_PATH);
        groupMessageDetailPath = (String) conf.get(ClientConfig.REPORT_GROUP_MESSAGE_DETAIL_PATH);
        groupUserPath = (String) conf.get(ClientConfig.REPORT_GROUP_USER_PATH);
        String authCode = ServiceHelper.getBasicAuthorization(appKey, masterSecret);
        _httpClient = new NativeHttpClient(authCode, proxy, conf);
    }
    public ReceivedsResult getReceiveds(String[] msgIdArray)
            throws APIConnectionException, APIRequestException {
        return getReceiveds(StringUtils.arrayToString(msgIdArray));
    }
    public ReceivedsResult getReceiveds(String msgIds)
            throws APIConnectionException, APIRequestException {
        checkMsgids(msgIds);
        String url = _hostName + _receivePath + "?msg_ids=" + msgIds;
        ResponseWrapper response = _httpClient.sendGet(url);
        return ReceivedsResult.fromResponse(response);
    }
    public ReceivedsResult getReceivedsDetail(String msgIds)
            throws APIConnectionException, APIRequestException {
        checkMsgids(msgIds);
        String url = _hostName + receiveDetailPath + "?msg_ids=" + msgIds;
        ResponseWrapper response = _httpClient.sendGet(url);
        return ReceivedsResult.fromResponse(response);
    }
    public MessagesResult getMessages(String msgIds)
            throws APIConnectionException, APIRequestException {
        checkMsgids(msgIds);
        String url = _hostName + _messagePath + "?msg_ids=" + msgIds;
        ResponseWrapper response = _httpClient.sendGet(url);
        return MessagesResult.fromResponse(response);
    }
    public MessageDetailResult getMessagesDetail(String msgIds)
            throws APIConnectionException, APIRequestException {
        checkMsgids(msgIds);
        String url = _hostName + messageDetailPath + "?msg_ids=" + msgIds;
        ResponseWrapper response = _httpClient.sendGet(url);
        return MessageDetailResult.fromResponse(response);
    }
    public GroupMessageDetailResult getGroupMessagesDetail(String groupMsgIds)
            throws APIConnectionException, APIRequestException {
        String url = _hostName + groupMessageDetailPath + "?group_msgids=" + groupMsgIds;
        ResponseWrapper response = _httpClient.sendGet(url);
        return GroupMessageDetailResult.fromResponse(response);
    }
    public Map<String, MessageStatus> getMessagesStatus(CheckMessagePayload payload)
            throws APIConnectionException, APIRequestException {
        String url = _hostName + (_statusPath.endsWith("/message")?_statusPath:(_statusPath+"/message"));
        ResponseWrapper result = _httpClient.sendPost(url, payload.toString());
        Type type = new TypeToken<Map<String, MessageStatus>>(){}.getType();
        return new Gson().fromJson(result.responseContent, type);
    }
    public UsersResult getUsers(TimeUnit timeUnit, String start, int duration)
            throws APIConnectionException, APIRequestException {
        String startEncoded = null;
        try {
            startEncoded = URLEncoder.encode(start, "utf-8");
        } catch (Exception e) {
        }
        String url = _hostName + _userPath
                + "?time_unit=" + timeUnit.toString()
                + "&start=" + startEncoded + "&duration=" + duration;
        ResponseWrapper response = _httpClient.sendGet(url);
        return BaseResult.fromResponse(response, UsersResult.class);
    }
    public GroupUsersResult getGroupUsers(TimeUnit timeUnit, String start, int duration)
            throws APIConnectionException, APIRequestException {
        String startEncoded = null;
        try {
            startEncoded = URLEncoder.encode(start, "utf-8");
        } catch (Exception e) {
        }
        String url = _hostName + groupUserPath
                + "?time_unit=" + timeUnit.toString()
                + "&start=" + startEncoded + "&duration=" + duration;
        ResponseWrapper response = _httpClient.sendGet(url);
        return BaseResult.fromResponse(response, GroupUsersResult.class);
    }
    private final static Pattern MSGID_PATTERNS = Pattern.compile("[^0-9, ]");
    public static void checkMsgids(String msgIds) {
        if (StringUtils.isTrimedEmpty(msgIds)) {
            throw new IllegalArgumentException("msgIds param is required.");
        }
        if (MSGID_PATTERNS.matcher(msgIds).find()) {
            throw new IllegalArgumentException("msgIds param format is incorrect. "
                    + "It should be msg_id (number) which response from JPush Push API. "
                    + "If there are many, use ',' as interval. ");
        }
        msgIds = msgIds.trim();
        if (msgIds.endsWith(",")) {
            msgIds = msgIds.substring(0, msgIds.length() - 1);
        }
        String[] splits = msgIds.split(",");
        try {
            for (String s : splits) {
                s = s.trim();
                if (!StringUtils.isEmpty(s)) {
                    Long.parseLong(s);
                }
            }
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("Every msg_id should be valid Long number which splits by ','");
        }
    }
}
service-push/src/main/java/cn/jpush/api/report/UsersResult.java
New file
@@ -0,0 +1,41 @@
package cn.jpush.api.report;
import java.util.ArrayList;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import cn.jiguang.common.TimeUnit;
import cn.jiguang.common.resp.BaseResult;
public class UsersResult extends BaseResult {
    private static final long serialVersionUID = -963296929272770550L;
    @Expose public TimeUnit time_unit;
    @Expose public String start;
    @Expose public int duration;
    @Expose public List<User> items = new ArrayList<User>();
    public static class User {
        @Expose public String time;
        @Expose public Android android;
        @Expose public Ios ios;
    }
    public static class Android {
        @SerializedName("new") @Expose public long add;
        @Expose public int online;
        @Expose public int active;
    }
    public static class Ios {
        @SerializedName("new") @Expose public long add;
        @Expose public int online;
        @Expose public int active;
    }
}
service-push/src/main/java/cn/jpush/api/report/model/CheckMessagePayload.java
New file
@@ -0,0 +1,115 @@
package cn.jpush.api.report.model;
import cn.jiguang.common.utils.Preconditions;
import cn.jiguang.common.utils.TimeUtils;
import cn.jpush.api.schedule.model.IModel;
import com.google.gson.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class CheckMessagePayload implements IModel{
    public final static String MSG_ID = "msg_id";
    public final static String REGISTRATION_IDS = "registration_ids";
    public final static String DATE = "date";
    private long msgId = -1L;
    private List<String> registrationIds;
    private String date;
    private Gson gson = new Gson();
    public CheckMessagePayload(long msgId, List<String> rids, String date) {
        this.msgId = msgId;
        this.registrationIds = rids;
        this.date = date;
    }
    public static Builder newBuilder() {
        return new Builder();
    }
    public static class Builder {
        private long msgId;
        private List<String> registrationIds = new ArrayList<String>();
        private String date;
        public Builder setMsgId(long msgId) {
            this.msgId = msgId;
            return this;
        }
        public Builder setRegistrationIds(String[] rids) {
            Preconditions.checkArgument(rids != null && rids.length > 0, "Registration ids is empty");
            Collections.addAll(registrationIds, rids);
            return this;
        }
        public Builder setRegistrationsIds(List<String> rids) {
            Preconditions.checkArgument(rids != null && rids.size() > 0, "Registration ids is empty");
            registrationIds = rids;
            return this;
        }
        public Builder addRegistrationIds(String... rids) {
            if (null == rids) {
                return this;
            }
            Collections.addAll(registrationIds, rids);
            return this;
        }
        public Builder addRegistrationIds(List<String> rids) {
            registrationIds.addAll(rids);
            return this;
        }
        public Builder setDate(String date) {
            Preconditions.checkArgument(isDayFormat(date), "Date format is invalid");
            this.date = date;
            return this;
        }
        public CheckMessagePayload build() {
            return new CheckMessagePayload(msgId, registrationIds, date);
        }
    }
    @Override
    public JsonElement toJSON() {
        JsonObject jsonObject = new JsonObject();
        if (msgId != -1L) {
            jsonObject.addProperty(MSG_ID, msgId);
        }
        if (null != registrationIds) {
            JsonArray jsonArray = new JsonArray();
            for (String rid : registrationIds) {
                jsonArray.add(new JsonPrimitive(rid));
            }
            jsonObject.add(REGISTRATION_IDS, jsonArray);
        }
        if (null != date) {
            jsonObject.addProperty(DATE, date);
        }
        return jsonObject;
    }
    @Override
    public String toString() {
        return gson.toJson(toJSON());
    }
    private static boolean isDayFormat(String date) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        try {
            simpleDateFormat.parse(date);
            return true;
        } catch (ParseException e) {
            e.printStackTrace();
            return false;
        }
    }
}
service-push/src/main/java/cn/jpush/api/report/package-info.java
New file
@@ -0,0 +1,6 @@
/**
 * Report API features.
 *
 * Url: https://report.jpush.cn/v3/report/
 */
package cn.jpush.api.report;
service-push/src/main/java/cn/jpush/api/schedule/ScheduleClient.java
New file
@@ -0,0 +1,169 @@
package cn.jpush.api.schedule;
import cn.jiguang.common.ClientConfig;
import cn.jiguang.common.ServiceHelper;
import cn.jiguang.common.connection.HttpProxy;
import cn.jiguang.common.connection.NativeHttpClient;
import cn.jiguang.common.resp.APIConnectionException;
import cn.jiguang.common.resp.APIRequestException;
import cn.jiguang.common.resp.ResponseWrapper;
import cn.jiguang.common.utils.Preconditions;
import cn.jiguang.common.utils.StringUtils;
import cn.jpush.api.push.CIDResult;
import cn.jpush.api.push.PushClient;
import cn.jpush.api.schedule.model.SchedulePayload;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ScheduleClient {
    private static final Logger LOG = LoggerFactory.getLogger(ScheduleClient.class);
    private final NativeHttpClient _httpClient;
    private String hostName;
    private String schedulePath;
    // If not present, true by default.
    private int apnsProduction;
    // If not present, the default value is 86400(s) (one day)
    private long timeToLive;
    public ScheduleClient(String masterSecret, String appkey) {
        this(masterSecret, appkey, null, ClientConfig.getInstance());
    }
    /**
     * This will be removed in the future. Please use ClientConfig{jiguang-common cn.jiguang.common.ClientConfig#setMaxRetryTimes} instead of this constructor.
     * @param masterSecret API access secret of the appKey.
     * @param appKey The KEY of one application on JPush.
     * @param maxRetryTimes The mas retry times.
     *
     */
    @Deprecated
    public ScheduleClient(String masterSecret, String appKey, int maxRetryTimes) {
        this(masterSecret, appKey, maxRetryTimes, null);
    }
    /**
     * This will be removed in the future. Please use ClientConfig{jiguang-common cn.jiguang.common.ClientConfig#setMaxRetryTimes} instead of this constructor.
     * @param masterSecret API access secret of the appKey.
     * @param appKey The KEY of one application on JPush.
     * @param maxRetryTimes The mas retry times.
     * @param proxy The proxy, if there is no proxy, should be null.
     */
    @Deprecated
    public ScheduleClient(String masterSecret, String appKey, int maxRetryTimes, HttpProxy proxy) {
        ServiceHelper.checkBasic(appKey, masterSecret);
        ClientConfig conf = ClientConfig.getInstance();
        conf.setMaxRetryTimes(maxRetryTimes);
        hostName = (String) conf.get(ClientConfig.SCHEDULE_HOST_NAME);
        schedulePath = (String) conf.get(ClientConfig.SCHEDULE_PATH);
        apnsProduction = (Integer) conf.get(ClientConfig.APNS_PRODUCTION);
        timeToLive = (Long) conf.get(ClientConfig.TIME_TO_LIVE);
        String authCode = ServiceHelper.getBasicAuthorization(appKey, masterSecret);
        this._httpClient = new NativeHttpClient(authCode, proxy, conf);
    }
    /**
     * Create a Schedule Client with custom configuration.
     * @param masterSecret API access secret of the appKey.
     * @param appKey The KEY of one application on JPush.
     * @param proxy The proxy, if there is no proxy, should be null.
     * @param conf The client configuration. Can use ClientConfig.getInstance() as default.
     */
    public ScheduleClient(String masterSecret, String appKey, HttpProxy proxy, ClientConfig conf) {
        ServiceHelper.checkBasic(appKey, masterSecret);
        hostName = (String) conf.get(ClientConfig.SCHEDULE_HOST_NAME);
        schedulePath = (String) conf.get(ClientConfig.SCHEDULE_PATH);
        apnsProduction = (Integer) conf.get(ClientConfig.APNS_PRODUCTION);
        timeToLive = (Long) conf.get(ClientConfig.TIME_TO_LIVE);
        String authCode = ServiceHelper.getBasicAuthorization(appKey, masterSecret);
        this._httpClient = new NativeHttpClient(authCode, proxy, conf);
    }
    public ScheduleResult createSchedule(SchedulePayload payload, String masterSecret,
                                         String appKey) throws APIConnectionException, APIRequestException {
        Preconditions.checkArgument(null != payload, "payload should not be null");
        if (apnsProduction > 0) {
            payload.resetPushApnsProduction(true);
        } else if(apnsProduction == 0) {
            payload.resetPushApnsProduction(false);
        }
        if (timeToLive >= 0) {
            payload.resetPushTimeToLive(timeToLive);
        }
        // 调用getCidList方法来获取cid并赋值到payload中
        String cid = payload.getCid();
        if (cid == null) {
            PushClient pushClient = new PushClient(masterSecret, appKey);
            CIDResult cidResult = pushClient.getCidList(1 , "schedule");
            payload.setCid(cidResult.cidlist.get(0));
        }
        ResponseWrapper response = _httpClient.sendPost(hostName  + schedulePath, payload.toString());
        return ScheduleResult.fromResponse(response, ScheduleResult.class);
    }
    public ScheduleListResult getScheduleList(int page) throws APIConnectionException, APIRequestException{
        Preconditions.checkArgument(page > 0, "page should more than 0.");
        ResponseWrapper response = _httpClient.sendGet(hostName + schedulePath + "?page=" + page);
        return ScheduleListResult.fromResponse(response, ScheduleListResult.class);
    }
    public ScheduleResult getSchedule(String scheduleId) throws APIConnectionException, APIRequestException{
        Preconditions.checkArgument(StringUtils.isNotEmpty(scheduleId), "scheduleId should not be empty");
        ResponseWrapper response = _httpClient.sendGet(hostName + schedulePath + "/" + scheduleId);
        return ScheduleResult.fromResponse(response, ScheduleResult.class);
    }
    public ScheduleMsgIdsResult getScheduleMsgIds(String scheduleId) throws APIConnectionException, APIRequestException{
        Preconditions.checkArgument(StringUtils.isNotEmpty(scheduleId), "scheduleId should not be empty");
        ResponseWrapper response = _httpClient.sendGet(hostName + schedulePath + "/" + scheduleId + "/msg_ids");
        return ScheduleResult.fromResponse(response, ScheduleMsgIdsResult.class);
    }
    public ScheduleResult updateSchedule(String scheduleId, SchedulePayload payload) throws APIConnectionException, APIRequestException{
        Preconditions.checkArgument(StringUtils.isNotEmpty(scheduleId), "scheduleId should not be empty");
        Preconditions.checkArgument(null != payload, "payload should not be null");
        if (apnsProduction > 0) {
            payload.resetPushApnsProduction(true);
        } else if(apnsProduction == 0) {
            payload.resetPushApnsProduction(false);
        }
        if (timeToLive >= 0) {
            payload.resetPushTimeToLive(timeToLive);
        }
        ResponseWrapper response = _httpClient.sendPut(hostName +  schedulePath + "/" + scheduleId,
                payload.toString());
        return ScheduleResult.fromResponse(response, ScheduleResult.class);
    }
    public void deleteSchedule(String scheduleId) throws APIConnectionException, APIRequestException{
        Preconditions.checkArgument(StringUtils.isNotEmpty(scheduleId), "scheduleId should not be empty");
        _httpClient.sendDelete(hostName + schedulePath + "/" + scheduleId);
    }
}
service-push/src/main/java/cn/jpush/api/schedule/ScheduleListResult.java
New file
@@ -0,0 +1,33 @@
package cn.jpush.api.schedule;
import java.util.List;
import com.google.gson.annotations.Expose;
import cn.jiguang.common.resp.BaseResult;
public class ScheduleListResult extends BaseResult{
    private static final long serialVersionUID = 86248096939746151L;
    @Expose int total_count;
    @Expose int total_pages;
    @Expose int page;
    @Expose List<ScheduleResult> schedules;
    public int getTotal_count() {
        return total_count;
    }
    public int getTotal_pages() {
        return total_pages;
    }
    public int getPage() {
        return page;
    }
    public List<ScheduleResult> getSchedules() {
        return schedules;
    }
}
service-push/src/main/java/cn/jpush/api/schedule/ScheduleMsgIdsResult.java
New file
@@ -0,0 +1,37 @@
package cn.jpush.api.schedule;
import java.util.List;
import com.google.gson.JsonObject;
import com.google.gson.annotations.Expose;
import cn.jiguang.common.resp.BaseResult;
public class ScheduleMsgIdsResult extends BaseResult{
    private static final long serialVersionUID = 995450157929893257L;
    @Expose int count;
    @Expose List<String> msgids;
    public int getCount() {
        return count;
    }
    public void setCount(int count) {
        this.count = count;
    }
    public List<String> getMsgids() {
        return msgids;
    }
    public void setMsgids(List<String> msgids) {
        this.msgids = msgids;
    }
}
service-push/src/main/java/cn/jpush/api/schedule/ScheduleResult.java
New file
@@ -0,0 +1,36 @@
package cn.jpush.api.schedule;
import com.google.gson.JsonObject;
import com.google.gson.annotations.Expose;
import cn.jiguang.common.resp.BaseResult;
public class ScheduleResult extends BaseResult{
    private static final long serialVersionUID = 995450157929190757L;
    @Expose String schedule_id;
    @Expose String name;
    @Expose Boolean enabled;
    @Expose JsonObject trigger;
    @Expose JsonObject push;
    public String getSchedule_id() {
        return schedule_id;
    }
    public String getName() {
        return name;
    }
    public Boolean getEnabled() {
        return enabled;
    }
    public JsonObject getTrigger() {
        return trigger;
    }
    public JsonObject getPush() {
        return push;
    }
}
service-push/src/main/java/cn/jpush/api/schedule/model/IModel.java
New file
@@ -0,0 +1,8 @@
package cn.jpush.api.schedule.model;
import com.google.gson.JsonElement;
public interface IModel {
    public JsonElement toJSON();
}
service-push/src/main/java/cn/jpush/api/schedule/model/SchedulePayload.java
New file
@@ -0,0 +1,127 @@
package cn.jpush.api.schedule.model;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import cn.jiguang.common.utils.StringUtils;
import cn.jpush.api.push.model.PushPayload;
public class SchedulePayload implements IModel {
    private static final String CID = "cid";
    private static final String NAME = "name";
    private static final String ENABLED = "enabled";
    private static final String TRIGGER = "trigger";
    private static final String PUSH = "push";
    private static Gson gson = new Gson();
    private String cid;
    private String name;
    private Boolean enabled;
    private TriggerPayload trigger;
    private PushPayload push;
    private SchedulePayload(String cid, String name, Boolean enabled, TriggerPayload trigger, PushPayload push) {
        this.cid = cid;
        this.name = name;
        this.enabled = enabled;
        this.trigger = trigger;
        this.push = push;
    }
    public SchedulePayload setCid(String cid) {
        this.cid = cid;
        return this;
    }
    public String getCid() {
        return cid;
    }
    /**
     *
     * The entrance for building a SchedulePayload object.
     * @return SchedulePayload builder
     */
    public static Builder newBuilder() {
        return new Builder();
    }
    @Override
    public JsonElement toJSON() {
        JsonObject json = new JsonObject();
        if ( null != cid ) {
            json.addProperty(CID, cid);
        }
        if ( StringUtils.isNotEmpty(name) ) {
            json.addProperty(NAME, name);
        }
        if ( null != enabled ) {
            json.addProperty(ENABLED, enabled);
        }
        if ( null != trigger ) {
            json.add(TRIGGER, trigger.toJSON());
        }
        if ( null != push ) {
            json.add(PUSH, push.toJSON());
        }
        return json;
    }
    @Override
    public String toString() {
        return gson.toJson(toJSON());
    }
    public void resetPushApnsProduction(boolean apnsProduction) {
        if(null != push) {
            push.resetOptionsApnsProduction(apnsProduction);
        }
    }
    public void resetPushTimeToLive(long timeToLive) {
        if(null != push) {
            push.resetOptionsTimeToLive(timeToLive);
        }
    }
    public static class Builder{
        private String cid;
        private String name;
        private Boolean enabled;
        private TriggerPayload trigger;
        private PushPayload push;
        public Builder setCid(String cid) {
            this.cid = cid;
            return this;
        }
        public Builder setName(String name) {
            this.name = name;
            return this;
        }
        public Builder setEnabled(Boolean enabled) {
            this.enabled = enabled;
            return this;
        }
        public Builder setTrigger(TriggerPayload trigger) {
            this.trigger = trigger;
            return this;
        }
        public Builder setPush(PushPayload push) {
            this.push = push;
            return this;
        }
        public SchedulePayload build() {
            return new SchedulePayload(cid, name, enabled, trigger, push);
        }
    }
}
service-push/src/main/java/cn/jpush/api/schedule/model/TriggerPayload.java
New file
@@ -0,0 +1,176 @@
package cn.jpush.api.schedule.model;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import cn.jiguang.common.TimeUnit;
import cn.jiguang.common.utils.Preconditions;
import cn.jiguang.common.utils.StringUtils;
import cn.jiguang.common.utils.TimeUtils;
public class TriggerPayload implements IModel {
    private static Gson gson = new Gson();
    private Type type;
    private String start;
    private String end;
    private String time;
    private TimeUnit time_unit;
    private int frequency;
    private String[] point;
    private TriggerPayload(String time) {
        this.type = Type.single;
        this.time = time;
    }
    private TriggerPayload(String start, String end, String time, TimeUnit time_unit, int frequency, String[] point) {
        this.type = Type.periodical;
        this.start = start;
        this.end = end;
        this.time = time;
        this.time_unit = time_unit;
        this.frequency = frequency;
        this.point = point;
    }
    public static Builder newBuilder() {
        return new Builder();
    }
    @Override
    public String toString() {
        return gson.toJson(toJSON());
    }
    @Override
    public JsonElement toJSON() {
        JsonObject json = new JsonObject();
        switch (type) {
            case single:
                JsonObject s = new JsonObject();
                s.addProperty("time", time);
                json.add(Type.single.name(), s);
                break;
            case periodical:
                JsonObject p = new JsonObject();
                p.addProperty("start", start);
                p.addProperty("end", end);
                p.addProperty("time", time);
                p.addProperty("time_unit", time_unit.name().toLowerCase());
                p.addProperty("frequency", frequency);
                if( !TimeUnit.DAY.equals(time_unit) ) {
                    JsonArray array = new JsonArray();
                    for (String aPoint : point) {
                        array.add(new JsonPrimitive(aPoint));
                    }
                    p.add("point", array);
                }
                json.add(Type.periodical.name(), p);
                break;
            default:
                // nothing
        }
        return json;
    }
    public static enum Type {
        single, periodical
    }
    public static class Builder{
        private String start;
        private String end;
        private String time;
        private TimeUnit time_unit;
        private int frequency;
        private String[] point;
        /**
         * Setup time for single trigger.
         * @param time The execute time, format yyyy-MM-dd HH:mm:ss
         * @return this Builder
         */
        public Builder setSingleTime(String time) {
            this.time = time;
            return this;
        }
        /**
         * Setup period for periodical trigger.
         * @param start The start time, format yyyy-MM-dd HH:mm:ss
         * @param end The end time, format yyyy-MM-dd HH:mm:ss
         * @param time The execute time, format HH:mm:ss
         * @return this Builder
         */
        public Builder setPeriodTime(String start, String end, String time) {
            this.start = start;
            this.end = end;
            this.time = time;
            return this;
        }
        /**
         * Setup frequency for periodical trigger.
         * @param time_unit The time unit, can be day, week or month.
         * @param frequency The frequency cooperate with time unit, must between 1 and 100.
         * @param point The time point cooperate with time unit.
         *              If time unit is day, the point should be null.
         *              If time unit is week, should be the abbreviation of the days. eg. {"MON", "TUE"}
         *              If time unit is month, should be the date of the days. eg. {"01", "03"}
         * @return this Builder
         */
        public Builder setTimeFrequency(TimeUnit time_unit, int frequency, String[] point) {
            this.time_unit = time_unit;
            this.frequency = frequency;
            this.point = point;
            return this;
        }
        public TriggerPayload buildSingle() {
            Preconditions.checkArgument(StringUtils.isNotEmpty(time), "The time must not be empty.");
            Preconditions.checkArgument(TimeUtils.isDateFormat(time), "The time format is incorrect.");
            return new TriggerPayload(time);
        }
        public TriggerPayload buildPeriodical() {
            Preconditions.checkArgument(StringUtils.isNotEmpty(start), "The start must not be empty.");
            Preconditions.checkArgument(StringUtils.isNotEmpty(end), "The end must not be empty.");
            Preconditions.checkArgument(StringUtils.isNotEmpty(time), "The time must not be empty.");
            Preconditions.checkArgument(TimeUtils.isDateFormat(start), "The start format is incorrect.");
            Preconditions.checkArgument(TimeUtils.isDateFormat(end), "The end format is incorrect.");
            Preconditions.checkArgument(TimeUtils.isTimeFormat(time), "The time format is incorrect.");
            Preconditions.checkNotNull(time_unit, "The time_unit must not be null.");
            Preconditions.checkArgument(isTimeUnitOk(time_unit), "The time unit must be DAY, WEEK or MONTH.");
            Preconditions.checkArgument(frequency > 0 && frequency < 101, "The frequency must be a int between 1 and 100.");
            return new TriggerPayload(start, end, time, time_unit, frequency, point);
        }
        private boolean isTimeUnitOk(TimeUnit timeUnit) {
            switch (timeUnit) {
                case HOUR:
                    return false;
                case DAY:
                case WEEK:
                case MONTH:
                    return true;
                default:
                    return false;
            }
        }
    }
}
service-push/src/main/java/cn/jpush/api/schedule/package-info.java
New file
@@ -0,0 +1,6 @@
/**
 * Schedule API features.
 *
 * Url: https://api.jpush.cn/v3/schedules
 */
package cn.jpush.api.schedule;
service-push/src/main/java/com/ks/push/PushApplication.java
@@ -63,7 +63,7 @@
            @Override
            public void run() {
                try {
                    List<CMQManager.MQMsgConsumeResult> list =  CMQManager.getInstance().consumeInvalidDeviceTokenQueue(16);
                    List<CMQManager.MQMsgConsumeResult> list = CMQManager.getInstance().consumeInvalidDeviceTokenQueue(16);
                    if (list != null) {
                        logger.info("清理无效token数量:" + list.size());
                        for (CMQManager.MQMsgConsumeResult result : list) {
@@ -76,6 +76,7 @@
                                    pushDeviceTokenManager.deleteByPrimaryKey(token.getId());
                                }
                            }
                            CMQManager.getInstance().deleteMsg(CMQManager.PUSH_TOKEN_INVALID, result.getReceiptHandle());
                        }
                    }
                } catch (Exception e) {
service-push/src/main/java/com/ks/push/manager/CMQManager.java
@@ -43,6 +43,11 @@
    public static String PUSH_MZ = "bpush-mz";
    /**
     * 极光推送队列
     */
    public static String PUSH_JPUSH = "bpush-jpush";
    /**
     * 无效设备队列
     */
    public static String PUSH_TOKEN_INVALID = "bpush-token-invalid";
@@ -55,6 +60,7 @@
        cmqUtil.createQueue(PUSH_OPPO);
        cmqUtil.createQueue(PUSH_VIVO);
        cmqUtil.createQueue(PUSH_MZ);
        cmqUtil.createQueue(PUSH_JPUSH);
        cmqUtil.createQueue(PUSH_TOKEN_INVALID);
        logger.info("创建队列完毕");
    }
@@ -79,6 +85,8 @@
            queueName = PUSH_VIVO;
        } else if (platform == PushPlatform.mz) {
            queueName = PUSH_MZ;
        }else if (platform == PushPlatform.jpush) {
            queueName = PUSH_JPUSH;
        }
        return queueName;
    }
service-push/src/main/java/com/ks/push/manager/PushManager.java
@@ -89,62 +89,65 @@
        }
        logger.info("开始启动推送#taskId:{}", task.getId());
        updateState(taskId, BPushTask.STATE_PUSHING, null);
        boolean hasDevice = false;
        try {
            logger.info("开始启动推送#taskId:{}", task.getId());
            updateState(taskId, BPushTask.STATE_PUSHING, null);
            boolean hasDevice = false;
        // 查询可推送的平台
        List<BPushPlatformAppInfo> list = bPushPlatformAppInfoManager.listByAppCode(task.getAppCode());
            // 查询可推送的平台
            List<BPushPlatformAppInfo> list = bPushPlatformAppInfoManager.listByAppCode(task.getAppCode());
        //先初始化推送结果数据数据
        List<BPushTaskExcuteResult> resultList = new ArrayList<>();
        Map<String, Long> resultCountMap = new HashMap<>();
        for (BPushPlatformAppInfo appInfo : list) {
            long count = pushDeviceTokenManager.count(task.getAppCode(), appInfo.getPlatform(), task.getFilter());
            if (count > 0) {
                hasDevice = true;
                //初始化推送结果数据
                BPushTaskExcuteResult result = pushExcuteResultManager.initPushExcuteResult(taskId, appInfo.getPlatform(), count);
                resultCountMap.put(result.getId(), count);
                resultList.add(result);
            }
        }
        for (BPushTaskExcuteResult result : resultList) {
            long count = resultCountMap.get(result.getId());
            int pageSize = 500;
            int totalPage = (int) (count % pageSize == 0 ? count / pageSize : count / pageSize + 1);
            long totalValidCount = 0L;
            //初始化批量推送的计数
            for (int page = 0; page < totalPage; page++) {
                pushExcuteResultManager.startBatchPush(taskId, result.getPushPlatform(), page + "");
            }
            for (int page = 0; page < totalPage; page++) {
                List<BPushDeviceToken> deviceTokenList = pushDeviceTokenManager.list(task.getAppCode(), result.getPushPlatform(), task.getFilter(), page + 1, pageSize);
                List<String> tokenList = new ArrayList<>();
                for (BPushDeviceToken deviceToken : deviceTokenList) {
                    //TODO 时间判断
                    tokenList.add(deviceToken.getToken());
            //先初始化推送结果数据数据
            List<BPushTaskExcuteResult> resultList = new ArrayList<>();
            Map<String, Long> resultCountMap = new HashMap<>();
            for (BPushPlatformAppInfo appInfo : list) {
                long count = pushDeviceTokenManager.count(task.getAppCode(), appInfo.getPlatform(), task.getFilter());
                if (count > 0) {
                    hasDevice = true;
                    //初始化推送结果数据
                    BPushTaskExcuteResult result = pushExcuteResultManager.initPushExcuteResult(taskId, appInfo.getPlatform(), count);
                    resultCountMap.put(result.getId(), count);
                    resultList.add(result);
                }
                totalValidCount += tokenList.size();
                BPushDeviceDataSet dataSet = new BPushDeviceDataSet(tokenList, page + "", taskId);
                //最后一页
                if (page == totalPage - 1 && totalValidCount != count) {
                    //修改总数
                    pushExcuteResultManager.setDeviceCount(result.getId(), totalValidCount);
                }
                CMQManager.getInstance().addToPushQueue(result.getPushPlatform(), dataSet);
            }
            logger.info("加入推送队列#任务Id:{}#平台:{}#推送数量:{}", task.getId(), result.getPushPlatform().name(), count);
        }
        if (!hasDevice) {
            updateState(taskId, BPushTask.STATE_FINSIH, "没有满足条件的可推送设备");
            for (BPushTaskExcuteResult result : resultList) {
                long count = resultCountMap.get(result.getId());
                int pageSize = 500;
                int totalPage = (int) (count % pageSize == 0 ? count / pageSize : count / pageSize + 1);
                long totalValidCount = 0L;
                //初始化批量推送的计数
                for (int page = 0; page < totalPage; page++) {
                    pushExcuteResultManager.startBatchPush(taskId, result.getPushPlatform(), page + "");
                }
                for (int page = 0; page < totalPage; page++) {
                    List<BPushDeviceToken> deviceTokenList = pushDeviceTokenManager.list(task.getAppCode(), result.getPushPlatform(), task.getFilter(), page + 1, pageSize);
                    List<String> tokenList = new ArrayList<>();
                    for (BPushDeviceToken deviceToken : deviceTokenList) {
                        //TODO 时间判断
                        tokenList.add(deviceToken.getToken());
                    }
                    totalValidCount += tokenList.size();
                    BPushDeviceDataSet dataSet = new BPushDeviceDataSet(tokenList, page + "", taskId);
                    //最后一页
                    if (page == totalPage - 1 && totalValidCount != count) {
                        //修改总数
                        pushExcuteResultManager.setDeviceCount(result.getId(), totalValidCount);
                    }
                    CMQManager.getInstance().addToPushQueue(result.getPushPlatform(), dataSet);
                }
                logger.info("加入推送队列#任务Id:{}#平台:{}#推送数量:{}", task.getId(), result.getPushPlatform().name(), count);
            }
            if (!hasDevice) {
                updateState(taskId, BPushTask.STATE_FINSIH, "没有满足条件的可推送设备");
            }
        } finally {
            redisTemplate.delete(key);
        }
        //没有可推送的设备
        logger.info("启动推送结束#taskId:{}", task.getId());
service-push/src/main/java/com/ks/push/utils/PushUtil.java
@@ -1,9 +1,10 @@
package com.ks.push.utils;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.huawei.push.util.push.*;
import com.ks.push.pojo.DO.BPushMessage;
import com.ks.push.pojo.DO.PushPlatform;
import com.ks.push.utils.push.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yeshi.utils.StringUtil;
@@ -70,6 +71,8 @@
            pushVIVO(appInfo, message, tokenList);
        } else if (platform == PushPlatform.mz) {
            pushMZ(appInfo, message, tokenList);
        } else if (platform == PushPlatform.jpush) {
            pushJPush(appInfo, message, tokenList,!pushMessage.isMessage());
        }
    }
@@ -135,4 +138,28 @@
    }
    /**
     * 极光推送
     *
     * @param appInfo
     * @param message
     * @param tokenList
     * @throws Exception
     */
    private static void pushJPush(PushAppInfo appInfo, PushMessage message, List<String> tokenList, boolean alert) throws Exception {
        if (alert) {
            JpushUtil.pushNotification(appInfo, message, tokenList, null);
        } else {
            JSONObject data = new JSONObject();
            if (message.getActivityParams() != null) {
                for (Iterator<String> its = message.getActivityParams().keySet().iterator(); its.hasNext(); ) {
                    String key = its.next();
                    data.put(key, message.getActivityParams().get(key));
                }
            }
            JpushUtil.pushMessage(appInfo, data, tokenList, null);
        }
    }
}
service-push/src/main/java/com/ks/push/utils/push/HuaWeiPushUtil.java
File was renamed from service-push/src/main/java/com/huawei/push/util/push/HuaWeiPushUtil.java
@@ -1,4 +1,4 @@
package com.huawei.push.util.push;
package com.ks.push.utils.push;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
service-push/src/main/java/com/ks/push/utils/push/JpushUtil.java
New file
@@ -0,0 +1,153 @@
package com.ks.push.utils.push;
import cn.jiguang.common.ClientConfig;
import cn.jiguang.common.resp.APIConnectionException;
import cn.jiguang.common.resp.APIRequestException;
import cn.jpush.api.JPushClient;
import cn.jpush.api.push.PushResult;
import cn.jpush.api.push.model.Message;
import cn.jpush.api.push.model.Platform;
import cn.jpush.api.push.model.PushPayload;
import cn.jpush.api.push.model.audience.Audience;
import cn.jpush.api.push.model.notification.Notification;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yeshi.utils.push.entity.PushAppInfo;
import org.yeshi.utils.push.entity.PushMessage;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * @author hxh
 * @title: JpushUtil
 * @description: 极光推送
 * @date 2021/12/2 15:34
 */
public class JpushUtil {
    private static Logger LOG = LoggerFactory.getLogger(JpushUtil.class);
    /**
     * @return java.lang.String
     * @author hxh
     * @description 推送通知
     * @date 15:59 2021/12/2
     * @param: appInfo
     * @param: message
     * @param: regIds
     * @param: aliasList
     **/
    public static String pushNotification(PushAppInfo appInfo, PushMessage message, List<String> regIds, List<String> aliasList) {
        JPushClient jpushClient = new JPushClient(appInfo.getAppSecret(), appInfo.getAppKey(), null, ClientConfig.getInstance());
        Map<String, String> params;
        if (message.getActivity() != null) {
            params = new HashMap<>();
            params.put("activity", message.getActivity());
            params.put("params", new Gson().toJson(message.getActivityParams()));
        } else {
            params = message.getActivityParams();
        }
        PushPayload.Builder payloadBuilder = PushPayload.newBuilder().
                setPlatform(Platform.all())
                .setNotification(Notification.android(message.getContent(), message.getTitle(), params));
        if (regIds != null) {
            payloadBuilder.setAudience(Audience.registrationId(regIds));
        } else if (aliasList != null) {
            payloadBuilder.setAudience(Audience.alias(aliasList));
        } else {
            payloadBuilder.setAudience(Audience.all());
        }
        PushPayload payload = payloadBuilder.build();
        try {
            PushResult result = jpushClient.sendPush(payload);
            LOG.info("Got result - " + result);
            return result.msg_id + "";
        } catch (APIConnectionException e) {
            LOG.error("Connection error, should retry later", e);
        } catch (APIRequestException e) {
            LOG.error("Should review the error, and fix the request", e);
            LOG.info("HTTP Status: " + e.getStatus());
            LOG.info("Error Code: " + e.getErrorCode());
            LOG.info("Error Message: " + e.getErrorMessage());
        }
        return null;
    }
    /**
     * @return java.lang.String
     * @author hxh
     * @description 推送透传消息
     * @date 11:48 2022/1/12
     * @param: appInfo
     * @param: data
     * @param: regIds
     * @param: aliasList
     **/
    public static String pushMessage(PushAppInfo appInfo, JSONObject data, List<String> regIds, List<String> aliasList) {
        JPushClient jpushClient = new JPushClient(appInfo.getAppSecret(), appInfo.getAppKey(), null, ClientConfig.getInstance());
        PushPayload.Builder payloadBuilder = PushPayload.newBuilder().
                setPlatform(Platform.all())
                .setMessage(Message.newBuilder()
                        .setMsgContent(data.toJSONString())
                        .build());
        if (regIds != null) {
            payloadBuilder.setAudience(Audience.registrationId(regIds));
        } else if (aliasList != null) {
            payloadBuilder.setAudience(Audience.alias(aliasList));
        } else {
            payloadBuilder.setAudience(Audience.all());
        }
        PushPayload payload = payloadBuilder.build();
        try {
            PushResult result = jpushClient.sendPush(payload);
            LOG.info("Got result - " + result);
            return result.msg_id + "";
        } catch (APIConnectionException e) {
            LOG.error("Connection error, should retry later", e);
        } catch (APIRequestException e) {
            LOG.error("Should review the error, and fix the request", e);
            LOG.info("HTTP Status: " + e.getStatus());
            LOG.info("Error Code: " + e.getErrorCode());
            LOG.info("Error Message: " + e.getErrorMessage());
        }
        return null;
    }
    public static void main(String[] args) {
        Map<String, String> activityParams = new HashMap<>();
        activityParams.put("activity", "test");
        activityParams.put("type", "requestLocation");
        JSONObject jumpParams = new JSONObject();
        jumpParams.put("id", 123123);
        activityParams.put("params", jumpParams.toJSONString());
        PushAppInfo appInfo = new PushAppInfo();
        appInfo.setAppKey("987d3d50f209994fa522d001");
        appInfo.setAppSecret("e4053447d0014576ea2c47ab");
//        PushMessage message = new PushMessage("你好啊", "下午好,hello world", null, null, null, activityParams);
        Map<String,Object> dataMap=new HashMap<>();
        dataMap.put("type","requestLocation");
        JSONObject data = new  JSONObject(dataMap);
        pushMessage(appInfo, data, Arrays.asList(new String[]{"100d8559098624d84e6"}), null);
    }
}
service-push/src/main/java/com/ks/push/utils/push/MeiZuPushUtil.java
File was renamed from service-push/src/main/java/com/huawei/push/util/push/MeiZuPushUtil.java
@@ -1,7 +1,6 @@
package com.huawei.push.util.push;
package com.ks.push.utils.push;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.meizu.push.sdk.constant.PushType;
import com.meizu.push.sdk.server.IFlymePush;
import com.meizu.push.sdk.server.constant.ResultPack;
service-push/src/main/java/com/ks/push/utils/push/OppoPushUtil.java
File was renamed from service-push/src/main/java/com/huawei/push/util/push/OppoPushUtil.java
@@ -1,4 +1,4 @@
package com.huawei.push.util.push;
package com.ks.push.utils.push;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
service-push/src/main/java/com/ks/push/utils/push/VIVOPushUtil.java
File was renamed from service-push/src/main/java/com/huawei/push/util/push/VIVOPushUtil.java
@@ -1,4 +1,4 @@
package com.huawei.push.util.push;
package com.ks.push.utils.push;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
service-push/src/main/java/com/ks/push/utils/push/XiaoMiPushUtil.java
File was renamed from service-push/src/main/java/com/huawei/push/util/push/XiaoMiPushUtil.java
@@ -1,4 +1,4 @@
package com.huawei.push.util.push;
package com.ks.push.utils.push;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
service-push/src/main/resources/logback.xml
@@ -102,14 +102,6 @@
    <logger name="io.seata" level="ERROR"></logger>
    <logger name="System.out" level="INFO"></logger>
    <logger name="org.springframework.data.redis" level="DEBUG">
        <appender-ref ref="redisAppender"></appender-ref>
    </logger>
    <logger name="io.lettuce.core" level="DEBUG">
        <appender-ref ref="redisAppender"></appender-ref>
    </logger>
    <!-- 一切logger都会继承自root,root默认的层级level为debug -->
    <root level="INFO">
service-push/src/main/resources/static/pushplatform-appinfo-add.html
@@ -88,11 +88,13 @@
</div>
<script>
    var form;
    var app = new Vue({
        el: '.layui-form',
        data: {
            appList: [],
            platformList: [
                {name: "极光", platform: "jpush"},
                {name: "小米", platform: "xm"},
                {name: "华为", platform: "hw"},
                {name: "OPPO", platform: "oppo"},
@@ -116,6 +118,10 @@
                        app.appList = e.data.list;
                    } else
                        layer.msg(e.msg);
                    setTimeout(function(){
                        form.render();
                    },1000);
                }, function (e) {
                });
            },
@@ -123,8 +129,8 @@
                console.log("render")
                layui.use(['form', 'layer', 'jquery'], function () {
                    $ = layui.jquery;
                    var form = layui.form,
                        layer = layui.layer;
                     form = layui.form;
                      var  layer = layui.layer;
                    //自定义验证规则
                    form.verify({
service-push/src/main/resources/static/pushtask-add.html
@@ -93,6 +93,7 @@
        data: {
            appList: [],
            platformList: [
                {name: "极光", platform: "jpush"},
                {name: "小米", platform: "xm"},
                {name: "华为", platform: "hw"},
                {name: "OPPO", platform: "oppo"},