修复push消息时连续for循环中推送时,android , java 客户端会收不到消息的问题

This commit is contained in:
远方夕阳 2020-03-07 15:49:09 +08:00
parent f3b7ec63e0
commit 5dc06714ff
78 changed files with 2092 additions and 2253 deletions

View File

@ -25,45 +25,45 @@
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.2.2.RELEASE" level="project" /> <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.2.2.RELEASE" level="project" /> <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-context:5.2.2.RELEASE" level="project" /> <orderEntry type="library" name="Maven: org.springframework:spring-context:5.2.4.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.2.2.RELEASE" level="project" /> <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-logging:2.2.2.RELEASE" level="project" /> <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-logging:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.2.3" level="project" /> <orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.2.3" level="project" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.2.3" level="project" /> <orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.2.3" level="project" />
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-to-slf4j:2.12.1" level="project" /> <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-to-slf4j:2.12.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.12.1" level="project" /> <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.12.1" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.29" level="project" /> <orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.30" level="project" />
<orderEntry type="library" name="Maven: jakarta.annotation:jakarta.annotation-api:1.3.5" level="project" /> <orderEntry type="library" name="Maven: jakarta.annotation:jakarta.annotation-api:1.3.5" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-core:5.2.2.RELEASE" level="project" /> <orderEntry type="library" name="Maven: org.springframework:spring-core:5.2.4.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.2.2.RELEASE" level="project" /> <orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.2.4.RELEASE" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: org.yaml:snakeyaml:1.25" level="project" /> <orderEntry type="library" scope="RUNTIME" name="Maven: org.yaml:snakeyaml:1.25" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-web:2.2.2.RELEASE" level="project" /> <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-web:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-json:2.2.2.RELEASE" level="project" /> <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-json:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.10.1" level="project" /> <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.10.2" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.10.1" level="project" /> <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.10.2" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.10.1" level="project" /> <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.10.2" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.10.1" level="project" /> <orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.10.2" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.10.1" level="project" /> <orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.10.2" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.10.1" level="project" /> <orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.10.2" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-tomcat:2.2.2.RELEASE" level="project" /> <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-tomcat:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-core:9.0.29" level="project" /> <orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-core:9.0.31" level="project" />
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-el:9.0.29" level="project" /> <orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-el:9.0.31" level="project" />
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-websocket:9.0.29" level="project" /> <orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-websocket:9.0.31" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-validation:2.2.2.RELEASE" level="project" /> <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-validation:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.1" level="project" /> <orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" />
<orderEntry type="library" name="Maven: org.hibernate.validator:hibernate-validator:6.0.18.Final" level="project" /> <orderEntry type="library" name="Maven: org.hibernate.validator:hibernate-validator:6.0.18.Final" level="project" />
<orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.1.Final" level="project" /> <orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.1.Final" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" /> <orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-web:5.2.2.RELEASE" level="project" /> <orderEntry type="library" name="Maven: org.springframework:spring-web:5.2.4.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-beans:5.2.2.RELEASE" level="project" /> <orderEntry type="library" name="Maven: org.springframework:spring-beans:5.2.4.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-webmvc:5.2.2.RELEASE" level="project" /> <orderEntry type="library" name="Maven: org.springframework:spring-webmvc:5.2.4.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-aop:5.2.2.RELEASE" level="project" /> <orderEntry type="library" name="Maven: org.springframework:spring-aop:5.2.4.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-expression:5.2.2.RELEASE" level="project" /> <orderEntry type="library" name="Maven: org.springframework:spring-expression:5.2.4.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-freemarker:2.2.2.RELEASE" level="project" /> <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-freemarker:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.freemarker:freemarker:2.3.29" level="project" /> <orderEntry type="library" name="Maven: org.freemarker:freemarker:2.3.29" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-context-support:5.2.2.RELEASE" level="project" /> <orderEntry type="library" name="Maven: org.springframework:spring-context-support:5.2.4.RELEASE" level="project" />
<orderEntry type="module-library"> <orderEntry type="module-library">
<library name="Maven: com.farsunset:cim-server-sdk:3.8.0"> <library name="Maven: com.farsunset:cim-server-sdk:3.8.0">
<CLASSES> <CLASSES>
@ -74,7 +74,7 @@
</library> </library>
</orderEntry> </orderEntry>
<orderEntry type="library" name="Maven: org.apache.mina:mina-core:2.1.3" level="project" /> <orderEntry type="library" name="Maven: org.apache.mina:mina-core:2.1.3" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.29" level="project" /> <orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.35.Final" level="project" /> <orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.35.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.35.Final" level="project" /> <orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.35.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.35.Final" level="project" /> <orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.35.Final" level="project" />

View File

@ -77,6 +77,23 @@
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</component>
<component name="libraryTable"> <component name="libraryTable">
<library name="Maven: ch.qos.logback:logback-classic:1.2.3"> <library name="Maven: ch.qos.logback:logback-classic:1.2.3">
<CLASSES> <CLASSES>
@ -111,70 +128,70 @@
<root url="jar://$MAVEN_REPOSITORY$/cn/teaey/apns4j/apns4j/1.1.4/apns4j-1.1.4-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/cn/teaey/apns4j/apns4j/1.1.4/apns4j-1.1.4-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.10.1"> <library name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.10.2">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-annotations/2.10.1/jackson-annotations-2.10.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-annotations/2.10.2/jackson-annotations-2.10.2.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-annotations/2.10.1/jackson-annotations-2.10.1-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-annotations/2.10.2/jackson-annotations-2.10.2-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-annotations/2.10.1/jackson-annotations-2.10.1-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-annotations/2.10.2/jackson-annotations-2.10.2-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: com.fasterxml.jackson.core:jackson-core:2.10.1"> <library name="Maven: com.fasterxml.jackson.core:jackson-core:2.10.2">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-core/2.10.1/jackson-core-2.10.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-core/2.10.2/jackson-core-2.10.2.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-core/2.10.1/jackson-core-2.10.1-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-core/2.10.2/jackson-core-2.10.2-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-core/2.10.1/jackson-core-2.10.1-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-core/2.10.2/jackson-core-2.10.2-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: com.fasterxml.jackson.core:jackson-databind:2.10.1"> <library name="Maven: com.fasterxml.jackson.core:jackson-databind:2.10.2">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-databind/2.10.1/jackson-databind-2.10.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-databind/2.10.2/jackson-databind-2.10.2.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-databind/2.10.1/jackson-databind-2.10.1-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-databind/2.10.2/jackson-databind-2.10.2-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-databind/2.10.1/jackson-databind-2.10.1-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-databind/2.10.2/jackson-databind-2.10.2-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.10.1"> <library name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.10.2">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.10.1/jackson-datatype-jdk8-2.10.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.10.2/jackson-datatype-jdk8-2.10.2.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.10.1/jackson-datatype-jdk8-2.10.1-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.10.2/jackson-datatype-jdk8-2.10.2-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.10.1/jackson-datatype-jdk8-2.10.1-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.10.2/jackson-datatype-jdk8-2.10.2-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.10.1"> <library name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.10.2">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.10.1/jackson-datatype-jsr310-2.10.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.10.2/jackson-datatype-jsr310-2.10.2.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.10.1/jackson-datatype-jsr310-2.10.1-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.10.2/jackson-datatype-jsr310-2.10.2-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.10.1/jackson-datatype-jsr310-2.10.1-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.10.2/jackson-datatype-jsr310-2.10.2-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.10.1"> <library name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.10.2">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/module/jackson-module-parameter-names/2.10.1/jackson-module-parameter-names-2.10.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/module/jackson-module-parameter-names/2.10.2/jackson-module-parameter-names-2.10.2.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/module/jackson-module-parameter-names/2.10.1/jackson-module-parameter-names-2.10.1-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/module/jackson-module-parameter-names/2.10.2/jackson-module-parameter-names-2.10.2-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/module/jackson-module-parameter-names/2.10.1/jackson-module-parameter-names-2.10.1-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/module/jackson-module-parameter-names/2.10.2/jackson-module-parameter-names-2.10.2-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: com.fasterxml:classmate:1.5.1"> <library name="Maven: com.fasterxml:classmate:1.5.1">
@ -298,15 +315,15 @@
<root url="jar://$MAVEN_REPOSITORY$/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: jakarta.validation:jakarta.validation-api:2.0.1"> <library name="Maven: jakarta.validation:jakarta.validation-api:2.0.2">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/jakarta/validation/jakarta.validation-api/2.0.1/jakarta.validation-api-2.0.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/jakarta/validation/jakarta.validation-api/2.0.2/jakarta.validation-api-2.0.2.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/jakarta/validation/jakarta.validation-api/2.0.1/jakarta.validation-api-2.0.1-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/jakarta/validation/jakarta.validation-api/2.0.2/jakarta.validation-api-2.0.2-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/jakarta/validation/jakarta.validation-api/2.0.1/jakarta.validation-api-2.0.1-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/jakarta/validation/jakarta.validation-api/2.0.2/jakarta.validation-api-2.0.2-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.apache.commons:commons-lang3:3.8.1"> <library name="Maven: org.apache.commons:commons-lang3:3.8.1">
@ -353,37 +370,37 @@
<root url="jar://$MAVEN_REPOSITORY$/org/apache/mina/mina-core/2.1.3/mina-core-2.1.3-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/apache/mina/mina-core/2.1.3/mina-core-2.1.3-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.apache.tomcat.embed:tomcat-embed-core:9.0.29"> <library name="Maven: org.apache.tomcat.embed:tomcat-embed-core:9.0.31">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-core/9.0.29/tomcat-embed-core-9.0.29.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-core/9.0.31/tomcat-embed-core-9.0.31.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-core/9.0.29/tomcat-embed-core-9.0.29-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-core/9.0.31/tomcat-embed-core-9.0.31-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-core/9.0.29/tomcat-embed-core-9.0.29-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-core/9.0.31/tomcat-embed-core-9.0.31-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.apache.tomcat.embed:tomcat-embed-el:9.0.29"> <library name="Maven: org.apache.tomcat.embed:tomcat-embed-el:9.0.31">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-el/9.0.29/tomcat-embed-el-9.0.29.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-el/9.0.31/tomcat-embed-el-9.0.31.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-el/9.0.29/tomcat-embed-el-9.0.29-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-el/9.0.31/tomcat-embed-el-9.0.31-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-el/9.0.29/tomcat-embed-el-9.0.29-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-el/9.0.31/tomcat-embed-el-9.0.31-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.apache.tomcat.embed:tomcat-embed-websocket:9.0.29"> <library name="Maven: org.apache.tomcat.embed:tomcat-embed-websocket:9.0.31">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.29/tomcat-embed-websocket-9.0.29.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.31/tomcat-embed-websocket-9.0.31.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.29/tomcat-embed-websocket-9.0.29-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.31/tomcat-embed-websocket-9.0.31-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.29/tomcat-embed-websocket-9.0.29-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.31/tomcat-embed-websocket-9.0.31-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.freemarker:freemarker:2.3.29"> <library name="Maven: org.freemarker:freemarker:2.3.29">
@ -419,224 +436,224 @@
<root url="jar://$MAVEN_REPOSITORY$/org/jboss/logging/jboss-logging/3.4.1.Final/jboss-logging-3.4.1.Final-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jboss/logging/jboss-logging/3.4.1.Final/jboss-logging-3.4.1.Final-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.slf4j:jul-to-slf4j:1.7.29"> <library name="Maven: org.slf4j:jul-to-slf4j:1.7.30">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/jul-to-slf4j/1.7.29/jul-to-slf4j-1.7.29.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/slf4j/jul-to-slf4j/1.7.30/jul-to-slf4j-1.7.30.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/jul-to-slf4j/1.7.29/jul-to-slf4j-1.7.29-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/slf4j/jul-to-slf4j/1.7.30/jul-to-slf4j-1.7.30-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/jul-to-slf4j/1.7.29/jul-to-slf4j-1.7.29-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/slf4j/jul-to-slf4j/1.7.30/jul-to-slf4j-1.7.30-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.slf4j:slf4j-api:1.7.29"> <library name="Maven: org.slf4j:slf4j-api:1.7.30">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.29/slf4j-api-1.7.29.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.29/slf4j-api-1.7.29-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.29/slf4j-api-1.7.29-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.2.2.RELEASE"> <library name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.2.5.RELEASE">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-autoconfigure/2.2.2.RELEASE/spring-boot-autoconfigure-2.2.2.RELEASE.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-autoconfigure/2.2.5.RELEASE/spring-boot-autoconfigure-2.2.5.RELEASE.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-autoconfigure/2.2.2.RELEASE/spring-boot-autoconfigure-2.2.2.RELEASE-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-autoconfigure/2.2.5.RELEASE/spring-boot-autoconfigure-2.2.5.RELEASE-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-autoconfigure/2.2.2.RELEASE/spring-boot-autoconfigure-2.2.2.RELEASE-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-autoconfigure/2.2.5.RELEASE/spring-boot-autoconfigure-2.2.5.RELEASE-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.springframework.boot:spring-boot-starter-freemarker:2.2.2.RELEASE"> <library name="Maven: org.springframework.boot:spring-boot-starter-freemarker:2.2.5.RELEASE">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-freemarker/2.2.2.RELEASE/spring-boot-starter-freemarker-2.2.2.RELEASE.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-freemarker/2.2.5.RELEASE/spring-boot-starter-freemarker-2.2.5.RELEASE.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-freemarker/2.2.2.RELEASE/spring-boot-starter-freemarker-2.2.2.RELEASE-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-freemarker/2.2.5.RELEASE/spring-boot-starter-freemarker-2.2.5.RELEASE-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-freemarker/2.2.2.RELEASE/spring-boot-starter-freemarker-2.2.2.RELEASE-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-freemarker/2.2.5.RELEASE/spring-boot-starter-freemarker-2.2.5.RELEASE-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.springframework.boot:spring-boot-starter-json:2.2.2.RELEASE"> <library name="Maven: org.springframework.boot:spring-boot-starter-json:2.2.5.RELEASE">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-json/2.2.2.RELEASE/spring-boot-starter-json-2.2.2.RELEASE.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-json/2.2.5.RELEASE/spring-boot-starter-json-2.2.5.RELEASE.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-json/2.2.2.RELEASE/spring-boot-starter-json-2.2.2.RELEASE-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-json/2.2.5.RELEASE/spring-boot-starter-json-2.2.5.RELEASE-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-json/2.2.2.RELEASE/spring-boot-starter-json-2.2.2.RELEASE-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-json/2.2.5.RELEASE/spring-boot-starter-json-2.2.5.RELEASE-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.springframework.boot:spring-boot-starter-logging:2.2.2.RELEASE"> <library name="Maven: org.springframework.boot:spring-boot-starter-logging:2.2.5.RELEASE">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-logging/2.2.2.RELEASE/spring-boot-starter-logging-2.2.2.RELEASE.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-logging/2.2.5.RELEASE/spring-boot-starter-logging-2.2.5.RELEASE.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-logging/2.2.2.RELEASE/spring-boot-starter-logging-2.2.2.RELEASE-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-logging/2.2.5.RELEASE/spring-boot-starter-logging-2.2.5.RELEASE-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-logging/2.2.2.RELEASE/spring-boot-starter-logging-2.2.2.RELEASE-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-logging/2.2.5.RELEASE/spring-boot-starter-logging-2.2.5.RELEASE-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.springframework.boot:spring-boot-starter-tomcat:2.2.2.RELEASE"> <library name="Maven: org.springframework.boot:spring-boot-starter-tomcat:2.2.5.RELEASE">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-tomcat/2.2.2.RELEASE/spring-boot-starter-tomcat-2.2.2.RELEASE.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-tomcat/2.2.5.RELEASE/spring-boot-starter-tomcat-2.2.5.RELEASE.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-tomcat/2.2.2.RELEASE/spring-boot-starter-tomcat-2.2.2.RELEASE-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-tomcat/2.2.5.RELEASE/spring-boot-starter-tomcat-2.2.5.RELEASE-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-tomcat/2.2.2.RELEASE/spring-boot-starter-tomcat-2.2.2.RELEASE-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-tomcat/2.2.5.RELEASE/spring-boot-starter-tomcat-2.2.5.RELEASE-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.springframework.boot:spring-boot-starter-validation:2.2.2.RELEASE"> <library name="Maven: org.springframework.boot:spring-boot-starter-validation:2.2.5.RELEASE">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-validation/2.2.2.RELEASE/spring-boot-starter-validation-2.2.2.RELEASE.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-validation/2.2.5.RELEASE/spring-boot-starter-validation-2.2.5.RELEASE.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-validation/2.2.2.RELEASE/spring-boot-starter-validation-2.2.2.RELEASE-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-validation/2.2.5.RELEASE/spring-boot-starter-validation-2.2.5.RELEASE-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-validation/2.2.2.RELEASE/spring-boot-starter-validation-2.2.2.RELEASE-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-validation/2.2.5.RELEASE/spring-boot-starter-validation-2.2.5.RELEASE-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.springframework.boot:spring-boot-starter-web:2.2.2.RELEASE"> <library name="Maven: org.springframework.boot:spring-boot-starter-web:2.2.5.RELEASE">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-web/2.2.2.RELEASE/spring-boot-starter-web-2.2.2.RELEASE.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-web/2.2.5.RELEASE/spring-boot-starter-web-2.2.5.RELEASE.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-web/2.2.2.RELEASE/spring-boot-starter-web-2.2.2.RELEASE-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-web/2.2.5.RELEASE/spring-boot-starter-web-2.2.5.RELEASE-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-web/2.2.2.RELEASE/spring-boot-starter-web-2.2.2.RELEASE-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-web/2.2.5.RELEASE/spring-boot-starter-web-2.2.5.RELEASE-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.springframework.boot:spring-boot-starter:2.2.2.RELEASE"> <library name="Maven: org.springframework.boot:spring-boot-starter:2.2.5.RELEASE">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter/2.2.2.RELEASE/spring-boot-starter-2.2.2.RELEASE.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter/2.2.5.RELEASE/spring-boot-starter-2.2.5.RELEASE.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter/2.2.2.RELEASE/spring-boot-starter-2.2.2.RELEASE-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter/2.2.5.RELEASE/spring-boot-starter-2.2.5.RELEASE-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter/2.2.2.RELEASE/spring-boot-starter-2.2.2.RELEASE-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter/2.2.5.RELEASE/spring-boot-starter-2.2.5.RELEASE-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.springframework.boot:spring-boot:2.2.2.RELEASE"> <library name="Maven: org.springframework.boot:spring-boot:2.2.5.RELEASE">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot/2.2.2.RELEASE/spring-boot-2.2.2.RELEASE.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot/2.2.5.RELEASE/spring-boot-2.2.5.RELEASE.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot/2.2.2.RELEASE/spring-boot-2.2.2.RELEASE-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot/2.2.5.RELEASE/spring-boot-2.2.5.RELEASE-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot/2.2.2.RELEASE/spring-boot-2.2.2.RELEASE-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot/2.2.5.RELEASE/spring-boot-2.2.5.RELEASE-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.springframework:spring-aop:5.2.2.RELEASE"> <library name="Maven: org.springframework:spring-aop:5.2.4.RELEASE">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-aop/5.2.2.RELEASE/spring-aop-5.2.2.RELEASE.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-aop/5.2.4.RELEASE/spring-aop-5.2.4.RELEASE.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-aop/5.2.2.RELEASE/spring-aop-5.2.2.RELEASE-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-aop/5.2.4.RELEASE/spring-aop-5.2.4.RELEASE-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-aop/5.2.2.RELEASE/spring-aop-5.2.2.RELEASE-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-aop/5.2.4.RELEASE/spring-aop-5.2.4.RELEASE-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.springframework:spring-beans:5.2.2.RELEASE"> <library name="Maven: org.springframework:spring-beans:5.2.4.RELEASE">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-beans/5.2.2.RELEASE/spring-beans-5.2.2.RELEASE.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-beans/5.2.4.RELEASE/spring-beans-5.2.4.RELEASE.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-beans/5.2.2.RELEASE/spring-beans-5.2.2.RELEASE-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-beans/5.2.4.RELEASE/spring-beans-5.2.4.RELEASE-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-beans/5.2.2.RELEASE/spring-beans-5.2.2.RELEASE-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-beans/5.2.4.RELEASE/spring-beans-5.2.4.RELEASE-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.springframework:spring-context-support:5.2.2.RELEASE"> <library name="Maven: org.springframework:spring-context-support:5.2.4.RELEASE">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-context-support/5.2.2.RELEASE/spring-context-support-5.2.2.RELEASE.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-context-support/5.2.4.RELEASE/spring-context-support-5.2.4.RELEASE.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-context-support/5.2.2.RELEASE/spring-context-support-5.2.2.RELEASE-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-context-support/5.2.4.RELEASE/spring-context-support-5.2.4.RELEASE-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-context-support/5.2.2.RELEASE/spring-context-support-5.2.2.RELEASE-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-context-support/5.2.4.RELEASE/spring-context-support-5.2.4.RELEASE-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.springframework:spring-context:5.2.2.RELEASE"> <library name="Maven: org.springframework:spring-context:5.2.4.RELEASE">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-context/5.2.2.RELEASE/spring-context-5.2.2.RELEASE.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-context/5.2.4.RELEASE/spring-context-5.2.4.RELEASE.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-context/5.2.2.RELEASE/spring-context-5.2.2.RELEASE-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-context/5.2.4.RELEASE/spring-context-5.2.4.RELEASE-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-context/5.2.2.RELEASE/spring-context-5.2.2.RELEASE-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-context/5.2.4.RELEASE/spring-context-5.2.4.RELEASE-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.springframework:spring-core:5.2.2.RELEASE"> <library name="Maven: org.springframework:spring-core:5.2.4.RELEASE">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-core/5.2.2.RELEASE/spring-core-5.2.2.RELEASE.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-core/5.2.4.RELEASE/spring-core-5.2.4.RELEASE.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-core/5.2.2.RELEASE/spring-core-5.2.2.RELEASE-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-core/5.2.4.RELEASE/spring-core-5.2.4.RELEASE-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-core/5.2.2.RELEASE/spring-core-5.2.2.RELEASE-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-core/5.2.4.RELEASE/spring-core-5.2.4.RELEASE-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.springframework:spring-expression:5.2.2.RELEASE"> <library name="Maven: org.springframework:spring-expression:5.2.4.RELEASE">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-expression/5.2.2.RELEASE/spring-expression-5.2.2.RELEASE.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-expression/5.2.4.RELEASE/spring-expression-5.2.4.RELEASE.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-expression/5.2.2.RELEASE/spring-expression-5.2.2.RELEASE-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-expression/5.2.4.RELEASE/spring-expression-5.2.4.RELEASE-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-expression/5.2.2.RELEASE/spring-expression-5.2.2.RELEASE-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-expression/5.2.4.RELEASE/spring-expression-5.2.4.RELEASE-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.springframework:spring-jcl:5.2.2.RELEASE"> <library name="Maven: org.springframework:spring-jcl:5.2.4.RELEASE">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-jcl/5.2.2.RELEASE/spring-jcl-5.2.2.RELEASE.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-jcl/5.2.4.RELEASE/spring-jcl-5.2.4.RELEASE.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-jcl/5.2.2.RELEASE/spring-jcl-5.2.2.RELEASE-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-jcl/5.2.4.RELEASE/spring-jcl-5.2.4.RELEASE-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-jcl/5.2.2.RELEASE/spring-jcl-5.2.2.RELEASE-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-jcl/5.2.4.RELEASE/spring-jcl-5.2.4.RELEASE-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.springframework:spring-web:5.2.2.RELEASE"> <library name="Maven: org.springframework:spring-web:5.2.4.RELEASE">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-web/5.2.2.RELEASE/spring-web-5.2.2.RELEASE.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-web/5.2.4.RELEASE/spring-web-5.2.4.RELEASE.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-web/5.2.2.RELEASE/spring-web-5.2.2.RELEASE-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-web/5.2.4.RELEASE/spring-web-5.2.4.RELEASE-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-web/5.2.2.RELEASE/spring-web-5.2.2.RELEASE-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-web/5.2.4.RELEASE/spring-web-5.2.4.RELEASE-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.springframework:spring-webmvc:5.2.2.RELEASE"> <library name="Maven: org.springframework:spring-webmvc:5.2.4.RELEASE">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-webmvc/5.2.2.RELEASE/spring-webmvc-5.2.2.RELEASE.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-webmvc/5.2.4.RELEASE/spring-webmvc-5.2.4.RELEASE.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-webmvc/5.2.2.RELEASE/spring-webmvc-5.2.2.RELEASE-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-webmvc/5.2.4.RELEASE/spring-webmvc-5.2.4.RELEASE-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-webmvc/5.2.2.RELEASE/spring-webmvc-5.2.2.RELEASE-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-webmvc/5.2.4.RELEASE/spring-webmvc-5.2.4.RELEASE-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Maven: org.yaml:snakeyaml:1.25"> <library name="Maven: org.yaml:snakeyaml:1.25">

View File

@ -23,7 +23,6 @@
<component name="ProjectId" id="1PxWQToGq56jcmMz176UjXWLSaU" /> <component name="ProjectId" id="1PxWQToGq56jcmMz176UjXWLSaU" />
<component name="ProjectViewState"> <component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" /> <option name="hideEmptyMiddlePackages" value="true" />
<option name="showExcludedFiles" value="true" />
<option name="showLibraryContents" value="true" /> <option name="showLibraryContents" value="true" />
</component> </component>
<component name="PropertiesComponent"> <component name="PropertiesComponent">
@ -34,7 +33,7 @@
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" /> <property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
<property name="WebServerToolWindowFactoryState" value="false" /> <property name="WebServerToolWindowFactoryState" value="false" />
<property name="aspect.path.notification.shown" value="true" /> <property name="aspect.path.notification.shown" value="true" />
<property name="last_opened_file_path" value="$PROJECT_DIR$/lib" /> <property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="node.js.detected.package.eslint" value="true" /> <property name="node.js.detected.package.eslint" value="true" />
<property name="node.js.detected.package.tslint" value="true" /> <property name="node.js.detected.package.tslint" value="true" />
<property name="node.js.path.for.package.eslint" value="project" /> <property name="node.js.path.for.package.eslint" value="project" />
@ -74,18 +73,6 @@
</list> </list>
</recent_temporary> </recent_temporary>
</component> </component>
<component name="ServiceViewManager">
<option name="viewStates">
<list>
<serviceView>
<treeState>
<expand />
<select />
</treeState>
</serviceView>
</list>
</option>
</component>
<component name="SvnConfiguration"> <component name="SvnConfiguration">
<configuration /> <configuration />
</component> </component>
@ -115,13 +102,23 @@
<workItem from="1568597528002" duration="523000" /> <workItem from="1568597528002" duration="523000" />
<workItem from="1579076046499" duration="14355000" /> <workItem from="1579076046499" duration="14355000" />
<workItem from="1579226829168" duration="8852000" /> <workItem from="1579226829168" duration="8852000" />
<workItem from="1583472574255" duration="526000" />
<workItem from="1583473153031" duration="14749000" />
</task> </task>
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
<option name="version" value="1" /> <option name="version" value="2" />
</component> </component>
<component name="WindowStateProjectService"> <component name="WindowStateProjectService">
<state x="609" y="345" key="#com.intellij.fileTypes.FileTypeChooser" timestamp="1583567210548">
<screen x="0" y="23" width="1920" height="1057" />
</state>
<state x="609" y="345" key="#com.intellij.fileTypes.FileTypeChooser/0.23.1920.1057@0.23.1920.1057" timestamp="1583567210548" />
<state x="645" y="287" key="#com.intellij.openapi.updateSettings.impl.PluginUpdateInfoDialog" timestamp="1583472646530">
<screen x="0" y="23" width="1920" height="1057" />
</state>
<state x="645" y="287" key="#com.intellij.openapi.updateSettings.impl.PluginUpdateInfoDialog/0.23.1920.1057@0.23.1920.1057" timestamp="1583472646530" />
<state x="627" y="209" key="#com.intellij.refactoring.safeDelete.UnsafeUsagesDialog" timestamp="1579149988674"> <state x="627" y="209" key="#com.intellij.refactoring.safeDelete.UnsafeUsagesDialog" timestamp="1579149988674">
<screen x="0" y="0" width="1920" height="1040" /> <screen x="0" y="0" width="1920" height="1040" />
</state> </state>
@ -130,38 +127,46 @@
<screen x="0" y="0" width="1920" height="1040" /> <screen x="0" y="0" width="1920" height="1040" />
</state> </state>
<state x="690" y="218" key="FileChooserDialogImpl/0.0.1920.1040@0.0.1920.1040" timestamp="1579230010253" /> <state x="690" y="218" key="FileChooserDialogImpl/0.0.1920.1040@0.0.1920.1040" timestamp="1579230010253" />
<state width="1867" height="615" key="GridCell.Tab.0.bottom" timestamp="1579243696749"> <state width="1870" height="565" key="GridCell.Tab.0.bottom" timestamp="1583566851136">
<screen x="0" y="0" width="1920" height="1040" /> <screen x="0" y="23" width="1920" height="1057" />
</state> </state>
<state width="1867" height="615" key="GridCell.Tab.0.bottom/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696749" /> <state width="1867" height="615" key="GridCell.Tab.0.bottom/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696749" />
<state width="1867" height="615" key="GridCell.Tab.0.center" timestamp="1579243696749"> <state width="1870" height="565" key="GridCell.Tab.0.bottom/0.23.1920.1057@0.23.1920.1057" timestamp="1583566851136" />
<screen x="0" y="0" width="1920" height="1040" /> <state width="1870" height="565" key="GridCell.Tab.0.center" timestamp="1583566851135">
<screen x="0" y="23" width="1920" height="1057" />
</state> </state>
<state width="1867" height="615" key="GridCell.Tab.0.center/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696749" /> <state width="1867" height="615" key="GridCell.Tab.0.center/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696749" />
<state width="1867" height="615" key="GridCell.Tab.0.left" timestamp="1579243696749"> <state width="1870" height="565" key="GridCell.Tab.0.center/0.23.1920.1057@0.23.1920.1057" timestamp="1583566851135" />
<screen x="0" y="0" width="1920" height="1040" /> <state width="1870" height="565" key="GridCell.Tab.0.left" timestamp="1583566851135">
<screen x="0" y="23" width="1920" height="1057" />
</state> </state>
<state width="1867" height="615" key="GridCell.Tab.0.left/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696749" /> <state width="1867" height="615" key="GridCell.Tab.0.left/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696749" />
<state width="1867" height="615" key="GridCell.Tab.0.right" timestamp="1579243696749"> <state width="1870" height="565" key="GridCell.Tab.0.left/0.23.1920.1057@0.23.1920.1057" timestamp="1583566851135" />
<screen x="0" y="0" width="1920" height="1040" /> <state width="1870" height="565" key="GridCell.Tab.0.right" timestamp="1583566851136">
<screen x="0" y="23" width="1920" height="1057" />
</state> </state>
<state width="1867" height="615" key="GridCell.Tab.0.right/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696749" /> <state width="1867" height="615" key="GridCell.Tab.0.right/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696749" />
<state width="1867" height="615" key="GridCell.Tab.1.bottom" timestamp="1579243696751"> <state width="1870" height="565" key="GridCell.Tab.0.right/0.23.1920.1057@0.23.1920.1057" timestamp="1583566851136" />
<screen x="0" y="0" width="1920" height="1040" /> <state width="1870" height="565" key="GridCell.Tab.1.bottom" timestamp="1583566851137">
<screen x="0" y="23" width="1920" height="1057" />
</state> </state>
<state width="1867" height="615" key="GridCell.Tab.1.bottom/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696751" /> <state width="1867" height="615" key="GridCell.Tab.1.bottom/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696751" />
<state width="1867" height="615" key="GridCell.Tab.1.center" timestamp="1579243696750"> <state width="1870" height="565" key="GridCell.Tab.1.bottom/0.23.1920.1057@0.23.1920.1057" timestamp="1583566851137" />
<screen x="0" y="0" width="1920" height="1040" /> <state width="1870" height="565" key="GridCell.Tab.1.center" timestamp="1583566851136">
<screen x="0" y="23" width="1920" height="1057" />
</state> </state>
<state width="1867" height="615" key="GridCell.Tab.1.center/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696750" /> <state width="1867" height="615" key="GridCell.Tab.1.center/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696750" />
<state width="1867" height="615" key="GridCell.Tab.1.left" timestamp="1579243696749"> <state width="1870" height="565" key="GridCell.Tab.1.center/0.23.1920.1057@0.23.1920.1057" timestamp="1583566851136" />
<screen x="0" y="0" width="1920" height="1040" /> <state width="1870" height="565" key="GridCell.Tab.1.left" timestamp="1583566851136">
<screen x="0" y="23" width="1920" height="1057" />
</state> </state>
<state width="1867" height="615" key="GridCell.Tab.1.left/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696749" /> <state width="1867" height="615" key="GridCell.Tab.1.left/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696749" />
<state width="1867" height="615" key="GridCell.Tab.1.right" timestamp="1579243696751"> <state width="1870" height="565" key="GridCell.Tab.1.left/0.23.1920.1057@0.23.1920.1057" timestamp="1583566851136" />
<screen x="0" y="0" width="1920" height="1040" /> <state width="1870" height="565" key="GridCell.Tab.1.right" timestamp="1583566851137">
<screen x="0" y="23" width="1920" height="1057" />
</state> </state>
<state width="1867" height="615" key="GridCell.Tab.1.right/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696751" /> <state width="1867" height="615" key="GridCell.Tab.1.right/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696751" />
<state width="1870" height="565" key="GridCell.Tab.1.right/0.23.1920.1057@0.23.1920.1057" timestamp="1583566851137" />
<state width="1867" height="386" key="GridCell.Tab.2.bottom" timestamp="1579156159339"> <state width="1867" height="386" key="GridCell.Tab.2.bottom" timestamp="1579156159339">
<screen x="0" y="0" width="1920" height="1040" /> <screen x="0" y="0" width="1920" height="1040" />
</state> </state>
@ -178,18 +183,20 @@
<screen x="0" y="0" width="1920" height="1040" /> <screen x="0" y="0" width="1920" height="1040" />
</state> </state>
<state width="1867" height="386" key="GridCell.Tab.2.right/0.0.1920.1040@0.0.1920.1040" timestamp="1579156159339" /> <state width="1867" height="386" key="GridCell.Tab.2.right/0.0.1920.1040@0.0.1920.1040" timestamp="1579156159339" />
<state x="574" y="193" width="771" height="654" key="find.popup" timestamp="1579242964438"> <state x="574" y="219" width="771" height="670" key="find.popup" timestamp="1583473246286">
<screen x="0" y="0" width="1920" height="1040" /> <screen x="0" y="23" width="1920" height="1057" />
</state> </state>
<state x="574" y="193" width="771" height="654" key="find.popup/0.0.1920.1040@0.0.1920.1040" timestamp="1579242964438" /> <state x="574" y="193" width="771" height="654" key="find.popup/0.0.1920.1040@0.0.1920.1040" timestamp="1579242964438" />
<state x="574" y="219" width="771" height="670" key="find.popup/0.23.1920.1057@0.23.1920.1057" timestamp="1583473246286" />
<state x="161" y="163" key="new project wizard" timestamp="1579242569030"> <state x="161" y="163" key="new project wizard" timestamp="1579242569030">
<screen x="0" y="0" width="1920" height="1040" /> <screen x="0" y="0" width="1920" height="1040" />
</state> </state>
<state x="161" y="163" key="new project wizard/0.0.1920.1040@0.0.1920.1040" timestamp="1579242569030" /> <state x="161" y="163" key="new project wizard/0.0.1920.1040@0.0.1920.1040" timestamp="1579242569030" />
<state x="539" y="5" width="840" height="1034" key="search.everywhere.popup" timestamp="1579235575382"> <state x="539" y="28" width="840" height="1051" key="search.everywhere.popup" timestamp="1583567214095">
<screen x="0" y="0" width="1920" height="1040" /> <screen x="0" y="23" width="1920" height="1057" />
</state> </state>
<state x="539" y="5" width="840" height="1034" key="search.everywhere.popup/0.0.1920.1040@0.0.1920.1040" timestamp="1579235575382" /> <state x="539" y="5" width="840" height="1034" key="search.everywhere.popup/0.0.1920.1040@0.0.1920.1040" timestamp="1579235575382" />
<state x="539" y="28" width="840" height="1051" key="search.everywhere.popup/0.23.1920.1057@0.23.1920.1057" timestamp="1583567214095" />
</component> </component>
<component name="XDebuggerManager"> <component name="XDebuggerManager">
<breakpoint-manager> <breakpoint-manager>
@ -204,6 +211,11 @@
<line>22</line> <line>22</line>
<option name="timeStamp" value="10" /> <option name="timeStamp" value="10" />
</line-breakpoint> </line-breakpoint>
<line-breakpoint enabled="true" type="java-line">
<url>file://$PROJECT_DIR$/src/main/java/com/farsunset/cim/handler/SessionClosedHandler.java</url>
<line>48</line>
<option name="timeStamp" value="12" />
</line-breakpoint>
</breakpoints> </breakpoints>
</breakpoint-manager> </breakpoint-manager>
</component> </component>

View File

@ -11,7 +11,7 @@
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version> <version>2.2.5.RELEASE</version>
<relativePath/> <relativePath/>
</parent> </parent>

View File

@ -1,4 +1,4 @@
/** /*
* Copyright 2013-2019 Xia Jun(3979434@qq.com). * Copyright 2013-2019 Xia Jun(3979434@qq.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -1,4 +1,4 @@
/** /*
* Copyright 2013-2019 Xia Jun(3979434@qq.com). * Copyright 2013-2019 Xia Jun(3979434@qq.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -1,4 +1,4 @@
/** /*
* Copyright 2013-2019 Xia Jun(3979434@qq.com). * Copyright 2013-2019 Xia Jun(3979434@qq.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -1,4 +1,4 @@
/** /*
* Copyright 2013-2019 Xia Jun(3979434@qq.com). * Copyright 2013-2019 Xia Jun(3979434@qq.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -21,12 +21,11 @@
*/ */
package com.farsunset.cim.api.controller; package com.farsunset.cim.api.controller;
import com.farsunset.cim.api.controller.dto.MessageResult;
import com.farsunset.cim.push.DefaultMessagePusher; import com.farsunset.cim.push.DefaultMessagePusher;
import com.farsunset.cim.sdk.server.model.Message; import com.farsunset.cim.sdk.server.model.Message;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource; import javax.annotation.Resource;
@ -39,30 +38,15 @@ public class MessageController {
@Resource @Resource
private DefaultMessagePusher defaultMessagePusher; private DefaultMessagePusher defaultMessagePusher;
/**
* 此方法仅仅在集群时通过服务器调用
*
* @param message
* @return
*/
@PostMapping(value = "/dispatch")
public MessageResult dispatchSend(Message message) {
return send(message);
}
@PostMapping(value = "/send")
public MessageResult send(Message message) {
MessageResult result = new MessageResult(); @PostMapping(value = "/send")
public ResponseEntity<Long> send(Message message) {
message.setId(System.currentTimeMillis()); message.setId(System.currentTimeMillis());
defaultMessagePusher.push(message); defaultMessagePusher.push(message);
result.id = message.getId(); return ResponseEntity.ok(message.getId());
result.timestamp = message.getTimestamp();
return result;
} }
} }

View File

@ -1,34 +0,0 @@
/**
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
***************************************************************************************
* *
* Website : http://www.farsunset.com *
* *
***************************************************************************************
*/
package com.farsunset.cim.api.controller.dto;
import org.springframework.http.HttpStatus;
import java.util.List;
public class BaseResult {
public int code = HttpStatus.OK.value();
public String message;
public Object data;
public List<?> dataList;
}

View File

@ -1,27 +0,0 @@
/**
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
***************************************************************************************
* *
* Website : http://www.farsunset.com *
* *
***************************************************************************************
*/
package com.farsunset.cim.api.controller.dto;
public class MessageResult extends BaseResult {
public long timestamp;
public long id;
}

View File

@ -13,7 +13,6 @@ import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.HashMap; import java.util.HashMap;
@ -23,19 +22,8 @@ public class CIMConfig implements CIMRequestHandler, ApplicationListener<Applica
@Resource @Resource
private ApplicationContext applicationContext; private ApplicationContext applicationContext;
private HashMap<String,Class<? extends CIMRequestHandler>> appHandlerMap = new HashMap<>(); private final HashMap<String,CIMRequestHandler> appHandlerMap = new HashMap<>();
@PostConstruct
private void initHandler() {
/*
* 账号绑定handler
*/
appHandlerMap.put("client_bind", BindHandler.class);
/*
* 连接关闭handler
*/
appHandlerMap.put("client_closed", SessionClosedHandler.class);
}
@Bean(destroyMethod = "destroy") @Bean(destroyMethod = "destroy")
public CIMNioSocketAcceptor getNioSocketAcceptor(@Value("${cim.app.port}") int port, public CIMNioSocketAcceptor getNioSocketAcceptor(@Value("${cim.app.port}") int port,
@ -52,28 +40,22 @@ public class CIMConfig implements CIMRequestHandler, ApplicationListener<Applica
@Override @Override
public void process(CIMSession session, SentBody body) { public void process(CIMSession session, SentBody body) {
CIMRequestHandler handler = findHandlerByKey(body.getKey()); CIMRequestHandler handler = appHandlerMap.get(body.getKey());
if(handler == null) {return ;} if(handler == null) {return ;}
handler.process(session, body); handler.process(session, body);
} }
/*
private CIMRequestHandler findHandlerByKey(String key){
Class<? extends CIMRequestHandler> handlerClass = appHandlerMap.get(key);
if (handlerClass==null){
return null;
}
return applicationContext.getBean(handlerClass);
}
/**
* springboot启动完成之后再启动cim服务的避免服务正在重启时客户端会立即开始连接导致意外异常发生. * springboot启动完成之后再启动cim服务的避免服务正在重启时客户端会立即开始连接导致意外异常发生.
*/ */
@Override @Override
public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) { public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {
appHandlerMap.put("client_bind",applicationContext.getBean(BindHandler.class));
appHandlerMap.put("client_closed",applicationContext.getBean(SessionClosedHandler.class));
applicationContext.getBean(CIMNioSocketAcceptor.class).bind(); applicationContext.getBean(CIMNioSocketAcceptor.class).bind();
} }
} }

View File

@ -1,4 +1,4 @@
/** /*
* Copyright 2013-2019 Xia Jun(3979434@qq.com). * Copyright 2013-2019 Xia Jun(3979434@qq.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -39,7 +39,7 @@ import javax.annotation.Resource;
import java.util.Objects; import java.util.Objects;
/** /*
* 账号绑定实现 * 账号绑定实现
* *
*/ */

View File

@ -1,4 +1,4 @@
/** /*
* Copyright 2013-2019 Xia Jun(3979434@qq.com). * Copyright 2013-2019 Xia Jun(3979434@qq.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -33,7 +33,7 @@ import javax.annotation.Resource;
import java.util.Objects; import java.util.Objects;
/** /*
* 断开连接清除session * 断开连接清除session
* *
*/ */
@ -56,15 +56,7 @@ public class SessionClosedHandler implements CIMRequestHandler {
return; return;
} }
CIMSession oldSession = cimSessionService.get(account.toString()); cimSessionService.remove(account.toString());
if (oldSession == null || oldSession.isApnsEnable()) {
return;
}
oldSession.setState(CIMSession.STATE_DISABLED);
oldSession.setNid(null);
cimSessionService.save(oldSession);
} }
} }

View File

@ -1,4 +1,4 @@
/** /*
* Copyright 2013-2019 Xia Jun(3979434@qq.com). * Copyright 2013-2019 Xia Jun(3979434@qq.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -23,13 +23,13 @@ package com.farsunset.cim.push;
import com.farsunset.cim.sdk.server.model.Message; import com.farsunset.cim.sdk.server.model.Message;
/** /*
* 消息发送实接口 * 消息发送实接口
* *
*/ */
public interface CIMMessagePusher { public interface CIMMessagePusher {
/** /*
* 向用户发送消息 * 向用户发送消息
* *
* @param msg * @param msg

View File

@ -1,4 +1,4 @@
/** /*
* Copyright 2013-2019 Xia Jun(3979434@qq.com). * Copyright 2013-2019 Xia Jun(3979434@qq.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -31,7 +31,7 @@ import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Objects; import java.util.Objects;
/** /*
* 消息发送实现类 * 消息发送实现类
* *
*/ */
@ -50,7 +50,7 @@ public class DefaultMessagePusher implements CIMMessagePusher {
private ApnsService apnsService; private ApnsService apnsService;
/** /*
* 向用户发送消息 * 向用户发送消息
* *
* @param message * @param message
@ -76,9 +76,10 @@ public class DefaultMessagePusher implements CIMMessagePusher {
* 如果连接到了其他服务器则转发请求到目标服务器 * 如果连接到了其他服务器则转发请求到目标服务器
*/ */
if (session.isConnected() && !Objects.equals(host, session.getHost())) { if (session.isConnected() && !Objects.equals(host, session.getHost())) {
/** /*
* @TODO * @TODO
* 在此调用目标服务器接口来发送 * 在此调用目标服务器接口来发送如session.host = 123.123.123.123
* 调用目标服务器的消息发送接口http://123.123.123.123:8080/message/send
*/ */
return; return;
} }

View File

@ -7,7 +7,7 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
/** /*
* 正式场景下使用redis或者数据库来存储session信息 * 正式场景下使用redis或者数据库来存储session信息
*/ */
@Repository @Repository

View File

@ -1,4 +1,4 @@
/** /*
* Copyright 2013-2019 Xia Jun(3979434@qq.com). * Copyright 2013-2019 Xia Jun(3979434@qq.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -26,7 +26,7 @@ import com.farsunset.cim.sdk.server.model.CIMSession;
import java.util.List; import java.util.List;
/** /*
* 集群 session管理实现示例 各位可以自行实现 AbstractSessionManager接口来实现自己的 session管理 服务器集群时 * 集群 session管理实现示例 各位可以自行实现 AbstractSessionManager接口来实现自己的 session管理 服务器集群时
* 须要将CIMSession 信息存入数据库或者redis中 第三方存储空间中便于所有服务器都可以访问 * 须要将CIMSession 信息存入数据库或者redis中 第三方存储空间中便于所有服务器都可以访问
*/ */

View File

@ -1,4 +1,4 @@
/** /*
* Copyright 2013-2019 Xia Jun(3979434@qq.com). * Copyright 2013-2019 Xia Jun(3979434@qq.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -46,7 +46,7 @@ public class CIMSessionServiceImpl implements CIMSessionService {
sessionRepository.save(session); sessionRepository.save(session);
} }
/** /*
* *
* @param account 用户id * @param account 用户id
* @return * @return

View File

@ -1,4 +1,4 @@
/** /*
* Copyright 2013-2019 Xia Jun(3979434@qq.com). * Copyright 2013-2019 Xia Jun(3979434@qq.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -1,4 +1,4 @@
/** /*
* Copyright 2013-2019 Xia Jun(3979434@qq.com). * Copyright 2013-2019 Xia Jun(3979434@qq.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -23,10 +23,4 @@ cim.websocket.port=34567
apple.apns.debug=false apple.apns.debug=false
apple.apns.p12.password= your p12 password apple.apns.p12.password= your p12 password
apple.apns.p12.file= /apns/lvxin.p12 apple.apns.p12.file= /apns/lvxin.p12
##################################################################
# System Config #
##################################################################
sys.message.dispatch.url = http://%1$s:${server.port}/api/message/dispatch

View File

@ -1,67 +1,67 @@
module.common.html.title = CIM\u7BA1\u7406\u7CFB\u7EDF module.common.html.title = CIM管理系统
module.common.account = \u5e10\u53f7 module.common.account = 帐号
module.common.password = \u5bc6\u7801 module.common.password = 密码
module.common.save = \u4fdd\u5b58 module.common.save = 保存
module.common.id = ID module.common.id = ID
module.common.type =\u7c7b\u578b module.common.type =类型
module.common.name =\u540d\u79f0 module.common.name =名称
module.common.time =\u65f6\u95f4 module.common.time =时间
module.common.operation =\u64cd\u4f5c module.common.operation =操作
module.common.query =\u67e5\u8be2 module.common.query =查询
module.common.look =\u67e5\u770b module.common.look =查看
module.common.preview =\u9884\u89c8 module.common.preview =预览
module.common.send =\u53d1\u9001 module.common.send =发送
module.common.saveing = \u6b63\u5728\u4fdd\u5b58,\u8bf7\u7a0d\u5019...... module.common.saveing = 正在保存,请稍候......
module.common.add = \u6dfb\u52a0 module.common.add = 添加
module.common.update = \u4fee\u6539 module.common.update = 修改
module.common.delete = \u5220\u9664 module.common.delete = 删除
module.common.save.success = \u4fdd\u5b58\u6210\u529f module.common.save.success = 保存成功
module.common.delete.success = \u5220\u9664\u6210\u529f module.common.delete.success = 删除成功
module.common.delete.loading =\u6b63\u5728\u5220\u9664,\u8bf7\u7a0d\u5019...... module.common.delete.loading =正在删除,请稍候......
module.common.loading = \u52a0\u8f7d\u4e2d,\u8bf7\u7a0d\u5019...... module.common.loading = 加载中,请稍候......
module.common.description = \u8bf4\u660e module.common.description = 说明
module.common.state = \u72b6\u6001 module.common.state = 状态
module.common.content = \u5185\u5bb9 module.common.content = 内容
module.common.location = \u4f4d\u7f6e module.common.location = 位置
module.common.longitude = \u7ecf\u5ea6 module.common.longitude = 经度
module.common.latitude = \u7eac\u5ea6 module.common.latitude = 纬度
module.common.sort = \u6392\u5e8f module.common.sort = 排序
module.common.code = \u7f16\u53f7 module.common.code = 编号
module.common.import = \u5bfc\u5165 module.common.import = 导入
module.common.headlogo = \u5934\u50cf module.common.headlogo = 头像
module.common.homepage = \u4e3b\u9875 module.common.homepage = 主页
module.common.website = \u7f51\u5740 module.common.website = 网址
module.common.text = \u6587\u5b57 module.common.text = 文字
module.global.error.500.hint = \u670d\u52a1\u7a0b\u5e8f\u53d1\u751f\u5185\u90e8\u9519\u8bef module.global.error.500.hint = 服务程序发生内部错误
module.global.error.400.hint = \u8bf7\u6c42\u53c2\u6570\u7c7b\u578b\u4e0d\u6b63\u786e module.global.error.400.hint = 请求参数类型不正确
module.global.error.404.hint = \u8bbf\u95ee\u7684\u8d44\u6e90\u4e0d\u5b58\u5728 module.global.error.404.hint = 访问的资源不存在
module.global.pager.next = \u4e0b\u4e00\u9875 module.global.pager.next = 下一页
module.global.pager.previou = \u4e0a\u4e00\u9875 module.global.pager.previou = 上一页
module.global.pager.description = \u5171{0}\u6761\u8bb0\u5f55,{1}\u9875 module.global.pager.description = 共{0}条记录,{1}页
module.console.about = \u5173\u4e8e module.console.about = 关于
module.console.about.version = v3.5.0 module.console.about.version = 1.0.0
module.console.about.author = \u4f5c\u8005: \u8fdc\u65b9\u5915\u9633 module.console.about.author = 作者: 远方夕阳
module.console.about.author.wechat = \u5fae\u4fe1: 3979434 module.console.about.author.wechat = 微信: 3979434
module.console.about.author.qq = Q Q: 3979434 module.console.about.author.qq = Q Q: 3979434
module.console.about.copyright = \u57FA\u4E8EJava\u670D\u52A1\u7AEF\u7684\u5373\u65F6\u901A\u4FE1\u89E3\u51B3\u65B9\u6848,\u4E0Eandroid \u5BA2\u6237\u7AEF\u5B8C\u7F8E\u7ED3\u5408\uFF0C\u540C\u65F6\u652F\u6301\u5176\u4ED6\u8BED\u8A00\u7684\u79FB\u52A8\u5E94\u7528\uFF0C\u7269\u8054\u7F51\uFF0C\u667A\u80FD\u5BB6\u5C45\uFF0C\u684C\u9762\u5E94\u7528\uFF0CWEB\u5E94\u7528\u4EE5\u53CA\u540E\u53F0\u7CFB\u7EDF\u4E4B\u95F4\u7684\u5373\u65F6\u6D88\u4EA4\u4E92\uFF0C\u4E3A\u4F60\u89E3\u51B3\u4E86\u957F\u8FDE\u63A5\u5404\u79CD\u6D88\u606F\u4E8B\u4EF6\uFF0C\u65AD\u7EBF\u91CD\u8FDE\u7B49\u7E41\u7410\u7684\u5904\u7406\uFF0C\u4F7F\u7528\u65B9\u4FBF\u6613\u4E8E\u96C6\u6210\u3002<br/><a target="_blank" href="https://gitee.com/farsunset/cim">https://gitee.com/farsunset/cim</a> module.console.about.copyright = 基于Java服务端的即时通信解决方案,与android 客户端完美结合同时支持其他语言的移动应用物联网智能家居桌面应用WEB应用以及后台系统之间的即时消交互为你解决了长连接各种消息事件断线重连等繁琐的处理使用方便易于集成。<br/><a target="_blank" href="https://gitee.com/farsunset/cim">https://gitee.com/farsunset/cim</a>
module.console.menu.onlineuser = \u5728\u7ebf\u7528\u6237 module.console.menu.onlineuser = 在线用户
module.console.cimsession.sending = \u6b63\u5728\u53d1\u9001\uff0c\u8bf7\u7a0d\u5019...... module.console.cimsession.sending = 正在发送,请稍候......
module.console.cimsession.send.success = \u53d1\u9001\u6210\u529f module.console.cimsession.send.success = 发送成功
module.console.cimsession.logo =\u5934\u50cf module.console.cimsession.logo =头像
module.console.cimsession.account =\u5e10\u53f7 module.console.cimsession.account =帐号
module.console.cimsession.nid = \u8fde\u63a5ID module.console.cimsession.nid = 连接ID
module.console.cimsession.channel = \u7ec8\u7aef module.console.cimsession.channel = 终端
module.console.cimsession.app.version =\u5e94\u7528\u7248\u672c module.console.cimsession.app.version =应用版本
module.console.cimsession.os.version =\u7cfb\u7edf\u7248\u672c module.console.cimsession.os.version =系统版本
module.console.cimsession.deviceid =\u8bbe\u5907\u7f16\u53f7 module.console.cimsession.deviceid =设备编号
module.console.cimsession.device.model =\u7ec8\u7aef\u578b\u53f7 module.console.cimsession.device.model =终端型号
module.console.cimsession.online.time =\u5728\u7ebf\u65f6\u957f(\u79d2) module.console.cimsession.online.time =在线时长(秒)
module.console.cimsession.time.format ={0}\u79d2 module.console.cimsession.time.format ={0}秒
module.console.cimsession.send.message =发送消息
module.console.cimsession.receiver = 接收帐号
module.console.cimsession.message = 消息内容
module.console.cimsession.send.message =\u53d1\u9001\u6d88\u606f
module.console.cimsession.receiver = \u63a5\u6536\u5e10\u53f7
module.console.cimsession.message = \u6d88\u606f\u5185\u5bb9

24
cim-client-sdk/cim-android-sdk/cim-android-sdk.iml Normal file → Executable file
View File

@ -1,2 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4" /> <module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: com.google.protobuf:protobuf-lite:3.0.1" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
<orderEntry type="module-library">
<library name="Maven: android:android:8.0.0">
<CLASSES>
<root url="jar://$MODULE_DIR$/libs/android.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
</component>
</module>

0
cim-client-sdk/cim-android-sdk/jar.bat Normal file → Executable file
View File

0
cim-client-sdk/cim-android-sdk/jar.sh Normal file → Executable file
View File

0
cim-client-sdk/cim-android-sdk/pom.xml Normal file → Executable file
View File

View File

@ -29,76 +29,74 @@ import android.net.Uri;
class CIMCacheManager { class CIMCacheManager {
public static final String CIM_CONFIG_INFO = "CIM_CONFIG_INFO"; public static final String KEY_ACCOUNT = "KEY_ACCOUNT";
public static final String KEY_ACCOUNT = "KEY_ACCOUNT"; public static final String KEY_DEVICE_ID = "KEY_DEVICE_ID";
public static final String KEY_DEVICE_ID = "KEY_DEVICE_ID"; public static final String KEY_MANUAL_STOP = "KEY_MANUAL_STOP";
public static final String KEY_MANUAL_STOP = "KEY_MANUAL_STOP"; public static final String KEY_CIM_DESTROYED = "KEY_CIM_DESTROYED";
public static final String KEY_CIM_DESTROYED = "KEY_CIM_DESTROYED"; public static final String KEY_CIM_SERVER_HOST = "KEY_CIM_SERVER_HOST";
public static final String KEY_CIM_SERVER_HOST = "KEY_CIM_SERVER_HOST"; public static final String KEY_CIM_SERVER_PORT = "KEY_CIM_SERVER_PORT";
public static final String KEY_CIM_SERVER_PORT = "KEY_CIM_SERVER_PORT"; public static final String KEY_CIM_CONNECTION_STATE = "KEY_CIM_CONNECTION_STATE";
public static final String KEY_CIM_CONNECTION_STATE = "KEY_CIM_CONNECTION_STATE"; public static final String CONTENT_URI = "content://%s.cim.provider";
public static final String CONTENT_URI = "content://%s.cim.provider";
public static void remove(Context context, String key) {
public static void remove(Context context, String key) { ContentResolver resolver = context.getContentResolver();
ContentResolver resolver = context.getContentResolver(); resolver.delete(Uri.parse(String.format(CONTENT_URI, context.getPackageName())), key, null);
resolver.delete(Uri.parse(String.format(CONTENT_URI,context.getPackageName())), key, null); }
}
public static void putString(Context context, String key, String value) { public static void putString(Context context, String key, String value) {
ContentResolver resolver = context.getContentResolver(); ContentResolver resolver = context.getContentResolver();
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put("value", value); values.put("value", value);
values.put("key", key); values.put("key", key);
resolver.insert(Uri.parse(String.format(CONTENT_URI,context.getPackageName())), values); resolver.insert(Uri.parse(String.format(CONTENT_URI, context.getPackageName())), values);
} }
public static String getString(Context context, String key) { public static String getString(Context context, String key) {
String value = null; String value = null;
ContentResolver resolver = context.getContentResolver(); ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(Uri.parse(String.format(CONTENT_URI,context.getPackageName())), new String[] { key }, null, null, null); Cursor cursor = resolver.query(Uri.parse(String.format(CONTENT_URI, context.getPackageName())), new String[]{key}, null, null, null);
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
value = cursor.getString(0); value = cursor.getString(0);
} }
closeQuietly(cursor); closeQuietly(cursor);
return value; return value;
} }
private static void closeQuietly(Cursor cursor) { private static void closeQuietly(Cursor cursor) {
try { try {
if (cursor != null) { if (cursor != null) {
cursor.close(); cursor.close();
} }
} catch (Exception ignore) { } catch (Exception ignore) {
} }
} }
public static void putBoolean(Context context, String key, boolean value) { public static void putBoolean(Context context, String key, boolean value) {
putString(context, key, Boolean.toString(value)); putString(context, key, Boolean.toString(value));
} }
public static boolean getBoolean(Context context, String key) { public static boolean getBoolean(Context context, String key) {
String value = getString(context, key); String value = getString(context, key);
return Boolean.parseBoolean(value); return Boolean.parseBoolean(value);
} }
public static void putInt(Context context, String key, int value) { public static void putInt(Context context, String key, int value) {
putString(context, key, String.valueOf(value)); putString(context, key, String.valueOf(value));
} }
public static int getInt(Context context, String key) { public static int getInt(Context context, String key) {
String value = getString(context, key); String value = getString(context, key);
return value == null ? 0 : Integer.parseInt(value); return value == null ? 0 : Integer.parseInt(value);
} }
} }

View File

@ -29,43 +29,43 @@ import android.database.MatrixCursor;
import android.net.Uri; import android.net.Uri;
public class CIMCacheProvider extends ContentProvider { public class CIMCacheProvider extends ContentProvider {
static final String MODEL_KEY = "PRIVATE_CIM_CONFIG"; static final String MODEL_KEY = "PRIVATE_CIM_CONFIG";
@Override @Override
public int delete(Uri arg0, String key, String[] arg2) { public int delete(Uri arg0, String key, String[] arg2) {
getContext().getSharedPreferences(MODEL_KEY, Context.MODE_PRIVATE).edit().remove(key).apply(); getContext().getSharedPreferences(MODEL_KEY, Context.MODE_PRIVATE).edit().remove(key).apply();
return 0; return 0;
} }
@Override @Override
public String getType(Uri arg0) { public String getType(Uri arg0) {
return null; return null;
} }
@Override @Override
public Uri insert(Uri arg0, ContentValues values) { public Uri insert(Uri arg0, ContentValues values) {
String key = values.getAsString("key"); String key = values.getAsString("key");
String value = values.getAsString("value"); String value = values.getAsString("value");
getContext().getSharedPreferences(MODEL_KEY, Context.MODE_PRIVATE).edit().putString(key, value).apply(); getContext().getSharedPreferences(MODEL_KEY, Context.MODE_PRIVATE).edit().putString(key, value).apply();
return null; return null;
} }
@Override @Override
public boolean onCreate() { public boolean onCreate() {
return true; return true;
} }
@Override @Override
public Cursor query(Uri arg0, String[] arg1, String key, String[] arg3, String arg4) { public Cursor query(Uri arg0, String[] arg1, String key, String[] arg3, String arg4) {
MatrixCursor cursor = new MatrixCursor(new String[] { "value" }); MatrixCursor cursor = new MatrixCursor(new String[]{"value"});
String value = getContext().getSharedPreferences(MODEL_KEY, Context.MODE_PRIVATE).getString(arg1[0], null); String value = getContext().getSharedPreferences(MODEL_KEY, Context.MODE_PRIVATE).getString(arg1[0], null);
cursor.addRow(new Object[] { value }); cursor.addRow(new Object[]{value});
return cursor; return cursor;
} }
@Override @Override
public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) { public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
return 0; return 0;
} }
} }

View File

@ -26,7 +26,7 @@ import android.content.Intent;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.Process; import android.os.Process;
import com.farsunset.cim.sdk.android.coder.CIMLogger; import com.farsunset.cim.sdk.android.logger.CIMLogger;
import com.farsunset.cim.sdk.android.coder.ClientMessageDecoder; import com.farsunset.cim.sdk.android.coder.ClientMessageDecoder;
import com.farsunset.cim.sdk.android.coder.ClientMessageEncoder; import com.farsunset.cim.sdk.android.coder.ClientMessageEncoder;
import com.farsunset.cim.sdk.android.constant.CIMConstant; import com.farsunset.cim.sdk.android.constant.CIMConstant;
@ -41,368 +41,312 @@ import java.nio.channels.SocketChannel;
import java.util.Random; import java.util.Random;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
/** /*
* 连接服务端管理cim核心处理类管理连接以及消息处理 * 连接服务端管理cim核心处理类管理连接以及消息处理
*
* @author 3979434@qq.com
*/ */
class CIMConnectorManager{ class CIMConnectorManager {
private static CIMConnectorManager manager; private static CIMConnectorManager manager;
private static final int READ_BUFFER_SIZE = 2048;
private static final int WRITE_BUFFER_SIZE = 1024;
private static final int READ_IDLE_TIME = 120 * 1000; private static final int READ_BUFFER_SIZE = 2048;
private static final int CONNECT_TIME_OUT = 10 * 1000; private static final int WRITE_BUFFER_SIZE = 1024;
private static final int READ_IDLE_TIME = 120 * 1000;
private static final int CONNECT_TIME_OUT = 10 * 1000;
private static final int CONNECT_ALIVE_TIME_OUT = 150 * 1000;
private static final int CONNECT_ALIVE_TIME_OUT = 150 * 1000;
private static final AtomicLong LAST_READ_TIME = new AtomicLong(0); private static final AtomicLong LAST_READ_TIME = new AtomicLong(0);
private static final CIMLogger LOGGER = CIMLogger.getLogger(); private static final CIMLogger LOGGER = CIMLogger.getLogger();
private static final HandlerThread IDLE_HANDLER_THREAD = new HandlerThread("READ-IDLE", Process.THREAD_PRIORITY_BACKGROUND); private static final HandlerThread IDLE_HANDLER_THREAD = new HandlerThread("READ-IDLE", Process.THREAD_PRIORITY_BACKGROUND);
private Semaphore semaphore = new Semaphore(1, true);
private volatile SocketChannel socketChannel ;
private Context context;
private ByteBuffer readBuffer = ByteBuffer.allocate(READ_BUFFER_SIZE);
private ExecutorService workerExecutor = Executors.newFixedThreadPool(1, r -> new Thread(r,"worker-"));
private ExecutorService bossExecutor = Executors.newFixedThreadPool(1, r -> new Thread(r,"boss-"));
private ClientMessageEncoder messageEncoder = new ClientMessageEncoder();
private ClientMessageDecoder messageDecoder = new ClientMessageDecoder();
static {
IDLE_HANDLER_THREAD.start();
}
private CIMConnectorManager(Context context) {
this.context = context;
}
public synchronized static CIMConnectorManager getManager(Context context) {
if (manager == null) {
manager = new CIMConnectorManager(context);
}
return manager;
} private volatile SocketChannel socketChannel;
public void connect(final String host, final int port) { private final Context context;
if (!CIMPushManager.isNetworkConnected(context)) { private final ByteBuffer headerBuffer = ByteBuffer.allocate(CIMConstant.DATA_HEADER_LENGTH);
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_CONNECT_FAILED);
context.sendBroadcast(intent);
return;
}
if (isConnected()) {
return;
}
bossExecutor.execute(() -> {
if (isConnected()) {
return;
}
LOGGER.startConnect(host, port);
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_CIM_CONNECTION_STATE, false);
try {
semaphore.acquire();
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(true);
socketChannel.socket().setTcpNoDelay(true);
socketChannel.socket().setKeepAlive(true);
socketChannel.socket().setReceiveBufferSize(READ_BUFFER_SIZE);
socketChannel.socket().setSendBufferSize(WRITE_BUFFER_SIZE);
socketChannel.socket().connect(new InetSocketAddress(host, port),CONNECT_TIME_OUT);
semaphore.release();
handelConnectedEvent();
int result = -1; private final ExecutorService workerExecutor = Executors.newFixedThreadPool(1, r -> new Thread(r, "worker-"));
while((result = socketChannel.read(readBuffer)) > 0) { private final ExecutorService bossExecutor = Executors.newFixedThreadPool(1, r -> new Thread(r, "boss-"));
if(readBuffer.position() == readBuffer.capacity()) { private final ClientMessageEncoder messageEncoder = new ClientMessageEncoder();
extendByteBuffer(); private final ClientMessageDecoder messageDecoder = new ClientMessageDecoder();
}
handelSocketReadEvent(result); static {
IDLE_HANDLER_THREAD.start();
}
} private CIMConnectorManager(Context context) {
this.context = context;
}
handelSocketReadEvent(result);
}catch(ConnectException | SocketTimeoutException ignore){ public synchronized static CIMConnectorManager getManager(Context context) {
semaphore.release();
handleConnectAbortedEvent(); if (manager == null) {
} catch(IOException ignore) { manager = new CIMConnectorManager(context);
semaphore.release(); }
handelDisconnectedEvent();
}catch(InterruptedException ignore) { return manager;
semaphore.release();
} }
});
} public void connect(final String host, final int port) {
if (!CIMPushManager.isNetworkConnected(context)) {
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_CONNECT_FAILED);
context.sendBroadcast(intent);
return;
}
if (isConnected()) {
return;
}
bossExecutor.execute(() -> {
if (isConnected()) {
return;
}
LOGGER.startConnect(host, port);
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_CIM_CONNECTION_STATE, false);
try {
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(true);
socketChannel.socket().setTcpNoDelay(true);
socketChannel.socket().setKeepAlive(true);
socketChannel.socket().setReceiveBufferSize(READ_BUFFER_SIZE);
socketChannel.socket().setSendBufferSize(WRITE_BUFFER_SIZE);
socketChannel.socket().connect(new InetSocketAddress(host, port), CONNECT_TIME_OUT);
handleConnectedEvent();
/*
*开始读取来自服务端的消息先读取3个字节的消息头
*/
while (socketChannel.read(headerBuffer) > 0) {
handleSocketReadEvent();
}
/*
*read 返回 <= 0的情况发生了意外需要断开重链
*/
closeSession();
} catch (ConnectException | SocketTimeoutException ignore) {
handleConnectAbortedEvent();
} catch (IOException ignore) {
handleDisconnectedEvent();
}
});
}
public void destroy() { public void destroy() {
closeSession();
closeSession(); }
} public void closeSession() {
public void closeSession() { if (!isConnected()) {
return;
if(!isConnected()) { }
return;
}
try {
socketChannel.close();
} catch (IOException ignore) {
}finally {
this.sessionClosed();
}
}
public boolean isConnected() { try {
return socketChannel != null && socketChannel.isConnected(); socketChannel.close();
} } catch (IOException ignore) {
} finally {
this.sessionClosed();
public void send(final Protobufable body) { }
}
if(!isConnected()) {
return;
}
workerExecutor.execute(() -> {
int result = 0;
try {
semaphore.acquire(); public boolean isConnected() {
return socketChannel != null && socketChannel.isConnected();
ByteBuffer buffer = messageEncoder.encode(body); }
while(buffer.hasRemaining()){
result += socketChannel.write(buffer);
}
} catch (Exception e) {
result = -1;
}finally {
semaphore.release();
if(result <= 0) {
closeSession();
}else {
messageSent(body);
}
}
});
}
private void sessionCreated() {
LOGGER.sessionCreated(socketChannel);
LAST_READ_TIME.set(System.currentTimeMillis());
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_CONNECT_FINISHED);
context.sendBroadcast(intent);
}
private void sessionClosed() {
idleHandler.removeMessages(0);
LAST_READ_TIME.set(0);
LOGGER.sessionClosed(socketChannel);
readBuffer.clear();
if(readBuffer.capacity() > READ_BUFFER_SIZE) {
readBuffer = ByteBuffer.allocate(READ_BUFFER_SIZE);
}
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_CONNECTION_CLOSED);
context.sendBroadcast(intent);
}
private void sessionIdle() {
LOGGER.sessionIdle(socketChannel);
/*
* 用于解决wifi情况下偶而路由器与服务器断开连接时客户端并没及时收到关闭事件 导致这样的情况下当前连接无效也不会重连的问题
*/
if (System.currentTimeMillis() - LAST_READ_TIME.get() >= CONNECT_ALIVE_TIME_OUT) {
closeSession();
}
}
private void messageReceived(Object obj) { public void send(final Protobufable body) {
if (obj instanceof Message) { if (!isConnected()) {
return;
}
Intent intent = new Intent(); workerExecutor.execute(() -> {
intent.setPackage(context.getPackageName()); int result = 0;
intent.setAction(CIMConstant.IntentAction.ACTION_MESSAGE_RECEIVED); try {
intent.putExtra(Message.class.getName(), (Message) obj);
context.sendBroadcast(intent);
} ByteBuffer buffer = messageEncoder.encode(body);
if (obj instanceof ReplyBody) { while (buffer.hasRemaining()) {
result += socketChannel.write(buffer);
}
Intent intent = new Intent(); } catch (Exception e) {
intent.setPackage(context.getPackageName()); result = -1;
intent.setAction(CIMConstant.IntentAction.ACTION_REPLY_RECEIVED); } finally {
intent.putExtra(ReplyBody.class.getName(), (ReplyBody) obj);
context.sendBroadcast(intent);
}
}
if (result <= 0) {
private void messageSent(Object message) { closeSession();
} else {
LOGGER.messageSent(socketChannel, message); messageSent(body);
}
if (message instanceof SentBody) { }
Intent intent = new Intent(); });
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_SEND_FINISHED); }
intent.putExtra(SentBody.class.getName(), (SentBody) message);
context.sendBroadcast(intent);
} private void sessionCreated() {
} LOGGER.sessionCreated(socketChannel);
LAST_READ_TIME.set(System.currentTimeMillis());
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_CONNECT_FINISHED);
context.sendBroadcast(intent);
}
private void sessionClosed() {
idleHandler.removeMessages(0);
LAST_READ_TIME.set(0);
LOGGER.sessionClosed(socketChannel);
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_CONNECTION_CLOSED);
context.sendBroadcast(intent);
}
private void sessionIdle() {
LOGGER.sessionIdle(socketChannel);
/*
* 用于解决wifi情况下偶而路由器与服务器断开连接时客户端并没及时收到关闭事件 导致这样的情况下当前连接无效也不会重连的问题
*/
if (System.currentTimeMillis() - LAST_READ_TIME.get() >= CONNECT_ALIVE_TIME_OUT) {
closeSession();
}
}
private void messageReceived(Object obj) {
if (obj instanceof Message) {
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_MESSAGE_RECEIVED);
intent.putExtra(Message.class.getName(), (Message) obj);
context.sendBroadcast(intent);
}
if (obj instanceof ReplyBody) {
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_REPLY_RECEIVED);
intent.putExtra(ReplyBody.class.getName(), (ReplyBody) obj);
context.sendBroadcast(intent);
}
}
private void messageSent(Object message) {
LOGGER.messageSent(socketChannel, message);
if (message instanceof SentBody) {
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_SEND_FINISHED);
intent.putExtra(SentBody.class.getName(), (SentBody) message);
context.sendBroadcast(intent);
}
}
private final Handler idleHandler = new Handler(IDLE_HANDLER_THREAD.getLooper()) {
@Override
public void handleMessage(android.os.Message m) {
sessionIdle();
}
};
private void handleDisconnectedEvent() {
closeSession();
}
private Handler idleHandler = new Handler(IDLE_HANDLER_THREAD.getLooper()) {
@Override
public void handleMessage(android.os.Message m) {
sessionIdle();
}
};
private void handelDisconnectedEvent() {
closeSession();
}
private void handleConnectAbortedEvent() { private void handleConnectAbortedEvent() {
long interval = CIMConstant.RECONNECT_INTERVAL_TIME - (5 * 1000 - new Random().nextInt(15 * 1000)); long interval = CIMConstant.RECONNECT_INTERVAL_TIME - (5 * 1000 - new Random().nextInt(15 * 1000));
LOGGER.connectFailure(interval);
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_CONNECT_FAILED);
intent.putExtra("interval", interval);
context.sendBroadcast(intent);
} LOGGER.connectFailure(interval);
private void handelConnectedEvent() { Intent intent = new Intent();
intent.setPackage(context.getPackageName());
sessionCreated(); intent.setAction(CIMConstant.IntentAction.ACTION_CONNECT_FAILED);
intent.putExtra("interval", interval);
idleHandler.sendEmptyMessageDelayed(0, READ_IDLE_TIME); context.sendBroadcast(intent);
}
}
private void handelSocketReadEvent(int result) {
private void handleConnectedEvent() {
if(result == -1) {
closeSession(); sessionCreated();
return;
} idleHandler.sendEmptyMessageDelayed(0, READ_IDLE_TIME);
}
markLastReadTime();
private void handleSocketReadEvent() throws IOException {
readBuffer.position(0);
onMessageDecodeFinished(messageDecoder.doDecode(headerBuffer,socketChannel));
Object message = messageDecoder.doDecode(readBuffer);
markLastReadTime();
if(message == null) {
return; }
}
private void onMessageDecodeFinished(Object message){
LOGGER.messageReceived(socketChannel,message);
LOGGER.messageReceived(socketChannel, message);
if (message instanceof HeartbeatRequest) {
send(HeartbeatResponse.getInstance());
return;
}
this.messageReceived(message);
}
if(isHeartbeatRequest(message)) {
send(HeartbeatResponse.getInstance());
return;
}
this.messageReceived(message);
}
private void extendByteBuffer() {
ByteBuffer newBuffer = ByteBuffer.allocate(readBuffer.capacity() + READ_BUFFER_SIZE / 2);
readBuffer.position(0);
newBuffer.put(readBuffer);
readBuffer.clear();
readBuffer = newBuffer;
}
private void markLastReadTime() { private void markLastReadTime() {
LAST_READ_TIME.set(System.currentTimeMillis()); LAST_READ_TIME.set(System.currentTimeMillis());
idleHandler.removeMessages(0);
idleHandler.sendEmptyMessageDelayed(0, READ_IDLE_TIME);
}
idleHandler.removeMessages(0);
private boolean isHeartbeatRequest(Object data) { idleHandler.sendEmptyMessageDelayed(0, READ_IDLE_TIME);
return data instanceof HeartbeatRequest;
} }
} }

View File

@ -31,189 +31,190 @@ import com.farsunset.cim.sdk.android.model.Message;
import com.farsunset.cim.sdk.android.model.ReplyBody; import com.farsunset.cim.sdk.android.model.ReplyBody;
import com.farsunset.cim.sdk.android.model.SentBody; import com.farsunset.cim.sdk.android.model.SentBody;
/** /*
* 消息入口所有消息都会经过这里 * 消息入口所有消息都会经过这里
*/ */
public abstract class CIMEventBroadcastReceiver extends BroadcastReceiver { public abstract class CIMEventBroadcastReceiver extends BroadcastReceiver {
protected Context context; protected Context context;
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
this.context = context; this.context = context;
String action = intent.getAction(); String action = intent.getAction();
/* /*
* 操作事件广播用于提高service存活率 * 操作事件广播用于提高service存活率
*/ */
if (Intent.ACTION_USER_PRESENT.equals(action) if (Intent.ACTION_USER_PRESENT.equals(action)
|| Intent.ACTION_POWER_CONNECTED.equals(action) || Intent.ACTION_POWER_CONNECTED.equals(action)
|| Intent.ACTION_POWER_DISCONNECTED.equals(action)) { || Intent.ACTION_POWER_DISCONNECTED.equals(action)) {
startPushService(); startPushService();
}
/*
* 设备网络状态变化事件
*/
if (CIMConstant.IntentAction.ACTION_NETWORK_CHANGED.equals(action)
||ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
onDevicesNetworkChanged();
}
/*
* cim断开服务器事件
*/
if (CIMConstant.IntentAction.ACTION_CONNECTION_CLOSED.equals(action)) {
onInnerConnectionClosed();
}
/*
* cim连接服务器失败事件
*/
if (CIMConstant.IntentAction.ACTION_CONNECT_FAILED.equals(action)) {
long interval = intent.getLongExtra("interval", CIMConstant.RECONNECT_INTERVAL_TIME);
onInnerConnectFailed(interval);
}
/*
* cim连接服务器成功事件
*/
if (CIMConstant.IntentAction.ACTION_CONNECT_FINISHED.equals(action)) {
onInnerConnectFinished();
}
/*
* 收到推送消息事件
*/
if (CIMConstant.IntentAction.ACTION_MESSAGE_RECEIVED.equals(action)) {
onInnerMessageReceived((Message) intent.getSerializableExtra(Message.class.getName()), intent);
}
/*
* 获取收到replyBody成功事件
*/
if (CIMConstant.IntentAction.ACTION_REPLY_RECEIVED.equals(action)) {
onReplyReceived((ReplyBody) intent.getSerializableExtra(ReplyBody.class.getName()));
}
/*
* 获取sendBody发送成功事件
*/
if (CIMConstant.IntentAction.ACTION_SEND_FINISHED.equals(action)) {
onSentSucceed((SentBody) intent.getSerializableExtra(SentBody.class.getName()));
}
/*
* 重新连接如果断开的话
*/
if (CIMConstant.IntentAction.ACTION_CONNECTION_RECOVERY.equals(action)) {
connect(0);
}
}
private void startPushService() {
Intent intent = new Intent(context, CIMPushService.class);
intent.setAction(CIMPushManager.ACTION_ACTIVATE_PUSH_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
} }
}
private void onInnerConnectionClosed() { /*
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_CIM_CONNECTION_STATE, false); * 设备网络状态变化事件
*/
if (CIMConstant.IntentAction.ACTION_NETWORK_CHANGED.equals(action)
|| ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
if (CIMPushManager.isNetworkConnected(context)) { onDevicesNetworkChanged();
connect(0); }
}
onConnectionClosed(); /*
} * cim断开服务器事件
*/
if (CIMConstant.IntentAction.ACTION_CONNECTION_CLOSED.equals(action)) {
onInnerConnectionClosed();
}
private void onInnerConnectFailed(long interval) { /*
* cim连接服务器失败事件
*/
if (CIMConstant.IntentAction.ACTION_CONNECT_FAILED.equals(action)) {
long interval = intent.getLongExtra("interval", CIMConstant.RECONNECT_INTERVAL_TIME);
onInnerConnectFailed(interval);
}
if (CIMPushManager.isNetworkConnected(context)) { /*
* cim连接服务器成功事件
onConnectFailed(); */
if (CIMConstant.IntentAction.ACTION_CONNECT_FINISHED.equals(action)) {
connect(interval); onInnerConnectFinished();
} }
}
private void onInnerConnectFinished() { /*
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_CIM_CONNECTION_STATE, true); * 收到推送消息事件
*/
if (CIMConstant.IntentAction.ACTION_MESSAGE_RECEIVED.equals(action)) {
onInnerMessageReceived((Message) intent.getSerializableExtra(Message.class.getName()), intent);
}
boolean autoBind = CIMPushManager.autoBindAccount(context); /*
onConnectFinished(autoBind); * 获取收到replyBody成功事件
} */
if (CIMConstant.IntentAction.ACTION_REPLY_RECEIVED.equals(action)) {
private void onDevicesNetworkChanged() { onReplyReceived((ReplyBody) intent.getSerializableExtra(ReplyBody.class.getName()));
}
if (CIMPushManager.isNetworkConnected(context)) {
connect(0);
}
onNetworkChanged();
}
private void connect(long delay) {
Intent serviceIntent = new Intent(context, CIMPushService.class);
serviceIntent.putExtra(CIMPushService.KEY_DELAYED_TIME, delay);
serviceIntent.setAction(CIMPushManager.ACTION_CREATE_CIM_CONNECTION);
CIMPushManager.startService(context,serviceIntent);
}
private void onInnerMessageReceived(Message message, Intent intent) {
if (isForceOfflineMessage(message.getAction())) {
CIMPushManager.stop(context);
}
onMessageReceived(message, intent);
}
private boolean isForceOfflineMessage(String action) {
return CIMConstant.MessageAction.ACTION_999.equals(action);
}
/** /*
* 接收消息实现方法 * 获取sendBody发送成功事件
* @param message */
* @param intent if (CIMConstant.IntentAction.ACTION_SEND_FINISHED.equals(action)) {
*/ onSentSucceed((SentBody) intent.getSerializableExtra(SentBody.class.getName()));
public abstract void onMessageReceived(Message message, Intent intent); }
public void onNetworkChanged() { /*
CIMListenerManager.notifyOnNetworkChanged(CIMPushManager.getNetworkInfo(context)); * 重新连接如果断开的话
} */
if (CIMConstant.IntentAction.ACTION_CONNECTION_RECOVERY.equals(action)) {
connect(0);
}
}
public void onConnectFinished(boolean hasAutoBind) { private void startPushService() {
CIMListenerManager.notifyOnConnectFinished(hasAutoBind);
}
public void onConnectFailed() { Intent intent = new Intent(context, CIMPushService.class);
CIMListenerManager.notifyOnConnectFailed(); intent.setAction(CIMPushManager.ACTION_ACTIVATE_PUSH_SERVICE);
}
public void onConnectionClosed() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CIMListenerManager.notifyOnConnectionClosed(); context.startForegroundService(intent);
} } else {
context.startService(intent);
}
}
private void onInnerConnectionClosed() {
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_CIM_CONNECTION_STATE, false);
if (CIMPushManager.isNetworkConnected(context)) {
connect(0);
}
onConnectionClosed();
}
private void onInnerConnectFailed(long interval) {
if (CIMPushManager.isNetworkConnected(context)) {
onConnectFailed();
connect(interval);
}
}
private void onInnerConnectFinished() {
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_CIM_CONNECTION_STATE, true);
boolean autoBind = CIMPushManager.autoBindAccount(context);
onConnectFinished(autoBind);
}
private void onDevicesNetworkChanged() {
if (CIMPushManager.isNetworkConnected(context)) {
connect(0);
}
onNetworkChanged();
}
private void connect(long delay) {
Intent serviceIntent = new Intent(context, CIMPushService.class);
serviceIntent.putExtra(CIMPushService.KEY_DELAYED_TIME, delay);
serviceIntent.setAction(CIMPushManager.ACTION_CREATE_CIM_CONNECTION);
CIMPushManager.startService(context, serviceIntent);
}
private void onInnerMessageReceived(Message message, Intent intent) {
if (isForceOfflineMessage(message.getAction())) {
CIMPushManager.stop(context);
}
onMessageReceived(message, intent);
}
private boolean isForceOfflineMessage(String action) {
return CIMConstant.MessageAction.ACTION_999.equals(action);
}
public void onReplyReceived(ReplyBody body) { /**
CIMListenerManager.notifyOnReplyReceived(body); * 接收消息实现方法
} *
* @param message
* @param intent
*/
public abstract void onMessageReceived(Message message, Intent intent);
public void onSentSucceed(SentBody body) { public void onNetworkChanged() {
CIMListenerManager.notifyOnSendFinished(body); CIMListenerManager.notifyOnNetworkChanged(CIMPushManager.getNetworkInfo(context));
} }
public void onConnectFinished(boolean hasAutoBind) {
CIMListenerManager.notifyOnConnectFinished(hasAutoBind);
}
public void onConnectFailed() {
CIMListenerManager.notifyOnConnectFailed();
}
public void onConnectionClosed() {
CIMListenerManager.notifyOnConnectionClosed();
}
public void onReplyReceived(ReplyBody body) {
CIMListenerManager.notifyOnReplyReceived(body);
}
public void onSentSucceed(SentBody body) {
CIMListenerManager.notifyOnSendFinished(body);
}
} }

View File

@ -31,57 +31,55 @@ import com.farsunset.cim.sdk.android.model.SentBody;
*/ */
public interface CIMEventListener { public interface CIMEventListener {
/** /**
* 当收到服务端推送过来的消息时调用 * 当收到服务端推送过来的消息时调用
* *
* @param message * @param message
*/ */
void onMessageReceived(Message message); void onMessageReceived(Message message);
/** /**
* 当调用CIMPushManager.sendRequest()向服务端发送请求获得相应时调用 * 当调用CIMPushManager.sendRequest()向服务端发送请求获得相应时调用
* *
* @param body * @param body
*/ */
void onReplyReceived(ReplyBody body); void onReplyReceived(ReplyBody body);
/** /**
* 当调用CIMPushManager.sendRequest()向服务端发送请求成功时 * 当调用CIMPushManager.sendRequest()向服务端发送请求成功时
* *
* @param body * @param body
*/ */
void onSendFinished(SentBody body); void onSendFinished(SentBody body);
/** /**
* 当手机网络发生变化时调用 * 当手机网络发生变化时调用
* *
* @param info * @param info
*/ */
void onNetworkChanged(NetworkInfo info); void onNetworkChanged(NetworkInfo info);
/** /**
* 当连接服务器成功时回调 * 当连接服务器成功时回调
* *
* @param hasAutoBind * @param hasAutoBind true 已经自动绑定账号到服务器了不需要再手动调用bindAccount
* true 已经自动绑定账号到服务器了不需要再手动调用bindAccount */
*/ void onConnectFinished(boolean hasAutoBind);
void onConnectFinished(boolean hasAutoBind);
/** /**
* 当断开服务器连接的时候回调 * 当断开服务器连接的时候回调
* */
*/ void onConnectionClosed();
void onConnectionClosed();
/** /**
* 当连接服务器失败的时候回调 * 当连接服务器失败的时候回调
* */
*/ void onConnectFailed();
void onConnectFailed();
/** /**
* 监听器在容器里面的排序值越大则越先接收 * 监听器在容器里面的排序值越大则越先接收
* @return 排序 值越大优先级越高 *
*/ * @return 排序 值越大优先级越高
int getEventDispatchOrder(); */
int getEventDispatchOrder();
} }

View File

@ -37,96 +37,96 @@ import java.util.Iterator;
*/ */
public class CIMListenerManager { public class CIMListenerManager {
private static ArrayList<CIMEventListener> cimListeners = new ArrayList<CIMEventListener>(); private static final ArrayList<CIMEventListener> cimListeners = new ArrayList<CIMEventListener>();
private static ReceiveComparator comparator = new ReceiveComparator(); private static final ReceiveComparator comparator = new ReceiveComparator();
private CIMListenerManager(){ private CIMListenerManager() {
} }
public static void registerMessageListener(CIMEventListener listener) { public static void registerMessageListener(CIMEventListener listener) {
if (!cimListeners.contains(listener)) { if (!cimListeners.contains(listener)) {
cimListeners.add(listener); cimListeners.add(listener);
Collections.sort(cimListeners,comparator); Collections.sort(cimListeners, comparator);
} }
} }
public static void removeMessageListener(CIMEventListener listener) { public static void removeMessageListener(CIMEventListener listener) {
Iterator<CIMEventListener> iterable = cimListeners.iterator(); Iterator<CIMEventListener> iterable = cimListeners.iterator();
while (iterable.hasNext()){ while (iterable.hasNext()) {
CIMEventListener target = iterable.next(); CIMEventListener target = iterable.next();
if (listener.getClass() == target.getClass()) { if (listener.getClass() == target.getClass()) {
iterable.remove(); iterable.remove();
} }
} }
} }
public static void notifyOnNetworkChanged(NetworkInfo info) { public static void notifyOnNetworkChanged(NetworkInfo info) {
for (CIMEventListener listener : cimListeners) { for (CIMEventListener listener : cimListeners) {
listener.onNetworkChanged(info); listener.onNetworkChanged(info);
} }
} }
public static void notifyOnConnectFinished(boolean hasAutoBind) { public static void notifyOnConnectFinished(boolean hasAutoBind) {
for (CIMEventListener listener : cimListeners) { for (CIMEventListener listener : cimListeners) {
listener.onConnectFinished(hasAutoBind); listener.onConnectFinished(hasAutoBind);
} }
} }
public static void notifyOnMessageReceived(Message message) { public static void notifyOnMessageReceived(Message message) {
for (CIMEventListener listener : cimListeners) { for (CIMEventListener listener : cimListeners) {
listener.onMessageReceived(message); listener.onMessageReceived(message);
} }
} }
public static void notifyOnConnectionClosed() { public static void notifyOnConnectionClosed() {
for (CIMEventListener listener : cimListeners) { for (CIMEventListener listener : cimListeners) {
listener.onConnectionClosed(); listener.onConnectionClosed();
} }
} }
public static void notifyOnConnectFailed() { public static void notifyOnConnectFailed() {
for (CIMEventListener listener : cimListeners) { for (CIMEventListener listener : cimListeners) {
listener.onConnectFailed(); listener.onConnectFailed();
} }
} }
public static void notifyOnReplyReceived(ReplyBody body) { public static void notifyOnReplyReceived(ReplyBody body) {
for (CIMEventListener listener : cimListeners) { for (CIMEventListener listener : cimListeners) {
listener.onReplyReceived(body); listener.onReplyReceived(body);
} }
} }
public static void notifyOnSendFinished(SentBody body) { public static void notifyOnSendFinished(SentBody body) {
for (CIMEventListener listener : cimListeners) { for (CIMEventListener listener : cimListeners) {
listener.onSendFinished(body); listener.onSendFinished(body);
} }
} }
public static void destroy() { public static void destroy() {
cimListeners.clear(); cimListeners.clear();
} }
public static void logListenersName() { public static void logListenersName() {
for (CIMEventListener listener : cimListeners) { for (CIMEventListener listener : cimListeners) {
Log.i(CIMEventListener.class.getSimpleName(), "#######" + listener.getClass().getName() + "#######"); Log.i(CIMEventListener.class.getSimpleName(), "#######" + listener.getClass().getName() + "#######");
} }
} }
/** /**
* 消息接收activity的接收顺序排序CIM_RECEIVE_ORDER倒序 * 消息接收activity的接收顺序排序CIM_RECEIVE_ORDER倒序
*/ */
private static class ReceiveComparator implements Comparator<CIMEventListener> { private static class ReceiveComparator implements Comparator<CIMEventListener> {
@Override @Override
public int compare(CIMEventListener arg1, CIMEventListener arg2) { public int compare(CIMEventListener arg1, CIMEventListener arg2) {
int order1 = arg1.getEventDispatchOrder(); int order1 = arg1.getEventDispatchOrder();
int order2 = arg2.getEventDispatchOrder(); int order2 = arg2.getEventDispatchOrder();
return Integer.compare(order2,order1); return Integer.compare(order2, order1);
} }
} }
} }

View File

@ -29,7 +29,7 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo; import android.net.NetworkInfo;
import android.os.Build; import android.os.Build;
import android.text.TextUtils; import android.text.TextUtils;
import com.farsunset.cim.sdk.android.coder.CIMLogger; import com.farsunset.cim.sdk.android.logger.CIMLogger;
import com.farsunset.cim.sdk.android.constant.CIMConstant; import com.farsunset.cim.sdk.android.constant.CIMConstant;
import com.farsunset.cim.sdk.android.model.SentBody; import com.farsunset.cim.sdk.android.model.SentBody;
@ -40,208 +40,211 @@ import java.util.UUID;
*/ */
public class CIMPushManager { public class CIMPushManager {
protected static String ACTION_ACTIVATE_PUSH_SERVICE = "ACTION_ACTIVATE_PUSH_SERVICE"; protected static final String ACTION_ACTIVATE_PUSH_SERVICE = "ACTION_ACTIVATE_PUSH_SERVICE";
protected static String ACTION_CREATE_CIM_CONNECTION = "ACTION_CREATE_CIM_CONNECTION"; protected static final String ACTION_CREATE_CIM_CONNECTION = "ACTION_CREATE_CIM_CONNECTION";
protected static String ACTION_SEND_REQUEST_BODY = "ACTION_SEND_REQUEST_BODY"; protected static final String ACTION_SEND_REQUEST_BODY = "ACTION_SEND_REQUEST_BODY";
protected static String ACTION_CLOSE_CIM_CONNECTION = "ACTION_CLOSE_CIM_CONNECTION"; protected static final String ACTION_CLOSE_CIM_CONNECTION = "ACTION_CLOSE_CIM_CONNECTION";
protected static String ACTION_SET_LOGGER_EATABLE = "ACTION_SET_LOGGER_EATABLE"; protected static final String ACTION_DESTROY_CIM_SERVICE = "ACTION_DESTROY_CIM_SERVICE";
protected static String KEY_SEND_BODY = "KEY_SEND_BODY"; protected static final String ACTION_SET_LOGGER_EATABLE = "ACTION_SET_LOGGER_EATABLE";
/** protected static final String KEY_SEND_BODY = "KEY_SEND_BODY";
* 初始化,连接服务端在程序启动页或者 在Application里调用
*/
public static void connect(Context context, String host, int port) {
if(TextUtils.isEmpty(host) || port == 0) { /**
CIMLogger.getLogger().invalidHostPort(host, port); * 初始化,连接服务端在程序启动页或者 在Application里调用
return; */
} public static void connect(Context context, String host, int port) {
CIMCacheManager.putString(context, CIMCacheManager.KEY_CIM_SERVER_HOST, host); if (TextUtils.isEmpty(host) || port == 0) {
CIMCacheManager.putInt(context, CIMCacheManager.KEY_CIM_SERVER_PORT, port); CIMLogger.getLogger().invalidHostPort(host, port);
return;
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_CIM_DESTROYED, false);
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_MANUAL_STOP, false);
CIMCacheManager.remove(context, CIMCacheManager.KEY_ACCOUNT);
Intent serviceIntent = new Intent(context, CIMPushService.class);
serviceIntent.setAction(ACTION_CREATE_CIM_CONNECTION);
startService(context,serviceIntent);
}
public static void setLoggerEnable(Context context,boolean enable) {
Intent serviceIntent = new Intent(context, CIMPushService.class);
serviceIntent.putExtra(CIMPushService.KEY_LOGGER_ENABLE, enable);
serviceIntent.setAction(ACTION_SET_LOGGER_EATABLE);
startService(context,serviceIntent);
}
/**
* 设置一个账号登录到服务端
*/
public static void bindAccount(Context context, String account) {
if (isDestroyed(context) || account == null || account.trim().length() == 0) {
return;
}
sendBindRequest(context, account);
}
private static void sendBindRequest(Context context, String account) {
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_MANUAL_STOP, false);
CIMCacheManager.putString(context, CIMCacheManager.KEY_ACCOUNT, account);
SentBody sent = new SentBody();
sent.setKey(CIMConstant.RequestKey.CLIENT_BIND);
sent.put("account", account);
sent.put("deviceId", getDeviceId(context));
sent.put("channel", "android");
sent.put("device", Build.MODEL);
sent.put("appVersion", getVersionName(context));
sent.put("osVersion", Build.VERSION.RELEASE);
sent.put("packageName", context.getPackageName());
sent.setTimestamp(System.currentTimeMillis());
sendRequest(context, sent);
}
protected static boolean autoBindAccount(Context context) {
String account = CIMCacheManager.getString(context, CIMCacheManager.KEY_ACCOUNT);
if (account == null || account.trim().length() == 0 || isDestroyed(context)) {
return false;
}
sendBindRequest(context, account);
return true;
}
/**
* 发送一个CIM请求
*/
public static void sendRequest(Context context, SentBody body) {
if (isDestroyed(context) || isStopped(context)) {
return;
}
Intent serviceIntent = new Intent(context, CIMPushService.class);
serviceIntent.putExtra(KEY_SEND_BODY, body);
serviceIntent.setAction(ACTION_SEND_REQUEST_BODY);
startService(context,serviceIntent);
}
/**
* 停止接受推送将会退出当前账号登录端口与服务端的连接
*/
public static void stop(Context context) {
if (isDestroyed(context)) {
return;
}
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_MANUAL_STOP, true);
Intent serviceIntent = new Intent(context, CIMPushService.class);
serviceIntent.setAction(ACTION_CLOSE_CIM_CONNECTION);
startService(context,serviceIntent);
}
/**
* 完全销毁CIM一般用于完全退出程序调用resume将不能恢复
*/
public static void destroy(Context context) {
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_CIM_DESTROYED, true);
CIMCacheManager.putString(context, CIMCacheManager.KEY_ACCOUNT, null);
context.stopService(new Intent(context, CIMPushService.class));
}
/**
* 重新恢复接收推送重新连接服务端并登录当前账号
*/
public static void resume(Context context) {
if (isDestroyed(context)) {
return;
}
autoBindAccount(context);
}
public static boolean isDestroyed(Context context) {
return CIMCacheManager.getBoolean(context, CIMCacheManager.KEY_CIM_DESTROYED);
}
public static boolean isStopped(Context context) {
return CIMCacheManager.getBoolean(context, CIMCacheManager.KEY_MANUAL_STOP);
}
public static boolean isConnected(Context context) {
return CIMCacheManager.getBoolean(context, CIMCacheManager.KEY_CIM_CONNECTION_STATE);
}
public static boolean isNetworkConnected(Context context) {
NetworkInfo networkInfo = getNetworkInfo(context);
return networkInfo != null && networkInfo.isConnected();
}
public static NetworkInfo getNetworkInfo(Context context) {
return ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
}
public static void startService(Context context,Intent intent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
} }
}
private static String getVersionName(Context context) {
try {
PackageInfo mPackageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
return mPackageInfo.versionName;
} catch (NameNotFoundException ignore) {
}
return null;
}
private static String getDeviceId(Context context){ CIMCacheManager.putString(context, CIMCacheManager.KEY_CIM_SERVER_HOST, host);
CIMCacheManager.putInt(context, CIMCacheManager.KEY_CIM_SERVER_PORT, port);
String currDeviceId = CIMCacheManager.getString(context, CIMCacheManager.KEY_DEVICE_ID); CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_CIM_DESTROYED, false);
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_MANUAL_STOP, false);
if (!TextUtils.isEmpty(currDeviceId)) { CIMCacheManager.remove(context, CIMCacheManager.KEY_ACCOUNT);
return currDeviceId;
}
String deviceId = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
CIMCacheManager.putString(context, CIMCacheManager.KEY_DEVICE_ID,deviceId); Intent serviceIntent = new Intent(context, CIMPushService.class);
serviceIntent.setAction(ACTION_CREATE_CIM_CONNECTION);
startService(context, serviceIntent);
return deviceId; }
}
public static void setLoggerEnable(Context context, boolean enable) {
Intent serviceIntent = new Intent(context, CIMPushService.class);
serviceIntent.putExtra(CIMPushService.KEY_LOGGER_ENABLE, enable);
serviceIntent.setAction(ACTION_SET_LOGGER_EATABLE);
startService(context, serviceIntent);
}
/**
* 设置一个账号登录到服务端
*/
public static void bindAccount(Context context, String account) {
if (isDestroyed(context) || account == null || account.trim().length() == 0) {
return;
}
sendBindRequest(context, account);
}
private static void sendBindRequest(Context context, String account) {
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_MANUAL_STOP, false);
CIMCacheManager.putString(context, CIMCacheManager.KEY_ACCOUNT, account);
SentBody sent = new SentBody();
sent.setKey(CIMConstant.RequestKey.CLIENT_BIND);
sent.put("account", account);
sent.put("deviceId", getDeviceId(context));
sent.put("channel", "android");
sent.put("device", Build.MODEL);
sent.put("appVersion", getVersionName(context));
sent.put("osVersion", Build.VERSION.RELEASE);
sent.put("packageName", context.getPackageName());
sent.setTimestamp(System.currentTimeMillis());
sendRequest(context, sent);
}
protected static boolean autoBindAccount(Context context) {
String account = CIMCacheManager.getString(context, CIMCacheManager.KEY_ACCOUNT);
if (account == null || account.trim().length() == 0 || isDestroyed(context)) {
return false;
}
sendBindRequest(context, account);
return true;
}
/**
* 发送一个CIM请求
*/
public static void sendRequest(Context context, SentBody body) {
if (isDestroyed(context) || isStopped(context)) {
return;
}
Intent serviceIntent = new Intent(context, CIMPushService.class);
serviceIntent.putExtra(KEY_SEND_BODY, body);
serviceIntent.setAction(ACTION_SEND_REQUEST_BODY);
startService(context, serviceIntent);
}
/**
* 停止接受推送将会退出当前账号登录端口与服务端的连接
*/
public static void stop(Context context) {
if (isDestroyed(context)) {
return;
}
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_MANUAL_STOP, true);
Intent serviceIntent = new Intent(context, CIMPushService.class);
serviceIntent.setAction(ACTION_CLOSE_CIM_CONNECTION);
startService(context, serviceIntent);
}
/**
* 完全销毁CIM一般用于完全退出程序调用resume将不能恢复
*/
public static void destroy(Context context) {
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_CIM_DESTROYED, true);
CIMCacheManager.putString(context, CIMCacheManager.KEY_ACCOUNT, null);
Intent serviceIntent = new Intent(context, CIMPushService.class);
serviceIntent.setAction(ACTION_DESTROY_CIM_SERVICE);
startService(context, serviceIntent);
}
/**
* 重新恢复接收推送重新连接服务端并登录当前账号
*/
public static void resume(Context context) {
if (isDestroyed(context)) {
return;
}
autoBindAccount(context);
}
public static boolean isDestroyed(Context context) {
return CIMCacheManager.getBoolean(context, CIMCacheManager.KEY_CIM_DESTROYED);
}
public static boolean isStopped(Context context) {
return CIMCacheManager.getBoolean(context, CIMCacheManager.KEY_MANUAL_STOP);
}
public static boolean isConnected(Context context) {
return CIMCacheManager.getBoolean(context, CIMCacheManager.KEY_CIM_CONNECTION_STATE);
}
public static boolean isNetworkConnected(Context context) {
NetworkInfo networkInfo = getNetworkInfo(context);
return networkInfo != null && networkInfo.isConnected();
}
public static NetworkInfo getNetworkInfo(Context context) {
return ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
}
public static void startService(Context context, Intent intent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
}
private static String getVersionName(Context context) {
try {
PackageInfo mPackageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
return mPackageInfo.versionName;
} catch (NameNotFoundException ignore) {
}
return null;
}
private static String getDeviceId(Context context) {
String currDeviceId = CIMCacheManager.getString(context, CIMCacheManager.KEY_DEVICE_ID);
if (!TextUtils.isEmpty(currDeviceId)) {
return currDeviceId;
}
String deviceId = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
CIMCacheManager.putString(context, CIMCacheManager.KEY_DEVICE_ID, deviceId);
return deviceId;
}
} }

View File

@ -35,199 +35,211 @@ import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.util.Log; import android.util.Log;
import com.farsunset.cim.sdk.android.coder.CIMLogger; import com.farsunset.cim.sdk.android.logger.CIMLogger;
import com.farsunset.cim.sdk.android.constant.CIMConstant; import com.farsunset.cim.sdk.android.constant.CIMConstant;
import com.farsunset.cim.sdk.android.model.SentBody; import com.farsunset.cim.sdk.android.model.SentBody;
/** /**
* 与服务端连接服务 * 与服务端连接服务
*
* @author 3979434
* *
* @author 3979434
*/ */
public class CIMPushService extends Service { public class CIMPushService extends Service {
public final static String KEY_DELAYED_TIME = "KEY_DELAYED_TIME"; public final static String KEY_DELAYED_TIME = "KEY_DELAYED_TIME";
public final static String KEY_LOGGER_ENABLE = "KEY_LOGGER_ENABLE"; public final static String KEY_LOGGER_ENABLE = "KEY_LOGGER_ENABLE";
private final static int NOTIFICATION_ID = Integer.MAX_VALUE; private final static int NOTIFICATION_ID = Integer.MAX_VALUE;
private CIMConnectorManager manager; private CIMConnectorManager manager;
private KeepAliveBroadcastReceiver keepAliveReceiver; private KeepAliveBroadcastReceiver keepAliveReceiver;
private ConnectivityManager connectivityManager; private ConnectivityManager connectivityManager;
@Override
public void onCreate() {
manager = CIMConnectorManager.getManager(this.getApplicationContext());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @Override
public void onCreate() {
manager = CIMConnectorManager.getManager(this.getApplicationContext());
keepAliveReceiver = new KeepAliveBroadcastReceiver(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
registerReceiver(keepAliveReceiver, keepAliveReceiver.getIntentFilter());
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); keepAliveReceiver = new KeepAliveBroadcastReceiver();
registerReceiver(keepAliveReceiver, keepAliveReceiver.getIntentFilter());
connectivityManager.registerDefaultNetworkCallback(networkCallback); }
} if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
}
ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(Network network) {
Intent intent = new Intent();
intent.setPackage(getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_NETWORK_CHANGED);
sendBroadcast(intent);
}
@Override
public void onUnavailable() {
Intent intent = new Intent();
intent.setPackage(getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_NETWORK_CHANGED);
sendBroadcast(intent);
}
};
Handler connectHandler = new Handler() { connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
@Override
public void handleMessage(android.os.Message message) {
connect();
}
};
Handler notificationHandler = new Handler() { connectivityManager.registerDefaultNetworkCallback(networkCallback);
@Override
public void handleMessage(android.os.Message message) {
stopForeground(true);
}
};
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { }
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); }
NotificationChannel channel = new NotificationChannel(getClass().getSimpleName(),getClass().getSimpleName(), NotificationManager.IMPORTANCE_LOW);
channel.enableLights(false);
channel.enableVibration(false);
channel.setSound(null, null);
notificationManager.createNotificationChannel(channel);
Notification notification = new Notification.Builder(this, channel.getId())
.setContentTitle("Push service")
.setContentText("Push service is running")
.build();
startForeground(NOTIFICATION_ID,notification);
}
String action = intent == null ? CIMPushManager.ACTION_ACTIVATE_PUSH_SERVICE : intent.getAction(); final ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(Network network) {
Intent intent = new Intent();
intent.setPackage(getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_NETWORK_CHANGED);
sendBroadcast(intent);
}
if (CIMPushManager.ACTION_CREATE_CIM_CONNECTION.equals(action)) { @Override
connect(intent.getLongExtra(KEY_DELAYED_TIME, 0)); public void onUnavailable() {
} Intent intent = new Intent();
intent.setPackage(getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_NETWORK_CHANGED);
sendBroadcast(intent);
}
if (CIMPushManager.ACTION_SEND_REQUEST_BODY.equals(action)) { };
manager.send((SentBody) intent.getSerializableExtra(CIMPushManager.KEY_SEND_BODY));
}
if (CIMPushManager.ACTION_CLOSE_CIM_CONNECTION.equals(action)) { final Handler connectHandler = new Handler() {
manager.closeSession(); @Override
} public void handleMessage(android.os.Message message) {
connect();
}
};
if (CIMPushManager.ACTION_ACTIVATE_PUSH_SERVICE.equals(action)) { final Handler notificationHandler = new Handler() {
handleKeepAlive(); @Override
} public void handleMessage(android.os.Message message) {
stopForeground(true);
}
};
if (CIMPushManager.ACTION_SET_LOGGER_EATABLE.equals(action)) { @Override
boolean enable = intent.getBooleanExtra(KEY_LOGGER_ENABLE, true); public int onStartCommand(Intent intent, int flags, int startId) {
CIMLogger.getLogger().debugMode(enable);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationHandler.sendEmptyMessageDelayed(0, 1000); NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
} NotificationChannel channel = new NotificationChannel(getClass().getSimpleName(), getClass().getSimpleName(), NotificationManager.IMPORTANCE_LOW);
channel.enableLights(false);
channel.enableVibration(false);
channel.setSound(null, null);
notificationManager.createNotificationChannel(channel);
Notification notification = new Notification.Builder(this, channel.getId())
.setContentTitle("Push service")
.setContentText("Push service is running")
.build();
startForeground(NOTIFICATION_ID, notification);
}
return super.onStartCommand(intent, flags, startId); String action = intent == null ? CIMPushManager.ACTION_ACTIVATE_PUSH_SERVICE : intent.getAction();
}
private void connect(long delayMillis) { if (CIMPushManager.ACTION_CREATE_CIM_CONNECTION.equals(action)) {
connect(intent.getLongExtra(KEY_DELAYED_TIME, 0));
}
if(delayMillis <= 0) { if (CIMPushManager.ACTION_SEND_REQUEST_BODY.equals(action)) {
connect(); manager.send((SentBody) intent.getSerializableExtra(CIMPushManager.KEY_SEND_BODY));
return; }
}
connectHandler.sendEmptyMessageDelayed(0, delayMillis);
} if (CIMPushManager.ACTION_CLOSE_CIM_CONNECTION.equals(action)) {
manager.closeSession();
}
private void connect() { if (CIMPushManager.ACTION_ACTIVATE_PUSH_SERVICE.equals(action)) {
handleKeepAlive();
}
if(CIMPushManager.isDestroyed(this) || CIMPushManager.isStopped(this)) { if (CIMPushManager.ACTION_SET_LOGGER_EATABLE.equals(action)) {
return; boolean enable = intent.getBooleanExtra(KEY_LOGGER_ENABLE, true);
} CIMLogger.getLogger().debugMode(enable);
}
String host = CIMCacheManager.getString(this, CIMCacheManager.KEY_CIM_SERVER_HOST);
int port = CIMCacheManager.getInt(this, CIMCacheManager.KEY_CIM_SERVER_PORT);
if(host == null || host.trim().length() == 0 || port <= 0) {
Log.e(this.getClass().getSimpleName(), "Invalid hostname or port. host:" + host + " port:" + port);
return;
}
manager.connect(host, port);
} if (CIMPushManager.ACTION_DESTROY_CIM_SERVICE.equals(action)) {
manager.destroy();
private void handleKeepAlive() { stopSelf();
}
if (manager.isConnected()) {
CIMLogger.getLogger().connectState(true);
return;
}
connect();
}
@Override if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
public IBinder onBind(Intent arg0) { notificationHandler.sendEmptyMessageDelayed(0, 1000);
return null; }
}
@Override return super.onStartCommand(intent, flags, startId);
public void onDestroy() { }
super.onDestroy();
manager.destroy();
connectHandler.removeMessages(0);
notificationHandler.removeMessages(0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
unregisterReceiver(keepAliveReceiver);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
connectivityManager.unregisterNetworkCallback(networkCallback);
}
}
public class KeepAliveBroadcastReceiver extends BroadcastReceiver { private void connect(long delayMillis) {
@Override if (delayMillis <= 0) {
public void onReceive(Context arg0, Intent arg1) { connect();
handleKeepAlive(); return;
} }
public IntentFilter getIntentFilter() { connectHandler.sendEmptyMessageDelayed(0, delayMillis);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_POWER_CONNECTED);
intentFilter.addAction(Intent.ACTION_POWER_DISCONNECTED);
intentFilter.addAction(Intent.ACTION_USER_PRESENT);
return intentFilter;
}
} }
private void connect() {
if (CIMPushManager.isDestroyed(this) || CIMPushManager.isStopped(this)) {
return;
}
String host = CIMCacheManager.getString(this, CIMCacheManager.KEY_CIM_SERVER_HOST);
int port = CIMCacheManager.getInt(this, CIMCacheManager.KEY_CIM_SERVER_PORT);
if (host == null || host.trim().length() == 0 || port <= 0) {
Log.e(this.getClass().getSimpleName(), "Invalid hostname or port. host:" + host + " port:" + port);
return;
}
manager.connect(host, port);
}
private void handleKeepAlive() {
if (manager.isConnected()) {
CIMLogger.getLogger().connectState(true,CIMPushManager.isStopped(this),CIMPushManager.isDestroyed(this));
return;
}
connect();
}
@Override
public IBinder onBind(Intent arg0) {
return null;
}
public void release() {
connectHandler.removeMessages(0);
notificationHandler.removeMessages(0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
unregisterReceiver(keepAliveReceiver);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
connectivityManager.unregisterNetworkCallback(networkCallback);
}
}
@Override
public void onDestroy() {
super.onDestroy();
release();
}
public class KeepAliveBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context arg0, Intent arg1) {
handleKeepAlive();
}
public IntentFilter getIntentFilter() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_POWER_CONNECTED);
intentFilter.addAction(Intent.ACTION_POWER_DISCONNECTED);
intentFilter.addAction(Intent.ACTION_USER_PRESENT);
return intentFilter;
}
}
} }

View File

@ -1,138 +0,0 @@
/*
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
***************************************************************************************
* *
* Website : http://www.farsunset.com *
* *
***************************************************************************************
*/
package com.farsunset.cim.sdk.android.coder;
import android.util.Log;
import java.nio.channels.SocketChannel;
/**
* 日志打印添加session 的id和ip address
*/
public class CIMLogger {
private final static String TAG = "CIM";
private boolean debug = true;
public static CIMLogger getLogger() {
return LoggerHolder.logger;
}
private CIMLogger() {
}
private static class LoggerHolder{
private static CIMLogger logger = new CIMLogger();
}
public void debugMode(boolean mode) {
debug = mode;
}
public void messageReceived(SocketChannel session, Object message) {
if(debug) {
Log.i(TAG,String.format("[RECEIVED]" + getSessionInfo(session) + "\n%s", message));
}
}
public void messageSent(SocketChannel session, Object message) {
if(debug) {
Log.i(TAG,String.format("[ SENT ]" + getSessionInfo(session) + "\n%s", message));
}
}
public void sessionCreated( SocketChannel session) {
if(debug) {
Log.i(TAG,"[ OPENED ]" + getSessionInfo(session));
}
}
public void sessionIdle( SocketChannel session) {
if(debug) {
Log.d(TAG,"[ IDLE ]" + getSessionInfo(session));
}
}
public void sessionClosed( SocketChannel session) {
if(debug) {
Log.w(TAG,"[ CLOSED ] ID = " + session.hashCode());
}
}
public void connectFailure(long interval) {
if(debug) {
Log.d(TAG,"CONNECT FAILURE, TRY RECONNECT AFTER " + interval +"ms");
}
}
public void startConnect(String host , int port) {
if(debug) {
Log.i(TAG,"START CONNECT REMOTE HOST:" + host + " PORT:" + port);
}
}
public void invalidHostPort(String host , int port) {
if(debug) {
Log.d(TAG,"INVALID SOCKET ADDRESS -> HOST:" + host + " PORT:" + port);
}
}
public void connectState(boolean isConnected) {
if(debug) {
Log.d(TAG,"CONNECTED:" + isConnected);
}
}
public void connectState(boolean isConnected,boolean isManualStop,boolean isDestroyed) {
if(debug) {
Log.d(TAG,"CONNECTED:" + isConnected + " STOPPED:"+isManualStop+ " DESTROYED:"+isDestroyed);
}
}
private String getSessionInfo(SocketChannel session) {
StringBuilder builder = new StringBuilder();
if (session == null) {
return "";
}
builder.append(" [");
builder.append("id:").append(session.hashCode());
try {
if (session.socket().getLocalAddress() != null) {
builder.append(" L:").append(session.socket().getLocalAddress()).append(":").append(session.socket().getLocalPort());
}
} catch (Exception ignore) {
}
try {
if (session.socket().getRemoteSocketAddress() != null) {
builder.append(" R:").append(session.socket().getRemoteSocketAddress().toString());
}
} catch (Exception ignore) {
}
builder.append("]");
return builder.toString();
}
}

View File

@ -28,98 +28,86 @@ import com.farsunset.cim.sdk.android.model.Message;
import com.farsunset.cim.sdk.android.model.ReplyBody; import com.farsunset.cim.sdk.android.model.ReplyBody;
import com.farsunset.cim.sdk.android.model.proto.MessageProto; import com.farsunset.cim.sdk.android.model.proto.MessageProto;
import com.farsunset.cim.sdk.android.model.proto.ReplyBodyProto; import com.farsunset.cim.sdk.android.model.proto.ReplyBodyProto;
import com.google.protobuf.InvalidProtocolBufferException;
import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
/**
* 客户端消息解码
*/
public class ClientMessageDecoder { public class ClientMessageDecoder {
/**
*
* @param headerBuffer 读取到的消息头
* @param socketChannel
* @return
*/
public Object doDecode(ByteBuffer headerBuffer , SocketChannel socketChannel) throws IOException {
public Object doDecode(ByteBuffer buffer) { headerBuffer.position(0);
/*
* 消息头3位
*/
if (buffer.remaining() < CIMConstant.DATA_HEADER_LENGTH) {
return null;
}
buffer.mark(); byte type = headerBuffer.get();
byte lv = headerBuffer.get();
byte hv = headerBuffer.get();
byte type = buffer.get(); headerBuffer.clear();
byte lv = buffer.get(); /*
byte hv = buffer.get(); * 先通过消息头拿到消息的长度然后进行定长读取
* 解决消息的断包和粘包情况
*/
int length = getContentLength(lv, hv); int dataLength = getContentLength(lv, hv);
/* ByteBuffer bodyBuffer = ByteBuffer.allocate(dataLength);
*如果消息体没有接收完整则重置读取等待下一次重新读取
*/
if (length > buffer.remaining()) {
buffer.reset();
return null;
}
byte[] dataBytes = new byte[length]; /*
buffer.get(dataBytes, 0, length); * 如果读取的消息长度不够则进行等待后续消息到来
* 当读取的消息长度(bodyBuffer.position() == dataLength)时意味着一个完整的消息已经接收完成
*/
do {
socketChannel.read(bodyBuffer);
} while (bodyBuffer.position() != dataLength);
buffer.position(0);
try {
return mappingMessageObject(dataBytes, type);
} catch (InvalidProtocolBufferException e) {
return null;
}
}
private Object mappingMessageObject(byte[] bytes, byte type) throws InvalidProtocolBufferException { /*
消息读取完成后通过type来解析成对应的消息体
*/
if (CIMConstant.ProtobufType.S_H_RQ == type) {
return HeartbeatRequest.getInstance();
}
if (CIMConstant.ProtobufType.S_H_RQ == type) { if (CIMConstant.ProtobufType.REPLY_BODY == type) {
return HeartbeatRequest.getInstance(); ReplyBodyProto.Model bodyProto = ReplyBodyProto.Model.parseFrom(bodyBuffer.array());
} ReplyBody body = new ReplyBody();
body.setKey(bodyProto.getKey());
body.setTimestamp(bodyProto.getTimestamp());
body.putAll(bodyProto.getDataMap());
body.setCode(bodyProto.getCode());
body.setMessage(bodyProto.getMessage());
return body;
}
if (CIMConstant.ProtobufType.REPLY_BODY == type) { MessageProto.Model bodyProto = MessageProto.Model.parseFrom(bodyBuffer.array());
ReplyBodyProto.Model bodyProto = ReplyBodyProto.Model.parseFrom(bytes); Message message = new Message();
ReplyBody body = new ReplyBody(); message.setId(bodyProto.getId());
body.setKey(bodyProto.getKey()); message.setAction(bodyProto.getAction());
body.setTimestamp(bodyProto.getTimestamp()); message.setContent(bodyProto.getContent());
body.putAll(bodyProto.getDataMap()); message.setSender(bodyProto.getSender());
body.setCode(bodyProto.getCode()); message.setReceiver(bodyProto.getReceiver());
body.setMessage(bodyProto.getMessage()); message.setTitle(bodyProto.getTitle());
return body; message.setExtra(bodyProto.getExtra());
} message.setTimestamp(bodyProto.getTimestamp());
message.setFormat(bodyProto.getFormat());
return message;
}
if (CIMConstant.ProtobufType.MESSAGE == type) { /**
MessageProto.Model bodyProto = MessageProto.Model.parseFrom(bytes); * 解析消息体长度
Message message = new Message(); */
message.setId(bodyProto.getId()); private int getContentLength(byte lv, byte hv) {
message.setAction(bodyProto.getAction()); int l = (lv & 0xff);
message.setContent(bodyProto.getContent()); int h = (hv & 0xff);
message.setSender(bodyProto.getSender()); return (l | h << 8);
message.setReceiver(bodyProto.getReceiver()); }
message.setTitle(bodyProto.getTitle());
message.setExtra(bodyProto.getExtra());
message.setTimestamp(bodyProto.getTimestamp());
message.setFormat(bodyProto.getFormat());
return message;
}
return null;
}
/**
* 解析消息体长度
*/
private int getContentLength(byte lv, byte hv) {
int l = (lv & 0xff);
int h = (hv & 0xff);
return (l | h << 8);
}
} }

View File

@ -23,38 +23,35 @@ package com.farsunset.cim.sdk.android.coder;
import com.farsunset.cim.sdk.android.constant.CIMConstant; import com.farsunset.cim.sdk.android.constant.CIMConstant;
import com.farsunset.cim.sdk.android.model.Protobufable; import com.farsunset.cim.sdk.android.model.Protobufable;
import com.farsunset.cim.sdk.android.model.SentBody;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
/** public class ClientMessageEncoder {
* 客户端消息发送前进行编码
*/
public class ClientMessageEncoder {
public ByteBuffer encode(Object object) { public ByteBuffer encode(Protobufable body) {
Protobufable data = (Protobufable) object; byte[] data = body.getByteArray();
byte[] byteArray = data.getByteArray();
ByteBuffer buffer = ByteBuffer.allocate(byteArray.length + CIMConstant.DATA_HEADER_LENGTH); ByteBuffer buffer = ByteBuffer.allocate(data.length + CIMConstant.DATA_HEADER_LENGTH);
buffer.put(createHeader(data.getType(), byteArray.length)); buffer.put(createHeader(body.getType(), data.length));
buffer.put(byteArray); buffer.put(data);
buffer.flip(); buffer.flip();
return buffer; return buffer;
} }
/** /**
* 消息体最大为65535 * 消息体最大为65535
*/ */
private byte[] createHeader(byte type, int length) { private byte[] createHeader(byte type, int length) {
byte[] header = new byte[CIMConstant.DATA_HEADER_LENGTH]; byte[] header = new byte[CIMConstant.DATA_HEADER_LENGTH];
header[0] = type; header[0] = type;
header[1] = (byte) (length & 0xff); header[1] = (byte) (length & 0xff);
header[2] = (byte) ((length >> 8) & 0xff); header[2] = (byte) ((length >> 8) & 0xff);
return header; return header;
} }
} }

View File

@ -21,99 +21,89 @@
*/ */
package com.farsunset.cim.sdk.android.constant; package com.farsunset.cim.sdk.android.constant;
/**
* 常量
*/
public interface CIMConstant { public interface CIMConstant {
long RECONNECT_INTERVAL_TIME = 30 * 1000; long RECONNECT_INTERVAL_TIME = 30 * 1000;
/** /*
消息头长度为3个字节第一个字节为消息类型第二第三字节 转换int后为消息长度 * 消息头长度为3个字节第一个字节为消息类型第二第三字节 转换int后为消息长度
*/ */
int DATA_HEADER_LENGTH = 3; int DATA_HEADER_LENGTH = 3;
interface ReturnCode { interface ProtobufType {
String CODE_404 = "404"; /*
客户端->服务端 发送的心跳响应
*/
byte C_H_RS = 0;
String CODE_403 = "403"; /*
服务端->客户端 发送的心跳请求
*/
byte S_H_RQ = 1;
String CODE_405 = "405"; byte MESSAGE = 2;
String CODE_200 = "200"; byte SENT_BODY = 3;
String CODE_206 = "206"; byte REPLY_BODY = 4;
}
String CODE_500 = "500"; interface RequestKey {
} String CLIENT_BIND = "client_bind";
interface ProtobufType { }
byte C_H_RS = 0;
byte S_H_RQ = 1;
byte MESSAGE = 2;
byte SENT_BODY = 3;
byte REPLY_BODY = 4;
}
interface RequestKey { interface MessageAction {
String CLIENT_BIND = "client_bind"; /*
被其他设备登录挤下线消息
*/
String ACTION_999 = "999";
}
String CLIENT_LOGOUT = "client_logout"; interface IntentAction {
} /*
消息广播action
*/
String ACTION_MESSAGE_RECEIVED = "com.farsunset.cim.MESSAGE_RECEIVED";
interface MessageAction { /*
发送sendBody成功广播
*/
String ACTION_SEND_FINISHED = "com.farsunset.cim.SEND_FINISHED";
/* /*
被其他设备登录挤下线消息 链接意外关闭广播
*/ */
String ACTION_999 = "999"; String ACTION_CONNECTION_CLOSED = "com.farsunset.cim.CONNECTION_CLOSED";
}
interface IntentAction { /*
链接失败广播
*/
String ACTION_CONNECT_FAILED = "com.farsunset.cim.CONNECT_FAILED";
/* /*
消息广播action 链接成功广播
*/ */
String ACTION_MESSAGE_RECEIVED = "com.farsunset.cim.MESSAGE_RECEIVED"; String ACTION_CONNECT_FINISHED = "com.farsunset.cim.CONNECT_FINISHED";
/* /*
发送sendBody成功广播 发送sendBody成功后获得replayBody回应广播
*/ */
String ACTION_SEND_FINISHED = "com.farsunset.cim.SEND_FINISHED"; String ACTION_REPLY_RECEIVED = "com.farsunset.cim.REPLY_RECEIVED";
/* /*
链接意外关闭广播 网络变化广播
*/ */
String ACTION_CONNECTION_CLOSED = "com.farsunset.cim.CONNECTION_CLOSED"; String ACTION_NETWORK_CHANGED = "com.farsunset.cim.NETWORK_CHANGED";
/* /*
链接失败广播 重试连接
*/ */
String ACTION_CONNECT_FAILED = "com.farsunset.cim.CONNECT_FAILED"; String ACTION_CONNECTION_RECOVERY = "com.farsunset.cim.CONNECTION_RECOVERY";
}
/*
链接成功广播
*/
String ACTION_CONNECT_FINISHED = "com.farsunset.cim.CONNECT_FINISHED";
/*
发送sendBody成功后获得replayBody回应广播
*/
String ACTION_REPLY_RECEIVED = "com.farsunset.cim.REPLY_RECEIVED";
/*
网络变化广播
*/
String ACTION_NETWORK_CHANGED = "com.farsunset.cim.NETWORK_CHANGED";
/*
重试连接
*/
String ACTION_CONNECTION_RECOVERY = "com.farsunset.cim.CONNECTION_RECOVERY";
}
} }

View File

@ -0,0 +1,133 @@
/*
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
***************************************************************************************
* *
* Website : http://www.farsunset.com *
* *
***************************************************************************************
*/
package com.farsunset.cim.sdk.android.logger;
import android.util.Log;
import java.nio.channels.SocketChannel;
/**
* 日志打印添加session 的id和ip address
*/
public class CIMLogger {
private final static String TAG = "CIM";
private boolean debug = true;
public static CIMLogger getLogger() {
return LoggerHolder.logger;
}
private CIMLogger() {
}
private static class LoggerHolder {
private static final CIMLogger logger = new CIMLogger();
}
public void debugMode(boolean mode) {
debug = mode;
}
public void messageReceived(SocketChannel session, Object message) {
if (debug) {
Log.i(TAG, String.format("[RECEIVED]" + getSessionInfo(session) + "\n%s", message));
}
}
public void messageSent(SocketChannel session, Object message) {
if (debug) {
Log.i(TAG, String.format("[ SENT ]" + getSessionInfo(session) + "\n%s", message));
}
}
public void sessionCreated(SocketChannel session) {
if (debug) {
Log.i(TAG, "[ OPENED ]" + getSessionInfo(session));
}
}
public void sessionIdle(SocketChannel session) {
if (debug) {
Log.d(TAG, "[ IDLE ]" + getSessionInfo(session));
}
}
public void sessionClosed(SocketChannel session) {
if (debug) {
Log.w(TAG, "[ CLOSED ] ID = " + session.hashCode());
}
}
public void connectFailure(long interval) {
if (debug) {
Log.d(TAG, "CONNECT FAILURE, TRY RECONNECT AFTER " + interval + "ms");
}
}
public void startConnect(String host, int port) {
if (debug) {
Log.i(TAG, "START CONNECT REMOTE HOST:" + host + " PORT:" + port);
}
}
public void invalidHostPort(String host, int port) {
if (debug) {
Log.d(TAG, "INVALID SOCKET ADDRESS -> HOST:" + host + " PORT:" + port);
}
}
public void connectState(boolean isConnected, boolean isManualStop, boolean isDestroyed) {
if (debug) {
Log.d(TAG, "CONNECTED:" + isConnected + " STOPPED:" + isManualStop + " DESTROYED:" + isDestroyed);
}
}
private String getSessionInfo(SocketChannel session) {
StringBuilder builder = new StringBuilder();
if (session == null) {
return "";
}
builder.append(" [");
builder.append("id:").append(session.hashCode());
try {
if (session.socket().getLocalAddress() != null) {
builder.append(" L:").append(session.socket().getLocalAddress()).append(":").append(session.socket().getLocalPort());
}
} catch (Exception ignore) {
}
try {
if (session.socket().getRemoteSocketAddress() != null) {
builder.append(" R:").append(session.socket().getRemoteSocketAddress().toString());
}
} catch (Exception ignore) {
}
builder.append("]");
return builder.toString();
}
}

View File

@ -27,37 +27,36 @@ import java.io.Serializable;
/** /**
* 服务端心跳请求 * 服务端心跳请求
*
*/ */
public class HeartbeatRequest implements Serializable, Protobufable { public class HeartbeatRequest implements Serializable, Protobufable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final String TAG = "SERVER_HEARTBEAT_REQUEST"; private static final String TAG = "SERVER_HEARTBEAT_REQUEST";
private static final String CMD_HEARTBEAT_REQUEST = "SR"; private static final String CMD_HEARTBEAT_REQUEST = "SR";
private static HeartbeatRequest object = new HeartbeatRequest(); private static final HeartbeatRequest object = new HeartbeatRequest();
private HeartbeatRequest() { private HeartbeatRequest() {
} }
public static HeartbeatRequest getInstance() { public static HeartbeatRequest getInstance() {
return object; return object;
} }
@Override @Override
public byte[] getByteArray() { public byte[] getByteArray() {
return CMD_HEARTBEAT_REQUEST.getBytes(); return CMD_HEARTBEAT_REQUEST.getBytes();
} }
@Override @Override
public String toString() { public String toString() {
return TAG; return TAG;
} }
@Override @Override
public byte getType() { public byte getType() {
return CIMConstant.ProtobufType.S_H_RQ; return CIMConstant.ProtobufType.S_H_RQ;
} }
} }

View File

@ -30,33 +30,33 @@ import java.io.Serializable;
*/ */
public class HeartbeatResponse implements Serializable, Protobufable { public class HeartbeatResponse implements Serializable, Protobufable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final String TAG = "CLIENT_HEARTBEAT_RESPONSE"; private static final String TAG = "CLIENT_HEARTBEAT_RESPONSE";
private static final String CMD_HEARTBEAT_RESPONSE = "CR"; private static final String CMD_HEARTBEAT_RESPONSE = "CR";
private static HeartbeatResponse object = new HeartbeatResponse(); private static final HeartbeatResponse object = new HeartbeatResponse();
private HeartbeatResponse() { private HeartbeatResponse() {
} }
public static HeartbeatResponse getInstance() { public static HeartbeatResponse getInstance() {
return object; return object;
} }
@Override @Override
public byte[] getByteArray() { public byte[] getByteArray() {
return CMD_HEARTBEAT_RESPONSE.getBytes(); return CMD_HEARTBEAT_RESPONSE.getBytes();
} }
@Override @Override
public String toString() { public String toString() {
return TAG; return TAG;
} }
@Override @Override
public byte getType() { public byte getType() {
return CIMConstant.ProtobufType.C_H_RS; return CIMConstant.ProtobufType.C_H_RS;
} }
} }

View File

@ -28,143 +28,143 @@ import java.io.Serializable;
*/ */
public class Message implements Serializable { public class Message implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* 消息类型用户自定义消息类别 * 消息类型用户自定义消息类别
*/ */
private long id; private long id;
/** /**
* 消息类型用户自定义消息类别 * 消息类型用户自定义消息类别
*/ */
private String action; private String action;
/** /**
* 消息标题 * 消息标题
*/ */
private String title; private String title;
/** /**
* 消息类容于action 组合为任何类型消息content 根据 format 可表示为 text,json ,xml数据格式 * 消息类容于action 组合为任何类型消息content 根据 format 可表示为 text,json ,xml数据格式
*/ */
private String content; private String content;
/** /**
* 消息发送者账号 * 消息发送者账号
*/ */
private String sender; private String sender;
/** /**
* 消息发送者接收者 * 消息发送者接收者
*/ */
private String receiver; private String receiver;
/** /**
* content 内容格式 * content 内容格式
*/ */
private String format; private String format;
/** /**
* 附加内容 内容 * 附加内容 内容
*/ */
private String extra; private String extra;
private long timestamp; private long timestamp;
public Message() { public Message() {
timestamp = System.currentTimeMillis(); timestamp = System.currentTimeMillis();
} }
public long getTimestamp() { public long getTimestamp() {
return timestamp; return timestamp;
} }
public void setTimestamp(long timestamp) { public void setTimestamp(long timestamp) {
this.timestamp = timestamp; this.timestamp = timestamp;
} }
public String getAction() { public String getAction() {
return action; return action;
} }
public void setAction(String action) { public void setAction(String action) {
this.action = action; this.action = action;
} }
public String getTitle() { public String getTitle() {
return title; return title;
} }
public void setTitle(String title) { public void setTitle(String title) {
this.title = title; this.title = title;
} }
public String getContent() { public String getContent() {
return content; return content;
} }
public void setContent(String content) { public void setContent(String content) {
this.content = content; this.content = content;
} }
public String getSender() { public String getSender() {
return sender; return sender;
} }
public void setSender(String sender) { public void setSender(String sender) {
this.sender = sender; this.sender = sender;
} }
public String getReceiver() { public String getReceiver() {
return receiver; return receiver;
} }
public void setReceiver(String receiver) { public void setReceiver(String receiver) {
this.receiver = receiver; this.receiver = receiver;
} }
public String getFormat() { public String getFormat() {
return format; return format;
} }
public void setFormat(String format) { public void setFormat(String format) {
this.format = format; this.format = format;
} }
public String getExtra() { public String getExtra() {
return extra; return extra;
} }
public void setExtra(String extra) { public void setExtra(String extra) {
this.extra = extra; this.extra = extra;
} }
@Override @Override
public String toString() { public String toString() {
StringBuffer buffer = new StringBuffer(); StringBuffer buffer = new StringBuffer();
buffer.append("#Message#").append("\n"); buffer.append("#Message#").append("\n");
buffer.append("id:").append(id).append("\n"); buffer.append("id:").append(id).append("\n");
buffer.append("action:").append(action).append("\n"); buffer.append("action:").append(action).append("\n");
buffer.append("title:").append(title).append("\n"); buffer.append("title:").append(title).append("\n");
buffer.append("content:").append(content).append("\n"); buffer.append("content:").append(content).append("\n");
buffer.append("extra:").append(extra).append("\n"); buffer.append("extra:").append(extra).append("\n");
buffer.append("sender:").append(sender).append("\n"); buffer.append("sender:").append(sender).append("\n");
buffer.append("receiver:").append(receiver).append("\n"); buffer.append("receiver:").append(receiver).append("\n");
buffer.append("format:").append(format).append("\n"); buffer.append("format:").append(format).append("\n");
buffer.append("timestamp:").append(timestamp); buffer.append("timestamp:").append(timestamp);
return buffer.toString(); return buffer.toString();
} }
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public boolean isNotEmpty(String txt) { public long getId() {
return txt != null && txt.trim().length() != 0; return id;
} }
public void setId(long id) {
this.id = id;
}
public boolean isNotEmpty(String txt) {
return txt != null && txt.trim().length() != 0;
}
} }

View File

@ -26,7 +26,7 @@ package com.farsunset.cim.sdk.android.model;
*/ */
public interface Protobufable { public interface Protobufable {
byte[] getByteArray(); byte[] getByteArray();
byte getType(); byte getType();
} }

View File

@ -28,101 +28,100 @@ import java.util.Set;
/** /**
* 请求应答对象 * 请求应答对象
*
*/ */
public class ReplyBody implements Serializable { public class ReplyBody implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* 请求key * 请求key
*/ */
private String key; private String key;
/** /**
* 返回码 * 返回码
*/ */
private String code; private String code;
/** /**
* 返回说明 * 返回说明
*/ */
private String message; private String message;
private long timestamp; private long timestamp;
/** /**
* 返回数据集合 * 返回数据集合
*/ */
private Hashtable<String, String> data = new Hashtable<String, String>(); private final Hashtable<String, String> data = new Hashtable<String, String>();
public long getTimestamp() { public long getTimestamp() {
return timestamp; return timestamp;
} }
public void setTimestamp(long timestamp) { public void setTimestamp(long timestamp) {
this.timestamp = timestamp; this.timestamp = timestamp;
} }
public String getKey() { public String getKey() {
return key; return key;
} }
public void setKey(String key) { public void setKey(String key) {
this.key = key; this.key = key;
} }
public void put(String k, String v) { public void put(String k, String v) {
data.put(k, v); data.put(k, v);
} }
public String get(String k) { public String get(String k) {
return data.get(k); return data.get(k);
} }
public void remove(String k) { public void remove(String k) {
data.remove(k); data.remove(k);
} }
public String getMessage() { public String getMessage() {
return message; return message;
} }
public void setMessage(String message) { public void setMessage(String message) {
this.message = message; this.message = message;
} }
public void putAll(Map<String, String> map) { public void putAll(Map<String, String> map) {
data.putAll(map); data.putAll(map);
} }
public Set<String> getKeySet() { public Set<String> getKeySet() {
return data.keySet(); return data.keySet();
} }
public String getCode() { public String getCode() {
return code; return code;
} }
public void setCode(String code) { public void setCode(String code) {
this.code = code; this.code = code;
} }
@Override @Override
public String toString() { public String toString() {
StringBuffer buffer = new StringBuffer(); StringBuffer buffer = new StringBuffer();
buffer.append("#ReplyBody#").append("\n"); buffer.append("#ReplyBody#").append("\n");
buffer.append("key:").append(this.getKey()).append("\n"); buffer.append("key:").append(this.getKey()).append("\n");
buffer.append("timestamp:").append(timestamp).append("\n"); buffer.append("timestamp:").append(timestamp).append("\n");
buffer.append("code:").append(code).append("\n"); buffer.append("code:").append(code).append("\n");
buffer.append("data{").append("\n"); buffer.append("data{").append("\n");
for (String key : getKeySet()) { for (String key : getKeySet()) {
buffer.append(key).append(":").append(this.get(key)).append("\n"); buffer.append(key).append(":").append(this.get(key)).append("\n");
} }
buffer.append("}"); buffer.append("}");
return buffer.toString(); return buffer.toString();
} }
} }

View File

@ -30,85 +30,84 @@ import java.util.Set;
/** /**
* java |android 客户端请求结构 * java |android 客户端请求结构
*
*/ */
public class SentBody implements Serializable, Protobufable { public class SentBody implements Serializable, Protobufable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private String key; private String key;
private Hashtable<String, String> data = new Hashtable<String, String>();; private final Hashtable<String, String> data = new Hashtable<String, String>();
private long timestamp; private long timestamp;
public SentBody() { public SentBody() {
timestamp = System.currentTimeMillis(); timestamp = System.currentTimeMillis();
} }
public String getKey() { public String getKey() {
return key; return key;
} }
public String get(String k) { public String get(String k) {
return data.get(k); return data.get(k);
} }
public long getTimestamp() { public long getTimestamp() {
return timestamp; return timestamp;
} }
public void setTimestamp(long timestamp) { public void setTimestamp(long timestamp) {
this.timestamp = timestamp; this.timestamp = timestamp;
} }
public void setKey(String key) { public void setKey(String key) {
this.key = key; this.key = key;
} }
public void put(String k, String v) { public void put(String k, String v) {
if (k == null || v == null) { if (k == null || v == null) {
return; return;
} }
data.put(k, v); data.put(k, v);
} }
public Set<String> getKeySet() { public Set<String> getKeySet() {
return data.keySet(); return data.keySet();
} }
public void remove(String k) { public void remove(String k) {
data.remove(k); data.remove(k);
} }
@Override @Override
public String toString() { public String toString() {
StringBuffer buffer = new StringBuffer(); StringBuffer buffer = new StringBuffer();
buffer.append("#SentBody#").append("\n"); buffer.append("#SentBody#").append("\n");
buffer.append("key:").append(key).append("\n"); buffer.append("key:").append(key).append("\n");
buffer.append("timestamp:").append(timestamp).append("\n"); buffer.append("timestamp:").append(timestamp).append("\n");
buffer.append("data{").append("\n"); buffer.append("data{").append("\n");
for (String key : getKeySet()) { for (String key : getKeySet()) {
buffer.append(key).append(":").append(this.get(key)).append("\n"); buffer.append(key).append(":").append(this.get(key)).append("\n");
} }
buffer.append("}"); buffer.append("}");
return buffer.toString(); return buffer.toString();
} }
@Override @Override
public byte[] getByteArray() { public byte[] getByteArray() {
SentBodyProto.Model.Builder builder = SentBodyProto.Model.newBuilder(); SentBodyProto.Model.Builder builder = SentBodyProto.Model.newBuilder();
builder.setKey(key); builder.setKey(key);
builder.setTimestamp(timestamp); builder.setTimestamp(timestamp);
if (!data.isEmpty()) { if (!data.isEmpty()) {
builder.putAllData(data); builder.putAllData(data);
} }
return builder.build().toByteArray(); return builder.build().toByteArray();
} }
@Override @Override
public byte getType() { public byte getType() {
return CIMConstant.ProtobufType.SENT_BODY; return CIMConstant.ProtobufType.SENT_BODY;
} }
} }

View File

@ -1,15 +1,15 @@
syntax = "proto3"; syntax = "proto3";
package com.farsunset.cim.sdk.android.model.proto; package com.farsunset.cim.sdk.android.model.proto;
option java_outer_classname="MessageProto"; option java_outer_classname = "MessageProto";
message Model { message Model {
int64 id = 1; int64 id = 1;
string action = 2; string action = 2;
string content = 3; string content = 3;
string sender = 4; string sender = 4;
string receiver = 5; string receiver = 5;
string extra = 6; string extra = 6;
string title = 7; string title = 7;
string format = 8; string format = 8;
int64 timestamp = 9; int64 timestamp = 9;
} }

View File

@ -1,13 +1,13 @@
syntax = "proto3"; syntax = "proto3";
package com.farsunset.cim.sdk.android.model.proto; package com.farsunset.cim.sdk.android.model.proto;
option java_outer_classname="ReplyBodyProto"; option java_outer_classname = "ReplyBodyProto";
message Model { message Model {
string key = 1; string key = 1;
string code = 2; string code = 2;
string message = 3; string message = 3;
int64 timestamp =4; int64 timestamp = 4;
map<string,string> data =5; map<string, string> data = 5;
} }

View File

@ -1,11 +1,11 @@
syntax = "proto3"; syntax = "proto3";
package com.farsunset.cim.sdk.android.model.proto; package com.farsunset.cim.sdk.android.model.proto;
option java_outer_classname="SentBodyProto"; option java_outer_classname = "SentBodyProto";
message Model { message Model {
string key = 1; string key = 1;
int64 timestamp =2; int64 timestamp = 2;
map<string,string> data =3; map<string, string> data = 3;
} }

15
cim-client-sdk/cim-java-sdk/cim-java-sdk.iml Normal file → Executable file
View File

@ -1,2 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4" /> <module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: com.google.protobuf:protobuf-java:3.11.1" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
</component>
</module>

0
cim-client-sdk/cim-java-sdk/jar.bat Normal file → Executable file
View File

0
cim-client-sdk/cim-java-sdk/jar.sh Normal file → Executable file
View File

0
cim-client-sdk/cim-java-sdk/pom.xml Normal file → Executable file
View File

View File

@ -25,7 +25,7 @@ import java.util.HashMap;
class CIMCacheManager { class CIMCacheManager {
private static HashMap<String, String> CIM_CONFIG_INFO = new HashMap<String, String>(); private static final HashMap<String, String> CIM_CONFIG_INFO = new HashMap<String, String>();
public static final String KEY_MANUAL_STOP = "KEY_MANUAL_STOP"; public static final String KEY_MANUAL_STOP = "KEY_MANUAL_STOP";

View File

@ -30,7 +30,6 @@ import java.nio.channels.SocketChannel;
import java.util.Random; import java.util.Random;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import com.farsunset.cim.sdk.client.coder.CIMLogger; import com.farsunset.cim.sdk.client.coder.CIMLogger;
import com.farsunset.cim.sdk.client.coder.ClientMessageDecoder; import com.farsunset.cim.sdk.client.coder.ClientMessageDecoder;
@ -64,18 +63,17 @@ class CIMConnectorManager {
private ByteBuffer readBuffer = ByteBuffer.allocate(READ_BUFFER_SIZE); private ByteBuffer readBuffer = ByteBuffer.allocate(READ_BUFFER_SIZE);
private ExecutorService workerExecutor = Executors.newFixedThreadPool(1, r -> new Thread(r,"worker-")); private final ExecutorService workerExecutor = Executors.newFixedThreadPool(1, r -> new Thread(r,"worker-"));
private ExecutorService bossExecutor = Executors.newFixedThreadPool(1, r -> new Thread(r,"boss-")); private final ExecutorService bossExecutor = Executors.newFixedThreadPool(1, r -> new Thread(r,"boss-"));
private ExecutorService eventExecutor = Executors.newFixedThreadPool(1, r -> new Thread(r,"event-")); private final ExecutorService eventExecutor = Executors.newFixedThreadPool(1, r -> new Thread(r,"event-"));
private Semaphore semaphore = new Semaphore(1, true);
private final ClientMessageEncoder messageEncoder = new ClientMessageEncoder();
private ClientMessageEncoder messageEncoder = new ClientMessageEncoder(); private final ClientMessageDecoder messageDecoder = new ClientMessageDecoder();
private ClientMessageDecoder messageDecoder = new ClientMessageDecoder();
private final ByteBuffer headerBuffer = ByteBuffer.allocate(CIMConstant.DATA_HEADER_LENGTH);
public synchronized static CIMConnectorManager getManager() { public synchronized static CIMConnectorManager getManager() {
if (manager == null) { if (manager == null) {
@ -105,8 +103,6 @@ class CIMConnectorManager {
try { try {
semaphore.acquire();
socketChannel = SocketChannel.open(); socketChannel = SocketChannel.open();
socketChannel.configureBlocking(true); socketChannel.configureBlocking(true);
socketChannel.socket().setTcpNoDelay(true); socketChannel.socket().setTcpNoDelay(true);
@ -116,40 +112,30 @@ class CIMConnectorManager {
socketChannel.socket().connect(new InetSocketAddress(host, port),CONNECT_TIME_OUT); socketChannel.socket().connect(new InetSocketAddress(host, port),CONNECT_TIME_OUT);
semaphore.release(); handleConnectedEvent();
handelConnectedEvent();
int result = -1; /*
*开始读取来自服务端的消息先读取3个字节的消息头
while((result = socketChannel.read(readBuffer)) > 0) { */
while (socketChannel.read(headerBuffer) > 0) {
if(readBuffer.position() == readBuffer.capacity()) { handleSocketReadEvent();
extendByteBuffer();
}
handelSocketReadEvent(result);
} }
handelSocketReadEvent(result); /*
*read 返回 <= 0的情况发生了意外需要断开重链
*/
closeSession();
}catch(ConnectException | SocketTimeoutException ignore){ }catch(ConnectException | SocketTimeoutException ignore){
semaphore.release();
handleConnectAbortedEvent(); handleConnectAbortedEvent();
} catch(IOException ignore) { } catch(IOException ignore) {
semaphore.release(); handleDisconnectedEvent();
handelDisconnectedEvent();
}catch (InterruptedException ignore) {
semaphore.release();
} }
}); });
} }
private void handleDisconnectedEvent() {
private void handelDisconnectedEvent() {
closeSession(); closeSession();
} }
@ -166,51 +152,27 @@ class CIMConnectorManager {
} }
private void handelConnectedEvent() { private void handleConnectedEvent() {
sessionCreated(); sessionCreated();
} }
private void handelSocketReadEvent(int result) { private void handleSocketReadEvent() throws IOException {
if(result == -1) { Object message = messageDecoder.doDecode(headerBuffer,socketChannel);
closeSession();
return; LOGGER.messageReceived(socketChannel, message);
}
if (isHeartbeatRequest(message)) {
send(HeartbeatResponse.getInstance());
readBuffer.position(0);
Object message = messageDecoder.doDecode(readBuffer);
if(message == null) {
return; return;
} }
LOGGER.messageReceived(socketChannel,message);
if(isHeartbeatRequest(message)) {
send(getHeartbeatResponse());
return;
}
this.messageReceived(message); this.messageReceived(message);
} }
private void extendByteBuffer() {
ByteBuffer newBuffer = ByteBuffer.allocate(readBuffer.capacity() + READ_BUFFER_SIZE / 2);
readBuffer.position(0);
newBuffer.put(readBuffer);
readBuffer.clear();
readBuffer = newBuffer;
}
public void send(final Protobufable body) { public void send(final Protobufable body) {
if(!isConnected()) { if(!isConnected()) {
@ -221,8 +183,6 @@ class CIMConnectorManager {
int result = 0; int result = 0;
try { try {
semaphore.acquire();
ByteBuffer buffer = messageEncoder.encode(body); ByteBuffer buffer = messageEncoder.encode(body);
while(buffer.hasRemaining()){ while(buffer.hasRemaining()){
result += socketChannel.write(buffer); result += socketChannel.write(buffer);
@ -232,8 +192,6 @@ class CIMConnectorManager {
result = -1; result = -1;
}finally { }finally {
semaphore.release();
if(result <= 0) { if(result <= 0) {
closeSession(); closeSession();
}else { }else {
@ -302,10 +260,6 @@ class CIMConnectorManager {
} }
} }
public HeartbeatResponse getHeartbeatResponse() {
return HeartbeatResponse.getInstance();
}
public boolean isHeartbeatRequest(Object data) { public boolean isHeartbeatRequest(Object data) {
return data instanceof HeartbeatRequest; return data instanceof HeartbeatRequest;
} }

View File

@ -38,7 +38,7 @@ public class CIMEventBroadcastReceiver {
private static CIMEventBroadcastReceiver receiver; private static CIMEventBroadcastReceiver receiver;
private CIMEventListener listener; private CIMEventListener listener;
private ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1, r -> { private final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1, r -> {
Thread thread = new Thread(r); Thread thread = new Thread(r);
thread.setName("cim-reconnect-"); thread.setName("cim-reconnect-");
return thread; return thread;

View File

@ -36,8 +36,8 @@ import com.farsunset.cim.sdk.client.model.ReplyBody;
*/ */
public class CIMListenerManager { public class CIMListenerManager {
private static ArrayList<CIMEventListener> cimListeners = new ArrayList<CIMEventListener>(); private static final ArrayList<CIMEventListener> cimListeners = new ArrayList<CIMEventListener>();
private static CIMMessageReceiveComparator comparator = new CIMMessageReceiveComparator(); private static final CIMMessageReceiveComparator comparator = new CIMMessageReceiveComparator();
private static final Logger LOGGER = LoggerFactory.getLogger(CIMListenerManager.class); private static final Logger LOGGER = LoggerFactory.getLogger(CIMListenerManager.class);
public static void registerMessageListener(CIMEventListener listener) { public static void registerMessageListener(CIMEventListener listener) {
@ -56,9 +56,9 @@ public class CIMListenerManager {
} }
} }
public static void notifyOnConnectionSuccessed(boolean antoBind) { public static void notifyOnConnectFinished(boolean autoBind) {
for (CIMEventListener listener : cimListeners) { for (CIMEventListener listener : cimListeners) {
listener.onConnectFinished(antoBind); listener.onConnectFinished(autoBind);
} }
} }
@ -80,13 +80,13 @@ public class CIMListenerManager {
} }
} }
public static void notifyOnConnectionFailed() { public static void notifyOnConnectFailed() {
for (CIMEventListener listener : cimListeners) { for (CIMEventListener listener : cimListeners) {
listener.onConnectFailed(); listener.onConnectFailed();
} }
} }
public static void destory() { public static void destroy() {
cimListeners.clear(); cimListeners.clear();
} }

View File

@ -87,10 +87,10 @@ public class CIMPushManager {
protected static void connect() { protected static void connect() {
boolean isManualStop = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_MANUAL_STOP); boolean isManualStopped = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_MANUAL_STOP);
boolean isManualDestroy = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED); boolean isManualDestroyed = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED);
if (isManualStop || isManualDestroy) { if (isManualStopped || isManualDestroyed) {
return; return;
} }
@ -124,8 +124,8 @@ public class CIMPushManager {
*/ */
public static void bindAccount(String account) { public static void bindAccount(String account) {
boolean isManualDestroy = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED); boolean isManualDestroyed = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED);
if (isManualDestroy || account == null || account.trim().length() == 0) { if (isManualDestroyed || account == null || account.trim().length() == 0) {
return; return;
} }
sendBindRequest(account); sendBindRequest(account);
@ -136,9 +136,9 @@ public class CIMPushManager {
String account = getAccount(); String account = getAccount();
boolean isManualDestory = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED); boolean isManualDestroyed = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED);
boolean isManualStoped = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_MANUAL_STOP); boolean isManualStopped = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_MANUAL_STOP);
if (isManualStoped || account == null || account.trim().length() == 0 || isManualDestory) { if (isManualStopped || account == null || account.trim().length() == 0 || isManualDestroyed) {
return false; return false;
} }
@ -154,10 +154,10 @@ public class CIMPushManager {
*/ */
public static void sendRequest(SentBody body) { public static void sendRequest(SentBody body) {
boolean isManualStop = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_MANUAL_STOP); boolean isManualStopped = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_MANUAL_STOP);
boolean isManualDestroy = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED); boolean isManualDestroyed = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED);
if (isManualStop || isManualDestroy) { if (isManualStopped || isManualDestroyed) {
return; return;
} }
@ -174,8 +174,8 @@ public class CIMPushManager {
*/ */
public static void stop() { public static void stop() {
boolean isManualDestroy = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED); boolean isManualDestroyed = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED);
if (isManualDestroy) { if (isManualDestroyed) {
return; return;
} }
@ -199,12 +199,12 @@ public class CIMPushManager {
} }
/** /**
* 重新恢复接收推送重新连接服务端并登录当前账号如果aotuBind == true * 重新恢复接收推送重新连接服务端并登录当前账号如果autoBind == true
*/ */
public static void resume() { public static void resume() {
boolean isManualDestroy = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED); boolean isManualDestroyed = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED);
if (isManualDestroy) { if (isManualDestroyed) {
return; return;
} }
@ -216,13 +216,13 @@ public class CIMPushManager {
} }
public static int getState() { public static int getState() {
boolean isManualDestroy = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED); boolean isManualDestroyed = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED);
if (isManualDestroy) { if (isManualDestroyed) {
return STATE_DESTROYED; return STATE_DESTROYED;
} }
boolean isManualStop = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_MANUAL_STOP); boolean isManualStopped = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_MANUAL_STOP);
if (isManualStop) { if (isManualStopped) {
return STATE_STOPPED; return STATE_STOPPED;
} }

View File

@ -29,7 +29,7 @@ import com.farsunset.cim.sdk.client.model.SentBody;
*/ */
public class CIMPushService { public class CIMPushService {
private CIMConnectorManager manager; private final CIMConnectorManager manager;
private static CIMPushService service; private static CIMPushService service;

View File

@ -42,7 +42,7 @@ public class CIMLogger {
} }
private static class LoggerHolder{ private static class LoggerHolder{
private static CIMLogger logger = new CIMLogger(); private static final CIMLogger logger = new CIMLogger();
} }

View File

@ -22,7 +22,9 @@
package com.farsunset.cim.sdk.client.coder; package com.farsunset.cim.sdk.client.coder;
import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import com.farsunset.cim.sdk.client.constant.CIMConstant; import com.farsunset.cim.sdk.client.constant.CIMConstant;
import com.farsunset.cim.sdk.client.model.HeartbeatRequest; import com.farsunset.cim.sdk.client.model.HeartbeatRequest;
@ -30,60 +32,56 @@ import com.farsunset.cim.sdk.client.model.Message;
import com.farsunset.cim.sdk.client.model.ReplyBody; import com.farsunset.cim.sdk.client.model.ReplyBody;
import com.farsunset.cim.sdk.model.proto.MessageProto; import com.farsunset.cim.sdk.model.proto.MessageProto;
import com.farsunset.cim.sdk.model.proto.ReplyBodyProto; import com.farsunset.cim.sdk.model.proto.ReplyBodyProto;
import com.google.protobuf.InvalidProtocolBufferException;
/** /**
* 客户端消息解码 * 客户端消息解码
*/ */
public class ClientMessageDecoder { public class ClientMessageDecoder {
/**
*
* @param headerBuffer 读取到的消息头
* @param socketChannel
* @return
*/
public Object doDecode(ByteBuffer headerBuffer , SocketChannel socketChannel) throws IOException {
headerBuffer.position(0);
byte type = headerBuffer.get();
byte lv = headerBuffer.get();
byte hv = headerBuffer.get();
headerBuffer.clear();
public Object doDecode(ByteBuffer iobuffer) {
/* /*
* 消息头3位 * 先通过消息头拿到消息的长度然后进行定长读取
* 解决消息的断包和粘包情况
*/ */
if (iobuffer.remaining() < CIMConstant.DATA_HEADER_LENGTH) {
return null;
}
iobuffer.mark(); int dataLength = getContentLength(lv, hv);
byte type = iobuffer.get(); ByteBuffer bodyBuffer = ByteBuffer.allocate(dataLength);
byte lv = iobuffer.get(); /*
byte hv = iobuffer.get(); * 如果读取的消息长度不够则进行等待后续消息到来
* 当读取的消息长度(bodyBuffer.position() == dataLength)时意味着一个完整的消息已经接收完成
*/
do {
socketChannel.read(bodyBuffer);
} while (bodyBuffer.position() != dataLength);
int length = getContentLength(lv, hv);
// 如果消息体没有接收完整则重置读取等待下一次重新读取
if (length > iobuffer.remaining()) {
iobuffer.reset();
return null;
}
byte[] dataBytes = new byte[length];
iobuffer.get(dataBytes, 0, length);
iobuffer.position(0);
try {
return mappingMessageObject(dataBytes, type);
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
return null;
}
}
private Object mappingMessageObject(byte[] bytes, byte type) throws InvalidProtocolBufferException {
/*
消息读取完成后通过type来解析成对应的消息体
*/
if (CIMConstant.ProtobufType.S_H_RQ == type) { if (CIMConstant.ProtobufType.S_H_RQ == type) {
return HeartbeatRequest.getInstance(); return HeartbeatRequest.getInstance();
} }
if (CIMConstant.ProtobufType.REPLY_BODY == type) { if (CIMConstant.ProtobufType.REPLY_BODY == type) {
ReplyBodyProto.Model bodyProto = ReplyBodyProto.Model.parseFrom(bytes); ReplyBodyProto.Model bodyProto = ReplyBodyProto.Model.parseFrom(bodyBuffer.array());
ReplyBody body = new ReplyBody(); ReplyBody body = new ReplyBody();
body.setKey(bodyProto.getKey()); body.setKey(bodyProto.getKey());
body.setTimestamp(bodyProto.getTimestamp()); body.setTimestamp(bodyProto.getTimestamp());
@ -93,29 +91,22 @@ public class ClientMessageDecoder {
return body; return body;
} }
if (CIMConstant.ProtobufType.MESSAGE == type) { MessageProto.Model bodyProto = MessageProto.Model.parseFrom(bodyBuffer.array());
MessageProto.Model bodyProto = MessageProto.Model.parseFrom(bytes); Message message = new Message();
Message message = new Message(); message.setId(bodyProto.getId());
message.setId(bodyProto.getId()); message.setAction(bodyProto.getAction());
message.setAction(bodyProto.getAction()); message.setContent(bodyProto.getContent());
message.setContent(bodyProto.getContent()); message.setSender(bodyProto.getSender());
message.setSender(bodyProto.getSender()); message.setReceiver(bodyProto.getReceiver());
message.setReceiver(bodyProto.getReceiver()); message.setTitle(bodyProto.getTitle());
message.setTitle(bodyProto.getTitle()); message.setExtra(bodyProto.getExtra());
message.setExtra(bodyProto.getExtra()); message.setTimestamp(bodyProto.getTimestamp());
message.setTimestamp(bodyProto.getTimestamp()); message.setFormat(bodyProto.getFormat());
message.setFormat(bodyProto.getFormat()); return message;
return message;
}
return null;
} }
/** /**
* 解析消息体长度 * 解析消息体长度
*
* @return
*/ */
private int getContentLength(byte lv, byte hv) { private int getContentLength(byte lv, byte hv) {
int l = (lv & 0xff); int l = (lv & 0xff);
@ -123,4 +114,5 @@ public class ClientMessageDecoder {
return (l | h << 8); return (l | h << 8);
} }
} }

View File

@ -32,15 +32,14 @@ import com.farsunset.cim.sdk.client.model.Protobufable;
*/ */
public class ClientMessageEncoder { public class ClientMessageEncoder {
public ByteBuffer encode(Object object) { public ByteBuffer encode(Protobufable body) {
Protobufable data = (Protobufable) object; byte[] data = body.getByteArray();
byte[] byteArray = data.getByteArray();
ByteBuffer ioBuffer = ByteBuffer.allocate(byteArray.length + CIMConstant.DATA_HEADER_LENGTH); ByteBuffer ioBuffer = ByteBuffer.allocate(data.length + CIMConstant.DATA_HEADER_LENGTH);
ioBuffer.put(createHeader(data.getType(), byteArray.length)); ioBuffer.put(createHeader(body.getType(), data.length));
ioBuffer.put(byteArray); ioBuffer.put(data);
ioBuffer.flip(); ioBuffer.flip();
return ioBuffer; return ioBuffer;

View File

@ -27,26 +27,11 @@ package com.farsunset.cim.sdk.client.constant;
public interface CIMConstant { public interface CIMConstant {
long RECONNECT_INTERVAL_TIME = 30 * 1000; long RECONNECT_INTERVAL_TIME = 30 * 1000;
/** /*
* 消息头长度为3个字节第一个字节为消息类型第二第三字节 转换int后为消息长度 * 消息头长度为3个字节第一个字节为消息类型第二第三字节 转换int后为消息长度
*/ */
int DATA_HEADER_LENGTH = 3; int DATA_HEADER_LENGTH = 3;
interface ReturnCode {
String CODE_404 = "404";
String CODE_403 = "403";
String CODE_405 = "405";
String CODE_200 = "200";
String CODE_206 = "206";
String CODE_500 = "500";
}
interface ConfigKey { interface ConfigKey {

View File

@ -35,7 +35,7 @@ public class HeartbeatRequest implements Serializable, Protobufable {
private static final String TAG = "SERVER_HEARTBEAT_REQUEST"; private static final String TAG = "SERVER_HEARTBEAT_REQUEST";
private static final String CMD_HEARTBEAT_RESPONSE = "SR"; private static final String CMD_HEARTBEAT_RESPONSE = "SR";
private static HeartbeatRequest object = new HeartbeatRequest(); private static final HeartbeatRequest object = new HeartbeatRequest();
private HeartbeatRequest() { private HeartbeatRequest() {

View File

@ -34,7 +34,7 @@ public class HeartbeatResponse implements Serializable, Protobufable {
private static final String TAG = "CLIENT_HEARTBEAT_RESPONSE"; private static final String TAG = "CLIENT_HEARTBEAT_RESPONSE";
private static final String CMD_HEARTBEAT_RESPONSE = "CR"; private static final String CMD_HEARTBEAT_RESPONSE = "CR";
private static HeartbeatResponse object = new HeartbeatResponse(); private static final HeartbeatResponse object = new HeartbeatResponse();
private HeartbeatResponse() { private HeartbeatResponse() {

View File

@ -34,7 +34,7 @@ public class Intent implements Serializable {
private String action; private String action;
private HashMap<String, Object> data = new HashMap<String, Object>(); private final HashMap<String, Object> data = new HashMap<String, Object>();
public Intent() { public Intent() {
} }

View File

@ -1,13 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings"> <component name="GradleSettings">
<option name="linkedExternalProjectsSettings"> <option name="linkedExternalProjectsSettings">
<GradleProjectSettings> <GradleProjectSettings>
<compositeConfiguration> <option name="testRunner" value="PLATFORM" />
<compositeBuild compositeDefinitionSource="SCRIPT" />
</compositeConfiguration>
<option name="distributionType" value="DEFAULT_WRAPPED" /> <option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" /> <option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings> </GradleProjectSettings>
</option> </option>

View File

@ -4,6 +4,8 @@
<modules> <modules>
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" /> <module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
<module fileurl="file://$PROJECT_DIR$/cim-client-android.iml" filepath="$PROJECT_DIR$/cim-client-android.iml" /> <module fileurl="file://$PROJECT_DIR$/cim-client-android.iml" filepath="$PROJECT_DIR$/cim-client-android.iml" />
<module fileurl="file://$PROJECT_DIR$/app/cim-client-android-app.iml" filepath="$PROJECT_DIR$/app/cim-client-android-app.iml" group="cim-client-android/app" />
<module fileurl="file://$PROJECT_DIR$/cim-use-examples-cim-client-android.iml" filepath="$PROJECT_DIR$/cim-use-examples-cim-client-android.iml" group="cim-client-android" />
</modules> </modules>
</component> </component>
</project> </project>

View File

@ -65,11 +65,6 @@ public class SystemMsgListViewAdapter extends BaseAdapter {
return 0; return 0;
} }
@Override
public void notifyDataSetChanged() {
//Collections.sort(list, new MessageTimeDescComparator());
}
@SuppressLint("ViewHolder") @SuppressLint("ViewHolder")
@Override @Override
public View getView(int position, View chatItemView, ViewGroup parent) { public View getView(int position, View chatItemView, ViewGroup parent) {

View File

@ -24,7 +24,7 @@ package com.farsunset.ichat.example.app;
public interface Constant { public interface Constant {
//服务端IP地址 //服务端IP地址
String CIM_SERVER_HOST = "192.168.1.88"; String CIM_SERVER_HOST = "192.168.2.103";
//注意这里的端口不是tomcat的端口没改动就使用默认的23456 //注意这里的端口不是tomcat的端口没改动就使用默认的23456
int CIM_SERVER_PORT = 23456; int CIM_SERVER_PORT = 23456;

View File

@ -88,7 +88,7 @@ public class LoginActivity extends CIMMonitorActivity implements OnClickListener
/* /*
* 收到code为200的回应 账号绑定成功 * 收到code为200的回应 账号绑定成功
*/ */
if (reply.getKey().equals(CIMConstant.RequestKey.CLIENT_BIND) && reply.getCode().equals(CIMConstant.ReturnCode.CODE_200)) { if (reply.getKey().equals(CIMConstant.RequestKey.CLIENT_BIND) && reply.getCode().equals("200")) {
Intent intent = new Intent(this, SystemMessageActivity.class); Intent intent = new Intent(this, SystemMessageActivity.class);
intent.putExtra("account", accountEdit.getText().toString().trim()); intent.putExtra("account", accountEdit.getText().toString().trim());
startActivity(intent); startActivity(intent);

View File

@ -87,9 +87,14 @@ public class SystemMessageActivity extends CIMMonitorActivity implements OnClick
startActivity(intent); startActivity(intent);
this.finish(); this.finish();
} else { } else {
list.add(message); list.add(0,message);
adapter.notifyDataSetChanged(); adapter.notifyDataSetChanged();
chatListView.setSelection(chatListView.getTop()); chatListView.postDelayed(new Runnable() {
@Override
public void run() {
chatListView.setSelection(0);
}
},500);
} }
} }

View File

@ -6,7 +6,7 @@ buildscript {
google() google()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.5.3' classpath 'com.android.tools.build:gradle:3.6.1'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files

View File

@ -1,7 +1,6 @@
#Mon May 13 14:19:31 CST 2019 #Fri Mar 06 13:42:48 CST 2020
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: com.google.protobuf:protobuf-java:3.11.1" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
<orderEntry type="module-library">
<library name="Maven: com.farsunset:cim-java-sdk:3.8.0">
<CLASSES>
<root url="jar://$MODULE_DIR$/libs/cim-java-sdk-3.8.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
</component>
</module>