carlos 2 жил өмнө
parent
commit
cb074ad666
100 өөрчлөгдсөн 4505 нэмэгдсэн , 0 устгасан
  1. 30 0
      .gitignore
  2. 3 0
      .idea/.gitignore
  3. 1 0
      .idea/.name
  4. 11 0
      .idea/aws.xml
  5. 132 0
      .idea/codeStyles/Project.xml
  6. 5 0
      .idea/codeStyles/codeStyleConfig.xml
  7. 6 0
      .idea/compiler.xml
  8. 17 0
      .idea/deploymentTargetDropDown.xml
  9. 6 0
      .idea/encodings.xml
  10. 23 0
      .idea/gradle.xml
  11. 25 0
      .idea/jarRepositories.xml
  12. 79 0
      .idea/misc.xml
  13. 6 0
      .idea/statistic.xml
  14. 6 0
      .idea/vcs.xml
  15. BIN
      XPCommonService.apk
  16. 1 0
      app/.gitignore
  17. 127 0
      app/build.gradle
  18. BIN
      app/libs/udid-1.2.jar
  19. BIN
      app/libs/xplibcommon.aar
  20. BIN
      app/libs/xplora_sdk_out.jar
  21. 21 0
      app/proguard-rules.pro
  22. 24 0
      app/src/androidTest/java/com/xplora/commonservice/ExampleInstrumentedTest.kt
  23. 186 0
      app/src/main/AndroidManifest.xml
  24. 20 0
      app/src/main/assets/aws_iot/cacert.crt
  25. 20 0
      app/src/main/assets/aws_iot/client.cert.pem
  26. 27 0
      app/src/main/assets/aws_iot/client.private.key
  27. BIN
      app/src/main/assets/aws_iot/p12.keystore
  28. 20 0
      app/src/main/assets/aws_iot_test/cacert.development.crt
  29. 20 0
      app/src/main/assets/aws_iot_test/client.cert.development.pem
  30. 27 0
      app/src/main/assets/aws_iot_test/client.private.development.key
  31. 82 0
      app/src/main/assets/config.xml
  32. BIN
      app/src/main/assets/my.keystore
  33. BIN
      app/src/main/assets/test.keystore
  34. 93 0
      app/src/main/java/com/xplora/commonservice/BaseApplication.kt
  35. 63 0
      app/src/main/java/com/xplora/commonservice/MainActivity.kt
  36. 321 0
      app/src/main/java/com/xplora/commonservice/model/Const.kt
  37. 53 0
      app/src/main/java/com/xplora/commonservice/model/MqttMessage.kt
  38. 17 0
      app/src/main/java/com/xplora/commonservice/model/NotificationDetails.kt
  39. 39 0
      app/src/main/java/com/xplora/commonservice/model/database/AlarmDbEntity.kt
  40. 3 0
      app/src/main/java/com/xplora/commonservice/model/database/BaseDbEntity.kt
  41. 130 0
      app/src/main/java/com/xplora/commonservice/model/database/ChatDbDbEntity.kt
  42. 67 0
      app/src/main/java/com/xplora/commonservice/model/database/ContactDbDbEntity.kt
  43. 37 0
      app/src/main/java/com/xplora/commonservice/model/database/GoplayContentDbDbEntity.kt
  44. 13 0
      app/src/main/java/com/xplora/commonservice/model/database/MusicDbDbEntity.kt
  45. 38 0
      app/src/main/java/com/xplora/commonservice/model/database/SilentDbDbEntity.kt
  46. 16 0
      app/src/main/java/com/xplora/commonservice/model/database/StepDbDbEntity.kt
  47. 31 0
      app/src/main/java/com/xplora/commonservice/model/database/WeatherDbDbEntity.kt
  48. 20 0
      app/src/main/java/com/xplora/commonservice/model/http/AlarmList.kt
  49. 3 0
      app/src/main/java/com/xplora/commonservice/model/http/BaseRequest.kt
  50. 30 0
      app/src/main/java/com/xplora/commonservice/model/http/BaseResponse.kt
  51. 26 0
      app/src/main/java/com/xplora/commonservice/model/http/ContactList.kt
  52. 17 0
      app/src/main/java/com/xplora/commonservice/model/http/EmojiList.kt
  53. 8 0
      app/src/main/java/com/xplora/commonservice/model/http/GetApnData.kt
  54. 29 0
      app/src/main/java/com/xplora/commonservice/model/http/Init.kt
  55. 20 0
      app/src/main/java/com/xplora/commonservice/model/http/ReportPushResult.kt
  56. 18 0
      app/src/main/java/com/xplora/commonservice/model/http/SilentList.kt
  57. 8 0
      app/src/main/java/com/xplora/commonservice/model/http/UploadBatteryPercent.kt
  58. 13 0
      app/src/main/java/com/xplora/commonservice/model/http/UploadCallLog.kt
  59. 15 0
      app/src/main/java/com/xplora/commonservice/model/http/WatchModelInfo.kt
  60. 8 0
      app/src/main/java/com/xplora/commonservice/model/http/chat/DeleteMsg.kt
  61. 31 0
      app/src/main/java/com/xplora/commonservice/model/http/chat/GetMsg.kt
  62. 23 0
      app/src/main/java/com/xplora/commonservice/model/http/chat/GetMsgList.kt
  63. 20 0
      app/src/main/java/com/xplora/commonservice/model/http/chat/GetMsgState.kt
  64. 23 0
      app/src/main/java/com/xplora/commonservice/model/http/chat/GetUnreadCount.kt
  65. 37 0
      app/src/main/java/com/xplora/commonservice/model/http/chat/SendMsg.kt
  66. 12 0
      app/src/main/java/com/xplora/commonservice/model/http/chat/SetMsgRead.kt
  67. 26 0
      app/src/main/java/com/xplora/commonservice/model/http/content/GetContent.kt
  68. 9 0
      app/src/main/java/com/xplora/commonservice/model/http/content/ReportContentDownloadStatus.kt
  69. 8 0
      app/src/main/java/com/xplora/commonservice/model/http/etc/FirmwareVersion.kt
  70. 25 0
      app/src/main/java/com/xplora/commonservice/model/http/etc/Guardians.kt
  71. 15 0
      app/src/main/java/com/xplora/commonservice/model/http/etc/SOS.kt
  72. 8 0
      app/src/main/java/com/xplora/commonservice/model/http/etc/XCoin.kt
  73. 8 0
      app/src/main/java/com/xplora/commonservice/model/http/file/Upload.kt
  74. 8 0
      app/src/main/java/com/xplora/commonservice/model/http/friend/Add.kt
  75. 8 0
      app/src/main/java/com/xplora/commonservice/model/http/friend/Code.kt
  76. 39 0
      app/src/main/java/com/xplora/commonservice/model/http/location/Cell.kt
  77. 22 0
      app/src/main/java/com/xplora/commonservice/model/http/location/Gps.kt
  78. 35 0
      app/src/main/java/com/xplora/commonservice/model/http/location/Multi.kt
  79. 8 0
      app/src/main/java/com/xplora/commonservice/model/http/location/TrackingStatus.kt
  80. 25 0
      app/src/main/java/com/xplora/commonservice/model/http/power/SavingMode.kt
  81. 13 0
      app/src/main/java/com/xplora/commonservice/model/http/power/Set.kt
  82. 14 0
      app/src/main/java/com/xplora/commonservice/model/http/status/Get.kt
  83. 13 0
      app/src/main/java/com/xplora/commonservice/model/http/status/Info.kt
  84. 13 0
      app/src/main/java/com/xplora/commonservice/model/http/status/Set.kt
  85. 10 0
      app/src/main/java/com/xplora/commonservice/model/http/step/Count.kt
  86. 13 0
      app/src/main/java/com/xplora/commonservice/model/http/step/count/History.kt
  87. 8 0
      app/src/main/java/com/xplora/commonservice/model/http/step/count/Total.kt
  88. 24 0
      app/src/main/java/com/xplora/commonservice/model/http/weather/GetCityId.kt
  89. 71 0
      app/src/main/java/com/xplora/commonservice/model/http/weather/WeatherInfo.kt
  90. 15 0
      app/src/main/java/com/xplora/commonservice/model/infobean/AwsIotInfo.kt
  91. 7 0
      app/src/main/java/com/xplora/commonservice/model/infobean/PositionInfoBean.kt
  92. 37 0
      app/src/main/java/com/xplora/commonservice/model/infobean/ServerInfo.kt
  93. 30 0
      app/src/main/java/com/xplora/commonservice/model/vm/OtaCheckResultViewModel.kt
  94. 54 0
      app/src/main/java/com/xplora/commonservice/model/vm/ShareViewModel.kt
  95. 500 0
      app/src/main/java/com/xplora/commonservice/modules/ChatMsgManager.kt
  96. 93 0
      app/src/main/java/com/xplora/commonservice/modules/ForbiddenManager.kt
  97. 521 0
      app/src/main/java/com/xplora/commonservice/modules/NotificationManager.kt
  98. 275 0
      app/src/main/java/com/xplora/commonservice/modules/OtaManager.kt
  99. 300 0
      app/src/main/java/com/xplora/commonservice/modules/SosManager.kt
  100. 43 0
      app/src/main/java/com/xplora/commonservice/modules/SoundManager.kt

+ 30 - 0
.gitignore

@@ -0,0 +1,30 @@
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx

+ 3 - 0
.idea/.gitignore

@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml

+ 1 - 0
.idea/.name

@@ -0,0 +1 @@
+XPWatchCommonService

+ 11 - 0
.idea/aws.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="accountSettings">
+    <option name="activeRegion" value="us-east-1" />
+    <option name="recentlyUsedRegions">
+      <list>
+        <option value="us-east-1" />
+      </list>
+    </option>
+  </component>
+</project>

+ 132 - 0
.idea/codeStyles/Project.xml

@@ -0,0 +1,132 @@
+<component name="ProjectCodeStyleConfiguration">
+  <code_scheme name="Project" version="173">
+    <JetCodeStyleSettings>
+      <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
+    </JetCodeStyleSettings>
+    <codeStyleSettings language="JAVA">
+      <option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" />
+      <option name="BLOCK_COMMENT_AT_FIRST_COLUMN" value="false" />
+      <option name="LINE_COMMENT_ADD_SPACE" value="true" />
+    </codeStyleSettings>
+    <codeStyleSettings language="XML">
+      <option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" />
+      <option name="BLOCK_COMMENT_AT_FIRST_COLUMN" value="false" />
+      <indentOptions>
+        <option name="CONTINUATION_INDENT_SIZE" value="4" />
+      </indentOptions>
+      <arrangement>
+        <rules>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>xmlns:android</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>xmlns:.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*:id</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*:name</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>name</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>style</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>ANDROID_ATTRIBUTE_ORDER</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>.*</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+        </rules>
+      </arrangement>
+    </codeStyleSettings>
+    <codeStyleSettings language="kotlin">
+      <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
+      <option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" />
+      <option name="BLOCK_COMMENT_AT_FIRST_COLUMN" value="false" />
+      <option name="LINE_COMMENT_ADD_SPACE" value="true" />
+    </codeStyleSettings>
+  </code_scheme>
+</component>

+ 5 - 0
.idea/codeStyles/codeStyleConfig.xml

@@ -0,0 +1,5 @@
+<component name="ProjectCodeStyleConfiguration">
+  <state>
+    <option name="USE_PER_PROJECT_SETTINGS" value="true" />
+  </state>
+</component>

+ 6 - 0
.idea/compiler.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="CompilerConfiguration">
+    <bytecodeTargetLevel target="11" />
+  </component>
+</project>

+ 17 - 0
.idea/deploymentTargetDropDown.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="deploymentTargetDropDown">
+    <targetSelectedWithDropDown>
+      <Target>
+        <type value="QUICK_BOOT_TARGET" />
+        <deviceKey>
+          <Key>
+            <type value="VIRTUAL_DEVICE_PATH" />
+            <value value="C:\Users\40877\.android\avd\Xlpora_X6_API_27.avd" />
+          </Key>
+        </deviceKey>
+      </Target>
+    </targetSelectedWithDropDown>
+    <timeTargetWasSelectedWithDropDown value="2022-08-18T12:45:31.014593500Z" />
+  </component>
+</project>

+ 6 - 0
.idea/encodings.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Encoding">
+    <file url="PROJECT" charset="UTF-8" />
+  </component>
+</project>

+ 23 - 0
.idea/gradle.xml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="GradleMigrationSettings" migrationVersion="1" />
+  <component name="GradleSettings">
+    <option name="linkedExternalProjectsSettings">
+      <GradleProjectSettings>
+        <option name="testRunner" value="GRADLE" />
+        <option name="distributionType" value="DEFAULT_WRAPPED" />
+        <option name="externalProjectPath" value="$PROJECT_DIR$" />
+        <option name="gradleJvm" value="Android Studio default JDK" />
+        <option name="modules">
+          <set>
+            <option value="$PROJECT_DIR$" />
+            <option value="$PROJECT_DIR$/app" />
+            <option value="$PROJECT_DIR$/libPaho" />
+            <option value="$PROJECT_DIR$/libPahoAndroid" />
+            <option value="$PROJECT_DIR$/libaws-mqtt-android" />
+          </set>
+        </option>
+      </GradleProjectSettings>
+    </option>
+  </component>
+</project>

+ 25 - 0
.idea/jarRepositories.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="RemoteRepositoriesConfiguration">
+    <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>
+    <remote-repository>
+      <option name="id" value="BintrayJCenter" />
+      <option name="name" value="BintrayJCenter" />
+      <option name="url" value="https://jcenter.bintray.com/" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="Google" />
+      <option name="name" value="Google" />
+      <option name="url" value="https://dl.google.com/dl/android/maven2/" />
+    </remote-repository>
+  </component>
+</project>

+ 79 - 0
.idea/misc.xml

@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="CMakeSettings">
+    <configurations>
+      <configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" />
+    </configurations>
+  </component>
+  <component name="DesignSurface">
+    <option name="filePathToZoomLevelMap">
+      <map>
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.2155" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/drawable/bg_gradient.xml" value="0.2305" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/drawable/ic_launcher_background.xml" value="0.2155" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/drawable/rotate_anim.xml" value="0.227" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/drawable/round_background.xml" value="0.2615" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/drawable/round_background2.xml" value="0.2615" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/drawable/shape_new_ringtone.xml" value="0.2965" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/drawable/sos_progress.xml" value="0.339" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/drawable/water_ripple.xml" value="0.226" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/layout/activity_force_update.xml" value="0.6" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/layout/activity_main.xml" value="0.8333333333333334" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/layout/activity_ota_check.xml" value="0.2817028985507246" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/layout/activity_permissions.xml" value="0.1" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/layout/activity_sos.xml" value="0.7333333333333334" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/layout/dialog_ringtone_new.xml" value="0.8333333333333334" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/layout/fragment_checking.xml" value="0.2817028985507246" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/layout/fragment_downloading.xml" value="0.7333333333333334" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/layout/fragment_ota_check_low_battery.xml" value="0.35520833333333335" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/layout/fragment_ota_check_need_wifi.xml" value="0.6666666666666666" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/layout/fragment_ota_check_success.xml" value="0.6666666666666666" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/layout/fragment_ota_ready_to_install.xml" value="0.6666666666666666" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/layout/fragment_ringtone_new.xml" value="0.597667638483965" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/layout/fragment_watchface_new.xml" value="0.5" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/layout/layout_empty_activity.xml" value="0.6" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/layout/layout_notification.xml" value="0.7333333333333334" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/layout/layout_shutdown.xml" value="0.6666666666666666" />
+        <entry key="..\:/40877/Documents/workspace/xp-common-service/app/src/main/res/layout/shape_new_ringtone.xml" value="0.35520833333333335" />
+        <entry key="..\:/Users/40877/.gradle/caches/transforms-3/3637adb04c249d86e40efc1ecc00749e/transformed/jetified-xplibcommon/res/drawable/shape_radius_all.xml" value="0.193" />
+        <entry key="..\:/Users/40877/StudioProjects/xp-common-service/app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.1715" />
+        <entry key="..\:/Users/40877/StudioProjects/xp-common-service/app/src/main/res/drawable/rotate_anim.xml" value="0.3125" />
+        <entry key="..\:/Users/40877/StudioProjects/xp-common-service/app/src/main/res/drawable/round_background.xml" value="0.3125" />
+        <entry key="..\:/Users/40877/StudioProjects/xp-common-service/app/src/main/res/layout-v28/fragment_ota_check_success.xml" value="0.20520833333333333" />
+        <entry key="..\:/Users/40877/StudioProjects/xp-common-service/app/src/main/res/layout/activity_force_update.xml" value="0.3619791666666667" />
+        <entry key="..\:/Users/40877/StudioProjects/xp-common-service/app/src/main/res/layout/activity_ota_check.xml" value="0.26556016597510373" />
+        <entry key="..\:/Users/40877/StudioProjects/xp-common-service/app/src/main/res/layout/activity_ota_install_overnight.xml" value="0.75" />
+        <entry key="..\:/Users/40877/StudioProjects/xp-common-service/app/src/main/res/layout/activity_sos.xml" value="0.1578125" />
+        <entry key="..\:/Users/40877/StudioProjects/xp-common-service/app/src/main/res/layout/fragment_checking.xml" value="0.75" />
+        <entry key="..\:/Users/40877/StudioProjects/xp-common-service/app/src/main/res/layout/fragment_downloading.xml" value="0.9" />
+        <entry key="..\:/Users/40877/StudioProjects/xp-common-service/app/src/main/res/layout/fragment_ota_check_low_battery.xml" value="0.9" />
+        <entry key="..\:/Users/40877/StudioProjects/xp-common-service/app/src/main/res/layout/fragment_ota_check_need_wifi.xml" value="0.67" />
+        <entry key="..\:/Users/40877/StudioProjects/xp-common-service/app/src/main/res/layout/fragment_ota_check_noudate.xml" value="0.9" />
+        <entry key="..\:/Users/40877/StudioProjects/xp-common-service/app/src/main/res/layout/fragment_ota_check_result.xml" value="0.67" />
+        <entry key="..\:/Users/40877/StudioProjects/xp-common-service/app/src/main/res/layout/fragment_ota_check_success.xml" value="0.67" />
+        <entry key="..\:/Users/40877/StudioProjects/xp-common-service/app/src/main/res/layout/fragment_ota_check_up_to_date.xml" value="0.9" />
+        <entry key="..\:/Users/40877/StudioProjects/xp-common-service/app/src/main/res/layout/fragment_ota_force_update_notify.xml" value="0.9" />
+        <entry key="..\:/Users/40877/StudioProjects/xp-common-service/app/src/main/res/layout/fragment_ota_ready_to_install.xml" value="0.9" />
+        <entry key="..\:/Users/40877/StudioProjects/xp-common-service/app/src/main/res/layout/layout_notification.xml" value="0.75" />
+      </map>
+    </option>
+  </component>
+  <component name="EntryPointsManager">
+    <list size="1">
+      <item index="0" class="java.lang.String" itemvalue="retrofit2.http.POST" />
+    </list>
+  </component>
+  <component name="MavenImportPreferences">
+    <option name="generalSettings">
+      <MavenGeneralSettings>
+        <option name="mavenHome" value="Bundled (Maven 3)" />
+      </MavenGeneralSettings>
+    </option>
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
+    <output url="file://$PROJECT_DIR$/build/classes" />
+  </component>
+  <component name="ProjectType">
+    <option name="id" value="Android" />
+  </component>
+</project>

+ 6 - 0
.idea/statistic.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Statistic">
+    <option name="fileTypes" value="class;svn-base;svn-work;Extra;gif;png;jpg;jpeg;bmp;tga;tiff;ear;war;zip;jar;iml;iws;ipr;bz2;gz;pyc;apk;bat;crt;gitignore;gradle;key;keystore;pem;pro;properties;aar;" />
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+  </component>
+</project>

BIN
XPCommonService.apk


+ 1 - 0
app/.gitignore

@@ -0,0 +1 @@
+/build

+ 127 - 0
app/build.gradle

@@ -0,0 +1,127 @@
+plugins {
+    id 'com.android.application'
+    id 'org.jetbrains.kotlin.android'
+}
+
+apply plugin: 'kotlin-kapt'
+
+android {
+    compileSdk 32
+
+    defaultConfig {
+        applicationId "com.xplora.commonservice"
+        minSdk 27
+        targetSdk 32
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+    }
+    buildFeatures {
+        viewBinding = true
+    }
+    buildTypes {
+        debug {
+            minifyEnabled false
+            zipAlignEnabled false
+            shrinkResources false
+            buildConfigField "boolean", "LOGGER_DEBUG", "true"
+        }
+        release {
+            buildConfigField "boolean", "LOGGER_DEBUG", "false"
+            minifyEnabled true
+            zipAlignEnabled true
+            shrinkResources true
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    kapt {
+        arguments {
+            arg("room.schemaLocation", "$projectDir/schemas".toString())
+        }
+    }
+
+    packagingOptions {
+        exclude 'META-INF/DEPENDENCIES'
+    }
+
+    applicationVariants.all { variant ->
+        def outputFileDir = "..\\"
+        variant.outputs.all {
+            def fileName = outputFile.name.replace(outputFile.name, "XPCommonService.apk")
+            outputFileName = fileName
+        }
+        variant.getAssembleProvider().configure() {
+            it..doLast {
+                File out = new File(outputFileDir)
+                copy {
+                    variant.outputs.forEach { output ->
+                        copy {
+                            from output.outputFile
+                            into out
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+//Add this to get SystemProperties
+String SDK_DIR = System.getenv("ANDROID_SDK_HOME")
+
+if (SDK_DIR == null) {
+    Properties properties = new Properties()
+    properties.load(new FileInputStream(project.rootProject.file("local.properties")))
+    SDK_DIR = properties.get('sdk.dir')
+}
+//Add this to get SystemProperties end
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
+    implementation 'com.google.android.material:material:1.4.0'
+    implementation 'androidx.appcompat:appcompat:1.4.2'
+    implementation 'androidx.core:core-ktx:1.7.0'
+    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
+    implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
+    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
+    implementation 'androidx.activity:activity-ktx:1.4.0'
+    implementation 'androidx.fragment:fragment-ktx:1.4.1'
+    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+    implementation project(path: ':libaws-mqtt-android')
+    implementation 'androidx.core:core-ktx:+'
+    implementation 'androidx.core:core-ktx:+'
+    testImplementation 'junit:junit:4.13.2'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+
+    //retrofit
+    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
+    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'
+    implementation 'com.squareup.retrofit2:retrofit-converters:2.5.0'
+    implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.10'
+    implementation 'io.reactivex.rxjava2:rxjava:2.2.21'
+    implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
+    //gson
+    implementation 'com.google.code.gson:gson:2.9.0'
+    //kotlin coroutines
+    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0"
+    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0"
+    //room
+    def roomVersion = "2.4.2"
+    implementation("androidx.room:room-runtime:$roomVersion")
+//    annotationProcessor("androidx.room:room-compiler:$roomVersion")
+    implementation("androidx.room:room-ktx:$roomVersion")
+    // To use Kotlin annotation processing tool (kapt)
+    kapt("androidx.room:room-compiler:$roomVersion")
+
+    // AWS IOT CORE - MQTT
+//    implementation 'com.amazonaws:aws-iot-device-sdk-java:1.3.9'
+//    implementation project(path: ':libawsmqtt')
+//    implementation project(path: ':libaws-mqtt-android')
+
+    //Add this to get SystemProperties
+    compileOnly(files("${SDK_DIR}/platforms/android-24/data/layoutlib.jar"))
+}

BIN
app/libs/udid-1.2.jar


BIN
app/libs/xplibcommon.aar


BIN
app/libs/xplora_sdk_out.jar


+ 21 - 0
app/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 24 - 0
app/src/androidTest/java/com/xplora/commonservice/ExampleInstrumentedTest.kt

@@ -0,0 +1,24 @@
+package com.xplora.commonservice
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+    @Test
+    fun useAppContext() {
+        // Context of the app under test.
+        val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+        assertEquals("com.intellif.commonservice", appContext.packageName)
+    }
+}

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

@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.xplora.commonservice"
+    android:sharedUserId="android.uid.system">
+    <!-- android:sharedUserId="android.uid.system" -->
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+    <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+    <uses-permission android:name="android.permission.READ_CALL_LOG" />
+    <uses-permission android:name="android.permission.CALL_PHONE" />
+    <uses-permission
+        android:name="android.permission.WRITE_EXTERNAL_STORAGE"
+        tools:ignore="ScopedStorage" />
+    <!--suppress DeprecatedClassUsageInspection -->
+    <uses-permission android:name="android.permission.GET_TASKS" /> <!-- system permissions -->
+    <uses-permission
+        android:name="android.permission.WRITE_SETTINGS"
+        tools:ignore="ProtectedPermissions" />
+    <uses-permission
+        android:name="android.permission.REBOOT"
+        tools:ignore="ProtectedPermissions" />
+    <uses-permission
+        android:name="android.permission.PACKAGE_USAGE_STATS"
+        tools:ignore="ProtectedPermissions" />
+    <uses-permission
+        android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
+        tools:ignore="ProtectedPermissions" />
+    <uses-permission
+        android:name="android.permission.READ_LOGS"
+        tools:ignore="ProtectedPermissions" />
+    <uses-permission
+        android:name="android.permission.WRITE_MEDIA_STORAGE"
+        tools:ignore="ProtectedPermissions" />
+    <uses-permission
+        android:name="android.permission.DELETE_CACHE_FILES"
+        tools:ignore="ProtectedPermissions" />
+    <uses-permission
+        android:name="android.permission.ACCESS_CACHE_FILESYSTEM"
+        tools:ignore="ProtectedPermissions" />
+    <uses-permission
+        android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"
+        tools:ignore="ProtectedPermissions" /> <!-- self permissions -->
+    <uses-permission android:name="com.xplora.provider" />
+    <uses-permission android:name="com.xplora.receiver" />
+
+    <permission
+        android:name="com.xplora.receiver"
+        android:protectionLevel="signature" />
+
+    <queries>
+        <package android:name="com.xplora.WatchCommonProvider" />
+        <package android:name="com.xplora.WatchContactProvider" />
+        <package android:name="com.xplora.WatchChatProvider" />
+    </queries>
+
+    <application
+        android:name=".BaseApplication"
+        android:allowBackup="true"
+        android:defaultToDeviceProtectedStorage="true"
+        android:directBootAware="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:persistent="true"
+        android:priority="-1000"
+        android:roundIcon="@mipmap/ic_launcher_round"
+        android:supportsRtl="true"
+        android:theme="@style/Theme.XploraWatch"
+        android:usesCleartextTraffic="true">
+        <activity
+            android:name=".ui.activity.ForceUpdateActivity"
+            android:exported="false"
+            android:launchMode="singleInstance"
+            android:theme="@style/Theme.XploraWatch.DisableSwipeDismiss" />
+        <activity
+            android:name=".ui.activity.SosActivity"
+            android:exported="false"
+            android:launchMode="singleInstance"
+            android:sharedUserId="android.uid.phone"
+            android:theme="@style/Theme.XploraWatch.DisableSwipeDismiss" />
+        <activity
+            android:name=".MainActivity"
+            android:exported="true"
+            android:launchMode="singleInstance">
+
+            <!-- test activity, open this only for debug -->
+            <!--
+                            <intent-filter>
+                            <action android:name="android.intent.action.MAIN" />
+                            <category android:name="android.intent.category.LAUNCHER" />
+                        </intent-filter>
+            -->
+        </activity>
+        <activity android:name=".utils.permission.PermissionActivity" />
+        <activity
+            android:name=".ui.activity.OtaCheckActivity"
+            android:exported="true"
+            android:launchMode="singleInstance">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <!-- <category android:name="android.intent.category.LAUNCHER" /> -->
+            </intent-filter>
+        </activity>
+        <activity android:name=".ui.activity.OtaInstallOverNightActivity" />
+        <activity
+            android:name=".ui.activity.EmptyActivity"
+            android:launchMode="singleInstance"
+            android:theme="@style/Theme.XploraWatch.EmptyAty" />
+
+        <receiver
+            android:name=".modules.callbacks.BootReceiver"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.BOOT_COMPLETED" />
+            </intent-filter>
+        </receiver>
+        <receiver
+            android:name=".modules.callbacks.LocalReceiver"
+            android:exported="true"
+            android:permission="com.xplora.receiver">
+            <intent-filter>
+                <action android:name="com.xplora.action.ADD_FRIEND" />
+                <action android:name="com.xplora.action.POWER_STATUS" />
+                <action android:name="com.xplora.action.SILENT_BEGIN" />
+                <action android:name="com.xplora.action.SILENT_END" />
+                <action android:name="com.xplora.action.REFRESH_CHAT_LIST" />
+                <action android:name="com.xplora.action.RESET_STEP" />
+                <action android:name="com.xplora.action.ALARM" />
+                <action android:name="com.xplora.action.OTA" />
+                <action android:name="com.xplora.action.OTA_CHECK" />
+            </intent-filter>
+        </receiver>
+        <receiver
+            android:name=".modules.callbacks.NotificationReceiver"
+            android:enabled="true"
+            android:exported="true"
+            tools:ignore="ExportedReceiver">
+            <intent-filter>
+                <action android:name="com.xplora.notification.Chat" />
+                <action android:name="com.xplora.notification.Missed_call" />
+                <action android:name="com.xplora.notification.Ringtone" />
+                <action android:name="com.xplora.notification.Watchface" />
+            </intent-filter>
+        </receiver>
+        <receiver
+            android:name=".modules.callbacks.CallStatusReceiver"
+            android:enabled="true"
+            android:exported="true"
+            tools:ignore="ExportedReceiver">
+            <intent-filter>
+                <action android:name="com.xplora.call_log" />
+                <action android:name="com.xplora.call.sos" />
+            </intent-filter>
+        </receiver> <!-- 是否测试模式 -->
+        <meta-data
+            android:name="ISTEST"
+            android:value="0" /> <!-- 是否周末检测 -->
+        <meta-data
+            android:name="ISWEEKCHECK"
+            android:value="0" /> <!-- 设置协议版本 -->
+        <meta-data
+            android:name="RSOTA_PROTOCOL_ID"
+            android:value="A1.0" /> <!-- 设置渠道 -->
+        <meta-data
+            android:name="RSOTA_CHANNEL_ID"
+            android:value="X6" /> <!-- 设置APPID值 ks9uh5u1i2xgttk1teqgfmia   zhangyue1234567890123456 -->
+        <meta-data
+            android:name="RSOTA_APP_ID"
+            android:value="cna4wmsjkifobytoyouzfjbi" />
+    </application>
+
+</manifest>

+ 20 - 0
app/src/main/assets/aws_iot/cacert.crt

@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF
+ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
+b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL
+MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
+b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj
+ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM
+9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw
+IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6
+VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L
+93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm
+jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA
+A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI
+U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs
+N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv
+o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU
+5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy
+rqXRfboQnoZsG4q5WTP468SQvvG5
+-----END CERTIFICATE-----

+ 20 - 0
app/src/main/assets/aws_iot/client.cert.pem

@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgIVANMKFD0dK1Hnov7LdglY57o8VvANMA0GCSqGSIb3DQEB
+CwUAME0xSzBJBgNVBAsMQkFtYXpvbiBXZWIgU2VydmljZXMgTz1BbWF6b24uY29t
+IEluYy4gTD1TZWF0dGxlIFNUPVdhc2hpbmd0b24gQz1VUzAeFw0yMjA1MjUwMjI0
+MTRaFw00OTEyMzEyMzU5NTlaMB4xHDAaBgNVBAMME0FXUyBJb1QgQ2VydGlmaWNh
+dGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwYzVxlmtwhUVHFjbF
+8Ooyj40v8OPi4WWqiIsfNqLmGH6/OMMNszCzsTDUpQSXANbMB/7z0a0nXuDJJeCw
+rjKi4HG/1RZshhCARa5BCxH9UjqFb4yDaUAyzQglLrzBaYSqrzfvSOTie99nNu1Z
+5wCDg7zLBCdI5tQxVCGA7aVNzXEJsqaBGNp3BkGSgCG0DE1ot79BlqEOJlnKzUDf
+MzBhIEdxVADY6VpOv6lVa4iVGR460kKX7f8MYIKauOrWAo738ogBt8VnIw4AosCI
+N5lY5Xj1MPsT7vIMWbN3opYS8LSINTyBXM1nUi7RAQG9cHsNm5nqTm7DjEpP1dB5
+iyUVAgMBAAGjYDBeMB8GA1UdIwQYMBaAFCT8fCdEdWHk1WfWVKxJWZ+2fTDuMB0G
+A1UdDgQWBBST8l7Ujw7sjwyyMpJ4yCEBzKEVvDAMBgNVHRMBAf8EAjAAMA4GA1Ud
+DwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAQEACQLTzJNw9gXaG5fWDrf+zzhd
+0kfLoQU/fZIssRc8mVbFZ6CzCPwnjyJruM9IeBdOcE3dunewzn+qLqDU0zBcV5lc
+cdj3MDLP2BSTVUZKK4UVtfRlgCMDrgjiiRWP/MYGmTXCERRwlKxt6Vvw0Gj2Z+X7
+hbLWO6z2W9nOZN7BVYmW3eg9u6PucxOPX4mkcpj/uYaWr/+Ie4SC0/aXob+/zooM
+Z5bAE0z0EoKGrgFax7zBhYyu+Tf7xp/1rZoidFWiF34ShQAxoeVlR/FQG3iaIIxU
+pfz1hCvq5xp7XmIgjQ3RE/pMPJla4ADjnqVJOAwox5zLsMGk3fC5t98oLpPLhg==
+-----END CERTIFICATE-----

+ 27 - 0
app/src/main/assets/aws_iot/client.private.key

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAsGM1cZZrcIVFRxY2xfDqMo+NL/Dj4uFlqoiLHzai5hh+vzjD
+DbMws7Ew1KUElwDWzAf+89GtJ17gySXgsK4youBxv9UWbIYQgEWuQQsR/VI6hW+M
+g2lAMs0IJS68wWmEqq8370jk4nvfZzbtWecAg4O8ywQnSObUMVQhgO2lTc1xCbKm
+gRjadwZBkoAhtAxNaLe/QZahDiZZys1A3zMwYSBHcVQA2OlaTr+pVWuIlRkeOtJC
+l+3/DGCCmrjq1gKO9/KIAbfFZyMOAKLAiDeZWOV49TD7E+7yDFmzd6KWEvC0iDU8
+gVzNZ1Iu0QEBvXB7DZuZ6k5uw4xKT9XQeYslFQIDAQABAoIBAD4+lEKEOV/Yt8zR
+Kv0I+ixjeghb8p9CuRTH4IhgSHX4WOn0BukTvfD7a2bu4MLE2gfgHnk+GeOV2K9C
+/Zp7roTJvXpboPElEjeh3082r998IAish6JsBuqmbeRpLNT3/ogZEx0O1S6TVg/f
+Uzzxhr3iREJEMfTI+EJ8FOKhej8M1OhtEM6Rpj4FJFX99n7Ozpzb5B6IRT+ElgOE
+7gBRiSyAcV84IFi+W92n7/IjKs0YS7Le6iws4affZHqWvHgWyDukPdOhCDEXOBEh
+ImldpOh1p43GEtbQ0S0X6jyMssscdhUiuvITzTqMBAvXTm2SPnImpBAsGZ/VqHpO
+7SsakHUCgYEA6MZi8MZkgI9SQyq+cIPGYieHzfemwgYQiAVxnYvH7QdcTx2gS5MY
+vaQjeBiDElj1UERxPNmGsh2B11L5UStnkS7Fyj0ixdwxY8WIaRgwmqX2nzv6CXUj
+5yWL2XtaKNKIGXKusrqaPrM9nu6khsS2TnCJI5Ee9k9ajuU0ZqrCL8sCgYEAwfyO
+fhDXQTw5YANVHnH6KsB9Z+ROKXxym4qMHaZr/PuMOfMYNhDLz+2jEh8Ksy3+AU+l
+bijbQ7cOy2dTqancayKINoCYitlEEwJWVMwtwbHgDPg8gUHFOzWEWP1YpoxoXwbF
+sA5kboKQiCMlYqosBZ+Bo94SGxtqTcwoYyHzop8CgYBl3MLZSIgcdhtyhxb1V8UN
+mOzl5hTUXSEI1REF93BSumDbwkQuLj2qZ1SaLmfVNJaNPJYtIae+9AiouIefzJWy
+w7wMJzq7SQNX/M4X510Fn9DPl8hwMRG4Cgcr8b3flVm3gScP0XbEL12s2hOnYhfB
+NZ/dr5jUAp/HXmfS8bCuhwKBgQCLNy4n6PwiNA9VCW8310JbFiOpwJ4bhdVWLRsb
+WEkdmIVtPTm961Xplji2k5KBPtuQo1Es+qNJOIGkljXjiCTIulKZErbmxr/lCINe
+fd3EqVwK0sfRV5stP94IY2EQvalVsS2EiP+ok4kYOmjlIw1Ox8cpDAFgdM9Yn9DI
+PCB2LwKBgDRmhuVtRgUnyd0R7Uuo4VE6aGNJ63YcahhGlXRp0Malg+Lak/70EQRV
+ZtjceMreabYjWEA7pMsx1DcXsBghQCtgwvBaL8/G+PbbqvGfqt7kRnq7l/x4YoXi
+Mr66yDB+GpzTVnNm5WdtSmXfvljxXKG+jOBvK7D3GOiMTWi2JV7C
+-----END RSA PRIVATE KEY-----

BIN
app/src/main/assets/aws_iot/p12.keystore


+ 20 - 0
app/src/main/assets/aws_iot_test/cacert.development.crt

@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF
+ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
+b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL
+MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
+b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj
+ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM
+9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw
+IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6
+VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L
+93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm
+jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA
+A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI
+U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs
+N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv
+o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU
+5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy
+rqXRfboQnoZsG4q5WTP468SQvvG5
+-----END CERTIFICATE-----

+ 20 - 0
app/src/main/assets/aws_iot_test/client.cert.development.pem

@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgIVAPkvIDNm2HOlhDE8z45CbewzFE5QMA0GCSqGSIb3DQEB
+CwUAME0xSzBJBgNVBAsMQkFtYXpvbiBXZWIgU2VydmljZXMgTz1BbWF6b24uY29t
+IEluYy4gTD1TZWF0dGxlIFNUPVdhc2hpbmd0b24gQz1VUzAeFw0yMjAyMjgxMzAy
+NDVaFw00OTEyMzEyMzU5NTlaMB4xHDAaBgNVBAMME0FXUyBJb1QgQ2VydGlmaWNh
+dGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDmdWWnPS1kuRfBVl64
+Uxj/++QQcon5ACNkWy+VrArc9pCDGVWmeRvzOzm/D9NgNTenuPXYLRdESNDHcP+w
+/8oDwD3YtfdQQTOJ6cQCLrCx3ME8ExkbQycBGCwBf66/ObaWXiFSSlQs7j3VvXcM
+teujstRQvJQVgG2XpVdqUIXbGdogwjIjp9PBe2HRqyrF9jZVFODiA1DcliYJX4mP
+62IchCDS7vpPOJ0RaZ9WKYMVLuSdECHzfKwIMAM53/9cySNLH8jag9Cx/wgrEADx
+dE8DV9o9uoanWVv+iVh5B3t4oAZL4LF4fqF9X2NUHMbLMhVWD50PLxieUw3gOLn5
+2MzbAgMBAAGjYDBeMB8GA1UdIwQYMBaAFCogox/RwpD42tPT0CEj1yPTPo9PMB0G
+A1UdDgQWBBQAxGwzSNkkSE95dCH+3fXzZ338ITAMBgNVHRMBAf8EAjAAMA4GA1Ud
+DwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAQEAJTqQRDYFuoTAACUfEkBNftFI
+7Pw8A2zLnOq5dko/I3WuJ04NKld4SOSHjEjms3uOZgooeOeMU+dYk68JKAYrplIi
+iExQWDmJTG8kzyw7589V8A4HkUSFtK071LMU92lHSvHb8V0mOO/2kWtBS/b22r63
+O4Ahp13VSVyqwsFa/52INXmDifafqp9hDgSThs64fH2P4Wl6icsit7kqa4utLSJY
+m/LsddQlE4WwNtTT8So0TYm/TfYH8I3QShSmXer4gSWXdSBkten1p0i6a82pzORZ
+88vt3IvCN7x0uTCiylmjzZqaBYWu0XmptnWfhj4jOA6FHooMsHocngiMuiqGXA==
+-----END CERTIFICATE-----

+ 27 - 0
app/src/main/assets/aws_iot_test/client.private.development.key

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEA5nVlpz0tZLkXwVZeuFMY//vkEHKJ+QAjZFsvlawK3PaQgxlV
+pnkb8zs5vw/TYDU3p7j12C0XREjQx3D/sP/KA8A92LX3UEEzienEAi6wsdzBPBMZ
+G0MnARgsAX+uvzm2ll4hUkpULO491b13DLXro7LUULyUFYBtl6VXalCF2xnaIMIy
+I6fTwXth0asqxfY2VRTg4gNQ3JYmCV+Jj+tiHIQg0u76TzidEWmfVimDFS7knRAh
+83ysCDADOd//XMkjSx/I2oPQsf8IKxAA8XRPA1faPbqGp1lb/olYeQd7eKAGS+Cx
+eH6hfV9jVBzGyzIVVg+dDy8YnlMN4Di5+djM2wIDAQABAoIBAQCSn15taJ7Y1mom
+t6zL2RKuyrPvQvBFW7EoyFsd/rdqnfSTdCrqH2OI/MVgqdDyBOnD7Al94DJNX4Z1
+XY9QLW15tXWjyyHuDY2c0P3D5bDFeI34iq4Oikrmh1p4GaQ0RaPFR14r3N5xN6Jf
+uotjqtqg68XpGBGfHHadZLSZD2IaYsMTyu/qGzV1MNx/Q2mEGH3JWvPnA2Kj9kRu
+Wh4TRpIUdSkDb6AdQNCeEcrGl+EVSmGC6VXp0xk8iOF3LW94p676sXlBCbgf67Nb
+LATa2r5EGiD9sLeklKX+B+os/Wh/wIuLD8vERMCGbh0Mz18Qb4ZQTLpBgqf1Ry4p
+EbWOjKMBAoGBAPm4C4+fyxKPWK5PS15sXt+jnztmIWPusAR/yc4//BTW4hpB8qG9
+W/Uhd1Tx4k/SGYlMeq0MzI/RwM9u4DkPP01aby0sDyDKPOFz4sxcG0gO7chaWCac
+xgt/rW1SQbyZrr35CzaUKI9omNhPalJ92mAI30OKA5DoFrT6LGaWTNS9AoGBAOxB
+VVwMu8Ac4MZ3w/GiE371SULYFI4vkDjjk7DRLuzV/mHGE+aJzQy7q7Qjn5CBGPMa
+rzz8cNQDJI5deVk5jYWRytAGdLqm5mE4p2jNWBZJVf+qsTmZVVyP3dU3CGnRIlSh
+QQFZuUIlx067uwasWChvD6vA6nDGQNhOxG0f8p13AoGACeNKueYm4VZBua00G8oI
+u6w76tsxaUaTO6pfsEYCUkTr8QQ5ekQINx2bHwbw4h48mp4noWCHXQ5+JviJ8lhz
+NjnlexFqi+7WGEtBaLwg7GqC6dMB+I1unhfxIwoFxlUDgUwyMHgPXxZSLwZ9/EQ1
+eGYBiSgW2g8GjyX0yh8bbbUCgYEAmLYacOeiLKn9+QvNUlFLfNMcSeLWVpoUBVHF
+U5PDWz9vsM+SaYBFVKeyczxbldJ8yBdFkowyejiV1phBKhVzjxUQHasupL/hRpKS
+U8H34GADAMHa0Lp2XOB5qapq+8s8/6tEE5VllIZK8G+UktmwGmK/pGuMntznaWpw
+uYDfGD0CgYEA4o2PD21DdUeA1dqiUtVT77MhWf+rMYhmCmAgZGgTfoZXeSIaIIgX
+EmVm0YnT/EnFAHLMDQTxAaEcVtwHC4bfIkpRVUH3HBDvrOmcAlMNDyAmZlJmpOCJ
+ysqTgV8xSbiBw/TPNI4w7gEqDstlGZin9L+1yeXEw0dyc8he9eBpVGU=
+-----END RSA PRIVATE KEY-----

+ 82 - 0
app/src/main/assets/config.xml

@@ -0,0 +1,82 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<map>
+    <!-- 自动适配模式 -->
+    <string name="auto_adapter_mode">false</string>
+    <!-- 是否转接属性 -->
+    <string name="transfer_prop">false</string>
+    <!-- 是否使用https -->
+    <string name="is_https">true</string>
+    <!-- 是否测试模式 -->
+    <string name="is_test">false</string>
+    
+    <!-- 升级包存储路径,data/sdcard/other -->
+    <string name="up_storage_partition">data</string>
+    <!-- 是否使用写command方式安装,该设置与升级包存储路径对应配置,即:data:false,其他:true -->
+    <string name="install_mode_command">true</string>
+    <!-- 屏幕横竖屏设置,true/false -->
+    <string name="screen_orientation_vertical">true</string>
+    <!-- 固件版本property属性值 -->
+    <string name="property_firmware_version">ro.build.id</string>
+    <!-- 设备机型property属性值 -->
+    <string name="property_model">ro.product.model</string>
+    <!-- 设备品牌property属性值 -->
+    <string name="property_brand">ro.product.manufacturer</string>
+    
+    <!-- 特殊下载路径 -->
+    <string name="other_app_mount_path">/data</string>
+    <!-- recovery模式下特殊特殊路径的识别路径) -->
+    <string name="other_recovery_mount_path">/data</string>
+    
+    <!-- sdCard下载路径 -->
+    <string name="sdcard_app_mount_path">/mnt/sdcard</string>
+    <!-- recovery内置sd卡识别路径) -->
+    <string name="sdcard_recovery_mount_internal_path">/data/media/0</string>  
+    <!-- recovery外置sd卡识别路径 -->
+    <string name="sdcard_recovery_mount_external_path" />
+    <!-- 本地升级 内置sd卡识别根目录 -->
+    <string name="sdcard_internal_root_path">/mnt/sdcard</string>
+    <!-- 本地升级 外置sd卡识别根目录 -->
+    <string name="sdcard_external_root_path" />
+
+    
+    <!-- 是否自动下载的默认值 -->
+    <string name="auto_download">true</string>
+     <!-- 是否AB模式 -->
+    <string name="is_ab_mode">false</string>
+    <!-- 是否开启广播 -->
+    <string name="is_broadcast_enable">false</string>
+    <!-- 自动检查更新周期的默认值(单位:正数:天,负数:小时) -->
+    <string name="auto_check_cycle_selected">1</string>
+    <!-- 自动检查更新周期的默认选项值(0:永不)(单位:正数:天,负数:小时) -->
+    <string name="auto_check_cycle_options">|1|7|14|0|</string>    
+    <!-- 低内存报警阀值(单位:MB) -->
+    <string name="memory_low_threshold">100</string>
+    <!-- 是否检测低电量 -->
+    <string name="battery_low_detect">true</string>
+    
+    <!-- 服务器URL -->
+    <string name="test_ota_serverl_url">http://fota.mwhtml5.com:6100/service/request</string>
+    <string name="test_ota_report_url">http://fota.mwhtml5.com:6100/service/report</string>
+    <string name="ota_serverl_url">http://fota.redstone.net.cn:6100/service/request</string>
+    <string name="ota_report_url">http://fota.redstone.net.cn:6100/service/report</string>
+    <!-- 服务器HTTPS URL -->
+    <string name="test_ota_serverl_https_url">https://fota.mwhtml5.com:6200/service/request</string>
+    <string name="test_ota_report_https_url">https://fota.mwhtml5.com:6200/service/report</string>
+    <string name="ota_serverl_https_url">https://fota.redstone.net.cn:7100/service/request</string>
+    <string name="ota_report_https_url">https://fota.redstone.net.cn:7100/service/report</string>
+    
+    <!-- 是否开启安装前校验SHA1值 -->
+    <string name="verify_package_sha1">true</string>
+    <!-- 是否显示拷贝日志选项 -->
+    <string name="copy_recovery_log">false</string>
+    <!-- 是否显示本地升级功能选项 -->
+    <string name="have_local_update">false</string>
+    <!-- 强制升级 -->
+    <string name="key_force_update">true</string>
+    
+    <!-- 检查下载进度的间隔时间 (单位 ms) -->
+    <string name="ui_progress_refresh_interval_time">1000</string>
+    <!-- 刷新进度条的间隔大小 (单位 KB) -->
+    <string name="ui_progress_refresh_interval_size">200</string>
+    
+</map>

BIN
app/src/main/assets/my.keystore


BIN
app/src/main/assets/test.keystore


+ 93 - 0
app/src/main/java/com/xplora/commonservice/BaseApplication.kt

@@ -0,0 +1,93 @@
+package com.xplora.commonservice
+
+import android.Manifest
+import android.annotation.SuppressLint
+import android.app.Application
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.net.ConnectivityManager
+import android.os.Handler
+import android.os.Looper
+import android.os.SystemProperties
+import com.xplora.commonservice.model.infobean.AwsIotInfo
+import com.xplora.commonservice.model.infobean.PositionInfoBean
+import com.xplora.commonservice.model.infobean.ServerInfo
+import com.xplora.commonservice.modules.OtaManager
+import com.xplora.commonservice.modules.OtaManager.initOta
+import com.xplora.commonservice.modules.WatchStateChangeImpl
+import com.xplora.commonservice.modules.WatchStateChangeImpl.onBootCompleted
+import com.xplora.commonservice.modules.callbacks.*
+import com.xplora.commonservice.modules.callbacks.CallLogObserver.Companion.callLogUri
+import com.xplora.commonservice.modules.database.DatabaseManager.CHAT_TABLE
+import com.xplora.commonservice.modules.database.DatabaseManager.GOPLAY_CONTENT_TABLE
+import com.xplora.commonservice.utils.Logger
+import com.xplora.commonservice.utils.permission.PermissionActivity
+
+class BaseApplication : Application() {
+
+    override fun onCreate() {
+        super.onCreate()
+        Logger.d(TAG, "on application create")
+        globalContext = applicationContext
+        checkPermission()
+        val netReceiver = NetStateChangeReceiver
+        netReceiver.registerObserver(stateObserver)
+        netReceiver.registerObserver(otaNetStateObserver)
+        registerReceiver(netReceiver)
+        registerObserver()
+        if (SystemProperties.getInt("sys.boot_completed", 0) == 1) {
+            onBootCompleted()
+        }
+        initOta()
+    }
+
+    private fun registerReceiver(receiver: NetStateChangeReceiver) {
+        val filter = IntentFilter()
+        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
+        registerReceiver(receiver, filter)
+
+        val systemInfoFilter = IntentFilter()
+        systemInfoFilter.addAction(Intent.ACTION_BATTERY_CHANGED)
+        systemInfoFilter.addAction(Intent.ACTION_TIME_CHANGED)
+        val systemInfoReceiver = SystemInfoReceiver()
+        registerReceiver(systemInfoReceiver, systemInfoFilter)
+    }
+
+    private fun registerObserver() {
+        contentResolver.registerContentObserver(
+            CHAT_TABLE, true, ChatDbObserver(Handler((Looper.myLooper()!!)))
+        )
+        contentResolver.registerContentObserver(
+            callLogUri, true, CallLogObserver(Handler((Looper.myLooper()!!)))
+        )
+        contentResolver.registerContentObserver(
+            GOPLAY_CONTENT_TABLE, true, GoplayDbObserver(Handler(Looper.myLooper()!!))
+        )
+    }
+
+    private fun checkPermission() {
+        val intent = Intent(this, PermissionActivity::class.java)
+        intent.putExtra(PermissionActivity.EXTRA_PERMISSIONS, permissionListTmp)
+        startActivity(intent)
+    }
+
+    companion object {
+        private const val TAG = "BaseApplication"
+
+        @SuppressLint("StaticFieldLeak")
+        lateinit var globalContext: Context
+        val stateObserver: WatchStateChangeImpl by lazy { WatchStateChangeImpl }
+        val otaNetStateObserver : OtaManager by lazy { OtaManager }
+        val serverInfo = ServerInfo()
+        val iotInfo = AwsIotInfo()
+        val positionInfo = PositionInfoBean()
+
+        private val permissionListTmp = arrayOf(
+            Manifest.permission.ACCESS_WIFI_STATE,
+            Manifest.permission.WRITE_EXTERNAL_STORAGE,
+            Manifest.permission.READ_EXTERNAL_STORAGE,
+            Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS
+        )
+    }
+}

+ 63 - 0
app/src/main/java/com/xplora/commonservice/MainActivity.kt

@@ -0,0 +1,63 @@
+package com.xplora.commonservice
+
+import android.annotation.SuppressLint
+import android.app.Activity
+import android.os.Bundle
+import kotlinx.coroutines.DelicateCoroutinesApi
+
+@OptIn(DelicateCoroutinesApi::class)
+class MainActivity : Activity() {
+
+    @SuppressLint("CheckResult", "SetTextI18n", "RemoteViewLayout")
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_main)
+/*
+        val btnReqModel = findViewById<Button>(R.id.btn_req_model)
+        btnReqModel.setOnClickListener {
+            GlobalScope.launch {
+*//*                GetCurrentWeather.execute(GetWeatherReq(lat = "0.312642", lng = "51.822222"))
+                    .let {z
+                        it as GetCurrentWeatherRepo
+                        val logString = StringBuilder()
+                        logString.append("GetForecastWeather success -> \n[\n")
+                        val weather = WeatherDbEntity(_id = 0).from(
+                            it.data
+                        )
+                        logString.append("${weather}\n")
+                        DatabaseManager.updateWeather(weather)
+                        logString.append("]")
+                        Logger.d("GetForecastWeather", "$logString")
+                    }*//*
+//            GetMsgList.execute(MsgListReq(userId = watchBaseInfo.userId))
+//                 GetEmojiList.execute(null)
+            }
+        }
+
+        val btn2 = findViewById<Button>(R.id.btn_req_json)
+        btn2.setOnClickListener {
+            GlobalScope.launch {
+//            GetEmojiList.execute(null)
+//            GetCurrentWeather.execute(GetWeatherReq(lat = "0.312642", lng = "51.822222"))
+*//*                GetForecastWeather.execute(GetWeatherReq(lat = "10.773082", lng = "59.915837"))
+                    .let {
+                        it as GetForecastWeatherRepo
+                        val logString = StringBuilder()
+                        logString.append("GetForecastWeather success -> \n[\n")
+                        for (i in it.data.indices) {
+                            val weather = WeatherDbEntity(_id = i.toLong()).from(
+                                it.data[i]
+                            )
+                            logString.append("${weather}\n")
+                            DatabaseManager.updateWeather(weather)
+                        }
+                        logString.append("]")
+                        Logger.d("GetForecastWeather", "$logString")
+                    }*//*
+//            GetContactList.execute(null)
+//            GetMsgList.execute(MsgListReq(userId = "01103c0b2d225f5f675f5f3836384162"))
+//            SetWatchStatus.execute(StatusSetReq(false))
+            }
+        }*/
+    }
+}

+ 321 - 0
app/src/main/java/com/xplora/commonservice/model/Const.kt

@@ -0,0 +1,321 @@
+package com.xplora.commonservice.model
+
+import android.os.SystemProperties
+import com.xplora.commonservice.model.Config.PROTOCOL_VERSION
+
+object Config {
+    const val LOCAL_RECEIVER_PERMISSION = "com.xplora.receiver"
+
+    /**
+     * test server: "https://htk-start-dev.myxplora.com"
+     * mp   server: "https://htk.myxplora.com"
+     **/
+    const val INIT_URL = "https://htk.myxplora.com"
+    const val BASE_DOWNLOAD_URL = "https://xplora-images.s3.eu-central-1.amazonaws.com/"
+    const val INIT_PORT = 443
+    const val PROTOCOL_VERSION = "/v2"
+    val INIT_SECRET = if (SystemProperties.getBoolean(
+            "ro.product.support.esim",
+            false
+        )
+    ) "91a837b21efe5aedadbd9b2b55d0d41b" else "3472ee0e78e85a9ab0075e2ca61b6d38"
+    val INIT_KEY = if (SystemProperties.getBoolean(
+            "ro.product.support.esim",
+            false
+        )
+    ) "ad4267b6f85655eca414672cc7a82ef3" else "b05a400d116b582c9b4742a50dcc5f48"
+    const val VENDOR = "sikey"
+    const val DEFAULT_CONTENT_TYPE = "application/json; charset=utf-8"
+    const val TYPE = "1"
+    const val REQUEST_QUERY = "tid"
+    val MODEL =
+        if (SystemProperties.getBoolean("ro.product.support.esim", false)) "X6E" else "X6"
+    val DEVICE_ATTRIBUTE =
+        if (SystemProperties.getBoolean("ro.product.support.esim", false)) "S200" else "S100"
+    val DEVICE_TYPE =
+        if (SystemProperties.getBoolean("ro.product.support.esim", false)) "122" else "102"
+    val IMEI: String = SystemProperties.get("persist.zs.imei.number", "")
+
+    //THRESHOLD
+    const val POSITIONING_DURATION: Long = 60 * 1000
+    const val POSITION_FREQ = 5 //Minute
+    const val POSITIONING_STEP_THRESHOLD = 180
+    const val POSITIONING_TIME_THRESHOLD = 2 * 60 * 60 * 1000
+    const val STEP_REPORT_TIME_THRESHOLD = 1 * 60 * 60 * 1000
+    const val FORECAST_WEATHER_UPDATE_THRESHOLD = 6 * 60 * 60 * 1000
+    const val CURRENT_WEATHER_UPDATE_THRESHOLD = 60 * 60 * 1000
+
+    val LOW_BATTERY_NOTIFY_NODE = listOf(0, 5, 10, 15, 20)
+
+    //THRESHOLD end
+    //test keys
+    const val DEFAULT_SN = "112GX110e100000"
+    const val BACK_DOOR = "62a58828255d54c190bf92b2b3542e0ef9285fa479d0595c92c357d53513d0bb"
+    const val TEST_IMEI = "867909043812052"
+    const val TEST_SECRET_KEY_IMEI = "241d017f9d835a9db3ae8a46311bee17"
+    //test keys end
+}
+
+object CkError {
+    const val SUCCESS = "S000001"
+    const val ERROR_UNKNOWN = "E000001"
+    const val ERROR_NEED_RESET = "E100015"
+}
+
+object CkMessage {
+    const val SUCCESS = "SUCCESS"
+    const val ERROR_UNKNOWN = "Failure. Failure not specifically defined."
+}
+
+object HttpApi {
+    const val INIT = "$PROTOCOL_VERSION/init"
+
+    const val PUSH_RESULT = "$PROTOCOL_VERSION/fcm/result"
+
+    object Location {
+        private const val PREFIX = "$PROTOCOL_VERSION/location"
+        const val GPS = "$PREFIX/gps"
+        const val CELL = "$PREFIX/cell"
+        const val MULTI = "$PREFIX/multi"
+        const val TRACKING_STATUS = "$PREFIX/tracking/status"
+    }
+
+    object Battery {
+        const val PERCENT = "$PROTOCOL_VERSION/battery/percent"
+    }
+
+    object Call {
+        const val LOG = "$PROTOCOL_VERSION/call/log"
+    }
+
+    object Step {
+        const val COUNT = "$PROTOCOL_VERSION/step/count"
+        const val COUNT_TOTAL = "$COUNT/total"
+        const val COUNT_HISTORY = "$COUNT/history"
+    }
+
+    object Watch {
+        private const val PREFIX = "$PROTOCOL_VERSION/watch"
+
+        object Status {
+            private const val STATUS_PREFIX = "$PREFIX/status"
+            const val GET = "$STATUS_PREFIX/get"
+            const val SET = "$STATUS_PREFIX/set"
+            const val INFO = "$STATUS_PREFIX/info"
+        }
+
+        object Factory {
+            const val RESET = "$PREFIX/factory/reset"
+        }
+
+        object Power {
+            const val SET = "$PREFIX/power/set"
+        }
+
+        object Network {
+            private const val NETWORK_STATUS_PREFIX = "$PREFIX/network/status"
+            const val SET = "$NETWORK_STATUS_PREFIX/set"
+        }
+    }
+
+    object Etc {
+        private const val PREFIX = "$PROTOCOL_VERSION/etc"
+        const val GUARDIANS = "$PREFIX/guardians"
+        const val SOS = "$PREFIX/sos"
+        const val FirmwareVersion = "$PREFIX/watch/firmware/version"
+        const val XCoin = "$PREFIX/coin/count"
+
+        object Model {
+            const val GET = "${PREFIX}/watch/model/get"
+            const val SET = "${PREFIX}/watch/model/set"
+        }
+    }
+
+    object Alarm {
+        const val LIST = "$PROTOCOL_VERSION/alarm/list"
+    }
+
+    object Silent {
+        const val LIST = "$PROTOCOL_VERSION/silent/list"
+    }
+
+    object Contact {
+        const val LIST = "$PROTOCOL_VERSION/contact/list"
+    }
+
+    object Friend {
+        private const val PREFIX = "$PROTOCOL_VERSION/friend"
+        const val CODE = "$PREFIX/code"
+        const val ADD = "$PREFIX/add"
+    }
+
+    object Emoji {
+        const val LIST = "$PROTOCOL_VERSION/emoji/list"
+    }
+
+    object Weather {
+        private const val PREFIX = "$PROTOCOL_VERSION/weather"
+        const val CURRENT = "$PREFIX/current"
+        const val FORECAST = "$PREFIX/forecast"
+        const val CITYID = "$PREFIX/cityid"
+
+        object CityId {
+            const val ALL = "$CITYID/all"
+        }
+    }
+
+    object File {
+        private const val PREFIX = "$PROTOCOL_VERSION/file"
+        const val UPLOAD = "$PREFIX/upload"
+
+        object Separate {
+            private const val SEPARATE_PREFIX = "$PREFIX/separate"
+            const val SEPARATE_UPLOAD = "$SEPARATE_PREFIX/upload"
+            const val SEPARATE_START = "$UPLOAD/start"
+            const val SEPARATE_END = "$UPLOAD/end"
+        }
+    }
+
+    object Chat {
+        private const val PREFIX = "$PROTOCOL_VERSION/chat"
+        const val VOICE = "$PREFIX/voice"
+        const val TEXT = "$PREFIX/text"
+        const val EMOJI = "$PREFIX/emoji"
+        const val PHOTO = "$PREFIX/photo"
+        const val VIDEO = "$PREFIX/video"
+        const val MUSIC = "$PREFIX/music"
+        const val MSG = "$PREFIX/msg"
+
+        object Msg {
+            const val STATE = "$MSG/state"
+            const val LIST = "$MSG/list"
+
+            const val UNREAD = "$MSG/count/unread"
+            const val UNREAD_ALL = "$UNREAD/all"
+
+            const val READ = "$MSG/read"
+            const val READ_ALL = "$READ/all"
+
+            const val REMOVE = "$MSG/remove"
+        }
+    }
+
+    object PowerSaving {
+        private const val PREFIX = "$PROTOCOL_VERSION/mode/power/saving"
+        const val GET = "$PREFIX/get"
+        const val SET = "$PREFIX/set"
+        const val INFO = "$PREFIX/info"
+    }
+
+    object APN {
+        const val DATA = "$PROTOCOL_VERSION/apn/data"
+    }
+
+    object Content {
+        private const val PREFIX = "$PROTOCOL_VERSION/content"
+        const val GET = "${PREFIX}/get"
+        const val RESULT = "${PREFIX}/download/result"
+    }
+
+    object MqttCode {
+        const val REQUEST_UPLOAD_LOCATION = 1000
+        const val REQUEST_UPLOAD_LOCATION_CONTINUOUS = 1010
+        const val REQUEST_UPLOAD_BATTERY = 1020
+        const val REQUEST_CURRENT_TOTAL_STEP = 1030
+        const val NOTIFY_STATUS_UPDATED = 1040
+        const val REQUEST_STATUS = 1050
+        const val NOTIFY_FACTORY_RESET = 1060
+        const val REQUEST_POWER_STATUS = 1070
+        const val NOTIFY_TURN_OFF = 1080
+        const val NOTIFY_REBOOT = 1090
+        const val NOTIFY_ALARM_UPDATED = 1100
+        const val NOTIFY_SILENT_MODE_UPDATED = 1110
+        const val NOTIFY_CONTACT_UPDATED = 1120
+        const val NOTIFY_EMOJI_UPDATED = 1130
+        const val NOTIFY_NEW_MSG = 1150
+        const val NOTIFY_SENT_MSG_STATUS = 1160
+        const val NOTIFY_DELETE_MSG = 1170
+        const val NOTIFY_GROUP_CHAT_UPDATED = 1180
+        const val NOTIFY_NEW_GROUP_MSG = 1190
+        const val NOTIFY_RECEIVED_GROUP_MSG_STATUS = 1200
+        const val NOTIFY_GROUP_MSG_DELETE = 1210
+        const val NOTIFY_POWER_SAVING_MODE = 1220
+        const val REQUEST_UPLOAD_POWER_SAVING_MODE = 1230
+        const val NOTIFY_APN_UPDATE = 1240
+        const val NOTIFY_SERVER_INFO_UPDATE = 1250
+        const val REQUEST_WATCH_MODEL = 1260
+        const val REQUEST_UPLOAD_DAILY_STEP = 1270
+        const val REQUEST_UPLOAD_VERSION = 1280
+        const val REQUEST_START_LOCATION_TRACKING = 1290
+        const val REQUEST_STOP_LOCATION_TRACKING = 1300
+        const val REQUEST_UPLOAD_LOCATION_TRACKING_STATUS = 1310
+        const val NOTIFY_PRE_DEFINED_PHRASE_UPDATE = 1320
+        const val REQUEST_NET_STATUS = 1330
+        const val NOTIFY_CONTENT_DOWNLOAD = 1500
+        const val NOTIFY_XCOIN_COST = 1510
+    }
+}
+
+object ChatMsgState {
+    const val MESSAGE_STATE_UNREAD = 0
+    const val MESSAGE_STATE_READ = 1
+    const val MESSAGE_STATE_SEND_ING = 2
+    const val MESSAGE_STATE_SEND_SUCCESS = 3
+    const val MESSAGE_STATE_SEND_FAIL = 4
+    const val MESSAGE_STATE_DELETE = 5
+    const val MESSAGE_STATE_UNKNOWN = 99
+}
+
+object ChatMsgType {
+    const val EMOJI = 100
+    const val TEXT = 101
+    const val PHOTO = 102
+    const val VOICE = 103
+    const val MP3 = 104
+    const val VIDEO = 105
+}
+
+enum class ChatFileType(private val desc: String) {
+    UNKNOWN("UNKNOWN"),
+    PHOTO("PHOTO"),
+    VOICE("VOICE"),
+    COVER("COVER"),
+    VIDEO("VIDEO");
+
+    override fun toString(): String {
+        return desc
+    }
+}
+
+enum class NetworkType(private val desc: String) {
+    NETWORK_WIFI("WiFi"),
+    NETWORK_4G("4G"),
+    NETWORK_2G("2G"),
+    NETWORK_3G("3G"),
+    NETWORK_UNKNOWN("Unknown"),
+    NETWORK_NO("No network");
+
+    override fun toString(): String {
+        return desc
+    }
+}
+
+object LocalActions {
+    const val ACTION_ADD_FRIEND = "com.xplora.action.ADD_FRIEND"
+    const val ACTION_POWER_STATUS = "com.xplora.action.POWER_STATUS"
+    const val ACTION_REFRESH_CHAT_LIST = "com.xplora.action.REFRESH_CHAT_LIST"
+    const val ACTION_SILENT_BEGIN = "com.xplora.action.SILENT_BEGIN"
+    const val ACTION_SILENT_END = "com.xplora.action.SILENT_END"
+    const val ACTION_RESET_STEP = "com.xplora.action.RESET_STEP"
+    const val ACTION_POSITION = "com.xplora.action.POSITION"
+    const val ACTION_ALARM = "com.xplora.action.ALARM"
+    const val ACTION_OTA = "com.xplora.action.OTA"
+    const val ACTION_OTA_CHECK = "com.xplora.action.OTA_CHECK"
+    const val ACTION_CALL_LOG = "com.xplora.call_log"
+    const val ACTION_SOS = "com.xplora.call.sos"
+
+    const val NOTIFICATION_CHAT = "com.xplora.notification.Chat"
+    const val NOTIFICATION_MISSED_CALL = "com.xplora.notification.Missed_call"
+    const val NOTIFICATION_RINGTONE = "com.xplora.notification.Ringtone"
+    const val NOTIFICATION_WATCHFACE = "com.xplora.notification.Watchface"
+}

+ 53 - 0
app/src/main/java/com/xplora/commonservice/model/MqttMessage.kt

@@ -0,0 +1,53 @@
+package com.xplora.commonservice.model.mqtt
+
+import com.google.gson.annotations.SerializedName
+
+open class BaseMessage<T>(
+    @SerializedName("data")
+    var data: PushData<T>? = null,
+    @SerializedName("background")
+    var background: Boolean = false,
+    @SerializedName("badge")
+    var badge: Int? = null,
+    @SerializedName("notification")
+    var notification: PushNotification? = null
+)
+
+open class PushData<T>(
+    @SerializedName("xpVersion")
+    var xpVersion: String,
+    @SerializedName("xpSrc")
+    var xpSrc: String,
+    @SerializedName("xpDest")
+    var xpDest: String,
+    @SerializedName("xpEt")
+    var xpEt: String,
+    @SerializedName("xpLang")
+    var xpLang: String? = null,
+    @SerializedName("xpTid")
+    var xpTid: String,
+    @SerializedName("xpTm")
+    var xpTm: String,
+    @SerializedName("xpCb")
+    var xpCb: String? = null,
+    @SerializedName("xpPushId")
+    var xpPushId: String,
+    @SerializedName("msgId")
+    var msgId: String? = null,
+    @SerializedName("msgIds")
+    var msgIds: ArrayList<String>? = null,
+    @SerializedName("cid")
+    var cid: String? = null,
+    var mData: T? = null
+)
+
+data class PushNotification(
+    @SerializedName("title")
+    var title: String? = null,
+    @SerializedName("body")
+    var body: String? = null,
+    @SerializedName("imageUrl")
+    var imageUrl: String? = null,
+    @SerializedName("sound")
+    var sound: String? = null
+)

+ 17 - 0
app/src/main/java/com/xplora/commonservice/model/NotificationDetails.kt

@@ -0,0 +1,17 @@
+package com.xplora.commonservice.model
+
+import android.app.PendingIntent
+import android.graphics.Color
+import androidx.core.graphics.drawable.IconCompat
+import com.xplora.commonservice.R
+
+data class NotificationDetails(
+    var id: Int = 0,
+    var channelId: String = "",
+    var packageName: String = "",
+    var color: Int? = null,
+    var notificationTitle: String = "",
+    var notificationText: String = "",
+    var icon: IconCompat,
+    var contentIntent: PendingIntent? = null,
+)

+ 39 - 0
app/src/main/java/com/xplora/commonservice/model/database/AlarmDbEntity.kt

@@ -0,0 +1,39 @@
+package com.xplora.commonservice.model.database
+
+import androidx.room.Ignore
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.AlarmInfo
+
+data class AlarmDbEntity @Ignore constructor(
+    @SerializedName("alarmId")
+    var alarmId: Int = 0,
+    @SerializedName("serverId")
+    var serverId: String? = null,
+    @SerializedName("title")
+    var title: String? = null,
+    @SerializedName("hour")
+    var hour: String? = null,
+    @SerializedName("minute")
+    var minute: String? = null,
+    @SerializedName("enabled")
+    var enabled: String? = null,
+    @SerializedName("repeat")
+    var repeat: String? = null
+) :BaseDbEntity(){
+    constructor() : this(0, null, null, null, null, null, null)
+
+    override fun toString(): String {
+        return StringBuilder().append(hour).append(",").append(minute).append(",").append(enabled)
+            .append(",").append(repeat).toString()
+    }
+
+    fun form(info: AlarmInfo): AlarmDbEntity {
+        serverId = info.id
+        title = info.name
+        hour = (info.min / 60).toString()
+        minute = (info.min % 60).toString()
+        enabled = info.status.toString()
+        repeat = info.week
+        return this
+    }
+}

+ 3 - 0
app/src/main/java/com/xplora/commonservice/model/database/BaseDbEntity.kt

@@ -0,0 +1,3 @@
+package com.xplora.commonservice.model.database
+
+open class BaseDbEntity()

+ 130 - 0
app/src/main/java/com/xplora/commonservice/model/database/ChatDbDbEntity.kt

@@ -0,0 +1,130 @@
+package com.xplora.commonservice.model.database
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.chat.BaseMsgInfo
+import com.xplora.commonservice.model.http.chat.MsgInfo
+import com.xplora.commonservice.model.http.chat.MsgListInfo
+import com.xplora.commonservice.modules.WatchStateChangeImpl
+
+/**
+ * Chat Message Type	Attributes
+ * Voice                msgId, type, fileUrl, dur, state, create, update
+ * Text	                msgId, type, text, state, create, update
+ * Emoji	            msgId, type, emojiId, state, create, update
+ * Photo	            msgId, type, fileUrl, state, create, update
+ * Short video	        msgId, type, dur, coverUrl, videoUrl, state, create, update
+ * Mp3	                msgId, type, fileUrl, dur, state, create, update
+ */
+data class ChatDbDbEntity(
+    @SerializedName("_id")
+    var _id: Long? = null,
+    @SerializedName("msgId")
+    var msgId: String? = null,
+    /**
+     * - 100 : Emoji
+     * - 101 : Text
+     * - 102 : Photo
+     * - 103 : Voice
+     * - 104 : Mp3
+     * - 105 : Short video
+     */
+    @SerializedName("type")
+    var type: String? = null,
+    @SerializedName("text")
+    var text: String? = null,
+    @SerializedName("emojiId")
+    var emojiId: String? = null,
+    /**
+     * -image
+     * -audio
+     * -cover
+     */
+    @SerializedName("fileUrl")
+    var fileUrl: String? = null,
+    /**
+     * -image
+     * -audio
+     * -cover
+     *
+     * MESSAGE_STATE_UNDOWNLOAD    = 0;
+     * MESSAGE_STATE_DOWNLOAD_ING  = 1;
+     * MESSAGE_STATE_NEED_DOWNLOAD  = 2
+     * MESSAGE_STATE_DOWNLOAD_DONE = uri;
+     */
+    @SerializedName("filePath")
+    var filePath: String? = null,
+    /**
+     * -audio
+     * -video
+     */
+    @SerializedName("dur")
+    var dur: String? = null,
+    /**
+     * -video
+     */
+    @SerializedName("videoUrl")
+    var videoUrl: String? = null,
+    /**
+     * MESSAGE_STATE_UNDOWNLOAD    = 0;
+     * MESSAGE_STATE_DOWNLOAD_ING  = 1;
+     * MESSAGE_STATE_NEED_DOWNLOAD  = 2
+     * MESSAGE_STATE_DOWNLOAD_DONE = uri;
+     */
+    @SerializedName("videoPath")
+    var videoPath: String? = null,
+    /**
+     * Message status
+     * - 0 : Unknown
+     * - 1 : Delivered
+     * - 2 : Read
+     * - 3 : Failed
+     */
+    @SerializedName("state")
+    var state: String? = null,
+    /**
+     * MESSAGE_STATE_UNREAD        = 0;
+     * MESSAGE_STATE_READ          = 1;
+     * MESSAGE_STATE_SEND_ING      = 2;
+     * MESSAGE_STATE_SEND_SUCCESS  = 3;e
+     * MESSAGE_STATE_SEND_FAIL     = 4;
+     * MESSAGE_STATE_DELETE        = 5;
+     * MESSAGE_STATE_UNKNOWN       = 99;
+     */
+    @SerializedName("localState")
+    var localState: String = "0",
+    @SerializedName("senderUserId")
+    var senderUserId: String? = null,
+    @SerializedName("receiverId")
+    var receiverId: String? = null,
+    @SerializedName("create")
+    var create: String? = null,
+    @SerializedName("update")
+    var update: String? = null,
+    @SerializedName("fid")
+    var fid: String? = null
+):BaseDbEntity() {
+    fun from(info: BaseMsgInfo<*>): ChatDbDbEntity {
+        msgId = info.msgId
+        type = info.type
+        text = info.text
+        emojiId = info.emojiId
+        fileUrl = if (info.type == "105") info.coverUrl else info.fileUrl
+        dur = info.dur
+        videoUrl = info.videoUrl
+        state = info.state
+        create = info.create
+        update = info.update
+        when (info) {
+            is MsgInfo -> {
+                senderUserId = info.senderUserId
+                receiverId = WatchStateChangeImpl.watchBaseInfo.userId
+            }
+            is MsgListInfo -> {
+                if (!info.incomming) {
+                    senderUserId = WatchStateChangeImpl.watchBaseInfo.userId
+                }
+            }
+        }
+        return this
+    }
+}

+ 67 - 0
app/src/main/java/com/xplora/commonservice/model/database/ContactDbDbEntity.kt

@@ -0,0 +1,67 @@
+package com.xplora.commonservice.model.database
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.ContactInfo
+
+data class ContactDbDbEntity(
+    @SerializedName("_id")
+    var _id: Long? = null,
+    /**
+     * Guardian's ID of contact. contact's resource ID
+     */
+    @SerializedName("id")
+    var id: String? = null,
+    @SerializedName("userId")
+    var userId: String? = null,
+    @SerializedName("name")
+    var name: String? = null,
+    /**
+     *  profile image url
+     */
+    @SerializedName("profile")
+    var profile: String? = null,
+    /**
+     *  phone number(without country phone number)
+     */
+    @SerializedName("phoneNumber")
+    var phoneNumber: String? = null,
+    /**
+     * Country phone number
+     */
+    @SerializedName("countryPN")
+    var countryPN: String? = null,
+    /**
+     * SIM type. Reserved column.
+     * - 1 : DATA SIM
+     * - 2 : DATA + Voice SIM
+     * - 100 : Unknown.
+     */
+    @SerializedName("sim")
+    var sim: String? = null,
+    /**
+     * - 1 : First guardian(SOS target)
+     * - 2 : Second guardian
+     * - 3 : General contact(Not member of XPLORA server)
+     * - 4 : Friend
+     */
+    @SerializedName("type")
+    var type: String? = null,
+    @SerializedName("rate")
+    var rate: String = "0",
+    @SerializedName("unRead")
+    var unRead: String = "0",
+    @SerializedName("profilePath")
+    var profilePath: String? = null
+):BaseDbEntity() {
+    fun from(info: ContactInfo): ContactDbDbEntity {
+        id = info.id
+        userId = info.userId
+        name = info.name
+        profile = info.profile
+        phoneNumber = info.phoneNumber
+        countryPN = info.countryPN
+        sim = info.sim
+        type = info.type
+        return this
+    }
+}

+ 37 - 0
app/src/main/java/com/xplora/commonservice/model/database/GoplayContentDbDbEntity.kt

@@ -0,0 +1,37 @@
+package com.xplora.commonservice.model.database
+
+import androidx.room.Ignore
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.content.ContentData
+
+data class GoplayContentDbDbEntity @Ignore constructor(
+    @SerializedName("_id")
+    var _id: Long = 0,
+    @SerializedName("cid")
+    var cid: String? = null,
+    @SerializedName("title")
+    var title: String? = null,
+    @SerializedName("contentType")
+    var contentType: String? = null,
+    @SerializedName("size")
+    var size: String? = null,
+    @SerializedName("url")
+    var url: String? = null,
+    @SerializedName("status")
+    var status: String = "0",
+    @SerializedName("hint")
+    var hint: String = "0",
+    @SerializedName("reason")
+    var reason: String = "0"
+):BaseDbEntity() {
+    constructor() : this(0, null, null, null, null, null, "0", "0", "0")
+
+    fun form(info: ContentData): GoplayContentDbDbEntity {
+        cid = info.cid
+        title = info.title
+        contentType = info.contentType
+        size = info.size.toString()
+        url = info.url
+        return this
+    }
+}

+ 13 - 0
app/src/main/java/com/xplora/commonservice/model/database/MusicDbDbEntity.kt

@@ -0,0 +1,13 @@
+package com.xplora.commonservice.model.database
+
+import com.google.gson.annotations.SerializedName
+
+
+data class MusicDbDbEntity(
+    @SerializedName("musicId")
+    var musicId: Int? = null,
+    @SerializedName("url")
+    var url: String? = null,
+    @SerializedName("path")
+    var path: String = "1"
+):BaseDbEntity()

+ 38 - 0
app/src/main/java/com/xplora/commonservice/model/database/SilentDbDbEntity.kt

@@ -0,0 +1,38 @@
+package com.xplora.commonservice.model.database
+
+import androidx.annotation.NonNull
+import androidx.room.Ignore
+import androidx.room.PrimaryKey
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.SilentInfo
+
+data class SilentDbDbEntity @Ignore constructor(
+    @PrimaryKey(autoGenerate = false)
+    @NonNull
+    @SerializedName("alarmId")
+    var alarmId: Int = 0,
+    @SerializedName("serverId")
+    var serverId: String? = null,
+    @SerializedName("startMin")
+    var startMin: String? = null,//start min
+    @SerializedName("endMin")
+    var endMin: String? = null,//end min
+    @SerializedName("repeat")
+    var repeat: String? = null
+):BaseDbEntity() {
+    constructor() : this(0, null, null, null)
+
+    override fun toString(): String {
+        return StringBuilder().append(startMin).append(",").append(endMin).append(",")
+            .append(repeat)
+            .toString()
+    }
+
+    fun form(info: SilentInfo): SilentDbDbEntity {
+        serverId = info.id
+        startMin = (info.startMin).toString()
+        endMin = (info.endMin).toString()
+        repeat = info.week
+        return this
+    }
+}

+ 16 - 0
app/src/main/java/com/xplora/commonservice/model/database/StepDbDbEntity.kt

@@ -0,0 +1,16 @@
+package com.xplora.commonservice.model.database
+
+import android.icu.util.Calendar
+import com.google.gson.annotations.SerializedName
+
+
+data class StepDbDbEntity(
+    @SerializedName("_id")
+    val _id: Long = 0,
+    @SerializedName("today")
+    var today: String = "0",
+    @SerializedName("history")
+    var history: String = "0,",
+    @SerializedName("day")
+    var day: String = Calendar.getInstance().get(Calendar.DAY_OF_YEAR).toString()
+):BaseDbEntity()

+ 31 - 0
app/src/main/java/com/xplora/commonservice/model/database/WeatherDbDbEntity.kt

@@ -0,0 +1,31 @@
+package com.xplora.commonservice.model.database
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.weather.WeatherData
+
+data class WeatherDbDbEntity(
+    @SerializedName("_id")
+    var _id: Long,
+    @SerializedName("city")
+    var city: String? = null,
+    @SerializedName("update_time")
+    var update_time: String? = null,
+    @SerializedName("weather_type")
+    var weather_type: String? = null,
+    @SerializedName("temperature_current")
+    var temperature_current: String? = null,
+    @SerializedName("temperature_low")
+    var temperature_low: String = "0",
+    @SerializedName("temperature_high")
+    var temperature_high: String = "0"
+) :BaseDbEntity(){
+    fun from(info: WeatherData): WeatherDbDbEntity {
+        city = info.cityName
+        update_time = info.obTime ?: info.datetime
+        weather_type = info.weather.code
+        temperature_current = info.temp
+        temperature_low = info.minTemp
+        temperature_high = info.maxTemp
+        return this
+    }
+}

+ 20 - 0
app/src/main/java/com/xplora/commonservice/model/http/AlarmList.kt

@@ -0,0 +1,20 @@
+package com.xplora.commonservice.model.http
+
+import com.google.gson.annotations.SerializedName
+
+data class GetAlarmListRepo(
+    @SerializedName("data") val data: List<AlarmInfo>
+) : BaseResponse<GetAlarmListRepo>()
+
+data class AlarmInfo(
+    @SerializedName("id")
+    val id: String,
+    @SerializedName("name")
+    val name: String,
+    @SerializedName("week")
+    val week: String,
+    @SerializedName("min")
+    val min: Int,
+    @SerializedName("status")
+    val status: Boolean
+)

+ 3 - 0
app/src/main/java/com/xplora/commonservice/model/http/BaseRequest.kt

@@ -0,0 +1,3 @@
+package com.xplora.commonservice.model.http
+
+open class BaseRequest()

+ 30 - 0
app/src/main/java/com/xplora/commonservice/model/http/BaseResponse.kt

@@ -0,0 +1,30 @@
+package com.xplora.commonservice.model.http
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.CkError
+import com.xplora.commonservice.model.CkMessage
+
+data class NormalResponse(val na: Any? = null) : BaseResponse<NormalResponse>()
+
+open class BaseResponse<T>(
+    @SerializedName("ckTid")
+    var ckTid: String? = "",
+    @SerializedName("ckError")
+    var ckError: String? = CkError.ERROR_UNKNOWN,
+    @SerializedName("ckMessage")
+    var ckMessage: String? = CkMessage.ERROR_UNKNOWN,
+    var mData: T? = null
+) {
+    companion object {
+        fun <T> unknownError(error: Throwable): BaseResponse<T> {
+            val baseModel: BaseResponse<T> = BaseResponse()
+            baseModel.ckMessage = error.message
+            return baseModel
+        }
+    }
+
+    override fun toString(): String {
+        return "BaseModel(ckTid=$ckTid, ckError=$ckError, ckMessage='$ckMessage')"
+    }
+
+}

+ 26 - 0
app/src/main/java/com/xplora/commonservice/model/http/ContactList.kt

@@ -0,0 +1,26 @@
+package com.xplora.commonservice.model.http
+
+import com.google.gson.annotations.SerializedName
+
+data class GetContactListRepo(
+    @SerializedName("data") val data: List<ContactInfo>
+) : BaseResponse<GetContactListRepo>()
+
+data class ContactInfo(
+    @SerializedName("id")
+    val id: String,
+    @SerializedName("userId")
+    val userId: String,
+    @SerializedName("name")
+    val name: String,
+    @SerializedName("profile")
+    val profile: String,
+    @SerializedName("phoneNumber")
+    val phoneNumber: String,
+    @SerializedName("countryPN")
+    val countryPN: String,
+    @SerializedName("sim")
+    val sim: String,
+    @SerializedName("type")
+    val type: String
+)

+ 17 - 0
app/src/main/java/com/xplora/commonservice/model/http/EmojiList.kt

@@ -0,0 +1,17 @@
+package com.xplora.commonservice.model.http
+
+import com.google.gson.annotations.SerializedName
+
+data class GetEmojiListRepo(
+    @SerializedName("data") val data: List<EmojiInfo>,
+    @SerializedName("updatedTm") val updatedTm: String
+) : BaseResponse<GetEmojiListRepo>()
+
+data class EmojiInfo(
+    @SerializedName("id")
+    val id: String,
+    @SerializedName("keyword")
+    val keyword: String,
+    @SerializedName("url")
+    val url: String
+)

+ 8 - 0
app/src/main/java/com/xplora/commonservice/model/http/GetApnData.kt

@@ -0,0 +1,8 @@
+package com.xplora.commonservice.model.http
+
+import com.google.gson.annotations.SerializedName
+
+data class GetApnDataRepo(
+    @SerializedName("url") val url: String,
+    @SerializedName("updatedTm") val updatedTm: Long
+) : BaseResponse<GetApnDataRepo>()

+ 29 - 0
app/src/main/java/com/xplora/commonservice/model/http/Init.kt

@@ -0,0 +1,29 @@
+package com.xplora.commonservice.model.http
+
+import androidx.annotation.Nullable
+import com.google.gson.annotations.SerializedName
+
+data class InitReq(
+    @SerializedName("secret") val secret: String,
+    @Nullable @SerializedName("firmwareVersion") var firmwareVersion: String? = null
+): BaseRequest()
+
+data class InitRepo(
+    @SerializedName("serverIP") val serverIP: String,
+    @SerializedName("serverPort") val serverPort: Int,
+    @SerializedName("apiKey") val apiKey: String,
+    @SerializedName("apiSecret") val apiSecret: String,
+    @SerializedName("secret") val secret: String,
+    @SerializedName("secretExpireTm") val secretExpireTm: Long,
+    @SerializedName("awsIotIP") val awsIotIP: String,
+    @SerializedName("awsIotPort") val awsIotPort: Int,
+    @SerializedName("awsIotTopic") val awsIotTopic: String,
+    @SerializedName("awsIotRegion") val awsIotRegion: String,
+    @SerializedName("awsIotClientId") val awsIotClientId: String,
+    @SerializedName("awsIotKeepAliveSec") val awsIotKeepAliveSec: Int,
+    @SerializedName("awsIotCaCert") val awsIotCaCert: String,
+    @SerializedName("awsIotCliCert") val awsIotCliCert: String,
+    @SerializedName("awsIotCliPriv") val awsIotCliPriv: String,
+    /** Server time. Unix timestamp **/
+    @SerializedName("tm") val tm: Long
+) : BaseResponse<InitRepo>()

+ 20 - 0
app/src/main/java/com/xplora/commonservice/model/http/ReportPushResult.kt

@@ -0,0 +1,20 @@
+package com.xplora.commonservice.model.http
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.Config.REQUEST_QUERY
+
+data class FcmResultReq(
+    @SerializedName("result")
+    var result: Int = 0,
+    @SerializedName("reason")
+    var reason: FcmResultReqReason? = null
+): BaseRequest()
+
+data class FcmResultReqReason(
+    @SerializedName("urlPath")
+    var urlPath: String = "",
+    @SerializedName("code")
+    var code: String = "",
+    @SerializedName(REQUEST_QUERY)
+    var tid: String = ""
+)

+ 18 - 0
app/src/main/java/com/xplora/commonservice/model/http/SilentList.kt

@@ -0,0 +1,18 @@
+package com.xplora.commonservice.model.http
+
+import com.google.gson.annotations.SerializedName
+
+data class GetSilentListRepo(
+    @SerializedName("data") val data: List<SilentInfo>
+) : BaseResponse<GetSilentListRepo>()
+
+data class SilentInfo(
+    @SerializedName("id")
+    val id: String,
+    @SerializedName("week")
+    val week: String,
+    @SerializedName("startMin")
+    val startMin: Int,
+    @SerializedName("endMin")
+    val endMin: Int
+)

+ 8 - 0
app/src/main/java/com/xplora/commonservice/model/http/UploadBatteryPercent.kt

@@ -0,0 +1,8 @@
+package com.xplora.commonservice.model.http
+
+import com.google.gson.annotations.SerializedName
+
+data class PercentReq(
+    @SerializedName("battery") val battery:String,
+    @SerializedName("isCharging") val isCharging:Boolean
+): BaseRequest()

+ 13 - 0
app/src/main/java/com/xplora/commonservice/model/http/UploadCallLog.kt

@@ -0,0 +1,13 @@
+package com.xplora.commonservice.model.http
+
+import androidx.annotation.Nullable
+import com.google.gson.annotations.SerializedName
+
+data class CallLogReq(
+    @SerializedName("caller") val caller: String,
+    @SerializedName("callee") val callee: String,
+    @SerializedName("start") val start: Long,
+    @SerializedName("end") val end: Long,
+    @SerializedName("type") val type: Int,
+    @SerializedName("voip") @Nullable var voip: Boolean? = null
+): BaseRequest()

+ 15 - 0
app/src/main/java/com/xplora/commonservice/model/http/WatchModelInfo.kt

@@ -0,0 +1,15 @@
+package com.xplora.commonservice.model.http
+
+import com.google.gson.annotations.SerializedName
+
+data class SetWatchModelInfoReq(
+    @SerializedName("model") val model: String,
+    @SerializedName("deviceAttribute") val deviceAttribute: String,
+    @SerializedName("deviceType") val deviceType: String
+) : BaseRequest()
+
+data class GetWatchModelInfoRepo(
+    @SerializedName("model") val model: String,
+    @SerializedName("deviceAttribute") val deviceAttribute: String,
+    @SerializedName("deviceType") val deviceType: String
+) : BaseResponse<GetWatchModelInfoRepo>()

+ 8 - 0
app/src/main/java/com/xplora/commonservice/model/http/chat/DeleteMsg.kt

@@ -0,0 +1,8 @@
+package com.xplora.commonservice.model.http.chat
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+
+data class DeleteMsgReq(
+    @SerializedName("msgIds") val msgIds: ArrayList<String>
+): BaseRequest()

+ 31 - 0
app/src/main/java/com/xplora/commonservice/model/http/chat/GetMsg.kt

@@ -0,0 +1,31 @@
+package com.xplora.commonservice.model.http.chat
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+import com.xplora.commonservice.model.http.BaseResponse
+
+data class MsgReq(
+    @SerializedName("msgId") val msgId: String
+): BaseRequest()
+
+data class MsgRepo(
+    @SerializedName("data") val data: MsgInfo
+) : BaseResponse<MsgRepo>()
+
+data class MsgInfo(
+    @SerializedName("senderUserId") var senderUserId: String? = null
+) : BaseMsgInfo<MsgInfo>()
+
+open class BaseMsgInfo<T>(
+    @SerializedName("msgId") var msgId: String? = null,
+    @SerializedName("type") var type: String? = null,
+    @SerializedName("text") var text: String? = null,
+    @SerializedName("emojiId") var emojiId: String? = null,
+    @SerializedName("fileUrl") var fileUrl: String? = null,
+    @SerializedName("dur") var dur: String? = null,
+    @SerializedName("coverUrl") var coverUrl: String? = null,
+    @SerializedName("videoUrl") var videoUrl: String? = null,
+    @SerializedName("state") var state: String? = null,
+    @SerializedName("create") var create: String? = null,
+    @SerializedName("update") var update: String? = null
+)

+ 23 - 0
app/src/main/java/com/xplora/commonservice/model/http/chat/GetMsgList.kt

@@ -0,0 +1,23 @@
+package com.xplora.commonservice.model.http.chat
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+import com.xplora.commonservice.model.http.BaseResponse
+
+data class MsgListReq(
+    @SerializedName("userId") val userId: String? = null,
+    @SerializedName("offset") val offset: Int = 0,
+    @SerializedName("limit") var limit: Int = 10,
+    @SerializedName("msgId") var msgId: String? = null
+): BaseRequest()
+
+data class MsgListRepo(
+    @SerializedName("total") val total: Int,
+    @SerializedName("offset") val offset: Int,
+    @SerializedName("limit") val limit: Int,
+    @SerializedName("list") val list: ArrayList<MsgListInfo>
+) : BaseResponse<MsgListRepo>()
+
+data class MsgListInfo(
+    @SerializedName("incomming") val incomming: Boolean
+) : BaseMsgInfo<MsgListInfo>()

+ 20 - 0
app/src/main/java/com/xplora/commonservice/model/http/chat/GetMsgState.kt

@@ -0,0 +1,20 @@
+package com.xplora.commonservice.model.http.chat
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+import com.xplora.commonservice.model.http.BaseResponse
+
+data class GetMsgStateReq(
+    @SerializedName("msgIds") val msgIds: ArrayList<String>
+): BaseRequest()
+
+data class GetMsgStateRepo(
+    @SerializedName("data") val data: ArrayList<MsgState>
+) : BaseResponse<GetMsgStateRepo>()
+
+data class MsgState(
+    @SerializedName("msgId") val msgId: String,
+    @SerializedName("state") val state: Int,
+    @SerializedName("create") val create: Long,
+    @SerializedName("update") val update: Long
+)

+ 23 - 0
app/src/main/java/com/xplora/commonservice/model/http/chat/GetUnreadCount.kt

@@ -0,0 +1,23 @@
+package com.xplora.commonservice.model.http.chat
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+import com.xplora.commonservice.model.http.BaseResponse
+
+data class GetUnreadCountReq(
+    @SerializedName("userId") val userId: String
+): BaseRequest()
+
+data class GetUnreadCountRepo(
+    @SerializedName("count") val count: Int
+) : BaseResponse<GetUnreadCountRepo>()
+
+data class GetUnreadAllRepo(
+    @SerializedName("data") val data: ArrayList<UnreadInfo>
+) : BaseResponse<GetUnreadAllRepo>()
+
+data class UnreadInfo(
+    @SerializedName("userId") val userId: String,
+    @SerializedName("count") val count: Int,
+    @SerializedName("lastDate") val lastDate: Long
+)

+ 37 - 0
app/src/main/java/com/xplora/commonservice/model/http/chat/SendMsg.kt

@@ -0,0 +1,37 @@
+package com.xplora.commonservice.model.http.chat
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+import com.xplora.commonservice.model.http.BaseResponse
+
+data class SendVoiceReq(
+    @SerializedName("fid") val fid: String,
+    @SerializedName("dur") val dur: Int,
+    @SerializedName("userId") val receiverId: String
+): BaseRequest()
+
+data class SendTextReq(
+    @SerializedName("text") val text: String,
+    @SerializedName("userId") val receiverId: String
+): BaseRequest()
+
+data class SendEmojiReq(
+    @SerializedName("emojiId") val emojiId: String,
+    @SerializedName("userId") val receiverId: String
+): BaseRequest()
+
+data class SendPhotoReq(
+    @SerializedName("fid") val fid: String,
+    @SerializedName("userId") val receiverId: String
+): BaseRequest()
+
+data class SendVideoReq(
+    @SerializedName("coverId") val coverFid: String,
+    @SerializedName("videoId") val videoFid: String,
+    @SerializedName("dur") val dur: Int,
+    @SerializedName("userId") val receiverId: String
+): BaseRequest()
+
+data class SendMsgRepo(
+    @SerializedName("msgId") val msgId: String
+) : BaseResponse<SendMsgRepo>()

+ 12 - 0
app/src/main/java/com/xplora/commonservice/model/http/chat/SetMsgRead.kt

@@ -0,0 +1,12 @@
+package com.xplora.commonservice.model.http.chat
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+
+data class SetMsgReadReq(
+    @SerializedName("msgIds") val msgIds: ArrayList<String>
+): BaseRequest()
+
+data class SetMsgReadAllReq(
+    @SerializedName("userId") val userId: String
+): BaseRequest()

+ 26 - 0
app/src/main/java/com/xplora/commonservice/model/http/content/GetContent.kt

@@ -0,0 +1,26 @@
+package com.xplora.commonservice.model.http.content
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+import com.xplora.commonservice.model.http.BaseResponse
+
+data class GetContentReq(
+    @SerializedName("cid") val cid: String
+): BaseRequest()
+
+data class GetContentRepo(
+    @SerializedName("data") val data: ContentData
+) : BaseResponse<GetContentRepo>()
+
+data class ContentData(
+    @SerializedName("cid") var cid: String,
+    @SerializedName("title") var title: String,
+    /**
+     * Content type
+     * - Ringtone: "Ringtone"
+     * - Watchface: "WatchFace"
+     **/
+    @SerializedName("contentType") var contentType: String,
+    @SerializedName("size") var size: Int,
+    @SerializedName("url") var url: String
+)

+ 9 - 0
app/src/main/java/com/xplora/commonservice/model/http/content/ReportContentDownloadStatus.kt

@@ -0,0 +1,9 @@
+package com.xplora.commonservice.model.http.content
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+
+data class ReportContentDownloadStatusReq(
+    @SerializedName("cid") val cid: String,
+    @SerializedName("status") val status: String
+): BaseRequest()

+ 8 - 0
app/src/main/java/com/xplora/commonservice/model/http/etc/FirmwareVersion.kt

@@ -0,0 +1,8 @@
+package com.xplora.commonservice.model.http.etc
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+
+data class FirmwareVersionReq(
+    @SerializedName("version") val version: String
+): BaseRequest()

+ 25 - 0
app/src/main/java/com/xplora/commonservice/model/http/etc/Guardians.kt

@@ -0,0 +1,25 @@
+package com.xplora.commonservice.model.http.etc
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseResponse
+
+data class GetGuardiansRepo(
+    @SerializedName("data") val data: List<GuardianInfo>
+) : BaseResponse<GetGuardiansRepo>()
+
+data class GuardianInfo(
+    @SerializedName("id")
+    val id: String,
+    @SerializedName("userId")
+    val userId: String,
+    @SerializedName("name")
+    val name: String,
+    @SerializedName("profile")
+    val profile: String,
+    @SerializedName("phoneNumber")
+    val phoneNumber: String,
+    @SerializedName("countryPN")
+    val countryPN: String,
+    @SerializedName("sim")
+    val sim: String
+)

+ 15 - 0
app/src/main/java/com/xplora/commonservice/model/http/etc/SOS.kt

@@ -0,0 +1,15 @@
+package com.xplora.commonservice.model.http.etc
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+
+data class SOSReq(
+    /**
+     * SOS state
+     * - 1 : Start
+     * - 2 : End - Success
+     * - 3 : End - Fail
+     */
+    @SerializedName("state") val state: Int,
+    @SerializedName("guardianId") val guardianId: String
+): BaseRequest()

+ 8 - 0
app/src/main/java/com/xplora/commonservice/model/http/etc/XCoin.kt

@@ -0,0 +1,8 @@
+package com.xplora.commonservice.model.http.etc
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseResponse
+
+data class GetXCoinRepo(
+    @SerializedName("coin") val coin: Int
+) : BaseResponse<GetXCoinRepo>()

+ 8 - 0
app/src/main/java/com/xplora/commonservice/model/http/file/Upload.kt

@@ -0,0 +1,8 @@
+package com.xplora.commonservice.model.http.file
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseResponse
+
+data class UploadRepo(
+    @SerializedName("fid") val fid: String
+) : BaseResponse<UploadRepo>()

+ 8 - 0
app/src/main/java/com/xplora/commonservice/model/http/friend/Add.kt

@@ -0,0 +1,8 @@
+package com.xplora.commonservice.model.http.friend
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+
+data class FriendAddReq(
+    @SerializedName("code") val code: String
+): BaseRequest()

+ 8 - 0
app/src/main/java/com/xplora/commonservice/model/http/friend/Code.kt

@@ -0,0 +1,8 @@
+package com.xplora.commonservice.model.http.friend
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+
+data class FriendCodeReq(
+    @SerializedName("code") val code: String
+): BaseRequest()

+ 39 - 0
app/src/main/java/com/xplora/commonservice/model/http/location/Cell.kt

@@ -0,0 +1,39 @@
+package com.xplora.commonservice.model.http.location
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+
+data class CellLocationReq(
+    @SerializedName("radioType") val radioType: String,
+    @SerializedName("cellTowers") val cellTowers: ArrayList<CellTowers>,
+    @SerializedName("wifiAPs") var wifiAPs: ArrayList<WifiAPs>? = null,
+    @SerializedName("tm") val tm: Long,
+    @SerializedName("countryCode") var countryCode: String? = null,
+    @SerializedName("province") var province: String? = null,
+    @SerializedName("city") var city: String? = null,
+    @SerializedName("addr1") var addr1: String? = null,
+    @SerializedName("addr2") var addr2: String? = null,
+    @SerializedName("poi") var poi: String? = null,
+    @SerializedName("battery") var battery: String? = null,
+    @SerializedName("isCharging") var isCharging: Boolean? = null,
+    @SerializedName("step") var step: Int? = null,
+    @SerializedName("distance") var distance: String? = null,
+    @SerializedName("cause") var cause: Int? = null
+): BaseRequest()
+
+data class CellTowers(
+    @SerializedName("id") val id: Int,
+    @SerializedName("areaCode") val areaCode: Int,
+    @SerializedName("strength") var strength: Int? = null,
+    @SerializedName("mcc") var mcc: Int? = null,
+    @SerializedName("mnc") val mnc: Int,
+    @SerializedName("connected") var connected: Boolean? = null
+)
+
+data class WifiAPs(
+    @SerializedName("macAddr") val macAddr: String,
+    @SerializedName("age") val age: Long? = null,
+    @SerializedName("channel") val channel: Int? = null,
+    @SerializedName("noise") val noise: Int? = null,
+    @SerializedName("strength") val strength: Int? = null
+)

+ 22 - 0
app/src/main/java/com/xplora/commonservice/model/http/location/Gps.kt

@@ -0,0 +1,22 @@
+package com.xplora.commonservice.model.http.location
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+
+data class GpsLocationReq(
+    @SerializedName("lat") val lat: String,
+    @SerializedName("lng") val lng: String,
+    @SerializedName("rad") val rad: String,
+    @SerializedName("tm") val tm: Long,
+    @SerializedName("countryCode") var countryCode: String? = null,
+    @SerializedName("province") var province: String? = null,
+    @SerializedName("city") var city: String? = null,
+    @SerializedName("addr1") var addr1: String? = null,
+    @SerializedName("addr2") var addr2: String? = null,
+    @SerializedName("poi") var poi: String? = null,
+    @SerializedName("battery") var battery: String? = null,
+    @SerializedName("isCharging") var isCharging: Boolean? = null,
+    @SerializedName("step") var step: Int? = null,
+    @SerializedName("distance") var distance: String? = null,
+    @SerializedName("cause") var cause: Int? = null
+): BaseRequest()

+ 35 - 0
app/src/main/java/com/xplora/commonservice/model/http/location/Multi.kt

@@ -0,0 +1,35 @@
+package com.xplora.commonservice.model.http.location
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+import com.xplora.commonservice.model.http.BaseResponse
+
+data class MultiPositionReq(
+    @SerializedName("list") val list: ArrayList<MultiList>
+): BaseRequest()
+
+data class MultiPositionRepo(
+    @SerializedName("list") val list: ArrayList<String>
+) : BaseResponse<MultiPositionRepo>()
+
+data class MultiList(
+    @SerializedName("gps") val gps: Boolean = false,
+    @SerializedName("lat") val lat: String? = null,
+    @SerializedName("lng") val lng: String? = null,
+    @SerializedName("rad") val rad: String? = null,
+    @SerializedName("radioType") val radioType: String? = null,
+    @SerializedName("cellTowers") val cellTowers: ArrayList<CellTowers>? = null,
+    @SerializedName("wifiAPs") var wifiAPs: ArrayList<WifiAPs>? = null,
+    @SerializedName("tm") val tm: Long = System.currentTimeMillis() / 1000,
+    @SerializedName("countryCode") var countryCode: String? = null,
+    @SerializedName("province") var province: String? = null,
+    @SerializedName("city") var city: String? = null,
+    @SerializedName("addr1") var addr1: String? = null,
+    @SerializedName("addr2") var addr2: String? = null,
+    @SerializedName("poi") var poi: String? = null,
+    @SerializedName("battery") var battery: String? = null,
+    @SerializedName("isCharging") var isCharging: Boolean? = null,
+    @SerializedName("step") var step: Int? = null,
+    @SerializedName("distance") var distance: String? = null,
+    @SerializedName("cause") var cause: Int? = null
+)

+ 8 - 0
app/src/main/java/com/xplora/commonservice/model/http/location/TrackingStatus.kt

@@ -0,0 +1,8 @@
+package com.xplora.commonservice.model.http.location
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+
+data class LocationTrackingStatusReq(
+    @SerializedName("status") val status: Int
+): BaseRequest()

+ 25 - 0
app/src/main/java/com/xplora/commonservice/model/http/power/SavingMode.kt

@@ -0,0 +1,25 @@
+package com.xplora.commonservice.model.http.power
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+import com.xplora.commonservice.model.http.BaseResponse
+
+data class SetSavingModeReq(
+    /**
+     * Power saving mode.
+     * - 1 : normal mode(Not power saving mode)
+     * - 2 : super-standby time mode
+     * - 3 : sleep mode
+     */
+    @SerializedName("mode") val mode: Int
+): BaseRequest()
+
+data class GetSavingModeRepo(
+    /**
+     * Power saving mode.
+     * - 1 : normal mode(Not power saving mode)
+     * - 2 : super-standby time mode
+     * - 3 : sleep mode
+     */
+    @SerializedName("mode") val mode: Int
+) : BaseResponse<GetSavingModeRepo>()

+ 13 - 0
app/src/main/java/com/xplora/commonservice/model/http/power/Set.kt

@@ -0,0 +1,13 @@
+package com.xplora.commonservice.model.http.power
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+
+data class PowerSetReq(
+    /**
+     * Kids watch power status
+     * - true : On
+     * - false : Off
+     */
+    @SerializedName("power") val power: Boolean,
+): BaseRequest()

+ 14 - 0
app/src/main/java/com/xplora/commonservice/model/http/status/Get.kt

@@ -0,0 +1,14 @@
+package com.xplora.commonservice.model.http.status
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseResponse
+
+data class StatusGetRepo(
+    @SerializedName("data") val data: StatusData
+) : BaseResponse<StatusGetRepo>()
+
+data class StatusData(
+    @SerializedName("id") val id: String,
+    @SerializedName("status") val status: Boolean,
+    @SerializedName("sim") val sim: Int
+)

+ 13 - 0
app/src/main/java/com/xplora/commonservice/model/http/status/Info.kt

@@ -0,0 +1,13 @@
+package com.xplora.commonservice.model.http.status
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+
+data class StatusInfoReq(
+    /**
+     * Kids watch activation status
+     * - true : activated
+     * - false : inactivated
+     */
+    @SerializedName("status") val status: Boolean
+): BaseRequest()

+ 13 - 0
app/src/main/java/com/xplora/commonservice/model/http/status/Set.kt

@@ -0,0 +1,13 @@
+package com.xplora.commonservice.model.http.status
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+
+data class StatusSetReq(
+    /**
+     * Kids watch activation status
+     * - true : activated
+     * - false : inactivated
+     */
+    @SerializedName("status") val status: Boolean
+): BaseRequest()

+ 10 - 0
app/src/main/java/com/xplora/commonservice/model/http/step/Count.kt

@@ -0,0 +1,10 @@
+package com.xplora.commonservice.model.http.step
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+
+data class StepCountReq(
+    @SerializedName("step") val step: Int,
+    @SerializedName("distance") var distance: String? = null,
+    @SerializedName("tm") val tm: Long,
+): BaseRequest()

+ 13 - 0
app/src/main/java/com/xplora/commonservice/model/http/step/count/History.kt

@@ -0,0 +1,13 @@
+package com.xplora.commonservice.model.http.step.count
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+
+data class StepCountHistoryReq(
+    @SerializedName("data") val data: List<StepCountHistoryData>
+): BaseRequest()
+
+data class StepCountHistoryData(
+    @SerializedName("date") val date: String,
+    @SerializedName("count") val count: String
+)

+ 8 - 0
app/src/main/java/com/xplora/commonservice/model/http/step/count/Total.kt

@@ -0,0 +1,8 @@
+package com.xplora.commonservice.model.http.step.count
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseResponse
+
+data class StepCountCountTotalRepo(
+    @SerializedName("step") val step: Int
+) : BaseResponse<StepCountCountTotalRepo>()

+ 24 - 0
app/src/main/java/com/xplora/commonservice/model/http/weather/GetCityId.kt

@@ -0,0 +1,24 @@
+package com.xplora.commonservice.model.http.weather
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+import com.xplora.commonservice.model.http.BaseResponse
+
+data class GetCityIdReq(
+    @SerializedName("name")
+    val name: String
+): BaseRequest()
+
+data class GetCityIdRepo(
+    @SerializedName("data")
+    val data: CityData
+) : BaseResponse<GetCityIdRepo>()
+
+data class CityData(
+    @SerializedName("cityId") val cityId: String,
+    @SerializedName("cityName") val cityName: String,
+    @SerializedName("countryCode") val countryCode: String,
+    @SerializedName("countryFull") val countryFull: String,
+    @SerializedName("lat") val lat: String,
+    @SerializedName("lng") val lng: String
+)

+ 71 - 0
app/src/main/java/com/xplora/commonservice/model/http/weather/WeatherInfo.kt

@@ -0,0 +1,71 @@
+package com.xplora.commonservice.model.http.weather
+
+import com.google.gson.annotations.SerializedName
+import com.xplora.commonservice.model.http.BaseRequest
+import com.xplora.commonservice.model.http.BaseResponse
+import com.xplora.commonservice.model.http.location.CellTowers
+import com.xplora.commonservice.model.http.location.WifiAPs
+
+data class GetWeatherReq(
+    @SerializedName("cityId") var cityId: Long? = null,
+    @SerializedName("lat") var lat: String? = null,
+    @SerializedName("lng") var lng: String? = null,
+    @SerializedName("radioType") var radioType: String? = null,
+    @SerializedName("cellTowers") var cellTowers: ArrayList<CellTowers>? = null,
+    @SerializedName("wifiAPs") var wifiAPs: ArrayList<WifiAPs>? = null
+): BaseRequest()
+
+data class GetCurrentWeatherRepo(
+    @SerializedName("data") val data: WeatherData
+) : BaseResponse<GetCurrentWeatherRepo>()
+
+data class GetForecastWeatherRepo(
+    @SerializedName("data") val data: ArrayList<WeatherData>
+) : BaseResponse<GetForecastWeatherRepo>()
+
+data class WeatherData(
+    @SerializedName("lat") var lat: String? = null,
+    @SerializedName("lng") var lng: String? = null,
+    @SerializedName("sunrise") var sunrise: String? = null,
+    @SerializedName("sunset") var sunset: String? = null,
+    @SerializedName("timezone") var timezone: String? = null,
+    @SerializedName("station") var station: String? = null,
+    @SerializedName("obTime") var obTime: String? = null,
+    @SerializedName("datetime") var datetime: String? = null,
+    @SerializedName("ts") var ts: String? = null,
+    @SerializedName("cityName") var cityName: String? = null,
+    @SerializedName("countryCode") var countryCode: String? = null,
+    @SerializedName("stateCode") var stateCode: String? = null,
+    @SerializedName("pres") var pres: String? = null,
+    @SerializedName("slp") var slp: String? = null,
+    @SerializedName("windSpd") var windSpd: String? = null,
+    @SerializedName("wndDir") var wndDir: String? = null,
+    @SerializedName("windCdir") var windCdir: String? = null,
+    @SerializedName("windCdirFull") var windCdirFull: String? = null,
+    @SerializedName("temp") var temp: String? = null,
+    @SerializedName("appTemp") var appTemp: String? = null,
+    @SerializedName("rh") var rh: String? = null,
+    @SerializedName("dewpt") var dewpt: String? = null,
+    @SerializedName("clouds") var clouds: String? = null,
+    @SerializedName("pod") var pod: String? = null,
+    @SerializedName("weather") var weather: Weather,
+    @SerializedName("vis") var vis: String? = null,
+    @SerializedName("precip") var precip: String? = null,
+    @SerializedName("snow") var snow: String? = null,
+    @SerializedName("uv") var uv: String? = null,
+    @SerializedName("aqi") var aqi: String? = null,
+    @SerializedName("dhi") var dhi: String? = null,
+    @SerializedName("dni") var dni: String? = null,
+    @SerializedName("ghi") var ghi: String? = null,
+    @SerializedName("solarRad") var solarRad: String? = null,
+    @SerializedName("elevAngle") var elevAngle: String? = null,
+    @SerializedName("hAngle") var hAngle: String? = null,
+    @SerializedName("minTemp") var minTemp: String = "0",
+    @SerializedName("maxTemp") var maxTemp: String = "0"
+)
+
+data class Weather(
+    @SerializedName("icon") var icon: String? = null,
+    @SerializedName("code") var code:String? = null,
+    @SerializedName("description") var description: String? = null,
+)

+ 15 - 0
app/src/main/java/com/xplora/commonservice/model/infobean/AwsIotInfo.kt

@@ -0,0 +1,15 @@
+package com.xplora.commonservice.model.infobean
+
+data class AwsIotInfo(
+    @Volatile
+    var ip: String? = null,
+    @Volatile
+    var port: Int? = null,
+    var topic: String? = null,
+    var region: String? = null,
+    var clientId: String? = null,
+    var keepAlive: Int? = null,
+    var certUrl: String? = null,
+    var cliCertUrl: String? = null,
+    var cliPrivUrl: String? = null
+)

+ 7 - 0
app/src/main/java/com/xplora/commonservice/model/infobean/PositionInfoBean.kt

@@ -0,0 +1,7 @@
+package com.xplora.commonservice.model.infobean
+
+data class PositionInfoBean(
+    var lastCID: Int = 0,
+    var lastStep: Int = 0,
+    var lastReportTime: Long = 0
+)

+ 37 - 0
app/src/main/java/com/xplora/commonservice/model/infobean/ServerInfo.kt

@@ -0,0 +1,37 @@
+package com.xplora.commonservice.model.infobean
+
+import android.os.SystemProperties
+import com.xplora.commonservice.model.Config.INIT_KEY
+import com.xplora.commonservice.model.Config.INIT_PORT
+import com.xplora.commonservice.model.Config.INIT_SECRET
+import com.xplora.commonservice.model.Config.INIT_URL
+import com.xplora.commonservice.model.Config.TEST_IMEI
+import com.xplora.commonservice.utils.Logger
+import com.xplora.commonservice.utils.Md5Util
+
+data class ServerInfo(
+    @Volatile
+    var serverUrl: String = INIT_URL,
+    @Volatile
+    var serverPort: Int = INIT_PORT,
+    @Volatile
+    var apiKey: String = INIT_KEY,
+    @Volatile
+    var apiSecret: String = INIT_SECRET,
+    @Volatile
+    private var imeiSecret: String = ""
+) {
+    fun getImeiSecret(isRefreshToken:Boolean): String {
+        if (isRefreshToken) {
+            imeiSecret =
+                Md5Util.md5(SystemProperties.get("persist.zs.imei.number")/*TEST_IMEI*/ + System.currentTimeMillis())
+        }
+        Logger.d("ServerInfo", "getImeiSecret : $imeiSecret")
+        return imeiSecret
+    }
+
+    fun setImeiSecret(secret: String) {
+        imeiSecret = secret
+        Logger.d("ServerInfo", "setImeiSecret~!~! : $imeiSecret")
+    }
+}

+ 30 - 0
app/src/main/java/com/xplora/commonservice/model/vm/OtaCheckResultViewModel.kt

@@ -0,0 +1,30 @@
+package com.xplora.commonservice.model.vm
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.xplora.commonservice.modules.OtaManager
+import com.xplora.commonservice.modules.OtaStatus
+import com.xplora.commonservice.modules.callbacks.ota.OtaUiFlowObserver
+import com.xplora.commonservice.utils.Logger
+
+class OtaCheckResultViewModel : ViewModel(), OtaUiFlowObserver {
+    private val _stageFlow = MutableLiveData<OtaStatus>()
+    val stageFlow: LiveData<OtaStatus>
+        get() = _stageFlow
+
+    private val _arg = MutableLiveData<Any?>()
+    val arg: LiveData<Any?>
+        get() = _arg
+
+    init {
+        _stageFlow.value = OtaStatus.UNKNOWN
+        _arg.value = null
+    }
+
+    override fun <T> onStageChange(stage: OtaStatus, arg: T?) {
+        Logger.d(OtaManager.TAG, "onStageChange: $stage")
+        arg?.let { _arg.value = arg }
+        _stageFlow.value = stage
+    }
+}

+ 54 - 0
app/src/main/java/com/xplora/commonservice/model/vm/ShareViewModel.kt

@@ -0,0 +1,54 @@
+package com.xplora.commonservice.model.vm
+
+import androidx.lifecycle.*
+
+val vMStores = HashMap<String, VMStore>()
+
+inline fun <reified VM : ViewModel> LifecycleOwner.shareViewModels(
+    scopeName: String,
+    factory: ViewModelProvider.Factory? = null
+): Lazy<VM> {
+    val store: VMStore
+    if (vMStores.keys.contains(scopeName)) {
+        store = vMStores[scopeName]!!
+    } else {
+        store = VMStore()
+        vMStores[scopeName] = store
+    }
+    store.register(this)
+    return ViewModelLazy(VM::class,
+        { store.viewModelStore },
+        { factory ?: ViewModelProvider.NewInstanceFactory() })
+}
+
+class VMStore : ViewModelStoreOwner {
+
+    private val bindTargets = ArrayList<LifecycleOwner>()
+    private var vmStore: ViewModelStore? = null
+
+    fun register(host: LifecycleOwner) {
+        if (!bindTargets.contains(host)) {
+            bindTargets.add(host)
+            host.lifecycle.addObserver(object : LifecycleEventObserver {
+                override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
+                    if (event == Lifecycle.Event.ON_DESTROY) {
+                        host.lifecycle.removeObserver(this)
+                        bindTargets.remove(host)
+                        if (bindTargets.isEmpty()) {//如果当前商店没有关联对象,则释放资源
+                            vMStores.entries.find { it.value == this@VMStore }?.also {
+                                vmStore?.clear()
+                                vMStores.remove(it.key)
+                            }
+                        }
+                    }
+                }
+            })
+        }
+    }
+
+    override fun getViewModelStore(): ViewModelStore {
+        if (vmStore == null)
+            vmStore = ViewModelStore()
+        return vmStore!!
+    }
+}

+ 500 - 0
app/src/main/java/com/xplora/commonservice/modules/ChatMsgManager.kt

@@ -0,0 +1,500 @@
+package com.xplora.commonservice.modules
+
+
+import com.xplora.commonservice.model.ChatFileType
+import com.xplora.commonservice.model.ChatMsgState.MESSAGE_STATE_READ
+import com.xplora.commonservice.model.ChatMsgState.MESSAGE_STATE_SEND_FAIL
+import com.xplora.commonservice.model.ChatMsgState.MESSAGE_STATE_SEND_ING
+import com.xplora.commonservice.model.ChatMsgState.MESSAGE_STATE_SEND_SUCCESS
+import com.xplora.commonservice.model.ChatMsgState.MESSAGE_STATE_UNKNOWN
+import com.xplora.commonservice.model.ChatMsgType.EMOJI
+import com.xplora.commonservice.model.ChatMsgType.MP3
+import com.xplora.commonservice.model.ChatMsgType.PHOTO
+import com.xplora.commonservice.model.ChatMsgType.TEXT
+import com.xplora.commonservice.model.ChatMsgType.VIDEO
+import com.xplora.commonservice.model.ChatMsgType.VOICE
+import com.xplora.commonservice.model.CkError
+import com.xplora.commonservice.model.HttpApi
+import com.xplora.commonservice.model.database.ChatDbDbEntity
+import com.xplora.commonservice.model.database.MusicDbDbEntity
+import com.xplora.commonservice.model.http.BaseResponse
+import com.xplora.commonservice.model.http.chat.*
+import com.xplora.commonservice.model.http.file.UploadRepo
+import com.xplora.commonservice.model.mqtt.BaseMessage
+import com.xplora.commonservice.modules.NotificationManager.showChatNotification
+import com.xplora.commonservice.modules.WatchStateChangeImpl.watchBaseInfo
+import com.xplora.commonservice.modules.database.DatabaseManager
+import com.xplora.commonservice.modules.database.DatabaseManager.deleteChatMsgByPush
+import com.xplora.commonservice.modules.database.DatabaseManager.insertMusic
+import com.xplora.commonservice.modules.database.DatabaseManager.queryChatMsgByMsgId
+import com.xplora.commonservice.modules.database.DatabaseManager.queryContact
+import com.xplora.commonservice.modules.database.DatabaseManager.updateChatMsg
+import com.xplora.commonservice.modules.database.DatabaseManager.updateContact
+import com.xplora.commonservice.modules.http.FileDownloader
+import com.xplora.commonservice.modules.http.FileUploader
+import com.xplora.commonservice.modules.http.FlowableFactory.getFlowable
+import com.xplora.commonservice.modules.http.XPHttpServiceExecutor
+import com.xplora.commonservice.utils.FileUtil
+import com.xplora.commonservice.utils.Logger
+import kotlinx.coroutines.*
+import okhttp3.HttpUrl.Companion.toHttpUrl
+
+@OptIn(DelicateCoroutinesApi::class)
+object ChatMsgManager {
+    const val TAG = "ChatMsgManager"
+
+    fun updateMsg(pushMsg: BaseMessage<*>) {
+        GlobalScope.launch {
+            val pushId = pushMsg.data!!.xpPushId!!
+            val request = MsgReq(pushMsg.data!!.msgId!!)
+            XPHttpServiceExecutor.execute(
+                pushId,
+                request,
+                getFlowable(HttpApi.Chat.MSG, request, pushId)
+            ).let {
+                if (it.ckError == CkError.SUCCESS) {
+                    (it as MsgRepo).run {
+                        val chatDbEntity = ChatDbDbEntity().from(this.data)
+                        if (this.data.type!!.toInt() != MP3) {
+                            chatDbEntity.filePath = "1"
+                            if (this.data.type!!.toInt() == VIDEO) chatDbEntity.videoPath = "1"
+                            chatDbEntity._id = DatabaseManager.insertChatMsg(chatDbEntity)
+                            val contacts = queryContact()
+                            for (contact in contacts) {
+                                if (contact.userId == chatDbEntity.senderUserId) {
+                                    contact.unRead = (contact.unRead.toInt() + 1).toString()
+                                    updateContact(contact)
+                                }
+                            }
+                            showChatNotification(chatDbEntity)
+                        }
+                        download(this.data.type!!.toInt(), chatDbEntity)
+                    }
+                }
+            }
+        }
+    }
+
+    fun getMsgState(pushMsg: BaseMessage<*>) {
+        GlobalScope.launch {
+            val pushId = pushMsg.data!!.xpPushId
+            val request = GetMsgStateReq(pushMsg.data!!.msgIds!!)
+            XPHttpServiceExecutor.execute(
+                pushId,
+                request,
+                getFlowable(HttpApi.Chat.Msg.STATE, request, pushId)
+            ).let {
+                if (it.ckError == CkError.SUCCESS) {
+                    for (msg in (it as GetMsgStateRepo).data) {
+                        val entity = queryChatMsgByMsgId(msg.msgId)
+                        if (entity != null) {
+                            entity.state = msg.state.toString()
+                            entity.create = msg.create.toString()
+                            entity.update = msg.update.toString()
+                            entity.localState = when (msg.state) {
+                                1 -> MESSAGE_STATE_SEND_SUCCESS.toString()
+                                2 -> MESSAGE_STATE_READ.toString()
+                                3 -> MESSAGE_STATE_SEND_FAIL.toString()
+                                else -> MESSAGE_STATE_UNKNOWN.toString()
+                            }
+                            updateChatMsg(entity)
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    fun refreshMsgList(userId: String, msgId: String?, offset: Int, limit: Int) {
+        GlobalScope.launch {
+            Logger.d(TAG, "refreshMsgList, $userId, $msgId")
+            val request = MsgListReq(
+                userId = userId,
+                offset = offset,
+                limit = limit,
+                msgId = msgId
+            )
+            XPHttpServiceExecutor.execute(
+                null,
+                request,
+                getFlowable(HttpApi.Chat.Msg.LIST, request, null)
+            ).let {
+                Logger.d(TAG, "refreshMsgList result")
+                if (it.ckError == CkError.SUCCESS) {
+                    (it as MsgListRepo).run {
+                        if (this.total > (offset + limit)) {
+                            refreshMsgList(userId, msgId, offset + limit, limit)
+                        }
+                        for (info in this.list) {
+                            val chatDbEntity = ChatDbDbEntity().from(info)
+                            if (info.type!!.toInt() != MP3 && info.incomming) {
+                                chatDbEntity.senderUserId = userId
+                                chatDbEntity.receiverId = watchBaseInfo.userId
+                                chatDbEntity.filePath = "1"
+                                if (info.type!!.toInt() == VIDEO) chatDbEntity.videoPath = "1"
+                                chatDbEntity._id = DatabaseManager.insertChatMsg(chatDbEntity)
+                                download(info.type!!.toInt(), chatDbEntity)
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    fun refreshMsgListBySelf() {
+        val contacts = queryContact()
+        for (contact in contacts) {
+            val lastMsg = DatabaseManager.queryLastChatMsg(contact.userId!!) ?: continue
+            refreshMsgList(contact.userId!!, lastMsg.msgId, 0, 10)
+        }
+    }
+
+    //        if (entity.senderUserId != watchBaseInfo.userId && (entity.localState != "5") && (entity.videoPath != "2" && entity.filePath != "2")) return
+    fun resolveChatMsgUpdate(entity: ChatDbDbEntity) {
+        GlobalScope.launch {
+            if (entity.localState.toInt() == MESSAGE_STATE_SEND_ING) {
+                sendChatMsg(entity)
+            } else if (entity.localState.toInt() == MESSAGE_STATE_READ && entity.senderUserId != watchBaseInfo.userId) {
+                val request = SetMsgReadReq(arrayListOf(entity.msgId!!))
+                XPHttpServiceExecutor.execute(
+                    null,
+                    request,
+                    getFlowable(HttpApi.Chat.Msg.READ, request, null)
+                )
+            }
+            if (entity.filePath == "2" || entity.videoPath == "2") {
+                download(entity.type!!.toInt(), entity)
+            }
+        }
+    }
+
+    fun deleteMsgFromPush(pushMsg: BaseMessage<*>) {
+        for (msgId in pushMsg.data!!.msgIds!!) {
+            deleteChatMsgByPush(msgId)
+        }
+    }
+
+    private fun sendChatMsg(entity: ChatDbDbEntity) {
+        GlobalScope.launch {
+            Logger.d(TAG, "sendChatMsg: ${entity.type!!.toInt()}")
+            var block: Deferred<BaseResponse<*>?>? = null
+            when (entity.type!!.toInt()) {
+                EMOJI -> {
+                    block = this.async {
+                        val request = SendEmojiReq(
+                            entity.emojiId!!,
+                            entity.receiverId!!
+                        )
+                        XPHttpServiceExecutor.execute(
+                            null,
+                            request,
+                            getFlowable(HttpApi.Chat.EMOJI, request, null)
+                        )
+                    }
+                }
+                TEXT -> {
+                    block = this.async {
+                        val request = SendTextReq(
+                            entity.text!!,
+                            entity.receiverId!!
+                        )
+                        XPHttpServiceExecutor.execute(
+                            null,
+                            request,
+                            getFlowable(HttpApi.Chat.TEXT, request, null)
+                        )
+                    }
+                }
+                PHOTO -> {
+                    if (entity.fid != null) {
+                        block = this.async {
+                            val request = SendPhotoReq(
+                                entity.fid!!,
+                                entity.receiverId!!
+                            )
+                            XPHttpServiceExecutor.execute(
+                                null,
+                                request,
+                                getFlowable(HttpApi.Chat.PHOTO, request, null)
+                            )
+                        }
+                    } else {
+                        block = this.async {
+                            val r: BaseResponse<*>
+                            FileUploader.execute(ChatFileType.PHOTO.ordinal, entity.filePath!!)
+                                .let {
+                                    r = if (it.ckError == CkError.SUCCESS) {
+                                        (it as UploadRepo).run {
+                                            entity.fid = arrayOf(this.fid).toString()
+                                            val request = SendPhotoReq(
+                                                this.fid,
+                                                entity.receiverId!!
+                                            )
+                                            XPHttpServiceExecutor.execute(
+                                                null,
+                                                request,
+                                                getFlowable(HttpApi.Chat.PHOTO, request, null)
+                                            )
+                                        }
+                                    } else it
+                                }
+                            r
+                        }
+                    }
+                }
+                VOICE -> {
+                    Logger.d(TAG, "sendChatMsg: VOICE")
+                    if (entity.fid != null) {
+                        block = this.async {
+                            val request = SendPhotoReq(entity.fid!!, entity.receiverId!!)
+                            XPHttpServiceExecutor.execute(
+                                null,
+                                request,
+                                getFlowable(HttpApi.Chat.PHOTO, request, null)
+                            )
+                        }
+                    } else {
+                        block = this.async {
+                            val r: BaseResponse<*>
+                            Logger.d(TAG, "sendChatMsg: VOICE new")
+                            FileUploader.execute(ChatFileType.VOICE.ordinal, entity.filePath!!)
+                                .let {
+                                    r = if (it.ckError == CkError.SUCCESS) {
+                                        (it as UploadRepo).run {
+                                            entity.fid = arrayOf(this.fid).toString()
+                                            val request = SendVoiceReq(
+                                                this.fid, entity.dur!!.toInt(), entity.receiverId!!
+                                            )
+                                            XPHttpServiceExecutor.execute(
+                                                null,
+                                                request,
+                                                getFlowable(HttpApi.Chat.VOICE, request, null)
+                                            )
+                                        }
+                                    } else it
+                                }
+                            r
+                        }
+                    }
+                }
+                VIDEO -> {
+                    val coverRepo: BaseResponse<*>?
+                    val videoRepo: BaseResponse<*>?
+                    val coverPath: String
+                    val videoPath: String
+                    if (entity.fid != null) {
+                        val list = entity.fid!!.split(",")
+                        coverPath = list[0]
+                        videoPath = list[1]
+                    } else {
+                        coverPath = entity.filePath!!
+                        videoPath = entity.videoPath!!
+                    }
+
+                    val coverBlock = this.async {
+                        withContext(Dispatchers.Default) {
+                            FileUploader.execute(
+                                ChatFileType.COVER.ordinal,
+                                coverPath
+                            )
+                        }
+                    }
+                    val videoBlock = this.async {
+                        withContext(Dispatchers.Default) {
+                            FileUploader.execute(
+                                ChatFileType.VIDEO.ordinal,
+                                videoPath
+                            )
+                        }
+                    }
+                    coverRepo = coverBlock.await()
+                    videoRepo = videoBlock.await()
+                    if (coverRepo.ckError == CkError.SUCCESS && videoRepo.ckError == CkError.SUCCESS
+                    ) {
+                        block = this.async {
+                            val request = SendVideoReq(
+                                (coverRepo as UploadRepo).fid,
+                                (videoRepo as UploadRepo).fid,
+                                entity.dur!!.toInt(),
+                                entity.receiverId!!
+                            )
+                            XPHttpServiceExecutor.execute(
+                                null,
+                                request,
+                                getFlowable(HttpApi.Chat.VIDEO, request, null)
+                            )
+                        }
+                    }
+                }
+            }
+
+            block?.let {
+                it.await()?.let { rs ->
+                    if (rs.ckError == CkError.SUCCESS) {
+                        (rs as SendMsgRepo).run {
+                            entity.localState = MESSAGE_STATE_SEND_SUCCESS.toString()
+                            entity.msgId = this.msgId
+                        }
+                    } else {
+                        entity.localState = MESSAGE_STATE_SEND_FAIL.toString()
+                    }
+                } ?: run { entity.localState = MESSAGE_STATE_SEND_FAIL.toString() }
+            } ?: run { entity.localState = MESSAGE_STATE_SEND_FAIL.toString() }
+            updateChatMsg(entity)
+        }
+    }
+
+    private fun download(type: Int, entity: ChatDbDbEntity) {
+        when (type) {
+            PHOTO -> {
+                downloadPhoto(entity)
+            }
+            VOICE -> {
+                downloadVoice(entity)
+            }
+            VIDEO -> {
+                downloadVideo(entity)
+            }
+            MP3 -> {
+                downloadMP3(entity)
+            }
+        }
+    }
+
+    private fun downloadMP3(entity: ChatDbDbEntity) {
+        GlobalScope.launch {
+            val musicDbEntity = MusicDbDbEntity()
+            musicDbEntity.url = entity.fileUrl
+            val t = this.async {
+                FileDownloader.execute(
+                    musicDbEntity.url!!,
+                    musicDbEntity.url!!.toHttpUrl().encodedPathSegments.last(),
+                    FileUtil.musicPath
+                )?.let {
+                    musicDbEntity.path = it.absolutePath
+                    musicDbEntity
+                } ?: run {
+                    musicDbEntity.path = "0"
+                    musicDbEntity
+                }
+            }
+            insertMusic(t.await())
+        }
+    }
+
+    private fun downloadPhoto(entity: ChatDbDbEntity) {
+        val timeout = GlobalScope.launch {
+            delay(30 * 1000)
+            entity.filePath = "0"
+            updateChatMsg(entity)
+        }
+        GlobalScope.launch {
+            entity.filePath = "1"
+            updateChatMsg(entity)
+            FileDownloader.execute(
+                entity.fileUrl!!,
+                entity.fileUrl!!.toHttpUrl().encodedPathSegments.last(),
+                FileUtil.photoPath
+            )?.let {
+                entity.filePath = it.absolutePath
+                updateChatMsg(entity)
+                if (!timeout.isCancelled && !timeout.isCompleted) {
+                    timeout.cancel()
+                }
+            } ?: run {
+                entity.filePath = "0"
+                updateChatMsg(entity)
+                if (!timeout.isCancelled && !timeout.isCompleted) {
+                    timeout.cancel()
+                }
+            }
+        }
+    }
+
+    private fun downloadVoice(entity: ChatDbDbEntity) {
+        val timeout = GlobalScope.launch {
+            delay(30 * 1000)
+            entity.filePath = "0"
+            updateChatMsg(entity)
+        }
+        GlobalScope.launch {
+            if (entity._id != null) {
+                entity.filePath = "1"
+                updateChatMsg(entity)
+            }
+            FileDownloader.execute(
+                entity.fileUrl!!,
+                entity.fileUrl!!.toHttpUrl().encodedPathSegments.last(),
+                FileUtil.voicePath
+            )?.let {
+                entity.filePath = it.absolutePath
+                updateChatMsg(entity)
+                if (!timeout.isCancelled && !timeout.isCompleted) {
+                    timeout.cancel()
+                }
+            } ?: run {
+                entity.filePath = "0"
+                updateChatMsg(entity)
+                if (!timeout.isCancelled && !timeout.isCompleted) {
+                    timeout.cancel()
+                }
+            }
+        }
+    }
+
+    private fun downloadVideo(entity: ChatDbDbEntity) {
+        val timeout = GlobalScope.launch {
+            delay(30 * 1000)
+            entity.filePath = "0"
+            entity.videoPath = "0"
+            updateChatMsg(entity)
+        }
+        entity.videoPath = "1"
+        entity.filePath = "1"
+        updateChatMsg(entity)
+        GlobalScope.launch {
+            if (entity.filePath!! == "1") {
+                FileDownloader.execute(
+                    entity.fileUrl!!,
+                    entity.fileUrl!!.toHttpUrl().encodedPathSegments.last(),
+                    FileUtil.videoPath
+                )?.let {
+                    entity.filePath = it.absolutePath
+                    updateChatMsg(entity)
+                    if (!timeout.isCancelled && !timeout.isCompleted) {
+                        timeout.cancel()
+                    }
+                } ?: run {
+                    entity.filePath = "0"
+                    updateChatMsg(entity)
+                    if (!timeout.isCancelled && !timeout.isCompleted) {
+                        timeout.cancel()
+                    }
+                }
+            }
+        }
+
+        GlobalScope.launch {
+            if (entity.videoPath!! == "1") {
+                FileDownloader.execute(
+                    entity.videoUrl!!,
+                    entity.videoUrl!!.toHttpUrl().encodedPathSegments.last(),
+                    FileUtil.videoPath
+                )?.let {
+                    entity.videoPath = it.absolutePath
+                    updateChatMsg(entity)
+                    if (!timeout.isCancelled && !timeout.isCompleted) {
+                        timeout.cancel()
+                    }
+                } ?: run {
+                    entity.videoPath = "0"
+                    updateChatMsg(entity)
+                    if (!timeout.isCancelled && !timeout.isCompleted) {
+                        timeout.cancel()
+                    }
+                }
+            }
+        }
+    }
+}

+ 93 - 0
app/src/main/java/com/xplora/commonservice/modules/ForbiddenManager.kt

@@ -0,0 +1,93 @@
+package com.xplora.commonservice.modules
+
+import android.content.ComponentName
+import android.content.Intent
+import android.provider.Settings
+import com.xplora.commonservice.BaseApplication
+import com.xplora.commonservice.modules.SoundManager.getRingerMode
+import com.xplora.commonservice.modules.SoundManager.setRingerMode
+import com.xplora.commonservice.utils.Logger
+import com.xplora.commonservice.utils.TeleUtil
+
+object ForbiddenManager {
+    private const val TAG = "ForbiddenManager"
+
+    private var soundMode = 2
+
+    enum class ForbiddenType {
+        NONE, S_MODE, CHARGING, HIGH_TEMP
+    }
+
+    fun changeForbiddenState(type: ForbiddenType, enable: Boolean) {
+        val onBoardingProgress =
+            Settings.Global.getInt(
+                BaseApplication.globalContext.contentResolver,
+                "onboarding_process",
+                0
+            )
+        if (onBoardingProgress < 2 || !WatchStateChangeImpl.watchBaseInfo.bootCompleted) {
+            Logger.d(TAG, "onBoarding in progress, donot disable")
+            return
+        }
+
+        val forbiddenStatus =
+            Settings.Global.getInt(
+                BaseApplication.globalContext.contentResolver,
+                "disable_status",
+                0
+            )
+        val silentStatus =
+            Settings.Global.getInt(
+                BaseApplication.globalContext.contentResolver,
+                "silent_status",
+                0
+            )
+
+        if (enable && type.ordinal > forbiddenStatus) {
+            if(forbiddenStatus == 0) {
+                soundMode = getRingerMode()
+                setRingerMode(0)
+            }
+            Settings.Global.putInt(
+                BaseApplication.globalContext.contentResolver,
+                "disable_status",
+                type.ordinal
+            )
+
+            if (Settings.Global.getInt(
+                    BaseApplication.globalContext.contentResolver,
+                    "close_disable",
+                    0
+                ) == 0
+            ) {
+                // back to home
+                val i = Intent()
+                val componentName = ComponentName(
+                    "com.xplora.xplauncher",
+                    "com.xplora.xplauncher.activity.MainActivity"
+                )
+                i.component = componentName
+                BaseApplication.globalContext.startActivity(i)
+
+                //End call
+                TeleUtil.endCall()
+            }
+
+        } else if (!enable && type.ordinal <= forbiddenStatus) {
+            /*else if (chargeStatus) {
+                       2
+           }*/
+            val value = if (silentStatus > 0) {
+                1
+            } else {
+                0
+            }
+            if(value == 0) setRingerMode(soundMode)
+            Settings.Global.putInt(
+                BaseApplication.globalContext.contentResolver,
+                "disable_status",
+                value
+            )
+        }
+    }
+}

+ 521 - 0
app/src/main/java/com/xplora/commonservice/modules/NotificationManager.kt

@@ -0,0 +1,521 @@
+package com.xplora.commonservice.modules
+
+import android.annotation.SuppressLint
+import android.app.*
+import android.app.NotificationManager
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.os.PowerManager
+import android.provider.Settings
+import android.widget.RemoteViews
+import android.widget.Toast
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationManagerCompat
+import androidx.core.graphics.drawable.IconCompat
+import com.xplora.commonservice.BaseApplication
+import com.xplora.commonservice.BaseApplication.Companion.globalContext
+import com.xplora.commonservice.R
+import com.xplora.commonservice.model.ChatMsgType
+import com.xplora.commonservice.model.LocalActions.NOTIFICATION_CHAT
+import com.xplora.commonservice.model.LocalActions.NOTIFICATION_MISSED_CALL
+import com.xplora.commonservice.model.LocalActions.NOTIFICATION_RINGTONE
+import com.xplora.commonservice.model.LocalActions.NOTIFICATION_WATCHFACE
+import com.xplora.commonservice.model.NotificationDetails
+import com.xplora.commonservice.model.database.ChatDbDbEntity
+import com.xplora.commonservice.modules.database.DatabaseManager
+import com.xplora.commonservice.modules.goplay.GoplayContentManager
+import com.xplora.commonservice.ui.activity.EmptyActivity
+import com.xplora.commonservice.ui.activity.OtaCheckActivity
+import java.text.SimpleDateFormat
+import java.util.*
+
+object NotificationManager {
+    var batteryNotifyLevel = -1
+    @SuppressLint("StaticFieldLeak")
+    private val notificationManager: NotificationManagerCompat =
+        NotificationManagerCompat.from(globalContext)
+    private val chatChannel = NotificationChannel(
+        "chat",
+        "xplora chat notification",
+        NotificationManager.IMPORTANCE_DEFAULT
+    )
+
+    private val systemChannel = NotificationChannel(
+        "system",
+        "system notification",
+        NotificationManager.IMPORTANCE_HIGH
+    )
+
+    private val otaChannel = NotificationChannel(
+        "ota",
+        "xplora ota notification",
+        NotificationManager.IMPORTANCE_DEFAULT
+    )
+
+    init {
+        chatChannel.vibrationPattern = longArrayOf(0, 500)
+        chatChannel.enableVibration(true)
+        notificationManager.createNotificationChannel(chatChannel)
+        systemChannel.vibrationPattern = longArrayOf(0, 500)
+        systemChannel.enableVibration(true)
+        notificationManager.createNotificationChannel(systemChannel)
+        otaChannel.vibrationPattern = longArrayOf(0, 500)
+        otaChannel.enableVibration(true)
+        notificationManager.createNotificationChannel(otaChannel)
+    }
+
+    private enum class NotificationTypes {
+        CHAT, BATTERY_LOW, CHARGING, MISSED_CALL, OTA, NEW_RINGTONE, NEW_WATCHFACE
+    }
+
+    private val ids = mapOf(
+        NotificationTypes.CHAT.name to 0x1a,
+        NotificationTypes.BATTERY_LOW.name to 0x1b,
+        NotificationTypes.CHARGING.name to 0x1c,
+        NotificationTypes.MISSED_CALL.name to 0x1d,
+        NotificationTypes.NEW_RINGTONE.name to 0x1e,
+        NotificationTypes.NEW_WATCHFACE.name to 0x1f,
+        NotificationTypes.OTA.name to 0xFF
+    )
+
+    fun showBatteryLowNotify() {
+        val icon = IconCompat.createWithResource(globalContext, R.drawable.ic_notify_battery_low)
+        val details = NotificationDetails(
+            ids[NotificationTypes.BATTERY_LOW.name]!!,
+            "system",
+            globalContext.getString(R.string.package_name_system),
+            globalContext.getColor(R.color.notify_battery_low),
+            globalContext.getString(R.string.battery_low),
+            globalContext.getString(R.string.battery_low),
+            icon
+        )
+        notifyNotification(details)
+    }
+
+    fun cancelBatteryLowNotify() {
+        val icon = IconCompat.createWithResource(globalContext, R.drawable.ic_notify_battery_low)
+        val details = NotificationDetails(
+            ids[NotificationTypes.BATTERY_LOW.name]!!,
+            "system",
+            globalContext.getString(R.string.package_name_system),
+            globalContext.getColor(R.color.notify_battery_low),
+            globalContext.getString(R.string.battery_low),
+            globalContext.getString(R.string.battery_low),
+            icon
+        )
+        cancelNotification(details)
+    }
+
+    fun showChargingNotify() {
+        val icon = IconCompat.createWithResource(globalContext, R.drawable.ic_notify_charging)
+        val details = NotificationDetails(
+            ids[NotificationTypes.CHARGING.name]!!,
+            "system",
+            globalContext.getString(R.string.package_name_system),
+            globalContext.getColor(R.color.notify_charging),
+            globalContext.getString(R.string.charging),
+            globalContext.getString(R.string.charging),
+            icon
+        )
+        notifyNotification(details)
+        cancelBatteryLowNotify()
+    }
+
+    fun cancelChargingNotify() {
+        val icon = IconCompat.createWithResource(globalContext, R.drawable.ic_notify_charging)
+        val details = NotificationDetails(
+            ids[NotificationTypes.CHARGING.name]!!,
+            "system",
+            globalContext.getString(R.string.package_name_system),
+            globalContext.getColor(R.color.notify_charging),
+            globalContext.getString(R.string.charging),
+            globalContext.getString(R.string.charging),
+            icon
+        )
+        cancelNotification(details)
+    }
+
+    fun showChatNotification(chatDbEntity: ChatDbDbEntity) {
+        if (isChat()) return
+        var name = ""
+        for (entity in DatabaseManager.queryContact()) {
+            if (chatDbEntity.senderUserId == entity.userId) {
+                name = entity.name!!
+            }
+        }
+        val content =
+            if (chatDbEntity.type == ChatMsgType.TEXT.toString()) {
+                chatDbEntity.text!!
+            } else {
+                globalContext.getString(R.string.new_msg)
+            }
+        val icon = IconCompat.createWithResource(globalContext, R.drawable.ic_notify_chat)
+
+        val intent = Intent(NOTIFICATION_CHAT)
+        // intent.action = LocalActions.NOTIFICATION_CHAT
+        intent.component = ComponentName(
+            "com.xplora.commonservice",
+            "com.xplora.commonservice.modules.callbacks.NotificationReceiver"
+        )
+        val sender: PendingIntent = PendingIntent.getBroadcast(
+            globalContext,
+            0,
+            intent,
+            PendingIntent.FLAG_UPDATE_CURRENT
+        )
+        val contacts = DatabaseManager.queryContact()
+        var unreadCount = 0
+        for (contact in contacts) {
+            unreadCount += contact.unRead.toInt()
+        }
+        if (unreadCount >= 2) {
+            name.plus("($unreadCount)")
+        }
+        val detail = NotificationDetails(
+            ids[NotificationTypes.CHAT.name]!!,
+            "chat",
+            globalContext.getString(R.string.package_chat),
+            globalContext.getColor(R.color.notify_chat),
+            name,
+            content,
+            icon,
+            sender
+        )
+        notifyNotification(detail)
+        postToast(globalContext.getString(R.string.new_msg))
+        notifyWatchHome(globalContext.getString(R.string.new_msg))
+    }
+
+    fun cancelChatNotification() {
+        val icon = IconCompat.createWithResource(globalContext, R.drawable.ic_notify_chat)
+        val detail = NotificationDetails(
+            ids[NotificationTypes.CHAT.name]!!,
+            "chat",
+            globalContext.getString(R.string.package_chat),
+            globalContext.getColor(R.color.notify_chat),
+            icon = icon
+        )
+        cancelNotification(detail)
+    }
+
+    fun showOtaNotification() {
+        if (isOtaActivity()) return
+        val title = globalContext.getString(R.string.ota_title)
+        val content = globalContext.getString(R.string.new_available)
+        val icon = IconCompat.createWithResource(globalContext, R.drawable.ic_notify_ota)
+
+        val intent = Intent()
+        intent.component = ComponentName(
+            globalContext.packageName,
+            OtaCheckActivity::class.java.name
+        )
+        val sender: PendingIntent = PendingIntent.getActivity(
+            globalContext,
+            0,
+            intent,
+            PendingIntent.FLAG_UPDATE_CURRENT
+        )
+        val detail = NotificationDetails(
+            ids[NotificationTypes.OTA.name]!!,
+            "ota",
+            globalContext.getString(R.string.package_name_ota),
+            globalContext.getColor(R.color.notify_ota),
+            title,
+            content,
+            icon,
+            sender
+        )
+        notifyNotification(detail)
+        postToast(globalContext.getString(R.string.new_available))
+        notifyWatchHome(globalContext.getString(R.string.new_available))
+    }
+
+    fun cancelOtaNotification() {
+        val icon = IconCompat.createWithResource(globalContext, R.drawable.ic_notify_ota)
+        val detail = NotificationDetails(
+            ids[NotificationTypes.OTA.name]!!,
+            "ota",
+            globalContext.getString(R.string.package_name_ota),
+            globalContext.getColor(R.color.notify_ota),
+            icon = icon
+        )
+        cancelNotification(detail)
+    }
+
+    fun showMissedCallNotification(number: String) {
+        val contacts = DatabaseManager.queryContact()
+        var name = "UNKNOWN"
+        for (contact in contacts) {
+            if (contact.phoneNumber == number || (contact.phoneNumber!!.length >= 5 && (contact.phoneNumber!!.takeLast(
+                    5
+                ) == number.takeLast(5)))
+            ) {
+                name = contact.name!!
+                break
+            }
+        }
+        val title = name
+        val content = globalContext.getString(R.string.missed_call)
+        val icon = IconCompat.createWithResource(globalContext, R.drawable.ic_notify_missed_call)
+
+        val intent = Intent(NOTIFICATION_MISSED_CALL)
+        intent.putExtra("number", number)
+        // intent.action = LocalActions.NOTIFICATION_CHAT
+        intent.component = ComponentName(
+            "com.xplora.commonservice",
+            "com.xplora.commonservice.modules.callbacks.NotificationReceiver"
+        )
+        val sender: PendingIntent = PendingIntent.getBroadcast(
+            globalContext,
+            0,
+            intent,
+            PendingIntent.FLAG_UPDATE_CURRENT
+        )
+        val detail = NotificationDetails(
+            ids[NotificationTypes.MISSED_CALL.name]!!,
+            "system",
+            globalContext.getString(R.string.missed_call),
+            globalContext.getColor(R.color.notify_missed_call),
+            title,
+            content,
+            icon,
+            sender
+        )
+        notifyNotification(detail)
+    }
+
+    fun cancelMissedCallNotification() {
+        val icon = IconCompat.createWithResource(globalContext, R.drawable.ic_notify_missed_call)
+        val detail = NotificationDetails(
+            ids[NotificationTypes.MISSED_CALL.name]!!,
+            "system",
+            globalContext.getString(R.string.missed_call_title),
+            globalContext.getColor(R.color.notify_missed_call),
+            icon = icon
+        )
+        cancelNotification(detail)
+    }
+
+    fun showGoplayContentNotification(name: String, type: String) {
+        // if (isInRingtone()) return
+        val id: Int
+        val packageName: String
+        val color: Int
+        val title: String
+        val icon: IconCompat
+        val i: Intent
+        val notifyString: String
+        when (type) {
+            GoplayContentManager.CONTENT_TYPE_RINGTONE -> {
+                id = ids[NotificationTypes.NEW_RINGTONE.name]!!
+                packageName = globalContext.getString(R.string.new_ringtone)
+                color = globalContext.getColor(R.color.notify_ringtone)
+                title = globalContext.getString(R.string.new_ringtone)
+                icon = IconCompat.createWithResource(globalContext, R.drawable.ic_ringtone)
+                i = Intent(NOTIFICATION_RINGTONE)
+                notifyString = globalContext.getString(R.string.new_ringtone)
+                if (!isInRingtone()) postToast(globalContext.getString(R.string.new_ringtone))
+            }
+            GoplayContentManager.CONTENT_TYPE_WATCHFACE -> {
+                id = ids[NotificationTypes.NEW_WATCHFACE.name]!!
+                packageName = globalContext.getString(R.string.new_watchface)
+                color = globalContext.getColor(R.color.notify_watchface)
+                title = globalContext.getString(R.string.new_watchface)
+                icon = IconCompat.createWithResource(globalContext, R.drawable.ic_watchface)
+                i = Intent(NOTIFICATION_WATCHFACE)
+                notifyString = globalContext.getString(R.string.new_watchface)
+                if (!isInWatchface()) postToast(globalContext.getString(R.string.new_watchface))
+            }
+            else -> {
+                return
+            }
+        }
+
+        i.component = ComponentName(
+            "com.xplora.commonservice",
+            "com.xplora.commonservice.modules.callbacks.NotificationReceiver"
+        )
+        val sender: PendingIntent = PendingIntent.getBroadcast(
+            globalContext,
+            0,
+            i,
+            PendingIntent.FLAG_UPDATE_CURRENT
+        )
+        val detail =
+            NotificationDetails(id, "system", packageName, color, title, name, icon, sender)
+        notifyNotification(detail)
+        notifyWatchHome(notifyString)
+        if (type == GoplayContentManager.CONTENT_TYPE_RINGTONE && (isLauncher() || isInRingtone())) {
+            val ii = Intent(globalContext, EmptyActivity::class.java)
+            ii.putExtra("type", EmptyActivity.TYPE_NEW_RINGTONE)
+            globalContext.startActivity(ii)
+        }
+        if (type == GoplayContentManager.CONTENT_TYPE_WATCHFACE && (isLauncher() || isInWatchface())) {
+            val ii = Intent(globalContext, EmptyActivity::class.java)
+            ii.putExtra("type", EmptyActivity.TYPE_NEW_WATCHFACE)
+            globalContext.startActivity(ii)
+        }
+    }
+
+    fun cancelRingtoneNotification() {
+        val icon = IconCompat.createWithResource(globalContext, R.drawable.ic_ringtone)
+        val detail = NotificationDetails(
+            ids[NotificationTypes.NEW_RINGTONE.name]!!,
+            "system",
+            globalContext.getString(R.string.new_ringtone),
+            globalContext.getColor(R.color.notify_ringtone),
+            icon = icon
+        )
+        cancelNotification(detail)
+    }
+
+    fun cancelWatchfaceNotification() {
+        val icon = IconCompat.createWithResource(globalContext, R.drawable.ic_watchface)
+        val detail = NotificationDetails(
+            ids[NotificationTypes.NEW_WATCHFACE.name]!!,
+            "system",
+            globalContext.getString(R.string.new_watchface),
+            globalContext.getColor(R.color.notify_watchface),
+            icon = icon
+        )
+        cancelNotification(detail)
+    }
+
+    private fun notifyNotification(detail: NotificationDetails) {
+        val notificationCompatBuilder =
+            NotificationCompat.Builder(globalContext, detail.channelId)
+                .setContentTitle(detail.notificationTitle)
+                .setContentText(detail.notificationText)
+                .setAutoCancel(true)
+
+        val icon: IconCompat = detail.icon
+        notificationCompatBuilder.setSmallIcon(icon)
+
+        val bundle = Bundle()
+        bundle.putString("android.substName", detail.packageName)
+        notificationCompatBuilder.addExtras(bundle)
+        if (Settings.Global.getInt(
+                globalContext.contentResolver,
+                Settings.System.VIBRATE_WHEN_RINGING,
+                0
+            ) == 0
+        ) {
+            notificationCompatBuilder.setVibrate(null)
+            notificationCompatBuilder.setVibrate(longArrayOf(0))
+        }
+        val forbiddenStatus =
+            Settings.Global.getInt(
+                globalContext.contentResolver,
+                "disable_status",
+                0
+            )
+        if (forbiddenStatus > 0) {
+            notificationCompatBuilder.setSilent(true)
+            notificationCompatBuilder.setVibrate(longArrayOf(0, 500))
+        }
+
+        val contentView = RemoteViews(globalContext.packageName, R.layout.layout_notification)
+        detail.contentIntent?.let { contentView.setOnClickPendingIntent(R.id.notification, it) }
+        contentView.setImageViewIcon(R.id.icon, detail.icon.toIcon(globalContext))
+        contentView.setTextViewText(R.id.name, detail.packageName)
+        contentView.setTextColor(R.id.name, detail.color!!)
+        val strTimeFormat = Settings.System.getString(
+            globalContext.contentResolver,
+            Settings.System.TIME_12_24
+        )
+        val format = if (strTimeFormat == "24") {
+            SimpleDateFormat("HH:mm")
+        } else {
+            SimpleDateFormat("hh:mm a")
+        }
+        contentView.setTextViewText(
+            R.id.time,
+            format.format(Date())
+        )
+        contentView.setTextViewText(R.id.title, detail.notificationTitle)
+        contentView.setTextViewText(R.id.text, detail.notificationText)
+        notificationCompatBuilder.setContent(contentView)
+        notificationCompatBuilder.setGroupSummary(false)
+        notificationCompatBuilder.setGroup("group")
+
+        val notification = notificationCompatBuilder.build()
+        notification.flags = Notification.FLAG_AUTO_CANCEL
+        notificationManager.notify(detail.id, notification)
+        val mPm =
+            globalContext.getSystemService(Context.POWER_SERVICE) as PowerManager
+        val wl = mPm.newWakeLock(
+            PowerManager.ACQUIRE_CAUSES_WAKEUP or PowerManager.FULL_WAKE_LOCK,
+            "CommonService:Notification"
+        )
+        wl.acquire(1)
+    }
+
+    private fun cancelNotification(detail: NotificationDetails) {
+        notificationManager.cancel(detail.id)
+    }
+
+    private fun notifyWatchHome(content: String) {
+        Settings.Global.putString(globalContext.contentResolver, "new_message", content)
+    }
+
+    private fun postToast(text: String) {
+        if (isLauncher()) return
+        val mPm =
+            globalContext.getSystemService(Context.POWER_SERVICE) as PowerManager
+        val wl = mPm.newWakeLock(
+            PowerManager.ACQUIRE_CAUSES_WAKEUP or PowerManager.FULL_WAKE_LOCK,
+            "NotificationManager:postNotification"
+        )
+        wl.acquire(1000)
+        val handler = Handler(Looper.getMainLooper())
+        handler.post {
+            Toast.makeText(
+                globalContext,
+                text,
+                Toast.LENGTH_SHORT
+            ).show()
+        }
+    }
+
+    private fun isOtaActivity(): Boolean {
+        val am: ActivityManager =
+            globalContext.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
+        val activityName: String? = am.getRunningTasks(1)[0].topActivity?.className
+        return (activityName == "com.xplora.commonservice.ui.activity.ForceUpdateActivity" ||
+                activityName == "com.xplora.commonservice.ui.activity.OtaCheckActivity" ||
+                activityName == "com.xplora.commonservice.ui.activity.OtaInstallOverNightActivity")
+    }
+
+    private fun isChat(): Boolean {
+        val am: ActivityManager =
+            globalContext.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
+        val packageName: String? = am.getRunningTasks(1)[0].topActivity?.packageName
+        return packageName == "com.xplora.xpchat"
+    }
+
+    private fun isLauncher(): Boolean {
+        val am: ActivityManager =
+            globalContext.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
+        val activityName: String? = am.getRunningTasks(1)[0].topActivity?.className
+        return activityName == "com.xplora.xplauncher.activity.MainActivity" &&
+                Settings.Global.getInt(globalContext.contentResolver, "launcher_home", 0) == 1
+    }
+
+    private fun isInRingtone(): Boolean {
+        val am: ActivityManager =
+            globalContext.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
+        val activityName: String? = am.getRunningTasks(1)[0].topActivity?.className
+        return activityName == "com.xplora.xpsettings.Activity.RingToneActivity"
+    }
+
+    private fun isInWatchface(): Boolean {
+        val am: ActivityManager =
+            globalContext.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
+        val activityName: String? = am.getRunningTasks(1)[0].topActivity?.className
+        return activityName == "com.xplora.xplauncher.activity.FaceSelectActivity"
+    }
+}

+ 275 - 0
app/src/main/java/com/xplora/commonservice/modules/OtaManager.kt

@@ -0,0 +1,275 @@
+package com.xplora.commonservice.modules
+
+import android.content.ComponentName
+import android.content.Intent
+import com.redstone.ota.https.RsNetStateHelpers
+import com.redstone.ota.main.*
+import com.xplora.commonservice.BaseApplication.Companion.globalContext
+import com.xplora.commonservice.model.NetworkType
+import com.xplora.commonservice.modules.NotificationManager.showOtaNotification
+import com.xplora.commonservice.modules.XPAlarmManager.setOtaAlarm
+import com.xplora.commonservice.modules.XPAlarmManager.setOtaCheckAlarm
+import com.xplora.commonservice.modules.callbacks.NetworkStateChangeObserver
+import com.xplora.commonservice.modules.callbacks.ota.*
+import com.xplora.commonservice.ui.activity.ForceUpdateActivity
+import com.xplora.commonservice.utils.Logger
+
+object OtaManager : OtaStatusObserver, NetworkStateChangeObserver {
+    const val TAG = "OtaManager"
+    private val checkResultCallback = OtaCheckRequestCallback(observer = this)
+    private val downloadCallback = OtaDownloadRequestCallback(observer = this)
+    private val installCallback = OtaInstallListener(observer = this)
+    private val mObservers: MutableList<OtaUiFlowObserver> =
+        ArrayList()
+    private var mStatus = OtaStatus.CHECKING
+    private var mArg: Any? = null
+
+    fun registerObserver(observer: OtaUiFlowObserver?) {
+        if (observer == null) {
+            return
+        }
+        if (!mObservers.contains(observer)) {
+            mObservers.add(observer)
+            if (mStatus == OtaStatus.NEED_WIFI) {
+                forceCheck(false)
+            }
+            notifyObservers(mArg)
+        }
+    }
+
+    fun unRegisterObserver(observer: OtaUiFlowObserver?) {
+        if (observer == null) {
+            return
+        }
+        mObservers.remove(observer)
+    }
+
+    fun initOta() {
+        RsOtaManager.getInstance().initAll(globalContext)
+        RsOtaManager.getInstance().setCheckListener(checkResultCallback)
+        RsOtaManager.getInstance().setDownloadRequestCallback(downloadCallback)
+        RsOtaManager.getInstance().setInstallCallback(installCallback)
+        RsOtaManager.getInstance().setDeviceInfo(OtaDeviceInfoCallback)
+        RsOtaAgent.getInstance().autoUpdate(globalContext)
+
+        RsInstallManger.getInstance().installResultVerify()
+
+        resetOtaStatus()
+    }
+
+    var lastCheck = 0L
+    fun forceCheck(byUser: Boolean) {
+        val now = System.currentTimeMillis()
+        val gap = now - lastCheck
+        if (!byUser && gap < 12 * 60 * 60 * 1000) {
+            Logger.d(TAG, "check in 12h, do nothing here")
+            return
+        }
+        when (RsUiStage.getInstance().uiStage) {
+            RsUiStage.RS_UI_STAGE_DOWNLOAD_END -> {
+                if (RsOtaManager.getInstance().updateEntity != null) {
+                    onDownloadSuccess()
+                } else {
+                    resetOtaStatus()
+                    mStatus = OtaStatus.CHECKING
+                    RsOtaAgent.getInstance().forceUpdate(globalContext)
+                }
+            }
+            else -> {
+                if ((mStatus == OtaStatus.NEED_WIFI || mStatus == OtaStatus.DOWNLOAD_FAIL) && !RsNetStateHelpers.getInstance()
+                        .isWifiAvailable(globalContext)
+                ) {
+                    mStatus = OtaStatus.NEED_WIFI
+                    notifyObservers(null)
+                } else if (mStatus == OtaStatus.DOWNLOADING) {
+                    //just downloading, do not do any thing
+                } else {
+                    mStatus = OtaStatus.CHECKING
+                    RsOtaAgent.getInstance().forceUpdate(globalContext)
+                }
+            }
+        }
+    }
+
+    fun beginDownload() {
+        if (RsNetStateHelpers.getInstance().isWifiAvailable(globalContext) ||
+            RsOtaManager.getInstance().updateEntity.autodl == 2
+        ) {
+            //wifi可用自动下载
+            mStatus = OtaStatus.DOWNLOADING
+            RsOtaAgent.getInstance()
+                .download(globalContext, RsOtaManager.getInstance().updateEntity)
+        } else {
+            mStatus = OtaStatus.NEED_WIFI
+            notifyObservers(RsOtaManager.getInstance().updateEntity)
+        }
+    }
+
+    fun install() {
+        if (WatchStateChangeImpl.watchBaseInfo.battery >= 35) {
+            RsInstallManger.getInstance()
+                .startInstall(RsOtaManager.getInstance().updateEntity.fileSavePath)
+        } else {
+            mStatus = OtaStatus.CHECK_FAIL
+            notifyObservers(18)
+        }
+    }
+
+    private fun resetOtaStatus() {
+        RsUiStage.getInstance().uiStage = RsUiStage.RS_UI_STAGE_IDLE
+        RsOtaManager.getInstance().deleteUpdatePkg()
+        RsOtaManager.getInstance().resetStateToIDlE()
+    }
+
+    private fun <T> notifyObservers(arg: T?) {
+        mArg = arg
+        for (observer in mObservers) {
+            observer.onStageChange(mStatus, arg)
+        }
+        if (mObservers.size == 0 && mStatus == OtaStatus.CHECK_SUCCESS &&
+            arg is RsFwUpdatePackage && arg.version != null && arg.version.isNotEmpty()
+        ) {
+            showOtaNotification()
+        }
+    }
+
+    override fun onCheckSuccess(pkg: RsFwUpdatePackage) {
+        lastCheck = System.currentTimeMillis()
+        setOtaCheckAlarm()
+        if (/*pkg.forceUpdate == 1 && */WatchStateChangeImpl.watchBaseInfo.battery < 35
+            && (pkg.version != null && pkg.version.isNotEmpty())
+        ) {
+            Logger.w(TAG, "check OTA when buttery less than 35%, do nothing")
+            mStatus = OtaStatus.CHECK_FAIL
+            notifyObservers(18)
+            return
+        }
+        mStatus = OtaStatus.CHECK_SUCCESS
+        // 自动下载控制
+        val autodl = pkg.autodl
+        if (autodl == 1) { // 后台勾选wifi下自动下载
+            if (RsNetStateHelpers.getInstance().isWifiAvailable(globalContext)) {
+                //wifi可用自动下载
+                beginDownload()
+            } else {
+                mStatus = OtaStatus.NEED_WIFI
+            }
+        } else if (autodl == 2) { // 后台勾选任意网络下载
+            if (RsNetStateHelpers.getInstance().isNetworkAvailable(globalContext)) {
+                val forceIntent = Intent()
+                forceIntent.component =
+                    ComponentName(
+                        globalContext.packageName,
+                        ForceUpdateActivity::class.qualifiedName!!
+                    )
+                forceIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+                globalContext.startActivity(forceIntent)
+                //beginDownload()
+            }
+        }
+        notifyObservers(pkg)
+    }
+
+    override fun onCheckFail(code: Int) {
+        if (code == -1) {
+            if (RsUiStage.getInstance().uiStage == RsUiStage.RS_UI_STAGE_CHECK_END
+                || RsOtaManager.getInstance().updateEntity != null
+            ) {
+                mStatus = OtaStatus.CHECK_SUCCESS
+                notifyObservers(RsOtaManager.getInstance().updateEntity)
+            }
+        } else {
+            mStatus = OtaStatus.CHECK_FAIL
+            notifyObservers(code)
+        }
+    }
+
+    override fun downloadProgress(percent: Long) {
+        Logger.d(TAG, "downloadProgress: $percent")
+        mStatus = OtaStatus.DOWNLOADING
+        notifyObservers(percent.toInt())
+    }
+
+    override fun onDownloadPause() {
+        if (!RsNetStateHelpers.getInstance().isWifiAvailable(globalContext)) {
+            mStatus = OtaStatus.NEED_WIFI
+            notifyObservers(RsOtaManager.getInstance().updateEntity)
+        }
+    }
+
+    override fun onDownloadResume() {
+        mStatus = OtaStatus.DOWNLOADING
+        notifyObservers(RsOtaManager.getInstance().updateEntity)
+    }
+
+    override fun onDownloadSuccess() {
+        mStatus = OtaStatus.DOWNLOAD_SUCCESS
+        notifyObservers(null)
+        val updatePackage = RsOtaManager.getInstance().updateEntity
+        if (updatePackage != null && updatePackage.forceUpdate == 1) {
+            if (updatePackage.installHour == null || updatePackage.installHour.isEmpty()) {
+                install()
+            }
+        }
+    }
+
+    override fun onDownloadFail(code: Int) {
+        mStatus = OtaStatus.DOWNLOAD_FAIL
+        notifyObservers(code)
+    }
+
+    override fun installProgress(percent: Int) {
+    }
+
+    override fun onInstallSuccessNeedReboot() {
+        notifyObservers(null)
+    }
+
+    override fun onInstallCheckSuccess() {
+        notifyObservers(null)
+    }
+
+    override fun onInstallCheckFail() {
+        notifyObservers(null)
+    }
+
+    override fun onInstallFailure(code: Int) {
+        notifyObservers(code)
+    }
+
+    override fun onNetConnected(networkType: NetworkType) {
+        Logger.d(TAG, "onNetConnected")
+        if (networkType == NetworkType.NETWORK_WIFI && mStatus == OtaStatus.NEED_WIFI) {
+            if (RsOtaAgent.getInstance().isDownloading) {
+                RsOtaAgent.getInstance().resumeDownload(globalContext)
+            } else {
+                beginDownload()
+            }
+        } else if (networkType != NetworkType.NETWORK_WIFI && mStatus == OtaStatus.DOWNLOADING) {
+            if (RsOtaAgent.getInstance().isDownloading && RsOtaManager.getInstance().updateEntity.autodl != 2) {
+                RsOtaAgent.getInstance().pauseDownload(globalContext)
+            }
+        } else if (mStatus == OtaStatus.DOWNLOAD_FAIL) {
+            if (networkType == NetworkType.NETWORK_WIFI || RsOtaManager.getInstance().updateEntity.autodl == 2) {
+                beginDownload()
+            }
+        }
+    }
+
+    override fun onNetDisconnected() {
+        if (RsOtaAgent.getInstance().isDownloading) {
+            RsOtaAgent.getInstance().pauseDownload(globalContext)
+        }
+    }
+}
+
+enum class OtaStatus {
+    UNKNOWN,
+    CHECKING,
+    CHECK_SUCCESS,
+    CHECK_FAIL,
+    NEED_WIFI,
+    DOWNLOADING,
+    DOWNLOAD_SUCCESS,
+    DOWNLOAD_FAIL
+}

+ 300 - 0
app/src/main/java/com/xplora/commonservice/modules/SosManager.kt

@@ -0,0 +1,300 @@
+package com.xplora.commonservice.modules
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import android.os.Looper
+import android.os.Message
+import android.provider.Settings
+import android.telephony.TelephonyManager
+import com.android.i18n.phonenumbers.PhoneNumberUtil
+import com.android.i18n.phonenumbers.Phonenumber
+import com.xplora.commonservice.BaseApplication.Companion.globalContext
+import com.xplora.commonservice.model.HttpApi
+import com.xplora.commonservice.model.database.ContactDbDbEntity
+import com.xplora.commonservice.model.http.etc.SOSReq
+import com.xplora.commonservice.modules.callbacks.SosStateChangeObserver
+import com.xplora.commonservice.modules.database.DatabaseManager
+import com.xplora.commonservice.modules.http.FlowableFactory.getFlowable
+import com.xplora.commonservice.modules.http.XPHttpServiceExecutor
+import com.xplora.commonservice.modules.service.position.Position
+import com.xplora.commonservice.ui.activity.SosActivity
+import com.xplora.commonservice.utils.Logger
+import com.xplora.commonservice.utils.TeleUtil
+import kotlinx.coroutines.DelicateCoroutinesApi
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
+
+/**
+ * sos status :
+ * 0 - no sos
+ * 1 - call first admin
+ * 2 - call second admin
+ * 3 - answered, in calling
+ */
+object SosManager {
+    const val TAG = "SosManager"
+
+    enum class SosStatus {
+        IDLE, PREPARE, FIRST, FIRST_TIMEOUT, SECOND, SECOND_TIMEOUT, ANSWERED
+    }
+
+    private class SosSettingObserver(mHandler: Handler) : ContentObserver(mHandler) {
+        override fun onChange(selfChange: Boolean, uri: Uri?) {
+            super.onChange(selfChange, uri)
+            val s = Settings.Global.getInt(globalContext.contentResolver, "sos_status", 0)
+            when {
+                s == 2 -> {
+                    status = SosStatus.ANSWERED
+                    sendHandlerMessage(0)
+                }
+                s == 0 && (status == SosStatus.ANSWERED || status == SosStatus.SECOND_TIMEOUT) -> {
+                    sosHandler.removeCallbacksAndMessages(null)
+                    status = SosStatus.IDLE
+                    sendHandlerMessage(0)
+                }
+                s == 0 && (status == SosStatus.FIRST_TIMEOUT) -> {
+                    sosHandler.removeCallbacksAndMessages(null)
+                    status = if (allGuardians!!.size > 1) {
+                        reportStatus(3, allGuardians!!["1"]!!)
+                        Settings.Global.putInt(globalContext.contentResolver, "sos_status", 1)
+                        SosStatus.SECOND
+                    } else {
+                        SosStatus.IDLE
+                    }
+                    sendHandlerMessage(0)
+                }
+            }
+        }
+    }
+
+    private var allGuardians: HashMap<String, ContactDbDbEntity>? = null
+        get() {
+            if (field == null) field = HashMap<String, ContactDbDbEntity>().apply {
+                val all = DatabaseManager.queryContact()
+                var i = 1
+                for (contact in all) {
+                    Logger.d(TAG, contact.type!!)
+                    if (contact.type == "1") {
+                        this[i.toString()] = contact
+                        i++
+                    }
+                }
+            }
+            return field
+        }
+
+    private val sosHandler: SosHandler by lazy {
+        SosHandler()
+    }
+
+    private val mSettingObserver: SosSettingObserver by lazy {
+        SosSettingObserver(sosHandler)
+    }
+
+    @Volatile
+    private var lastStatus = SosStatus.IDLE
+
+    @Volatile
+    internal var status = SosStatus.IDLE
+        get() {
+            Logger.d(TAG, "get status: $field")
+            return field
+        }
+        set(value) {
+            if(value == field) return
+            lastStatus = field
+            field = value
+            Logger.d(TAG, "status changed: $field")
+        }
+
+    private val mObservers: MutableList<SosStateChangeObserver> = ArrayList()
+
+    fun notifyObservers() {
+        for (observer in mObservers) {
+            observer.sosFinish()
+        }
+    }
+
+    fun registerObserver(observer: SosStateChangeObserver?) {
+        if (observer == null) {
+            return
+        }
+        if (!mObservers.contains(observer)) {
+            mObservers.add(observer)
+        }
+    }
+
+    fun unRegisterObserver(observer: SosStateChangeObserver?) {
+        if (observer == null) {
+            return
+        }
+        mObservers.remove(observer)
+    }
+
+    fun executeSos() {
+        Logger.d(TAG, "executeSos")
+        val tm = globalContext
+            .getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
+        if (tm.simState != TelephonyManager.SIM_STATE_READY || tm.voiceNetworkType == TelephonyManager.NETWORK_TYPE_UNKNOWN) {
+            Logger.d(TAG, "executeSos no sim return")
+            return
+        }
+        if (Settings.Global.getInt(globalContext.contentResolver, "disable_status", 0) > 1) {
+            Settings.Global.putInt(globalContext.contentResolver, "sos_status", 0)
+            status = SosStatus.IDLE
+            lastStatus = SosStatus.IDLE
+            Logger.d(TAG, "executeSos disable_status return")
+            return
+        }
+        if (Settings.Global.getInt(globalContext.contentResolver, "sos_status", 0) > 0
+            || status != SosStatus.IDLE
+        ) {
+            Logger.d(TAG, "executeSos already executed return")
+            return
+        }
+
+        status = SosStatus.PREPARE
+        Logger.d(TAG, "executeSos PREPARE")
+        sendHandlerMessage(0)
+        globalContext.contentResolver.registerContentObserver(
+            Settings.Global.getUriFor("sos_status"),
+            false,
+            mSettingObserver
+        )
+    }
+
+    fun onPowerKeyUp() {
+        if (status == SosStatus.PREPARE || status == SosStatus.FIRST) {
+            sosHandler.removeCallbacksAndMessages(null)
+            status = SosStatus.IDLE
+            sendHandlerMessage(0)
+        }
+    }
+
+    private fun startPrepareActivity() {
+        Settings.Global.putInt(globalContext.contentResolver, "sos_status", 1)
+        val i = Intent()
+        i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+        i.component =
+            ComponentName(
+                globalContext.packageName,
+                SosActivity::class.java.name
+            )
+        globalContext.startActivity(i)
+    }
+
+    private fun executeSosNative(contact: ContactDbDbEntity) {
+        Logger.d(TAG, "executeSosNative ${contact.name}")
+        reportStatus(1, contact)
+        val phoneNumber = Phonenumber.PhoneNumber()
+        phoneNumber.countryCode = contact.countryPN!!.toInt()
+        phoneNumber.nationalNumber = contact.phoneNumber!!.toLong()
+        val number = PhoneNumberUtil.getInstance()
+            .format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164)
+        val iCall = Intent(Intent.ACTION_CALL, Uri.parse("tel:$number"))
+        iCall.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+        globalContext.startActivity(iCall)
+    }
+
+    @OptIn(DelicateCoroutinesApi::class)
+    private fun reportStatus(status: Int, contact: ContactDbDbEntity) {
+        Logger.d(TAG, "reportStatuss: $status;  ${contact.name}")
+        GlobalScope.launch {
+            val request = SOSReq(status, contact.userId!!)
+            XPHttpServiceExecutor.execute(
+                null,
+                request,
+                getFlowable(HttpApi.Etc.SOS, request, null)
+            )
+            if (status == 1) Position().executePosition(null, 1)
+        }
+    }
+
+    internal fun sendHandlerMessage(timeout: Long) {
+        Logger.d(TAG, "sendHandlerMessage: $timeout")
+        if (timeout > 0) {
+            sosHandler.sendEmptyMessageDelayed(status.ordinal, timeout * 1000)
+        } else {
+            sosHandler.sendEmptyMessage(status.ordinal)
+        }
+    }
+
+    private class SosHandler : Handler(Looper.myLooper()!!) {
+        override fun handleMessage(msg: Message) {
+            super.handleMessage(msg)
+            Logger.d(TAG, "handleMessage ${status.ordinal}, ${msg.what}")
+            when (msg.what) {
+                SosStatus.PREPARE.ordinal -> {
+                    startPrepareActivity()
+/*                    status = SosStatus.FIRST
+                    sendHandlerMessage(4)*/
+                }
+                SosStatus.FIRST.ordinal -> {
+                    Logger.d(TAG, "FIRST!!!")
+                    allGuardians?.get("1")?.let {
+                        Logger.d(TAG, "FIRST!!! got")
+                        executeSosNative(it)
+                        status = SosStatus.FIRST_TIMEOUT
+                        sendHandlerMessage(60)
+                    }
+                    // notifyObservers()
+                }
+                SosStatus.FIRST_TIMEOUT.ordinal -> {
+                    TeleUtil.endCall()
+                }
+                SosStatus.SECOND.ordinal -> {
+                    allGuardians!!["2"]?.let {
+                        executeSosNative(it)
+                        status = SosStatus.SECOND_TIMEOUT
+                        sendHandlerMessage(60)
+                    }
+                }
+                SosStatus.SECOND_TIMEOUT.ordinal -> {
+                    TeleUtil.endCall()
+                }
+                SosStatus.ANSWERED.ordinal -> {
+                    when (lastStatus) {
+                        SosStatus.FIRST, SosStatus.FIRST_TIMEOUT -> {
+                            allGuardians!!["1"]
+                        }
+                        SosStatus.SECOND, SosStatus.SECOND_TIMEOUT -> {
+                            allGuardians!!["2"]
+                        }
+                        else -> {
+                            null
+                        }
+                    }?.let {
+                        reportStatus(2, it)
+                    }
+                    this.removeCallbacksAndMessages(null)
+                }
+                SosStatus.IDLE.ordinal -> {
+                    val result = when (lastStatus) {
+                        SosStatus.FIRST_TIMEOUT -> {
+                            allGuardians!!["1"]
+                        }
+                        SosStatus.SECOND_TIMEOUT -> {
+                            allGuardians!!["2"]
+                        }
+                        else -> {
+                            null
+                        }
+                    }
+                    Logger.d(TAG, "IDLE after when")
+                    notifyObservers()
+                    this.removeCallbacksAndMessages(null)
+                    lastStatus = SosStatus.IDLE
+                    Settings.Global.putInt(globalContext.contentResolver, "sos_status", 0)
+                    allGuardians = null
+                    if (result != null) {
+                        reportStatus(3, result)
+                    }
+                }
+            }
+        }
+    }
+}

+ 43 - 0
app/src/main/java/com/xplora/commonservice/modules/SoundManager.kt

@@ -0,0 +1,43 @@
+package com.xplora.commonservice.modules
+
+import android.content.Context
+import android.media.AudioManager
+import com.xplora.commonservice.BaseApplication.Companion.globalContext
+
+object SoundManager {
+    fun setSystemVolume(value: Int) {
+        val audioManager: AudioManager =
+            globalContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
+        audioManager.setStreamVolume(
+            AudioManager.STREAM_SYSTEM,
+            value,
+            AudioManager.FLAG_PLAY_SOUND
+        )
+    }
+
+
+    fun setMediaVolume(value: Int) {
+        val audioManager: AudioManager =
+            globalContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
+        audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, value, AudioManager.FLAG_PLAY_SOUND)
+    }
+
+
+    fun getRingerMode(): Int {
+//        public static final int RINGER_MODE_SILENT = 0;
+//        public static final int RINGER_MODE_VIBRATE = 1;
+//        public static final int RINGER_MODE_NORMAL = 2;
+        val audioManager: AudioManager =
+            globalContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
+        return audioManager.ringerMode
+    }
+
+    fun setRingerMode(value: Int) {
+//        public static final int RINGER_MODE_SILENT = 0;
+//        public static final int RINGER_MODE_VIBRATE = 1;
+//        public static final int RINGER_MODE_NORMAL = 2;
+            val audioManager: AudioManager =
+                globalContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
+            audioManager.ringerMode = value
+    }
+}

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно