Browse Source

增加同步服务器时间和时区功能

weizhengliang 2 years ago
parent
commit
1a339bf988

+ 13 - 0
app/build.gradle

@@ -63,4 +63,17 @@ dependencies {
 
     implementation files('libs/xwalk_core_library.jar')
     implementation files('libs/com_blankj_utilcode.jar')
+
+    /**
+     * netty
+     */
+    implementation 'io.netty:netty-all:4.1.42.Final'
+
+    /**
+     * json
+     */
+    implementation 'com.alibaba:fastjson:1.2.23'
+
+    //eventbus
+    implementation 'org.greenrobot:eventbus:3.0.0'
 }

+ 2 - 0
app/src/main/AndroidManifest.xml

@@ -9,6 +9,8 @@
     <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
 
+    <uses-permission android:name="android.permission.SET_TIME" />
+    <uses-permission android:name="android.permission.SET_TIME_ZONE" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
 

+ 76 - 2
app/src/main/java/com/example/informationkanban/MainActivity.java

@@ -26,9 +26,22 @@ import android.webkit.WebView;
 import android.webkit.WebViewClient;
 import android.widget.Toast;
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.example.informationkanban.application.MyApplication;
+import com.example.informationkanban.common.Constant;
+import com.example.informationkanban.common.MessageEvent;
+import com.example.informationkanban.tcp.TcpClient;
+import com.example.informationkanban.tcp.dto.TcpModel;
+import com.example.informationkanban.tcp.enums.TcpAction;
+import com.example.informationkanban.utils.AppUtil;
 import com.example.informationkanban.utils.GetInformationUtils;
 import com.example.informationkanban.utils.NetFunctionConfig;
 
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
 public class MainActivity extends AppCompatActivity {
     private String TAG = MainActivity.class.getSimpleName();
     private WebView webView;
@@ -53,6 +66,13 @@ public class MainActivity extends AppCompatActivity {
             Toast.makeText(this, "设备信息获取失败", Toast.LENGTH_LONG).show();
         }
         timeMonitor();
+
+        if (!EventBus.getDefault().isRegistered(this)) {
+            EventBus.getDefault().register(this);
+        }
+
+        //连接tcp服务
+        startTcp();
     }
 
     /**
@@ -68,6 +88,18 @@ public class MainActivity extends AppCompatActivity {
         //updataTime();
     }
 
+    private void startTcp() {
+        Constant.TCP_SERVER_URL = NetFunctionConfig.getServerIp();
+        if (Constant.TCP_SERVER_URL != null) {
+            new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    TcpClient.getInstance().init(NetFunctionConfig.getServerIp(), Constant.TCP_PORT, Constant.TCP_HEART_BEAT);
+                }
+            }).start();
+        }
+    }
+
     @Override
     protected void onResume() {
         super.onResume();
@@ -127,7 +159,7 @@ public class MainActivity extends AppCompatActivity {
     /**
      * 时间发生改变调用
      */
-    private void updataTime() {
+    private void updateTime() {
         Time time = new Time();
         time.setToNow();
         int year = time.year;
@@ -204,6 +236,9 @@ public class MainActivity extends AppCompatActivity {
         if(broadcastReceiver != null){
             unregisterReceiver(broadcastReceiver);
         }
+        if (EventBus.getDefault().isRegistered(this)) {
+            EventBus.getDefault().unregister(this);
+        }
         super.onDestroy();
     }
 
@@ -215,10 +250,49 @@ public class MainActivity extends AppCompatActivity {
             if (Intent.ACTION_TIME_TICK.equals(action)
                     || Intent.ACTION_TIME_CHANGED.equals(action)
                     || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
-                updataTime();
+                updateTime();
             }
         }
     };
 
 
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    public void onMoonEvent(MessageEvent messageEvent) {
+        if (messageEvent.getType() == Constant.EVENT_TCP_MSG) {
+            TcpModel tcpModel = (TcpModel) messageEvent.getMessage();
+            if (tcpModel.getAction() == TcpAction.TimeAction.SYNC) {
+                //时间同步
+                long time = 0L;
+                String timeZone="Asia/Shanghai";
+                if(canParseJson(tcpModel.getData().toString())){
+                    JSONObject json = JSON.parseObject(tcpModel.getData().toString());
+                    time =json.getLong("time")*1000;
+                    timeZone=json.getString("time_zone");
+                }else{
+                    time = Long.parseLong(tcpModel.getData().toString()) * 1000;
+                }
+
+                try {
+                    AppUtil.setSystemTime(MyApplication.getInstance(), time, timeZone);
+                    Log.d("setTime", "set sys time1: " + time + ", " + timeZone);
+                } catch (Exception e) {
+                    //"20211213:092314"
+                    String timeStr = AppUtil.getDateTime(time, "yyyyMMdd.HHmmss", timeZone);
+                    AppUtil.setSysTime(timeStr, timeZone);
+                    Log.d("setTime", "set sys time2: " + timeStr + ", " + timeZone);
+                }
+            }
+        }
+    }
+
+    private boolean canParseJson(String str){
+        boolean result = false;
+        try {
+            JSON.parseObject(str);
+            result = true;
+        }catch (Exception e){
+            result = false;
+        }
+        return result;
+    }
 }

+ 25 - 0
app/src/main/java/com/example/informationkanban/common/Constant.java

@@ -0,0 +1,25 @@
+package com.example.informationkanban.common;
+
+public class Constant {
+
+    //TCP服务器地址:
+    public static String TCP_SERVER_URL = "172.28.100.100";
+
+    //数据接口端口:
+    public static int URL_PORT = 8006;
+
+    //TCP服务端口
+    public static int TCP_PORT = 5080;
+
+    //TCP服务连接心跳
+    public static int TCP_HEART_BEAT = 40;
+
+    //TCP连接是否成功
+    public static boolean TCP_CONNECTED = false;
+
+
+    /**
+     * TCP消息
+     */
+    public static final int EVENT_TCP_MSG = 0x05;
+}

+ 30 - 0
app/src/main/java/com/example/informationkanban/common/MessageEvent.java

@@ -0,0 +1,30 @@
+package com.example.informationkanban.common;
+
+/**
+ * Created by Administrator on 2016/8/15 0015.
+ */
+public class MessageEvent {
+    private Object message;
+    private int type;
+
+    public MessageEvent(Object message, int type) {
+        this.message = message;
+        this.type = type;
+    }
+
+    public Object getMessage() {
+        return message;
+    }
+
+    public void setMessage(Object message) {
+        this.message = message;
+    }
+
+    public int getType() {
+        return type;
+    }
+
+    public void setType(int type) {
+        this.type = type;
+    }
+}

+ 154 - 0
app/src/main/java/com/example/informationkanban/tcp/TcpClient.java

@@ -0,0 +1,154 @@
+package com.example.informationkanban.tcp;
+
+import com.example.informationkanban.common.Constant;
+
+import java.util.concurrent.TimeUnit;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import io.netty.handler.codec.LengthFieldPrepender;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import io.netty.handler.timeout.IdleStateHandler;
+import io.netty.util.CharsetUtil;
+
+//单例
+public class TcpClient {
+   private String TAG = TcpClient.class.getSimpleName();
+
+    private NioEventLoopGroup workGroup;
+    public Channel channel;
+    private Bootstrap bootstrap;
+
+    //数据处理
+    private TcpClientHandler tcpClientHandler = new TcpClientHandler();
+    //是否运行中
+    public boolean isRunning = false;
+    //重试间隔
+    private Integer retrySeconds = 5;
+    //重试计数
+    private Integer retryTimes = 1;
+    private Integer reconnetTimes = 0;
+    //tcp是否完成初始化
+    private boolean inited = false;
+    private boolean connecting = false;
+
+
+    //单例
+    private static class TcpClientHolder{
+        private static TcpClient instance = new TcpClient();
+    }
+
+    public static TcpClient getInstance(){
+        return TcpClientHolder.instance;
+    }
+
+    //初始化Netty Tcp Client 并连接
+    public void init(String serverIP, Integer serverPort, Integer heartBeatSeconds) {
+        final Integer hbSeconds = heartBeatSeconds;
+        workGroup = new NioEventLoopGroup(2);
+        bootstrap = new Bootstrap();
+        bootstrap.group(workGroup)
+                .channel(NioSocketChannel.class)
+                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 15*1000)
+                .option(ChannelOption.SO_KEEPALIVE,true)
+                .handler(new ChannelInitializer<SocketChannel>() {
+                    @Override
+                    protected void initChannel(SocketChannel socketChannel) throws Exception {
+                        // 这里将LengthFieldBasedFrameDecoder添加到pipeline的首位,因为其需要对接收到的数据
+                        // 进行长度字段解码,这里也会对数据进行粘包和拆包处理
+                        socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(2048, 0, 2, 0, 2));
+                        // LengthFieldPrepender是一个编码器,主要是在响应字节数据前面添加字节长度字段
+                        socketChannel.pipeline().addLast(new LengthFieldPrepender(2));
+                        //心跳包应当小于服务器间隔
+//                        socketChannel.pipeline().addLast(new IdleStateHandler(0, hbSeconds, 0, TimeUnit.SECONDS));
+                        socketChannel.pipeline().addLast(new IdleStateHandler(hbSeconds*2, hbSeconds, 0, TimeUnit.SECONDS));
+                        socketChannel.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8));
+                        socketChannel.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8));
+                        socketChannel.pipeline().addLast(tcpClientHandler);
+                    }
+                }).remoteAddress(serverIP,serverPort);
+        inited = true;
+        doConnect();
+        System.out.println("connect server host: " + serverIP + ", port: " + serverPort);
+    }
+
+    //独立连接方法,用于重新连接
+    public synchronized void doConnect(){
+        if (!inited) {
+            System.out.println("tcp is not initialized");
+            return;
+        }
+
+        if (channel != null && (channel.isActive() || channel.isOpen())) {
+            System.out.println("TcpClient connecting");
+            return;
+        }
+
+        //正在连接
+        if (connecting || Constant.TCP_CONNECTED) {
+            System.out.println("tcp is connecting or connected");
+            return;
+        }
+        connecting = true;
+
+        System.out.println("connect start");
+        ChannelFuture future = bootstrap.connect().addListener(new ChannelFutureListener() {
+            @Override
+            public void operationComplete(ChannelFuture channelFuture) throws Exception {
+                connecting = false;
+                if (channelFuture.isSuccess()){
+                    isRunning = true;
+                    channel = channelFuture.channel();
+                    retryTimes = 1;
+                    System.out.println("connect success");
+                } else {
+                    //连接失败时的处理
+                    isRunning = false;
+                    System.out.println("connect retry : " + retryTimes);
+                    channelFuture.channel().eventLoop().schedule(new Runnable() {
+                        @Override
+                        public void run() {
+                            retryTimes++;
+                            if (retryTimes > 30) { //重试30次还没连成功,等10分钟后再试
+                                retryTimes=1;
+                                reconnetTimes++;
+                                channelFuture.channel().eventLoop().schedule(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        System.out.println("TcpClientHandler 重新连接,第" + retryTimes + "次" + ", 重试" + reconnetTimes + "次");
+                                        doConnect();
+                                    }
+                                }, 60*5, TimeUnit.SECONDS);
+
+                            }else{
+                                doConnect();
+                            }
+
+                        }
+                    }, 5, TimeUnit.SECONDS);
+                }
+            }
+        });
+    }
+
+    //发送消息,线程安全
+    public synchronized void sendMsg(String content){
+        if (tcpClientHandler != null) {
+            tcpClientHandler.sendMsg(content);
+        }
+    }
+
+
+    public Channel getChannel() {
+        return channel;
+    }
+}

+ 117 - 0
app/src/main/java/com/example/informationkanban/tcp/TcpClientHandler.java

@@ -0,0 +1,117 @@
+package com.example.informationkanban.tcp;
+
+import android.util.Log;
+
+import com.example.informationkanban.application.MyApplication;
+import com.example.informationkanban.common.Constant;
+import com.example.informationkanban.common.MessageEvent;
+import com.example.informationkanban.tcp.channel.DeviceChannel;
+import com.example.informationkanban.tcp.channel.DeviceUtil;
+import com.example.informationkanban.tcp.dto.TcpModel;
+import com.example.informationkanban.utils.GetInformationUtils;
+
+import org.greenrobot.eventbus.EventBus;
+
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.handler.timeout.IdleState;
+import io.netty.handler.timeout.IdleStateEvent;
+import io.netty.util.CharsetUtil;
+import io.netty.util.ReferenceCountUtil;
+
+@ChannelHandler.Sharable
+public class TcpClientHandler extends SimpleChannelInboundHandler<String> {
+    private static final String TAG = TcpClientHandler.class.getSimpleName();
+    ChannelHandlerContext ctx;
+    //重连间隔
+    private static Integer retrySeconds = 5;
+    //重连计数
+    private static Integer retryTimes = 0;
+    //总共总连接次数,总连接次数过多可能是网络不稳定
+    private static Integer totalRetryTimes = 0;
+    //是否连接成功
+    private static Boolean connected = false;
+
+    //连接成功执行的方法
+    @Override
+    public void channelActive(ChannelHandlerContext ctx) throws Exception {
+        super.channelActive(ctx);
+        Log.i(TAG, "连接成功");
+        this.ctx = ctx;
+        connected = true;
+        retryTimes = 0;
+        Constant.TCP_CONNECTED = true;
+        //EventBus.getDefault().post(new MessageEvent(1, Constant.EVENT_TCP_STATE));
+        TcpModel tcpModel = DeviceUtil.deviceConnect(GetInformationUtils.getSerialNo(MyApplication.getInstance()));
+        TcpClient.getInstance().sendMsg(tcpModel.toJson());
+    }
+
+    //断开连接
+    @Override
+    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+        super.channelInactive(ctx);
+        connected = false;
+        Constant.TCP_CONNECTED = false;
+        //EventBus.getDefault().post(new MessageEvent(0, Constant.EVENT_TCP_STATE));
+        Log.i(TAG, "失去连接");
+        TcpClient.getInstance().doConnect();
+    }
+
+    //读取String消息
+    @Override
+    protected void channelRead0(ChannelHandlerContext ctx, String source) throws Exception {
+        if(source.equals("1")){
+            Log.i(TAG,"收到服务器返回的心跳 "+source);
+        }else {
+            TcpModel tcpModel = TcpModel.getModelByJson(source);
+
+            if (tcpModel != null) {
+                TcpModel responseTcpModel = DeviceChannel.handleTcpReceived(tcpModel);
+                if (responseTcpModel != null) {
+                    ctx.writeAndFlush(responseTcpModel.toJson());
+                } else {
+                    ReferenceCountUtil.release(source);
+                }
+            }
+        }
+
+    }
+
+    //写心跳包。没有消息发送时,每间隔一定时间会由此方法向服务端发送心跳
+    @Override
+    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+        if (evt instanceof IdleStateEvent) {
+            IdleStateEvent event = (IdleStateEvent) evt;
+            if (event.state() == IdleState.WRITER_IDLE) { //超时未执行写操作,在指定的超时时间内未有写操作,要发送心跳包,告诉服务器连接还存活。服务器收到心跳立马回应,正常客户端收到后执行读操作,
+                ctx.writeAndFlush("0");                //这种情况下不会引发READER_IDLE事件。如果服务器因为网络或其他原因导致回应的心跳,客户端没有收到,在超过写超时时间2个周期后依然没有收到,
+            } else if (event.state() == IdleState.READER_IDLE) { //认为服务不可用,主动断开连接
+                Log.i(TAG, "TcpClientHandler ===> READER_IDLE");
+                ctx.close();
+            } else if (event.state() == IdleState.ALL_IDLE) {
+                Log.i(TAG, "TcpClientHandler ===> ALL_IDLE");
+            }
+        }
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+        cause.printStackTrace();
+        ctx.close();
+        Constant.TCP_CONNECTED = false;
+        //EventBus.getDefault().post(new MessageEvent(0, Constant.EVENT_TCP_STATE));
+        connected = false;
+        System.out.println("失去连接,错误引起");
+    }
+
+    //发送消息,不直接调用些方法,调用TcpClient中的发送消息
+    public void sendMsg(String msg){
+        if (ctx==null){
+            System.out.println("ctx is null");
+            return;
+        }
+        System.out.println("wzlll: tcp msg====" + msg);
+        ctx.writeAndFlush(Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8));
+    }
+}

+ 30 - 0
app/src/main/java/com/example/informationkanban/tcp/channel/DeviceChannel.java

@@ -0,0 +1,30 @@
+package com.example.informationkanban.tcp.channel;
+
+import android.util.Log;
+
+import com.example.informationkanban.common.MessageEvent;
+import com.example.informationkanban.tcp.dto.TcpModel;
+
+import org.greenrobot.eventbus.EventBus;
+
+import static com.example.informationkanban.common.Constant.EVENT_TCP_MSG;
+
+/**
+ * 前提:所有安卓设备可以互相通话。
+ * 在此前提下,所有安卓设备接收TCP信号的逻辑一致
+ */
+public class DeviceChannel {
+
+    public static TcpModel handleTcpReceived(TcpModel tcpModel){
+        TcpModel responseTcpModel = null;
+
+        Log.d("wzlll", "received tcp model: " + tcpModel.toJson());
+        switch (tcpModel.getType()){
+            case TIME:
+                EventBus.getDefault().post(new MessageEvent(tcpModel, EVENT_TCP_MSG));
+                break;
+        }
+
+        return responseTcpModel;
+    }
+}

+ 25 - 0
app/src/main/java/com/example/informationkanban/tcp/channel/DeviceUtil.java

@@ -0,0 +1,25 @@
+package com.example.informationkanban.tcp.channel;
+
+import android.os.Build;
+
+import com.example.informationkanban.BuildConfig;
+import com.example.informationkanban.tcp.dto.DeviceConnectDTO;
+import com.example.informationkanban.tcp.dto.TcpModel;
+import com.example.informationkanban.tcp.enums.TcpAction;
+import com.example.informationkanban.tcp.enums.TcpType;
+
+public class DeviceUtil {
+    public static TcpModel deviceConnect(String mac){
+        TcpModel tcpModel = new TcpModel();
+        DeviceConnectDTO connectDTO = new DeviceConnectDTO();
+        connectDTO.setIdentification(mac);
+        connectDTO.setHardware_version(Build.PRODUCT.toLowerCase());
+        connectDTO.setSoftware_version("V"+ BuildConfig.VERSION_NAME);
+        connectDTO.setModel(Build.MODEL);
+        connectDTO.setCode(Build.SERIAL);
+        tcpModel.setType(TcpType.DEVICE);
+        tcpModel.setAction(TcpAction.DeviceAction.CONNECT);
+        tcpModel.setData(connectDTO);
+        return tcpModel;
+    }
+}

+ 73 - 0
app/src/main/java/com/example/informationkanban/tcp/dto/DeviceConnectDTO.java

@@ -0,0 +1,73 @@
+package com.example.informationkanban.tcp.dto;
+
+import java.io.Serializable;
+
+/**
+ * @author wuyunfeng
+ * 2021-12-07 16:29
+ */
+public class DeviceConnectDTO implements Serializable {
+
+    /**设备唯一标识 */
+    private String identification;
+
+    /**
+     * 软件版本,客户端运行的app 版本号
+     */
+    private String software_version;
+
+    /**
+     * 固件版本号
+     */
+    private String hardware_version;
+
+    /**
+     * 设备型号
+     */
+    private String model;
+
+    /**
+     * 设备序列号
+     */
+    private String code;
+
+    public String getIdentification() {
+        return identification;
+    }
+
+    public void setIdentification(String identification) {
+        this.identification = identification;
+    }
+
+    public String getSoftware_version() {
+        return software_version;
+    }
+
+    public void setSoftware_version(String software_version) {
+        this.software_version = software_version;
+    }
+
+    public String getHardware_version() {
+        return hardware_version;
+    }
+
+    public void setHardware_version(String hardware_version) {
+        this.hardware_version = hardware_version;
+    }
+
+    public String getModel() {
+        return model;
+    }
+
+    public void setModel(String model) {
+        this.model = model;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+}

+ 179 - 0
app/src/main/java/com/example/informationkanban/tcp/dto/TcpModel.java

@@ -0,0 +1,179 @@
+package com.example.informationkanban.tcp.dto;
+
+import android.text.TextUtils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.example.informationkanban.tcp.enums.TcpAction;
+import com.example.informationkanban.tcp.enums.TcpType;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * tcp传输对象
+ *
+ * @author allen
+ * 2021-03-30 11:49
+ */
+public class TcpModel implements Serializable {
+    /**
+     * TCP传输对象的类型
+     */
+    private TcpType type;
+    /**
+     * TCP传输对象类型的动作
+     */
+    private TcpAction action;
+
+    /**
+     * 来源设备id
+     */
+    private Integer fromId;
+    /**
+     * 目标设备id,呼叫分机或其它设备时需要。分机呼叫不需要
+     */
+    private Integer toId;
+
+    /**
+     * TCP传输对象的数据
+     */
+    private Object data;
+
+    public TcpType getType() {
+        return type;
+    }
+
+    public void setType(TcpType type) {
+        this.type = type;
+    }
+
+    public TcpAction getAction() {
+        return action;
+    }
+
+    public void setAction(TcpAction action) {
+        this.action = action;
+    }
+
+    public Integer getFromId() {
+        return fromId;
+    }
+
+    public void setFromId(Integer fromId) {
+        this.fromId = fromId;
+    }
+
+    public Integer getToId() {
+        return toId;
+    }
+
+    public void setToId(Integer toId) {
+        this.toId = toId;
+    }
+
+    public Object getData() {
+        return data;
+    }
+
+    public void setData(Object data) {
+        this.data = data;
+    }
+
+    public String toJson(){
+        return JSON.toJSONString(this);
+    }
+
+    public static TcpModel getModelByJson(String tcpModelJsonString){
+        if (TextUtils.isEmpty(tcpModelJsonString)){
+            return null;
+        }
+        TcpModel tcpModel = new TcpModel();
+
+        JSONObject jsonObject = JSON.parseObject(tcpModelJsonString);
+        String type = jsonObject.getString("type");
+        String action = jsonObject.getString("action");
+        Integer fromId = jsonObject.getInteger("fromId");
+        Integer toId = jsonObject.getInteger("toId");
+        String dataString = jsonObject.getString("data");
+
+        TcpType tcpType = TcpType.fromString(type);
+        TcpAction tcpAction = null;
+        switch (tcpType){
+            case CALLBACK:
+                tcpAction = TcpAction.CallbackAction.fromString(action);
+                break;
+            case VOICE:
+                tcpAction = TcpAction.VoiceAction.fromString(action);
+                break;
+            case VIDEO:
+                tcpAction = TcpAction.VideoAction.fromString(action);
+                break;
+            case IM:
+                tcpAction = TcpAction.IMAction.fromString(action);
+                break;
+            case BROADCAST:
+                tcpAction = TcpAction.BroadcastAction.fromString(action);
+                break;
+            case DATA:
+                tcpAction = TcpAction.DataAction.fromString(action);
+                break;
+            case DEVICE:
+                tcpAction = TcpAction.DeviceAction.fromString(action);
+                break;
+            case TIME:
+                tcpAction = TcpAction.TimeAction.fromString(action);
+                break;
+            case EVENT:
+                tcpAction = TcpAction.EventAction.fromString(action);
+                break;
+            case SOS:
+                tcpAction = TcpAction.SOSAction.fromString(action);
+                break;
+            case SIDE:
+                tcpAction = TcpAction.SideAction.fromString(action);
+                break;
+            case ENTRACEGUARD:
+                tcpAction =TcpAction.EntraceGuardAction.fromString(action);
+                break;
+        }
+
+        tcpModel.setType(tcpType);
+        tcpModel.setAction(tcpAction);
+        tcpModel.setToId(toId);
+        tcpModel.setFromId(fromId);
+        tcpModel.setData(dataString);
+
+        return tcpModel;
+    }
+
+    public static String callbackSuccess(){
+        TcpModel tcpModel = new TcpModel();
+        tcpModel.setType(TcpType.CALLBACK);
+        tcpModel.setAction(TcpAction.CallbackAction.SUCCESS);
+        return tcpModel.toJson();
+    }
+
+    public static String callbackFailed(){
+        TcpModel tcpModel = new TcpModel();
+        tcpModel.setType(TcpType.CALLBACK);
+        tcpModel.setAction(TcpAction.CallbackAction.FAILED);
+        return tcpModel.toJson();
+    }
+
+    public static String refresh() {
+        TcpModel tcpModel = new TcpModel();
+        tcpModel.setType(TcpType.DATA);
+        tcpModel.setAction(TcpAction.DataAction.REFRESH);
+        return tcpModel.toJson();
+    }
+
+
+    public static String interaction() {
+        TcpModel tcpModel = new TcpModel();
+        tcpModel.setType(TcpType.DATA);
+        tcpModel.setAction(TcpAction.DataAction.INTERACTION);
+        return tcpModel.toJson();
+    }
+}

+ 82 - 0
app/src/main/java/com/example/informationkanban/tcp/enums/DeviceTypeEnum.java

@@ -0,0 +1,82 @@
+package com.example.informationkanban.tcp.enums;
+
+/**
+ * @author wuyunfeng
+ * 2021-05-12 09:49
+ * 设备类型枚举
+ */
+
+public enum DeviceTypeEnum {
+
+    NURSE_HOST(1, "护士主机"),
+    DOCTOR_HOST(2, "医生主机"),
+    DOOR_DEVICE(3, "门口机"),
+    DIGIT_BED_DEVICE(4, "病床分机"),
+    LCD_SCREEN(5, "LCD走廊屏"),
+    LED_SCREEN(6, "LED点阵屏"),
+    NURSE_WATCH(7, "护士移动"),
+    WORKER_WATCH(8, "护工移动"),
+    USER_WATCH(9, "用户移动"),
+    CELL_PHONE(10, "手机App"),
+    TRANSFER_DEVICE(11, "总线转换盒"),
+    SIMULATE_BED_DEVICE(12, "模拟分机"),
+    SIMULATE_EMERGENCY_BUTTON(13, "模拟紧急按钮"),
+    SIMULATE_DOOR_LIGHT(14, "模拟门灯"),
+    REMOTE_CONTROL(15, "遥控器"),
+    BEACON(16, "信标"),
+    INFORMATION_BOARD(17, "信息看版"),
+    ENTRACE_GUARD(18,"门禁设备"),
+    VISITATION(19,"探视机"),
+    RS485_TRANSFER(20,"485转换盒"),
+    EMERGENCY_BUTTON(21, "紧急按钮");
+    private int value;
+    private String typeName;
+
+
+    DeviceTypeEnum(int value, String typeName) {
+        this.value = value;
+        this.typeName = typeName;
+    }
+
+    public int value() {
+        return this.value;
+    }
+
+    public String typeName() {
+        return this.typeName;
+    }
+
+    /**
+     * 通过typeName 转换成枚举
+     *
+     * @param typeName
+     * @return
+     */
+    public static DeviceTypeEnum parse(String typeName) {
+        DeviceTypeEnum[] values = DeviceTypeEnum.values();
+        for (DeviceTypeEnum value : values) {
+            if (value.typeName().equals(typeName)) {
+                return value;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 通过value值转换成枚举
+     *
+     * @param value
+     * @return
+     */
+    public static DeviceTypeEnum parse(int value) {
+        DeviceTypeEnum[] values = DeviceTypeEnum.values();
+        for (DeviceTypeEnum deviceTypeEnum : values) {
+            if (deviceTypeEnum.value() == value) {
+                return deviceTypeEnum;
+            }
+        }
+        return null;
+    }
+
+
+}

+ 579 - 0
app/src/main/java/com/example/informationkanban/tcp/enums/TcpAction.java

@@ -0,0 +1,579 @@
+package com.example.informationkanban.tcp.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * TCP传输类型的动作
+ *
+ * @author allen
+ * 2021-03-30 15:30
+ */
+public interface TcpAction {
+    String getName();
+    String getDescription();
+
+    enum CallbackAction implements TcpAction {
+        SUCCESS("同步"),
+        FAILED("失败"),
+        NO_MATCH("没有匹配");
+        private final String description;
+        CallbackAction(String description){
+            this.description = description;
+        }
+        public String getDescription() {
+            return description;
+        }
+
+        public String getName(){
+            return this.name();
+        }
+
+        private final static Map<String , CallbackAction> ENUM_MAP = new HashMap<String, CallbackAction>();
+        static {
+            for(CallbackAction v : values()) {
+                ENUM_MAP.put(v.toString() , v);
+            }
+        }
+        public static CallbackAction fromString(String v) {
+            CallbackAction userOptionEnum = ENUM_MAP.get(v);
+            return userOptionEnum == null ? NO_MATCH :userOptionEnum;
+        }
+    }
+
+    enum PhoneAction implements TcpAction {
+        CALL("呼叫"),
+        ACCEPT("接受呼叫"),
+        REJECT("拒绝"),
+        CALLING("通话中"),
+        HANDOFF("挂断"),
+        DATA("数据传输"),
+        CANCEL("取消"),
+        NO_MATCH("没有匹配");
+
+
+        private final String description;
+        PhoneAction(String description){
+            this.description = description;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public String getName(){
+            return this.name();
+        }
+
+        private final static Map<String , PhoneAction> ENUM_MAP = new HashMap<String, PhoneAction>();
+        static {
+            for(PhoneAction v : values()) {
+                ENUM_MAP.put(v.toString() , v);
+            }
+        }
+        public static PhoneAction fromString(String v) {
+            PhoneAction userOptionEnum = ENUM_MAP.get(v);
+            return userOptionEnum == null ? NO_MATCH :userOptionEnum;
+        }
+    }
+
+    enum VoiceAction implements TcpAction {
+        CALL("呼叫"),
+        VCALL("视频呼叫"),
+        ACCEPT("接受呼叫"),
+        ACCEPTED("已接听"),
+        REJECT("拒绝"),
+        CALLING("通话中"),
+        TRANSFER("转接"),
+        HANDOFF("挂断"),
+        CANCEL("取消"),
+        PCALLING("已经通话中"),
+        VOICE_OFF("通话被接听"),
+        RS485CALL("485界面发起呼叫"),
+        RS485CANCEL("485界面呼叫取消"),
+        RS485CANCEL_BY_DOOR("485门口机取消房间内的呼叫"),
+        RS485HANDOFF("485界面挂断"),
+        RS485ACCEPT("485界面接听"),
+        RS485REJECT("485界面拒绝"),
+        CANCEL_BY_DOOR("门口机取消分机呼叫"),
+        SUCCESS("呼叫成功"),
+        FAILED("呼叫失败"),
+        GAIN_CALL("应答"),
+        GAIN_OK("应答成功"),
+        GAIN_FAIL("应答失败"),
+        GAINED("呼叫被应答"),
+        HCALL("手柄呼叫"),
+        HRESPONSE("响应手柄"),
+        NO_MATCH("没有匹配");
+
+
+        private final String description;
+        VoiceAction(String description){
+            this.description = description;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public String getName(){
+            return this.name();
+        }
+
+        private final static Map<String , VoiceAction> ENUM_MAP = new HashMap<String, VoiceAction>();
+        static {
+            for(VoiceAction v : values()) {
+                ENUM_MAP.put(v.toString() , v);
+            }
+        }
+        public static VoiceAction fromString(String v) {
+            VoiceAction userOptionEnum = ENUM_MAP.get(v);
+            return userOptionEnum == null ? NO_MATCH :userOptionEnum;
+        }
+    }
+
+    enum VideoAction implements TcpAction {
+        CALL("视频通话"),   //正常视频对讲
+        VIDEO_OUT_CALL("我方视频输出"),   //我方视频输出(我方语音,对方可视)
+        VIDEO_IN_CALL("对方视频输出"),    //对方视频输出(我方可视,对方语音)
+        VIDEO_ON("打开视频"),   //打开视频
+        VIDEO_INVITE_JOIN("邀请加入视频"),    //邀请加入视频
+        ACCEPT("接受呼叫"),
+        REJECT("拒绝"),
+        CALLING("通话中"),
+        HANDOFF("挂断"),
+        CANCEL("取消"),
+        SUCCESS("呼叫成功"),
+        FAILED("呼叫失败"),
+        NO_MATCH("没有匹配");
+
+        private final String description;
+        VideoAction(String description){
+            this.description = description;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public String getName(){
+            return this.name();
+        }
+
+        private final static Map<String , VideoAction> ENUM_MAP = new HashMap<String, VideoAction>();
+        static {
+            for(VideoAction v : values()) {
+                ENUM_MAP.put(v.toString() , v);
+            }
+        }
+        public static VideoAction fromString(String v) {
+            VideoAction userOptionEnum = ENUM_MAP.get(v);
+            return userOptionEnum == null ? NO_MATCH :userOptionEnum;
+        }
+    }
+
+
+    enum SOSAction implements TcpAction {
+        CALL("紧急呼叫"),
+        CANCEL("取消"),
+        TRANSFER("转接"),
+        ALARM_TEST("测试报警"),
+        ALARM_INTRUSION("侵入报警"),
+        ALARM_INFRARED_NO_TRIGGER("红外报警"),
+        ALARM_ON_EIGHT_HOURS("八小时无人报警"),
+        ALARM_ON_TWELVE_HOURS("十二小时无人报警"),
+        ALARM_ON_TWENTY_FOUR_HOURS("二十四小时无人报警"),
+        ALARM_DISASSEMBLE("防拆报警"),
+        ALARM_FAULT("故障报警"),
+        ALARM_DOOR_LOCK("门磁报警"),
+        ALARM_RESTRAINT_BAND("约束带报警"),
+        ALARM_SMOKE("烟感报警"),
+        ALARM_GAS("燃气报警"),
+        ALARM_WATER_OVERFLOW("浸水报警"),
+        ALARM_LOW_VOLTAGE("低电压报警"),
+        ALARM_TEMPERATURE("温度报警"),
+        ALARM_FALL("跌到报警"),
+        ALARM_VITAL("体征报警"),
+        AlARM_BUTTON("紧急按钮"),
+        ALARM_FALL_RADAR("跌到报警"),
+        NO_MATCH("没有匹配");
+
+        private final String description;
+        SOSAction(String description){
+            this.description = description;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public String getName(){
+            return this.name();
+        }
+
+        private final static Map<String , SOSAction> ENUM_MAP = new HashMap<String, SOSAction>();
+        static {
+            for(SOSAction v : values()) {
+                ENUM_MAP.put(v.toString() , v);
+            }
+        }
+        public static SOSAction fromString(String v) {
+            SOSAction userOptionEnum = ENUM_MAP.get(v);
+            return userOptionEnum == null ? NO_MATCH :userOptionEnum;
+        }
+    }
+
+    enum ReinforceAction implements TcpAction {
+        CALL("增援"),
+        RESPONSED("已响应"),
+        NO_MATCH("没有匹配");
+
+        private final String description;
+        ReinforceAction(String description){
+            this.description = description;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public String getName(){
+            return this.name();
+        }
+
+        private final static Map<String , ReinforceAction> ENUM_MAP = new HashMap<String, ReinforceAction>();
+        static {
+            for(ReinforceAction v : values()) {
+                ENUM_MAP.put(v.toString() , v);
+            }
+        }
+        public static ReinforceAction fromString(String v) {
+            ReinforceAction userOptionEnum = ENUM_MAP.get(v);
+            return userOptionEnum == null ? NO_MATCH :userOptionEnum;
+        }
+    }
+
+
+    enum IMAction implements TcpAction {
+        MSG("语音留言"),
+        MSG_READ("语音留言已读"),
+        RECEIVED("客户端收到确认"),
+        NO_MATCH("没有匹配");
+
+        private final String description;
+        IMAction(String description){
+            this.description = description;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public String getName(){
+            return this.name();
+        }
+
+        private final static Map<String , IMAction> ENUM_MAP = new HashMap<String, IMAction>();
+        static {
+            for(IMAction v : values()) {
+                ENUM_MAP.put(v.toString() , v);
+            }
+        }
+        public static IMAction fromString(String v) {
+            IMAction userOptionEnum = ENUM_MAP.get(v);
+            return userOptionEnum == null ? NO_MATCH :userOptionEnum;
+        }
+
+    }
+
+    enum CHANNELIMAction implements TcpAction {
+        MSG("语音留言"),
+        MSG_READ("语音留言已读"),
+        RECEIVED("接收端收到确认"),
+        NO_MATCH("没有匹配");
+
+        private final String description;
+        CHANNELIMAction(String description){
+            this.description = description;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public String getName(){
+            return this.name();
+        }
+
+        private final static Map<String , CHANNELIMAction> ENUM_MAP = new HashMap<String, CHANNELIMAction>();
+        static {
+            for(CHANNELIMAction v : values()) {
+                ENUM_MAP.put(v.toString() , v);
+            }
+        }
+        public static CHANNELIMAction fromString(String v) {
+            CHANNELIMAction userOptionEnum = ENUM_MAP.get(v);
+            return userOptionEnum == null ? NO_MATCH :userOptionEnum;
+        }
+
+    }
+
+    enum DeviceAction implements TcpAction {
+        RESTART("重启"),
+        CONNECT("连接"),
+        APP_UPDATE("APP更新"),
+        DEVICE_REFRESH("设备刷新"),
+        SYSTEM_SETTING("系统设置"),
+        DEVICE_CHANGE("设备更换"),
+        USER_CHANGE("用户绑定"),
+        SERVER_CHANGE("设备ip地址更换"),
+        NO_MATCH("没有匹配");
+
+        private final String description;
+        DeviceAction(String description){
+            this.description = description;
+        }
+        public String getDescription() {
+            return description;
+        }
+
+        public String getName(){
+            return this.name();
+        }
+
+        private final static Map<String , DeviceAction> ENUM_MAP = new HashMap<String, DeviceAction>();
+        static {
+            for(DeviceAction v : values()) {
+                ENUM_MAP.put(v.toString() , v);
+            }
+        }
+        public static DeviceAction fromString(String v) {
+            DeviceAction userOptionEnum = ENUM_MAP.get(v);
+            return userOptionEnum == null ? NO_MATCH :userOptionEnum;
+        }
+    }
+
+    enum EventAction implements TcpAction {
+        KEY_CLICK("按键事件"),
+        RESPONSE("已响应"),
+        RECEIVED("接收端收到确认"),
+        CANCEL("取消"),
+        CANCEL_CONFIRM("接收端确认收到取消"),
+        COMPLETED("完成"),
+        NO_MATCH("没有匹配");
+
+        private final String description;
+        EventAction(String description){
+            this.description = description;
+        }
+        public String getDescription() {
+            return description;
+        }
+
+        public String getName(){
+            return this.name();
+        }
+
+        private final static Map<String , EventAction> ENUM_MAP = new HashMap<String, EventAction>();
+        static {
+            for(EventAction v : values()) {
+                ENUM_MAP.put(v.toString() , v);
+            }
+        }
+        public static EventAction fromString(String v) {
+            EventAction userOptionEnum = ENUM_MAP.get(v);
+            return userOptionEnum == null ? NO_MATCH :userOptionEnum;
+        }
+    }
+
+    enum SideAction implements TcpAction {
+        CALL("呼叫"),
+        ACCEPT("接听"),
+        CANCEL("结束"),
+        SOS_CALL("紧急呼叫"),
+        SOS_CANCEL("取消"),
+        NURSING("护理"),
+        NURSING_END("护理结束"),
+        NO_MATCH("没有匹配");
+
+        private final String description;
+        SideAction(String description){
+            this.description = description;
+        }
+        public String getDescription() {
+            return description;
+        }
+
+        public String getName(){
+            return this.name();
+        }
+
+        private final static Map<String , SideAction> ENUM_MAP = new HashMap<String, SideAction>();
+        static {
+            for(SideAction v : values()) {
+                ENUM_MAP.put(v.toString() , v);
+            }
+        }
+        public static SideAction fromString(String v) {
+            SideAction userOptionEnum = ENUM_MAP.get(v);
+            return userOptionEnum == null ? NO_MATCH :userOptionEnum;
+        }
+    }
+
+
+
+    enum DataAction implements TcpAction {
+        REFRESH("刷新数据"),
+        INTERACTION("刷新交互列表"),
+        NO_MATCH("没有匹配");
+
+        private final String description;
+        DataAction(String description){
+            this.description = description;
+        }
+        public String getDescription() {
+            return description;
+        }
+
+        public String getName(){
+            return this.name();
+        }
+
+        private final static Map<String , DataAction> ENUM_MAP = new HashMap<String, DataAction>();
+        static {
+            for(DataAction v : values()) {
+                ENUM_MAP.put(v.toString() , v);
+            }
+        }
+        public static DataAction fromString(String v) {
+            DataAction userOptionEnum = ENUM_MAP.get(v);
+            return userOptionEnum == null ? NO_MATCH :userOptionEnum;
+        }
+    }
+
+    enum BroadcastAction implements TcpAction {
+        START("开始"),
+        STOP("停止"),
+        NO_MATCH("没有匹配");
+
+        private final String description;
+        BroadcastAction(String description){
+            this.description = description;
+        }
+        public String getDescription() {
+            return description;
+        }
+
+        public String getName(){
+            return this.name();
+        }
+
+        private final static Map<String , BroadcastAction> ENUM_MAP = new HashMap<String, BroadcastAction>();
+        static {
+            for(BroadcastAction v : values()) {
+                ENUM_MAP.put(v.toString() , v);
+            }
+        }
+        public static BroadcastAction fromString(String v) {
+            BroadcastAction userOptionEnum = ENUM_MAP.get(v);
+            return userOptionEnum == null ? NO_MATCH :userOptionEnum;
+        }
+    }
+
+    enum TimeAction implements TcpAction {
+        SYNC("同步"),
+        NO_MATCH("没有匹配");
+
+        private final String description;
+        TimeAction(String description){
+            this.description = description;
+        }
+        public String getDescription() {
+            return description;
+        }
+
+        public String getName(){
+            return this.name();
+        }
+
+        private final static Map<String , TimeAction> ENUM_MAP = new HashMap<String, TimeAction>();
+        static {
+            for(TimeAction v : values()) {
+                ENUM_MAP.put(v.toString() , v);
+            }
+        }
+        public static TimeAction fromString(String v) {
+            TimeAction userOptionEnum = ENUM_MAP.get(v);
+            return userOptionEnum == null ? NO_MATCH :userOptionEnum;
+        }
+    }
+
+    enum EntraceGuardAction implements TcpAction {
+        STRANGER("陌生访客"),
+        LOCKDOOR("锁门"),
+        UNLOCKDOOR("开门"),
+        ACCEPT("主机接受视频"),//通话空闲时自动接受
+        REJECT("拒绝"), // 通话忙时自动拒接
+        OPENSPEAKER("打开麦克风"),//护士主机打开麦克风,门禁机也打开,双方停止等待音乐,打开后可以通话,默认只有视频界面,没有声音
+        CLOSESPEAKER("关闭麦克风"),//关闭音频
+        SUCCESS("连接主机成功"),
+        HANGUP("挂断"),
+        TIMEOUT("响应超时"),
+        FAILED("连接失败"),
+        NO_MATCH("没有匹配");
+
+
+        private final String description;
+
+        EntraceGuardAction(String description) {
+            this.description = description;
+        }
+        public String getDescription() {
+            return description;
+        }
+
+        public String getName(){
+            return this.name();
+        }
+
+        private final static Map<String , EntraceGuardAction> ENUM_MAP = new HashMap<String, EntraceGuardAction>();
+        static {
+            for(EntraceGuardAction v : values()) {
+                ENUM_MAP.put(v.toString() , v);
+            }
+        }
+        public static EntraceGuardAction fromString(String v) {
+            EntraceGuardAction userOptionEnum = ENUM_MAP.get(v);
+            return userOptionEnum == null ? NO_MATCH :userOptionEnum;
+        }
+    }
+
+    enum LocationAction implements TcpAction {
+        ASK("主动询问"),
+        UPDATE("上传数据"),
+        NO_MATCH("没有匹配");
+
+        private final String description;
+        LocationAction(String description){
+            this.description = description;
+        }
+        public String getDescription() {
+            return description;
+        }
+
+        public String getName(){
+            return this.name();
+        }
+
+        private final static Map<String , LocationAction> ENUM_MAP = new HashMap<String, LocationAction>();
+        static {
+            for(LocationAction v : values()) {
+                ENUM_MAP.put(v.toString() , v);
+            }
+        }
+        public static LocationAction fromString(String v) {
+            LocationAction userOptionEnum = ENUM_MAP.get(v);
+            return userOptionEnum == null ? NO_MATCH :userOptionEnum;
+        }
+    }
+}

+ 44 - 0
app/src/main/java/com/example/informationkanban/tcp/enums/TcpType.java

@@ -0,0 +1,44 @@
+package com.example.informationkanban.tcp.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * TCP传输对象的类型
+ */
+public enum TcpType {
+    CALLBACK("TCP反馈"),
+    PHONE("普通电话"),
+    VOICE("语音"),
+    VIDEO("视频"),
+    SOS("紧急呼叫"),
+    REINFORCE("增援"),
+    IM("留言"),
+    EVENT("事件"),
+    SIDE("边外信息"),
+    DEVICE("设备"),
+    DATA("数据"),
+    BROADCAST("广播"),
+    ENTRACEGUARD("门禁"),
+    CHANNELIM("频道留言"),
+    TIME("时间");
+
+    private String description;
+    TcpType(String description){
+        this.description = description;
+    }
+    public String getDescription() {
+        return description;
+    }
+
+    private final static Map<String , TcpType> ENUM_MAP = new HashMap<String, TcpType>();
+    static {
+        for(TcpType v : values()) {
+            ENUM_MAP.put(v.toString() , v);
+        }
+    }
+    public static TcpType fromString(String v) {
+        TcpType userOptionEnum = ENUM_MAP.get(v);
+        return userOptionEnum == null ? CALLBACK :userOptionEnum;
+    }
+}

+ 107 - 0
app/src/main/java/com/example/informationkanban/utils/AppUtil.java

@@ -0,0 +1,107 @@
+package com.example.informationkanban.utils;
+
+import android.app.AlarmManager;
+import android.content.Context;
+import android.text.TextUtils;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+public class AppUtil {
+
+    /**
+     * 设置系统时间
+     *
+     * @param time
+     */
+    public static void setSysTime(String time, String timezone) {
+        try {
+            Process process = Runtime.getRuntime().exec("su");
+            if (null == process) return;
+            DataOutputStream os = new DataOutputStream(process.getOutputStream());
+            //os.writeBytes("setprop persist.sys.timezone Asia/Shanghai\n");
+            os.writeBytes("setprop persist.sys.timezone " + timezone + "\n");
+            if (android.os.Build.VERSION.SDK_INT >= 24) {//7.1以上的系统
+                String datetime = changeTimeForm(time); //20211213:092314  ------  051315372019.00
+                os.writeBytes("/system/bin/date " + datetime + " set\n");
+            } else {
+                os.writeBytes("/system/bin/date -s " + time + "\n");//【时间格式 yyyyMMdd.HHmmss】"20131023.112800"
+            }
+            os.writeBytes("clock -w\n");
+            os.writeBytes("exit\n");
+            os.flush();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static String changeTimeForm(String t) {
+        if (!TextUtils.isEmpty(t) && t.length() >= 15) {
+            String yyyy = substringByLengh(t, 0, 4);
+            String MMdd = substringByLengh(t, 4, 8);
+            String HHmm = substringByLengh(t, 9, 13);
+            String ss = substringByLengh(t, 13, 15);
+
+            return MMdd + HHmm + yyyy + "." + ss;
+        } else {
+            return "051315372019.00";
+        }
+    }
+
+    public static String getDateTime(long timeMillis, String format, String timezone) {
+        if (timeMillis > 0) {
+            Date date = new Date(timeMillis);
+            SimpleDateFormat sdf = new SimpleDateFormat(format);
+            sdf.setTimeZone(TimeZone.getTimeZone(timezone));
+            return sdf.format(date);
+        }
+        return "null";
+    }
+
+    /**
+     * 字符串按索引截取
+     *
+     * @param str
+     * @return
+     */
+    public static String substringByLengh(String str, int start, int end) {
+        if (str == null) {
+            return "";
+        }
+        if (start > end) {
+            return "";
+        }
+        if (str.length() - 1 < start || str.length() < end) {
+            return "";
+        }
+
+        return str.substring(start, end);
+    }
+
+
+    public static void setSystemTime(Context context, int year, int month, int day, int hour, int minute, int mill) {
+        Calendar c = Calendar.getInstance();
+        c.set(Calendar.YEAR, year);
+        c.set(Calendar.MONTH, month);
+        c.set(Calendar.DAY_OF_MONTH, day);
+        c.set(Calendar.HOUR_OF_DAY, hour);
+        c.set(Calendar.MINUTE, minute);
+        c.set(Calendar.SECOND, mill);
+        c.set(Calendar.MILLISECOND, 0);
+        long when = c.getTimeInMillis();
+        if (when / 1000 < Integer.MAX_VALUE) {
+            ((AlarmManager) context.getSystemService(Context.ALARM_SERVICE)).setTime(when);
+        }
+    }
+
+    public static void setSystemTime(Context context, long timeMills, String timezone) {
+        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+        //alarmManager.setTimeZone("Asia/Shanghai");
+        alarmManager.setTimeZone(timezone);
+        alarmManager.setTime(timeMills);
+    }
+}

+ 1 - 0
app/src/main/java/com/example/informationkanban/utils/NetFunctionConfig.java

@@ -19,6 +19,7 @@ public class NetFunctionConfig {
     private static final String UUID = "UUID";
 
     private static final String default_server_ip = "172.28.100.100";
+    //private static final String default_server_ip = "8.129.220.143";
     private static final String default_server_port = "8100";
 
     public static void setWebviewType(int type) {