init project.
authorTrillGates <williamtrillgates@gmail.com>
Tue, 26 Jan 2021 07:33:38 +0000 (15:33 +0800)
committerTrillGates <williamtrillgates@gmail.com>
Tue, 26 Jan 2021 07:33:38 +0000 (15:33 +0800)
87 files changed:
.gitignore [new file with mode: 0644]
.idea/$PROJECT_FILE$ [new file with mode: 0644]
.idea/.gitignore [new file with mode: 0644]
.idea/codeStyles/Project.xml [new file with mode: 0644]
.idea/codeStyles/codeStyleConfig.xml [new file with mode: 0644]
.idea/compiler.xml [new file with mode: 0644]
.idea/dictionaries/Administrator.xml [new file with mode: 0644]
.idea/dictionaries/HDC.xml [new file with mode: 0644]
.idea/gradle.xml [new file with mode: 0644]
.idea/jarRepositories.xml [new file with mode: 0644]
.idea/misc.xml [new file with mode: 0644]
.idea/qaplug_profiles.xml [new file with mode: 0644]
app/.gitignore [new file with mode: 0644]
app/EQC_G5.jks [new file with mode: 0644]
app/FotaBrand8541e/debug/FotaBrand8541e_V1_202101251756_debug.apk [new file with mode: 0644]
app/FotaBrand8541e/debug/output-metadata.json [new file with mode: 0644]
app/FotaBrand8541e/release/FotaBrand8541e_V1_202101260957_release.apk [new file with mode: 0644]
app/FotaBrand8541e/release/output-metadata.json [new file with mode: 0644]
app/build.gradle [new file with mode: 0644]
app/libs/android-logging-log4j-1.0.3.jar [new file with mode: 0644]
app/libs/ftp4j-1.7.2.jar [new file with mode: 0644]
app/libs/log4j-1.2.17.jar [new file with mode: 0644]
app/libs/okio-2.4.3.jar [new file with mode: 0644]
app/libs/ota.da-sdk-v3.2.1.aar [new file with mode: 0644]
app/proguard-rules.pro [new file with mode: 0644]
app/src/androidTest/java/com/ic/icfotaclient/ExampleInstrumentedTest.kt [new file with mode: 0644]
app/src/main/AndroidManifest.xml [new file with mode: 0644]
app/src/main/aidl/com/ic/icfotaclient/IOtaCheckCallback.aidl [new file with mode: 0644]
app/src/main/aidl/com/ic/icfotaclient/IOtaCheckService.aidl [new file with mode: 0644]
app/src/main/aidl/com/ic/icfotaclient/bean/OtaInfo.aidl [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/App.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/base/AbstractOtaRepository.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/base/IOtaBaseRepository.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/bean/DownloadInfo.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/bean/ICOtaInfo.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/bean/OtaInfo.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/presenter/OtaReceiver.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/presenter/OtaService.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/presenter/OtaViewModel.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/repository/CarOtaRepository.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/repository/ICOtaRepository.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/repository/IOtaRepositoryCallback.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/utils/CarOtaApiUtils.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/utils/Constants.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/utils/ICOtaApiUtils.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/utils/NetworkUtils.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/utils/OtaViewModelCreator.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/utils/SPUtil.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/utils/SysPropUtils.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/utils/UpdateActionState.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/utils/VersionCheckFrom.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/utils/VersionInfoUtils.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/view/ApiTestActivity.kt [new file with mode: 0644]
app/src/main/java/com/ic/icfotaclient/view/SystemUpdateActivity.kt [new file with mode: 0644]
app/src/main/res/drawable-v24/ic_launcher_foreground.xml [new file with mode: 0644]
app/src/main/res/drawable/ic_launcher_background.xml [new file with mode: 0644]
app/src/main/res/drawable/selector_btn_bg.xml [new file with mode: 0644]
app/src/main/res/drawable/shape_btn_disable.xml [new file with mode: 0644]
app/src/main/res/drawable/shape_btn_normal.xml [new file with mode: 0644]
app/src/main/res/drawable/shape_btn_press.xml [new file with mode: 0644]
app/src/main/res/layout/activity_api_test.xml [new file with mode: 0644]
app/src/main/res/layout/activity_main.xml [new file with mode: 0644]
app/src/main/res/layout/dialog_tips.xml [new file with mode: 0644]
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml [new file with mode: 0644]
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml [new file with mode: 0644]
app/src/main/res/mipmap-hdpi/ic_launcher.png [new file with mode: 0644]
app/src/main/res/mipmap-hdpi/ic_launcher_round.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/ic_launcher.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/ic_launcher_round.png [new file with mode: 0644]
app/src/main/res/mipmap-xhdpi/ic_launcher.png [new file with mode: 0644]
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png [new file with mode: 0644]
app/src/main/res/mipmap-xxhdpi/ic_launcher.png [new file with mode: 0644]
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png [new file with mode: 0644]
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png [new file with mode: 0644]
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png [new file with mode: 0644]
app/src/main/res/values/colors.xml [new file with mode: 0644]
app/src/main/res/values/strings.xml [new file with mode: 0644]
app/src/main/res/values/themes.xml [new file with mode: 0644]
app/src/test/java/com/ic/icfotaclient/ExampleUnitTest.kt [new file with mode: 0644]
app/version_code.properties [new file with mode: 0644]
build.gradle [new file with mode: 0644]
gradle.properties [new file with mode: 0644]
gradle/wrapper/gradle-wrapper.jar [new file with mode: 0644]
gradle/wrapper/gradle-wrapper.properties [new file with mode: 0644]
gradlew [new file with mode: 0644]
gradlew.bat [new file with mode: 0644]
settings.gradle [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..aa724b7
--- /dev/null
@@ -0,0 +1,15 @@
+*.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
+local.properties
diff --git a/.idea/$PROJECT_FILE$ b/.idea/$PROJECT_FILE$
new file mode 100644 (file)
index 0000000..58b7e3e
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="AnalysisProjectProfileManager">
+    <option name="PROJECT_PROFILE" />
+    <option name="USE_PROJECT_LEVEL_SETTINGS" value="false" />
+    <list size="0" />
+  </component>
+  <component name="SuppressionsComponent">
+    <option name="suppComments" value="[]" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644 (file)
index 0000000..26d3352
--- /dev/null
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644 (file)
index 0000000..208123b
--- /dev/null
@@ -0,0 +1,130 @@
+<component name="ProjectCodeStyleConfiguration">
+  <code_scheme name="Project" version="173">
+    <JetCodeStyleSettings>
+      <option name="PACKAGES_TO_USE_STAR_IMPORTS">
+        <value>
+          <package name="java.util" withSubpackages="false" static="false" />
+          <package name="kotlinx.android.synthetic" withSubpackages="true" static="false" />
+          <package name="io.ktor" withSubpackages="true" static="false" />
+        </value>
+      </option>
+      <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
+    </JetCodeStyleSettings>
+    <codeStyleSettings language="XML">
+      <option name="FORCE_REARRANGE_MODE" value="1" />
+      <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" />
+    </codeStyleSettings>
+  </code_scheme>
+</component>
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644 (file)
index 0000000..79ee123
--- /dev/null
@@ -0,0 +1,5 @@
+<component name="ProjectCodeStyleConfiguration">
+  <state>
+    <option name="USE_PER_PROJECT_SETTINGS" value="true" />
+  </state>
+</component>
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644 (file)
index 0000000..61a9130
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="CompilerConfiguration">
+    <bytecodeTargetLevel target="1.8" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/dictionaries/Administrator.xml b/.idea/dictionaries/Administrator.xml
new file mode 100644 (file)
index 0000000..1295ce2
--- /dev/null
@@ -0,0 +1,7 @@
+<component name="ProjectDictionaryState">
+  <dictionary name="Administrator">
+    <words>
+      <w>imei</w>
+    </words>
+  </dictionary>
+</component>
\ No newline at end of file
diff --git a/.idea/dictionaries/HDC.xml b/.idea/dictionaries/HDC.xml
new file mode 100644 (file)
index 0000000..5b14ef4
--- /dev/null
@@ -0,0 +1,7 @@
+<component name="ProjectDictionaryState">
+  <dictionary name="HDC">
+    <words>
+      <w>gson</w>
+    </words>
+  </dictionary>
+</component>
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644 (file)
index 0000000..b617266
--- /dev/null
@@ -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="PLATFORM" />
+        <option name="disableWrapperSourceDistributionNotification" value="true" />
+        <option name="distributionType" value="DEFAULT_WRAPPED" />
+        <option name="externalProjectPath" value="$PROJECT_DIR$" />
+        <option name="gradleJvm" value="1.8" />
+        <option name="modules">
+          <set>
+            <option value="$PROJECT_DIR$" />
+            <option value="$PROJECT_DIR$/app" />
+          </set>
+        </option>
+        <option name="resolveModulePerSourceSet" value="false" />
+        <option name="useQualifiedModuleNames" value="true" />
+      </GradleProjectSettings>
+    </option>
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644 (file)
index 0000000..54ee638
--- /dev/null
@@ -0,0 +1,35 @@
+<?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>
+    <remote-repository>
+      <option name="id" value="maven" />
+      <option name="name" value="maven" />
+      <option name="url" value="http://maven.aliyun.com/nexus/content/groups/public/" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="maven2" />
+      <option name="name" value="maven2" />
+      <option name="url" value="http://maven.aliyun.com/nexus/content/repositories/jcenter" />
+    </remote-repository>
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644 (file)
index 0000000..9f83622
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="FindBugsConfigurable">
+    <option name="make" value="true" />
+    <option name="effort" value="default" />
+    <option name="priority" value="Medium" />
+    <option name="excludeFilter" value="" />
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
+    <output url="file://$PROJECT_DIR$/build/classes" />
+  </component>
+  <component name="ProjectType">
+    <option name="id" value="Android" />
+  </component>
+  <component name="SuppressionsComponent">
+    <option name="suppComments" value="[]" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/qaplug_profiles.xml b/.idea/qaplug_profiles.xml
new file mode 100644 (file)
index 0000000..3dfd21f
--- /dev/null
@@ -0,0 +1,465 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="AnalysisProjectProfileManager">
+    <option name="PROJECT_PROFILE" value="Project Default" />
+    <option name="USE_PROJECT_LEVEL_SETTINGS" value="false" />
+    <scopes />
+    <profiles>
+      <profile profile_name="Project Default" version="1.0" is_locked="false">
+        <coding_rule class="AM_CREATES_EMPTY_JAR_FILE_ENTRY" level="MAJOR" enabled="true" />
+        <coding_rule class="AM_CREATES_EMPTY_ZIP_FILE_ENTRY" level="MAJOR" enabled="true" />
+        <coding_rule class="AT_OPERATION_SEQUENCE_ON_CONCURRENT_ABSTRACTION" level="CRITICAL" enabled="false" />
+        <coding_rule class="BAC_BAD_APPLET_CONSTRUCTOR" level="MAJOR" enabled="false" />
+        <coding_rule class="BC_BAD_CAST_TO_ABSTRACT_COLLECTION" level="MAJOR" enabled="true" />
+        <coding_rule class="BC_BAD_CAST_TO_CONCRETE_COLLECTION" level="CRITICAL" enabled="true" />
+        <coding_rule class="BC_EQUALS_METHOD_SHOULD_WORK_FOR_ALL_OBJECTS" level="CRITICAL" enabled="true" />
+        <coding_rule class="BC_IMPOSSIBLE_CAST" level="BLOCKER" enabled="true" />
+        <coding_rule class="BC_IMPOSSIBLE_DOWNCAST" level="MAJOR" enabled="false" />
+        <coding_rule class="BC_IMPOSSIBLE_DOWNCAST_OF_TOARRAY" level="MAJOR" enabled="false" />
+        <coding_rule class="BC_IMPOSSIBLE_INSTANCEOF" level="CRITICAL" enabled="true" />
+        <coding_rule class="BC_UNCONFIRMED_CAST" level="CRITICAL" enabled="true" />
+        <coding_rule class="BC_UNCONFIRMED_CAST_OF_RETURN_VALUE" level="CRITICAL" enabled="false" />
+        <coding_rule class="BC_VACUOUS_INSTANCEOF" level="CRITICAL" enabled="true" />
+        <coding_rule class="BIT_ADD_OF_SIGNED_BYTE" level="CRITICAL" enabled="true" />
+        <coding_rule class="BIT_AND" level="CRITICAL" enabled="true" />
+        <coding_rule class="BIT_AND_ZZ" level="CRITICAL" enabled="true" />
+        <coding_rule class="BIT_IOR" level="CRITICAL" enabled="true" />
+        <coding_rule class="BIT_IOR_OF_SIGNED_BYTE" level="CRITICAL" enabled="true" />
+        <coding_rule class="BIT_SIGNED_CHECK" level="CRITICAL" enabled="true" />
+        <coding_rule class="BIT_SIGNED_CHECK_HIGH_BIT" level="CRITICAL" enabled="true" />
+        <coding_rule class="BOA_BADLY_OVERRIDDEN_ADAPTER" level="CRITICAL" enabled="true" />
+        <coding_rule class="BSHIFT_WRONG_ADD_PRIORITY" level="MAJOR" enabled="false" />
+        <coding_rule class="BX_BOXING_IMMEDIATELY_UNBOXED" level="MAJOR" enabled="true" />
+        <coding_rule class="BX_BOXING_IMMEDIATELY_UNBOXED_TO_PERFORM_COERCION" level="MAJOR" enabled="true" />
+        <coding_rule class="BX_UNBOXED_AND_COERCED_FOR_TERNARY_OPERATOR" level="MAJOR" enabled="true" />
+        <coding_rule class="BX_UNBOXING_IMMEDIATELY_REBOXED" level="CRITICAL" enabled="false" />
+        <coding_rule class="CAA_COVARIANT_ARRAY_ELEMENT_STORE" level="MAJOR" enabled="false" />
+        <coding_rule class="CAA_COVARIANT_ARRAY_FIELD" level="MAJOR" enabled="false" />
+        <coding_rule class="CAA_COVARIANT_ARRAY_LOCAL" level="MAJOR" enabled="false" />
+        <coding_rule class="CAA_COVARIANT_ARRAY_RETURN" level="MAJOR" enabled="false" />
+        <coding_rule class="CD_CIRCULAR_DEPENDENCY" level="MAJOR" enabled="false" />
+        <coding_rule class="CI_CONFUSED_INHERITANCE" level="MINOR" enabled="true" />
+        <coding_rule class="CNT_ROUGH_CONSTANT_VALUE" level="MAJOR" enabled="false" />
+        <coding_rule class="CN_IDIOM" level="MAJOR" enabled="true" />
+        <coding_rule class="CN_IDIOM_NO_SUPER_CALL" level="MAJOR" enabled="true" />
+        <coding_rule class="CN_IMPLEMENTS_CLONE_BUT_NOT_CLONEABLE" level="MAJOR" enabled="true" />
+        <coding_rule class="CO_ABSTRACT_SELF" level="MAJOR" enabled="true" />
+        <coding_rule class="CO_COMPARETO_INCORRECT_FLOATING" level="MAJOR" enabled="false" />
+        <coding_rule class="CO_COMPARETO_RESULTS_MIN_VALUE" level="CRITICAL" enabled="false" />
+        <coding_rule class="CO_SELF_NO_OBJECT" level="MAJOR" enabled="true" />
+        <coding_rule class="DB_DUPLICATE_BRANCHES" level="CRITICAL" enabled="true" />
+        <coding_rule class="DB_DUPLICATE_SWITCH_CLAUSES" level="CRITICAL" enabled="true" />
+        <coding_rule class="DC_DOUBLECHECK" level="MAJOR" enabled="true" />
+        <coding_rule class="DC_PARTIALLY_CONSTRUCTED" level="MAJOR" enabled="false" />
+        <coding_rule class="DE_MIGHT_DROP" level="MAJOR" enabled="true" />
+        <coding_rule class="DE_MIGHT_IGNORE" level="MAJOR" enabled="true" />
+        <coding_rule class="DLS_DEAD_LOCAL_INCREMENT_IN_RETURN" level="MAJOR" enabled="false" />
+        <coding_rule class="DLS_DEAD_LOCAL_STORE" level="CRITICAL" enabled="true" />
+        <coding_rule class="DLS_DEAD_LOCAL_STORE_IN_RETURN" level="CRITICAL" enabled="true" />
+        <coding_rule class="DLS_DEAD_LOCAL_STORE_OF_NULL" level="CRITICAL" enabled="true" />
+        <coding_rule class="DLS_DEAD_LOCAL_STORE_SHADOWS_FIELD" level="CRITICAL" enabled="false" />
+        <coding_rule class="DLS_DEAD_STORE_OF_CLASS_LITERAL" level="CRITICAL" enabled="true" />
+        <coding_rule class="DLS_OVERWRITTEN_INCREMENT" level="CRITICAL" enabled="true" />
+        <coding_rule class="DL_SYNCHRONIZATION_ON_BOOLEAN" level="CRITICAL" enabled="true" />
+        <coding_rule class="DL_SYNCHRONIZATION_ON_BOXED_PRIMITIVE" level="CRITICAL" enabled="true" />
+        <coding_rule class="DL_SYNCHRONIZATION_ON_SHARED_CONSTANT" level="CRITICAL" enabled="true" />
+        <coding_rule class="DL_SYNCHRONIZATION_ON_UNSHARED_BOXED_PRIMITIVE" level="CRITICAL" enabled="true" />
+        <coding_rule class="DMI_ANNOTATION_IS_NOT_VISIBLE_TO_REFLECTION" level="MAJOR" enabled="true" />
+        <coding_rule class="DMI_ARGUMENTS_WRONG_ORDER" level="CRITICAL" enabled="false" />
+        <coding_rule class="DMI_BAD_MONTH" level="CRITICAL" enabled="true" />
+        <coding_rule class="DMI_BIGDECIMAL_CONSTRUCTED_FROM_DOUBLE" level="CRITICAL" enabled="false" />
+        <coding_rule class="DMI_BLOCKING_METHODS_ON_URL" level="BLOCKER" enabled="true" />
+        <coding_rule class="DMI_CALLING_NEXT_FROM_HASNEXT" level="CRITICAL" enabled="true" />
+        <coding_rule class="DMI_COLLECTIONS_SHOULD_NOT_CONTAIN_THEMSELVES" level="CRITICAL" enabled="true" />
+        <coding_rule class="DMI_COLLECTION_OF_URLS" level="BLOCKER" enabled="true" />
+        <coding_rule class="DMI_CONSTANT_DB_PASSWORD" level="BLOCKER" enabled="true" />
+        <coding_rule class="DMI_DOH" level="CRITICAL" enabled="false" />
+        <coding_rule class="DMI_EMPTY_DB_PASSWORD" level="CRITICAL" enabled="true" />
+        <coding_rule class="DMI_ENTRY_SETS_MAY_REUSE_ENTRY_OBJECTS" level="CRITICAL" enabled="false" />
+        <coding_rule class="DMI_FUTILE_ATTEMPT_TO_CHANGE_MAXPOOL_SIZE_OF_SCHEDULED_THREAD_POOL_EXECUTOR" level="MINOR" enabled="true" />
+        <coding_rule class="DMI_HARDCODED_ABSOLUTE_FILENAME" level="CRITICAL" enabled="true" />
+        <coding_rule class="DMI_INVOKING_HASHCODE_ON_ARRAY" level="CRITICAL" enabled="true" />
+        <coding_rule class="DMI_INVOKING_TOSTRING_ON_ANONYMOUS_ARRAY" level="CRITICAL" enabled="true" />
+        <coding_rule class="DMI_INVOKING_TOSTRING_ON_ARRAY" level="CRITICAL" enabled="true" />
+        <coding_rule class="DMI_LONG_BITS_TO_DOUBLE_INVOKED_ON_INT" level="CRITICAL" enabled="true" />
+        <coding_rule class="DMI_NONSERIALIZABLE_OBJECT_WRITTEN" level="CRITICAL" enabled="true" />
+        <coding_rule class="DMI_RANDOM_USED_ONLY_ONCE" level="CRITICAL" enabled="true" />
+        <coding_rule class="DMI_SCHEDULED_THREAD_POOL_EXECUTOR_WITH_ZERO_CORE_THREADS" level="MINOR" enabled="true" />
+        <coding_rule class="DMI_THREAD_PASSED_WHERE_RUNNABLE_EXPECTED" level="MAJOR" enabled="true" />
+        <coding_rule class="DMI_UNSUPPORTED_METHOD" level="MAJOR" enabled="true" />
+        <coding_rule class="DMI_USELESS_SUBSTRING" level="CRITICAL" enabled="true" />
+        <coding_rule class="DMI_USING_REMOVEALL_TO_CLEAR_COLLECTION" level="CRITICAL" enabled="true" />
+        <coding_rule class="DMI_VACUOUS_CALL_TO_EASYMOCK_METHOD" level="MINOR" enabled="true" />
+        <coding_rule class="DMI_VACUOUS_SELF_COLLECTION_CALL" level="CRITICAL" enabled="true" />
+        <coding_rule class="DM_BOOLEAN_CTOR" level="MAJOR" enabled="true" />
+        <coding_rule class="DM_BOXED_PRIMITIVE_FOR_COMPARE" level="MAJOR" enabled="false" />
+        <coding_rule class="DM_BOXED_PRIMITIVE_FOR_PARSING" level="MAJOR" enabled="false" />
+        <coding_rule class="DM_BOXED_PRIMITIVE_TOSTRING" level="MAJOR" enabled="true" />
+        <coding_rule class="DM_CONVERT_CASE" level="INFO" enabled="true" />
+        <coding_rule class="DM_DEFAULT_ENCODING" level="CRITICAL" enabled="false" />
+        <coding_rule class="DM_EXIT" level="MAJOR" enabled="true" />
+        <coding_rule class="DM_FP_NUMBER_CTOR" level="MAJOR" enabled="true" />
+        <coding_rule class="DM_GC" level="MAJOR" enabled="true" />
+        <coding_rule class="DM_INVALID_MIN_MAX" level="MAJOR" enabled="false" />
+        <coding_rule class="DM_MONITOR_WAIT_ON_CONDITION" level="MAJOR" enabled="true" />
+        <coding_rule class="DM_NEW_FOR_GETCLASS" level="MAJOR" enabled="true" />
+        <coding_rule class="DM_NEXTINT_VIA_NEXTDOUBLE" level="MAJOR" enabled="true" />
+        <coding_rule class="DM_NUMBER_CTOR" level="CRITICAL" enabled="true" />
+        <coding_rule class="DM_RUN_FINALIZERS_ON_EXIT" level="MAJOR" enabled="true" />
+        <coding_rule class="DM_STRING_CTOR" level="MAJOR" enabled="true" />
+        <coding_rule class="DM_STRING_TOSTRING" level="INFO" enabled="true" />
+        <coding_rule class="DM_STRING_VOID_CTOR" level="MAJOR" enabled="true" />
+        <coding_rule class="DM_USELESS_THREAD" level="MAJOR" enabled="true" />
+        <coding_rule class="DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED" level="MAJOR" enabled="true" />
+        <coding_rule class="DP_DO_INSIDE_DO_PRIVILEGED" level="MAJOR" enabled="true" />
+        <coding_rule class="EC_ARRAY_AND_NONARRAY" level="CRITICAL" enabled="true" />
+        <coding_rule class="EC_BAD_ARRAY_COMPARE" level="CRITICAL" enabled="true" />
+        <coding_rule class="EC_INCOMPATIBLE_ARRAY_COMPARE" level="MAJOR" enabled="false" />
+        <coding_rule class="EC_NULL_ARG" level="CRITICAL" enabled="true" />
+        <coding_rule class="EC_UNRELATED_CLASS_AND_INTERFACE" level="CRITICAL" enabled="true" />
+        <coding_rule class="EC_UNRELATED_INTERFACES" level="CRITICAL" enabled="true" />
+        <coding_rule class="EC_UNRELATED_TYPES" level="CRITICAL" enabled="true" />
+        <coding_rule class="EC_UNRELATED_TYPES_USING_POINTER_EQUALITY" level="CRITICAL" enabled="true" />
+        <coding_rule class="EI_EXPOSE_REP" level="MAJOR" enabled="true" />
+        <coding_rule class="EI_EXPOSE_REP2" level="MAJOR" enabled="true" />
+        <coding_rule class="EI_EXPOSE_STATIC_REP2" level="MAJOR" enabled="true" />
+        <coding_rule class="EQ_ABSTRACT_SELF" level="MAJOR" enabled="true" />
+        <coding_rule class="EQ_ALWAYS_FALSE" level="BLOCKER" enabled="true" />
+        <coding_rule class="EQ_ALWAYS_TRUE" level="BLOCKER" enabled="true" />
+        <coding_rule class="EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS" level="MAJOR" enabled="true" />
+        <coding_rule class="EQ_COMPARETO_USE_OBJECT_EQUALS" level="CRITICAL" enabled="true" />
+        <coding_rule class="EQ_COMPARING_CLASS_NAMES" level="MAJOR" enabled="true" />
+        <coding_rule class="EQ_DOESNT_OVERRIDE_EQUALS" level="MAJOR" enabled="false" />
+        <coding_rule class="EQ_DONT_DEFINE_EQUALS_FOR_ENUM" level="MAJOR" enabled="true" />
+        <coding_rule class="EQ_GETCLASS_AND_CLASS_CONSTANT" level="CRITICAL" enabled="true" />
+        <coding_rule class="EQ_OTHER_NO_OBJECT" level="MAJOR" enabled="true" />
+        <coding_rule class="EQ_OTHER_USE_OBJECT" level="MAJOR" enabled="true" />
+        <coding_rule class="EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC" level="MAJOR" enabled="true" />
+        <coding_rule class="EQ_SELF_NO_OBJECT" level="MAJOR" enabled="true" />
+        <coding_rule class="EQ_SELF_USE_OBJECT" level="MAJOR" enabled="true" />
+        <coding_rule class="EQ_UNUSUAL" level="MINOR" enabled="true" />
+        <coding_rule class="ES_COMPARING_PARAMETER_STRING_WITH_EQ" level="MAJOR" enabled="true" />
+        <coding_rule class="ES_COMPARING_STRINGS_WITH_EQ" level="MAJOR" enabled="true" />
+        <coding_rule class="ESync_EMPTY_SYNC" level="MAJOR" enabled="true" />
+        <coding_rule class="FB_MISSING_EXPECTED_WARNING" level="CRITICAL" enabled="false" />
+        <coding_rule class="FB_UNEXPECTED_WARNING" level="CRITICAL" enabled="false" />
+        <coding_rule class="FE_FLOATING_POINT_EQUALITY" level="CRITICAL" enabled="true" />
+        <coding_rule class="FE_TEST_IF_EQUAL_TO_NOT_A_NUMBER" level="CRITICAL" enabled="true" />
+        <coding_rule class="FI_EMPTY" level="MAJOR" enabled="true" />
+        <coding_rule class="FI_EXPLICIT_INVOCATION" level="MAJOR" enabled="true" />
+        <coding_rule class="FI_FINALIZER_NULLS_FIELDS" level="MAJOR" enabled="true" />
+        <coding_rule class="FI_FINALIZER_ONLY_NULLS_FIELDS" level="MAJOR" enabled="true" />
+        <coding_rule class="FI_MISSING_SUPER_CALL" level="MAJOR" enabled="true" />
+        <coding_rule class="FI_NULLIFY_SUPER" level="CRITICAL" enabled="true" />
+        <coding_rule class="FI_PUBLIC_SHOULD_BE_PROTECTED" level="MAJOR" enabled="true" />
+        <coding_rule class="FI_USELESS" level="MINOR" enabled="true" />
+        <coding_rule class="FL_MATH_USING_FLOAT_PRECISION" level="CRITICAL" enabled="true" />
+        <coding_rule class="GC_UNCHECKED_TYPE_IN_GENERIC_CALL" level="CRITICAL" enabled="true" />
+        <coding_rule class="GC_UNRELATED_TYPES" level="CRITICAL" enabled="true" />
+        <coding_rule class="HE_EQUALS_NO_HASHCODE" level="MAJOR" enabled="true" />
+        <coding_rule class="HE_EQUALS_USE_HASHCODE" level="CRITICAL" enabled="true" />
+        <coding_rule class="HE_HASHCODE_NO_EQUALS" level="CRITICAL" enabled="true" />
+        <coding_rule class="HE_HASHCODE_USE_OBJECT_EQUALS" level="CRITICAL" enabled="true" />
+        <coding_rule class="HE_INHERITS_EQUALS_USE_HASHCODE" level="CRITICAL" enabled="true" />
+        <coding_rule class="HE_SIGNATURE_DECLARES_HASHING_OF_UNHASHABLE_CLASS" level="CRITICAL" enabled="true" />
+        <coding_rule class="HE_USE_OF_UNHASHABLE_CLASS" level="CRITICAL" enabled="true" />
+        <coding_rule class="HRS_REQUEST_PARAMETER_TO_COOKIE" level="MAJOR" enabled="true" />
+        <coding_rule class="HRS_REQUEST_PARAMETER_TO_HTTP_HEADER" level="MAJOR" enabled="true" />
+        <coding_rule class="HSC_HUGE_SHARED_STRING_CONSTANT" level="CRITICAL" enabled="true" />
+        <coding_rule class="IA_AMBIGUOUS_INVOCATION_OF_INHERITED_OR_OUTER_METHOD" level="MAJOR" enabled="true" />
+        <coding_rule class="ICAST_BAD_SHIFT_AMOUNT" level="CRITICAL" enabled="true" />
+        <coding_rule class="ICAST_IDIV_CAST_TO_DOUBLE" level="CRITICAL" enabled="true" />
+        <coding_rule class="ICAST_INTEGER_MULTIPLY_CAST_TO_LONG" level="CRITICAL" enabled="true" />
+        <coding_rule class="ICAST_INT_2_LONG_AS_INSTANT" level="CRITICAL" enabled="false" />
+        <coding_rule class="ICAST_INT_CAST_TO_DOUBLE_PASSED_TO_CEIL" level="CRITICAL" enabled="true" />
+        <coding_rule class="ICAST_INT_CAST_TO_FLOAT_PASSED_TO_ROUND" level="CRITICAL" enabled="true" />
+        <coding_rule class="ICAST_QUESTIONABLE_UNSIGNED_RIGHT_SHIFT" level="CRITICAL" enabled="true" />
+        <coding_rule class="IC_INIT_CIRCULARITY" level="CRITICAL" enabled="true" />
+        <coding_rule class="IC_SUPERCLASS_USES_SUBCLASS_DURING_INITIALIZATION" level="MAJOR" enabled="true" />
+        <coding_rule class="IIL_ELEMENTS_GET_LENGTH_IN_LOOP" level="MAJOR" enabled="false" />
+        <coding_rule class="IIL_PATTERN_COMPILE_IN_LOOP" level="MAJOR" enabled="false" />
+        <coding_rule class="IIL_PATTERN_COMPILE_IN_LOOP_INDIRECT" level="MAJOR" enabled="false" />
+        <coding_rule class="IIL_PREPARE_STATEMENT_IN_LOOP" level="MAJOR" enabled="false" />
+        <coding_rule class="IIO_INEFFICIENT_INDEX_OF" level="MAJOR" enabled="false" />
+        <coding_rule class="IIO_INEFFICIENT_LAST_INDEX_OF" level="MAJOR" enabled="false" />
+        <coding_rule class="IJU_ASSERT_METHOD_INVOKED_FROM_RUN_METHOD" level="CRITICAL" enabled="true" />
+        <coding_rule class="IJU_BAD_SUITE_METHOD" level="CRITICAL" enabled="true" />
+        <coding_rule class="IJU_NO_TESTS" level="CRITICAL" enabled="true" />
+        <coding_rule class="IJU_SETUP_NO_SUPER" level="CRITICAL" enabled="true" />
+        <coding_rule class="IJU_SUITE_NOT_STATIC" level="CRITICAL" enabled="true" />
+        <coding_rule class="IJU_TEARDOWN_NO_SUPER" level="CRITICAL" enabled="true" />
+        <coding_rule class="IL_CONTAINER_ADDED_TO_ITSELF" level="CRITICAL" enabled="true" />
+        <coding_rule class="IL_INFINITE_LOOP" level="CRITICAL" enabled="true" />
+        <coding_rule class="IL_INFINITE_RECURSIVE_LOOP" level="CRITICAL" enabled="true" />
+        <coding_rule class="IMA_INEFFICIENT_MEMBER_ACCESS" level="MAJOR" enabled="false" />
+        <coding_rule class="IMSE_DONT_CATCH_IMSE" level="MAJOR" enabled="true" />
+        <coding_rule class="IM_AVERAGE_COMPUTATION_COULD_OVERFLOW" level="CRITICAL" enabled="true" />
+        <coding_rule class="IM_BAD_CHECK_FOR_ODD" level="CRITICAL" enabled="true" />
+        <coding_rule class="IM_MULTIPLYING_RESULT_OF_IREM" level="CRITICAL" enabled="true" />
+        <coding_rule class="INT_BAD_COMPARISON_WITH_INT_VALUE" level="CRITICAL" enabled="false" />
+        <coding_rule class="INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE" level="CRITICAL" enabled="true" />
+        <coding_rule class="INT_BAD_COMPARISON_WITH_SIGNED_BYTE" level="CRITICAL" enabled="true" />
+        <coding_rule class="INT_BAD_REM_BY_1" level="CRITICAL" enabled="true" />
+        <coding_rule class="INT_VACUOUS_BIT_OPERATION" level="CRITICAL" enabled="true" />
+        <coding_rule class="INT_VACUOUS_COMPARISON" level="CRITICAL" enabled="true" />
+        <coding_rule class="IO_APPENDING_TO_OBJECT_OUTPUT_STREAM" level="CRITICAL" enabled="true" />
+        <coding_rule class="IP_PARAMETER_IS_DEAD_BUT_OVERWRITTEN" level="CRITICAL" enabled="true" />
+        <coding_rule class="IS2_INCONSISTENT_SYNC" level="CRITICAL" enabled="true" />
+        <coding_rule class="ISC_INSTANTIATE_STATIC_CLASS" level="MAJOR" enabled="true" />
+        <coding_rule class="IS_FIELD_NOT_GUARDED" level="CRITICAL" enabled="true" />
+        <coding_rule class="ITA_INEFFICIENT_TO_ARRAY" level="CRITICAL" enabled="true" />
+        <coding_rule class="IT_NO_SUCH_ELEMENT" level="MINOR" enabled="true" />
+        <coding_rule class="J2EE_STORE_OF_NON_SERIALIZABLE_OBJECT_INTO_SESSION" level="CRITICAL" enabled="true" />
+        <coding_rule class="JCIP_FIELD_ISNT_FINAL_IN_IMMUTABLE_CLASS" level="MINOR" enabled="true" />
+        <coding_rule class="JLM_JSR166_LOCK_MONITORENTER" level="CRITICAL" enabled="true" />
+        <coding_rule class="JLM_JSR166_UTILCONCURRENT_MONITORENTER" level="CRITICAL" enabled="false" />
+        <coding_rule class="JML_JSR166_CALLING_WAIT_RATHER_THAN_AWAIT" level="CRITICAL" enabled="false" />
+        <coding_rule class="LG_LOST_LOGGER_DUE_TO_WEAK_REFERENCE" level="MAJOR" enabled="false" />
+        <coding_rule class="LI_LAZY_INIT_STATIC" level="CRITICAL" enabled="true" />
+        <coding_rule class="LI_LAZY_INIT_UPDATE_STATIC" level="CRITICAL" enabled="true" />
+        <coding_rule class="ME_ENUM_FIELD_SETTER" level="MAJOR" enabled="false" />
+        <coding_rule class="ME_MUTABLE_ENUM_FIELD" level="MAJOR" enabled="false" />
+        <coding_rule class="MF_CLASS_MASKS_FIELD" level="MAJOR" enabled="true" />
+        <coding_rule class="MF_METHOD_MASKS_FIELD" level="MAJOR" enabled="true" />
+        <coding_rule class="ML_SYNC_ON_FIELD_TO_GUARD_CHANGING_THAT_FIELD" level="MAJOR" enabled="true" />
+        <coding_rule class="ML_SYNC_ON_UPDATED_FIELD" level="MAJOR" enabled="true" />
+        <coding_rule class="MSF_MUTABLE_SERVLET_FIELD" level="MAJOR" enabled="true" />
+        <coding_rule class="MS_CANNOT_BE_FINAL" level="MAJOR" enabled="true" />
+        <coding_rule class="MS_EXPOSE_REP" level="CRITICAL" enabled="true" />
+        <coding_rule class="MS_FINAL_PKGPROTECT" level="MAJOR" enabled="true" />
+        <coding_rule class="MS_MUTABLE_ARRAY" level="MAJOR" enabled="true" />
+        <coding_rule class="MS_MUTABLE_COLLECTION" level="MAJOR" enabled="false" />
+        <coding_rule class="MS_MUTABLE_COLLECTION_PKGPROTECT" level="MAJOR" enabled="false" />
+        <coding_rule class="MS_MUTABLE_HASHTABLE" level="MAJOR" enabled="true" />
+        <coding_rule class="MS_OOI_PKGPROTECT" level="MAJOR" enabled="true" />
+        <coding_rule class="MS_PKGPROTECT" level="MAJOR" enabled="true" />
+        <coding_rule class="MS_SHOULD_BE_FINAL" level="MAJOR" enabled="true" />
+        <coding_rule class="MS_SHOULD_BE_REFACTORED_TO_BE_FINAL" level="CRITICAL" enabled="false" />
+        <coding_rule class="MTIA_SUSPECT_SERVLET_INSTANCE_FIELD" level="CRITICAL" enabled="true" />
+        <coding_rule class="MTIA_SUSPECT_STRUTS_INSTANCE_FIELD" level="CRITICAL" enabled="true" />
+        <coding_rule class="MWN_MISMATCHED_NOTIFY" level="CRITICAL" enabled="true" />
+        <coding_rule class="MWN_MISMATCHED_WAIT" level="CRITICAL" enabled="true" />
+        <coding_rule class="NM_BAD_EQUAL" level="MAJOR" enabled="false" />
+        <coding_rule class="NM_CLASS_NAMING_CONVENTION" level="MAJOR" enabled="false" />
+        <coding_rule class="NM_CLASS_NOT_EXCEPTION" level="MAJOR" enabled="true" />
+        <coding_rule class="NM_CONFUSING" level="MAJOR" enabled="true" />
+        <coding_rule class="NM_FIELD_NAMING_CONVENTION" level="MAJOR" enabled="false" />
+        <coding_rule class="NM_FUTURE_KEYWORD_USED_AS_IDENTIFIER" level="MAJOR" enabled="true" />
+        <coding_rule class="NM_FUTURE_KEYWORD_USED_AS_MEMBER_IDENTIFIER" level="MAJOR" enabled="true" />
+        <coding_rule class="NM_LCASE_HASHCODE" level="MAJOR" enabled="false" />
+        <coding_rule class="NM_LCASE_TOSTRING" level="MAJOR" enabled="false" />
+        <coding_rule class="NM_METHOD_CONSTRUCTOR_CONFUSION" level="MAJOR" enabled="true" />
+        <coding_rule class="NM_METHOD_NAMING_CONVENTION" level="MAJOR" enabled="false" />
+        <coding_rule class="NM_SAME_SIMPLE_NAME_AS_INTERFACE" level="MAJOR" enabled="true" />
+        <coding_rule class="NM_SAME_SIMPLE_NAME_AS_SUPERCLASS" level="MAJOR" enabled="true" />
+        <coding_rule class="NM_VERY_CONFUSING" level="MAJOR" enabled="true" />
+        <coding_rule class="NM_VERY_CONFUSING_INTENTIONAL" level="MAJOR" enabled="true" />
+        <coding_rule class="NM_WRONG_PACKAGE" level="MAJOR" enabled="true" />
+        <coding_rule class="NM_WRONG_PACKAGE_INTENTIONAL" level="MAJOR" enabled="true" />
+        <coding_rule class="NN_NAKED_NOTIFY" level="CRITICAL" enabled="true" />
+        <coding_rule class="NOISE_FIELD_REFERENCE" level="MAJOR" enabled="false" />
+        <coding_rule class="NOISE_METHOD_CALL" level="MAJOR" enabled="false" />
+        <coding_rule class="NOISE_NULL_DEREFERENCE" level="MAJOR" enabled="false" />
+        <coding_rule class="NOISE_OPERATION" level="MAJOR" enabled="false" />
+        <coding_rule class="NO_NOTIFY_NOT_NOTIFYALL" level="CRITICAL" enabled="true" />
+        <coding_rule class="NP_ALWAYS_NULL" level="CRITICAL" enabled="true" />
+        <coding_rule class="NP_ALWAYS_NULL_EXCEPTION" level="CRITICAL" enabled="true" />
+        <coding_rule class="NP_ARGUMENT_MIGHT_BE_NULL" level="MAJOR" enabled="true" />
+        <coding_rule class="NP_BOOLEAN_RETURN_NULL" level="MAJOR" enabled="true" />
+        <coding_rule class="NP_CLONE_COULD_RETURN_NULL" level="CRITICAL" enabled="true" />
+        <coding_rule class="NP_CLOSING_NULL" level="MAJOR" enabled="false" />
+        <coding_rule class="NP_DEREFERENCE_OF_READLINE_VALUE" level="CRITICAL" enabled="true" />
+        <coding_rule class="NP_EQUALS_SHOULD_HANDLE_NULL_ARGUMENT" level="CRITICAL" enabled="true" />
+        <coding_rule class="NP_GUARANTEED_DEREF" level="BLOCKER" enabled="true" />
+        <coding_rule class="NP_GUARANTEED_DEREF_ON_EXCEPTION_PATH" level="CRITICAL" enabled="true" />
+        <coding_rule class="NP_IMMEDIATE_DEREFERENCE_OF_READLINE" level="CRITICAL" enabled="true" />
+        <coding_rule class="NP_LOAD_OF_KNOWN_NULL_VALUE" level="CRITICAL" enabled="true" />
+        <coding_rule class="NP_METHOD_PARAMETER_TIGHTENS_ANNOTATION" level="MAJOR" enabled="false" />
+        <coding_rule class="NP_METHOD_RETURN_RELAXING_ANNOTATION" level="MAJOR" enabled="false" />
+        <coding_rule class="NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR" level="CRITICAL" enabled="false" />
+        <coding_rule class="NP_NONNULL_PARAM_VIOLATION" level="CRITICAL" enabled="true" />
+        <coding_rule class="NP_NONNULL_RETURN_VIOLATION" level="CRITICAL" enabled="true" />
+        <coding_rule class="NP_NULL_INSTANCEOF" level="BLOCKER" enabled="true" />
+        <coding_rule class="NP_NULL_ON_SOME_PATH" level="CRITICAL" enabled="true" />
+        <coding_rule class="NP_NULL_ON_SOME_PATH_EXCEPTION" level="CRITICAL" enabled="true" />
+        <coding_rule class="NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE" level="CRITICAL" enabled="true" />
+        <coding_rule class="NP_NULL_ON_SOME_PATH_MIGHT_BE_INFEASIBLE" level="CRITICAL" enabled="true" />
+        <coding_rule class="NP_NULL_PARAM_DEREF" level="CRITICAL" enabled="true" />
+        <coding_rule class="NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS" level="CRITICAL" enabled="true" />
+        <coding_rule class="NP_NULL_PARAM_DEREF_NONVIRTUAL" level="CRITICAL" enabled="true" />
+        <coding_rule class="NP_OPTIONAL_RETURN_NULL" level="MAJOR" enabled="false" />
+        <coding_rule class="NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE" level="CRITICAL" enabled="true" />
+        <coding_rule class="NP_STORE_INTO_NONNULL_FIELD" level="CRITICAL" enabled="true" />
+        <coding_rule class="NP_SYNC_AND_NULL_CHECK_FIELD" level="MAJOR" enabled="true" />
+        <coding_rule class="NP_TOSTRING_COULD_RETURN_NULL" level="CRITICAL" enabled="true" />
+        <coding_rule class="NP_UNWRITTEN_FIELD" level="MAJOR" enabled="false" />
+        <coding_rule class="NP_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD" level="CRITICAL" enabled="false" />
+        <coding_rule class="NS_DANGEROUS_NON_SHORT_CIRCUIT" level="CRITICAL" enabled="true" />
+        <coding_rule class="NS_NON_SHORT_CIRCUIT" level="MAJOR" enabled="true" />
+        <coding_rule class="OBL_UNSATISFIED_OBLIGATION" level="CRITICAL" enabled="false" />
+        <coding_rule class="OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE" level="CRITICAL" enabled="false" />
+        <coding_rule class="ODR_OPEN_DATABASE_RESOURCE" level="CRITICAL" enabled="true" />
+        <coding_rule class="ODR_OPEN_DATABASE_RESOURCE_EXCEPTION_PATH" level="CRITICAL" enabled="true" />
+        <coding_rule class="OS_OPEN_STREAM" level="CRITICAL" enabled="true" />
+        <coding_rule class="OS_OPEN_STREAM_EXCEPTION_PATH" level="CRITICAL" enabled="true" />
+        <coding_rule class="PS_PUBLIC_SEMAPHORES" level="CRITICAL" enabled="true" />
+        <coding_rule class="PT_ABSOLUTE_PATH_TRAVERSAL" level="CRITICAL" enabled="false" />
+        <coding_rule class="PT_RELATIVE_PATH_TRAVERSAL" level="CRITICAL" enabled="false" />
+        <coding_rule class="PZLA_PREFER_ZERO_LENGTH_ARRAYS" level="MAJOR" enabled="true" />
+        <coding_rule class="PZ_DONT_REUSE_ENTRY_OBJECTS_IN_ITERATORS" level="CRITICAL" enabled="false" />
+        <coding_rule class="QBA_QUESTIONABLE_BOOLEAN_ASSIGNMENT" level="CRITICAL" enabled="true" />
+        <coding_rule class="QF_QUESTIONABLE_FOR_LOOP" level="CRITICAL" enabled="true" />
+        <coding_rule class="RANGE_ARRAY_INDEX" level="MAJOR" enabled="false" />
+        <coding_rule class="RANGE_ARRAY_LENGTH" level="MAJOR" enabled="false" />
+        <coding_rule class="RANGE_ARRAY_OFFSET" level="MAJOR" enabled="false" />
+        <coding_rule class="RANGE_STRING_INDEX" level="MAJOR" enabled="false" />
+        <coding_rule class="RCN_REDUNDANT_COMPARISON_OF_NULL_AND_NONNULL_VALUE" level="CRITICAL" enabled="true" />
+        <coding_rule class="RCN_REDUNDANT_COMPARISON_TWO_NULL_VALUES" level="CRITICAL" enabled="true" />
+        <coding_rule class="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE" level="CRITICAL" enabled="true" />
+        <coding_rule class="RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE" level="CRITICAL" enabled="true" />
+        <coding_rule class="RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE" level="CRITICAL" enabled="true" />
+        <coding_rule class="RC_REF_COMPARISON" level="CRITICAL" enabled="true" />
+        <coding_rule class="RC_REF_COMPARISON_BAD_PRACTICE" level="MAJOR" enabled="false" />
+        <coding_rule class="RC_REF_COMPARISON_BAD_PRACTICE_BOOLEAN" level="MAJOR" enabled="false" />
+        <coding_rule class="REC_CATCH_EXCEPTION" level="MAJOR" enabled="true" />
+        <coding_rule class="RE_BAD_SYNTAX_FOR_REGULAR_EXPRESSION" level="CRITICAL" enabled="true" />
+        <coding_rule class="RE_CANT_USE_FILE_SEPARATOR_AS_REGULAR_EXPRESSION" level="CRITICAL" enabled="true" />
+        <coding_rule class="RE_POSSIBLE_UNINTENDED_PATTERN" level="CRITICAL" enabled="true" />
+        <coding_rule class="RI_REDUNDANT_INTERFACES" level="MAJOR" enabled="true" />
+        <coding_rule class="RR_NOT_CHECKED" level="MAJOR" enabled="true" />
+        <coding_rule class="RS_READOBJECT_SYNC" level="CRITICAL" enabled="true" />
+        <coding_rule class="RU_INVOKE_RUN" level="MAJOR" enabled="true" />
+        <coding_rule class="RV_01_TO_INT" level="MAJOR" enabled="true" />
+        <coding_rule class="RV_ABSOLUTE_VALUE_OF_HASHCODE" level="CRITICAL" enabled="true" />
+        <coding_rule class="RV_ABSOLUTE_VALUE_OF_RANDOM_INT" level="CRITICAL" enabled="true" />
+        <coding_rule class="RV_CHECK_COMPARETO_FOR_SPECIFIC_RETURN_VALUE" level="CRITICAL" enabled="false" />
+        <coding_rule class="RV_CHECK_FOR_POSITIVE_INDEXOF" level="MINOR" enabled="true" />
+        <coding_rule class="RV_DONT_JUST_NULL_CHECK_READLINE" level="MAJOR" enabled="true" />
+        <coding_rule class="RV_EXCEPTION_NOT_THROWN" level="CRITICAL" enabled="true" />
+        <coding_rule class="RV_NEGATING_RESULT_OF_COMPARETO" level="CRITICAL" enabled="false" />
+        <coding_rule class="RV_REM_OF_HASHCODE" level="CRITICAL" enabled="true" />
+        <coding_rule class="RV_REM_OF_RANDOM_INT" level="CRITICAL" enabled="true" />
+        <coding_rule class="RV_RETURN_VALUE_IGNORED" level="MINOR" enabled="true" />
+        <coding_rule class="RV_RETURN_VALUE_IGNORED_BAD_PRACTICE" level="MAJOR" enabled="true" />
+        <coding_rule class="RV_RETURN_VALUE_IGNORED_INFERRED" level="CRITICAL" enabled="false" />
+        <coding_rule class="RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT" level="MAJOR" enabled="false" />
+        <coding_rule class="RV_RETURN_VALUE_OF_PUTIFABSENT_IGNORED" level="MAJOR" enabled="false" />
+        <coding_rule class="RpC_REPEATED_CONDITIONAL_TEST" level="MAJOR" enabled="true" />
+        <coding_rule class="SA_FIELD_DOUBLE_ASSIGNMENT" level="CRITICAL" enabled="true" />
+        <coding_rule class="SA_FIELD_SELF_ASSIGNMENT" level="CRITICAL" enabled="true" />
+        <coding_rule class="SA_FIELD_SELF_COMPARISON" level="CRITICAL" enabled="true" />
+        <coding_rule class="SA_FIELD_SELF_COMPUTATION" level="CRITICAL" enabled="true" />
+        <coding_rule class="SA_LOCAL_DOUBLE_ASSIGNMENT" level="CRITICAL" enabled="true" />
+        <coding_rule class="SA_LOCAL_SELF_ASSIGNMENT" level="CRITICAL" enabled="true" />
+        <coding_rule class="SA_LOCAL_SELF_ASSIGNMENT_INSTEAD_OF_FIELD" level="CRITICAL" enabled="false" />
+        <coding_rule class="SA_LOCAL_SELF_COMPARISON" level="CRITICAL" enabled="true" />
+        <coding_rule class="SA_LOCAL_SELF_COMPUTATION" level="CRITICAL" enabled="true" />
+        <coding_rule class="SBSC_USE_STRINGBUFFER_CONCATENATION" level="CRITICAL" enabled="true" />
+        <coding_rule class="SC_START_IN_CTOR" level="CRITICAL" enabled="true" />
+        <coding_rule class="SE_BAD_FIELD" level="MINOR" enabled="false" />
+        <coding_rule class="SE_BAD_FIELD_INNER_CLASS" level="MINOR" enabled="true" />
+        <coding_rule class="SE_BAD_FIELD_STORE" level="CRITICAL" enabled="true" />
+        <coding_rule class="SE_COMPARATOR_SHOULD_BE_SERIALIZABLE" level="MAJOR" enabled="true" />
+        <coding_rule class="SE_INNER_CLASS" level="MAJOR" enabled="true" />
+        <coding_rule class="SE_METHOD_MUST_BE_PRIVATE" level="MAJOR" enabled="true" />
+        <coding_rule class="SE_NONFINAL_SERIALVERSIONID" level="CRITICAL" enabled="true" />
+        <coding_rule class="SE_NONLONG_SERIALVERSIONID" level="MAJOR" enabled="true" />
+        <coding_rule class="SE_NONSTATIC_SERIALVERSIONID" level="MAJOR" enabled="true" />
+        <coding_rule class="SE_NO_SERIALVERSIONID" level="MAJOR" enabled="true" />
+        <coding_rule class="SE_NO_SUITABLE_CONSTRUCTOR" level="MAJOR" enabled="true" />
+        <coding_rule class="SE_NO_SUITABLE_CONSTRUCTOR_FOR_EXTERNALIZATION" level="MAJOR" enabled="true" />
+        <coding_rule class="SE_PRIVATE_READ_RESOLVE_NOT_INHERITED" level="MAJOR" enabled="true" />
+        <coding_rule class="SE_READ_RESOLVE_IS_STATIC" level="MAJOR" enabled="true" />
+        <coding_rule class="SE_READ_RESOLVE_MUST_RETURN_OBJECT" level="MAJOR" enabled="true" />
+        <coding_rule class="SE_TRANSIENT_FIELD_NOT_RESTORED" level="MAJOR" enabled="true" />
+        <coding_rule class="SE_TRANSIENT_FIELD_OF_NONSERIALIZABLE_CLASS" level="MAJOR" enabled="true" />
+        <coding_rule class="SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH" level="MAJOR" enabled="false" />
+        <coding_rule class="SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH_TO_THROW" level="MAJOR" enabled="false" />
+        <coding_rule class="SF_SWITCH_FALLTHROUGH" level="MAJOR" enabled="false" />
+        <coding_rule class="SF_SWITCH_NO_DEFAULT" level="MAJOR" enabled="false" />
+        <coding_rule class="SIC_INNER_SHOULD_BE_STATIC" level="MAJOR" enabled="true" />
+        <coding_rule class="SIC_INNER_SHOULD_BE_STATIC_ANON" level="MAJOR" enabled="true" />
+        <coding_rule class="SIC_INNER_SHOULD_BE_STATIC_NEEDS_THIS" level="MAJOR" enabled="true" />
+        <coding_rule class="SIC_THREADLOCAL_DEADLY_EMBRACE" level="MAJOR" enabled="false" />
+        <coding_rule class="SIO_SUPERFLUOUS_INSTANCEOF" level="CRITICAL" enabled="true" />
+        <coding_rule class="SI_INSTANCE_BEFORE_FINALS_ASSIGNED" level="CRITICAL" enabled="true" />
+        <coding_rule class="SP_SPIN_ON_FIELD" level="MAJOR" enabled="true" />
+        <coding_rule class="SQL_BAD_PREPARED_STATEMENT_ACCESS" level="CRITICAL" enabled="true" />
+        <coding_rule class="SQL_BAD_RESULTSET_ACCESS" level="CRITICAL" enabled="true" />
+        <coding_rule class="SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE" level="CRITICAL" enabled="true" />
+        <coding_rule class="SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING" level="CRITICAL" enabled="true" />
+        <coding_rule class="SR_NOT_CHECKED" level="MAJOR" enabled="true" />
+        <coding_rule class="SS_SHOULD_BE_STATIC" level="MAJOR" enabled="true" />
+        <coding_rule class="STCAL_INVOKE_ON_STATIC_CALENDAR_INSTANCE" level="CRITICAL" enabled="true" />
+        <coding_rule class="STCAL_INVOKE_ON_STATIC_DATE_FORMAT_INSTANCE" level="CRITICAL" enabled="true" />
+        <coding_rule class="STCAL_STATIC_CALENDAR_INSTANCE" level="CRITICAL" enabled="true" />
+        <coding_rule class="STCAL_STATIC_SIMPLE_DATE_FORMAT_INSTANCE" level="CRITICAL" enabled="true" />
+        <coding_rule class="STI_INTERRUPTED_ON_CURRENTTHREAD" level="CRITICAL" enabled="true" />
+        <coding_rule class="STI_INTERRUPTED_ON_UNKNOWNTHREAD" level="CRITICAL" enabled="true" />
+        <coding_rule class="ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD" level="CRITICAL" enabled="true" />
+        <coding_rule class="SWL_SLEEP_WITH_LOCK_HELD" level="CRITICAL" enabled="true" />
+        <coding_rule class="SW_SWING_METHODS_INVOKED_IN_SWING_THREAD" level="MAJOR" enabled="true" />
+        <coding_rule class="TLW_TWO_LOCK_WAIT" level="MAJOR" enabled="true" />
+        <coding_rule class="TQ_ALWAYS_VALUE_USED_WHERE_NEVER_REQUIRED" level="CRITICAL" enabled="true" />
+        <coding_rule class="TQ_COMPARING_VALUES_WITH_INCOMPATIBLE_TYPE_QUALIFIERS" level="CRITICAL" enabled="false" />
+        <coding_rule class="TQ_EXPLICIT_UNKNOWN_SOURCE_VALUE_REACHES_ALWAYS_SINK" level="CRITICAL" enabled="true" />
+        <coding_rule class="TQ_EXPLICIT_UNKNOWN_SOURCE_VALUE_REACHES_NEVER_SINK" level="CRITICAL" enabled="true" />
+        <coding_rule class="TQ_MAYBE_SOURCE_VALUE_REACHES_ALWAYS_SINK" level="CRITICAL" enabled="true" />
+        <coding_rule class="TQ_MAYBE_SOURCE_VALUE_REACHES_NEVER_SINK" level="CRITICAL" enabled="true" />
+        <coding_rule class="TQ_NEVER_VALUE_USED_WHERE_ALWAYS_REQUIRED" level="CRITICAL" enabled="true" />
+        <coding_rule class="TQ_UNKNOWN_VALUE_USED_WHERE_ALWAYS_STRICTLY_REQUIRED" level="CRITICAL" enabled="false" />
+        <coding_rule class="UCF_USELESS_CONTROL_FLOW" level="CRITICAL" enabled="true" />
+        <coding_rule class="UCF_USELESS_CONTROL_FLOW_NEXT_LINE" level="CRITICAL" enabled="true" />
+        <coding_rule class="UC_USELESS_CONDITION" level="MAJOR" enabled="false" />
+        <coding_rule class="UC_USELESS_CONDITION_TYPE" level="MAJOR" enabled="false" />
+        <coding_rule class="UC_USELESS_OBJECT" level="MAJOR" enabled="false" />
+        <coding_rule class="UC_USELESS_OBJECT_STACK" level="MAJOR" enabled="false" />
+        <coding_rule class="UC_USELESS_VOID_METHOD" level="MAJOR" enabled="false" />
+        <coding_rule class="UG_SYNC_SET_UNSYNC_GET" level="MAJOR" enabled="true" />
+        <coding_rule class="UI_INHERITANCE_UNSAFE_GETRESOURCE" level="MAJOR" enabled="true" />
+        <coding_rule class="UL_UNRELEASED_LOCK" level="CRITICAL" enabled="true" />
+        <coding_rule class="UL_UNRELEASED_LOCK_EXCEPTION_PATH" level="CRITICAL" enabled="true" />
+        <coding_rule class="UMAC_UNCALLABLE_METHOD_OF_ANONYMOUS_CLASS" level="CRITICAL" enabled="true" />
+        <coding_rule class="UM_UNNECESSARY_MATH" level="CRITICAL" enabled="true" />
+        <coding_rule class="UPM_UNCALLED_PRIVATE_METHOD" level="CRITICAL" enabled="true" />
+        <coding_rule class="URF_UNREAD_FIELD" level="MAJOR" enabled="true" />
+        <coding_rule class="URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD" level="CRITICAL" enabled="false" />
+        <coding_rule class="UR_UNINIT_READ" level="MAJOR" enabled="true" />
+        <coding_rule class="UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR" level="MAJOR" enabled="false" />
+        <coding_rule class="USM_USELESS_ABSTRACT_METHOD" level="MAJOR" enabled="false" />
+        <coding_rule class="USM_USELESS_SUBCLASS_METHOD" level="MAJOR" enabled="false" />
+        <coding_rule class="UUF_UNUSED_FIELD" level="MAJOR" enabled="true" />
+        <coding_rule class="UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD" level="CRITICAL" enabled="false" />
+        <coding_rule class="UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR" level="MAJOR" enabled="false" />
+        <coding_rule class="UWF_NULL_FIELD" level="CRITICAL" enabled="true" />
+        <coding_rule class="UWF_UNWRITTEN_FIELD" level="MAJOR" enabled="false" />
+        <coding_rule class="UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD" level="CRITICAL" enabled="false" />
+        <coding_rule class="UW_UNCOND_WAIT" level="MAJOR" enabled="true" />
+        <coding_rule class="VA_FORMAT_STRING_BAD_ARGUMENT" level="CRITICAL" enabled="true" />
+        <coding_rule class="VA_FORMAT_STRING_BAD_CONVERSION" level="CRITICAL" enabled="true" />
+        <coding_rule class="VA_FORMAT_STRING_BAD_CONVERSION_FROM_ARRAY" level="MAJOR" enabled="true" />
+        <coding_rule class="VA_FORMAT_STRING_BAD_CONVERSION_TO_BOOLEAN" level="MAJOR" enabled="true" />
+        <coding_rule class="VA_FORMAT_STRING_EXPECTED_MESSAGE_FORMAT_SUPPLIED" level="MAJOR" enabled="false" />
+        <coding_rule class="VA_FORMAT_STRING_EXTRA_ARGUMENTS_PASSED" level="MAJOR" enabled="true" />
+        <coding_rule class="VA_FORMAT_STRING_ILLEGAL" level="CRITICAL" enabled="true" />
+        <coding_rule class="VA_FORMAT_STRING_MISSING_ARGUMENT" level="CRITICAL" enabled="true" />
+        <coding_rule class="VA_FORMAT_STRING_NO_PREVIOUS_ARGUMENT" level="CRITICAL" enabled="true" />
+        <coding_rule class="VA_FORMAT_STRING_USES_NEWLINE" level="CRITICAL" enabled="false" />
+        <coding_rule class="VA_PRIMITIVE_ARRAY_PASSED_TO_OBJECT_VARARG" level="CRITICAL" enabled="true" />
+        <coding_rule class="VO_VOLATILE_INCREMENT" level="CRITICAL" enabled="false" />
+        <coding_rule class="VO_VOLATILE_REFERENCE_TO_ARRAY" level="MAJOR" enabled="true" />
+        <coding_rule class="VR_UNRESOLVABLE_REFERENCE" level="MAJOR" enabled="false" />
+        <coding_rule class="WA_AWAIT_NOT_IN_LOOP" level="CRITICAL" enabled="true" />
+        <coding_rule class="WA_NOT_IN_LOOP" level="CRITICAL" enabled="true" />
+        <coding_rule class="WL_USING_GETCLASS_RATHER_THAN_CLASS_LITERAL" level="CRITICAL" enabled="true" />
+        <coding_rule class="WMI_WRONG_MAP_ITERATOR" level="CRITICAL" enabled="true" />
+        <coding_rule class="WS_WRITEOBJECT_SYNC" level="CRITICAL" enabled="true" />
+        <coding_rule class="XFB_XML_FACTORY_BYPASS" level="CRITICAL" enabled="true" />
+        <coding_rule class="XSS_REQUEST_PARAMETER_TO_JSP_WRITER" level="CRITICAL" enabled="true" />
+        <coding_rule class="XSS_REQUEST_PARAMETER_TO_SEND_ERROR" level="CRITICAL" enabled="true" />
+        <coding_rule class="XSS_REQUEST_PARAMETER_TO_SERVLET_WRITER" level="CRITICAL" enabled="true" />
+      </profile>
+    </profiles>
+    <list size="0" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644 (file)
index 0000000..42afabf
--- /dev/null
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/EQC_G5.jks b/app/EQC_G5.jks
new file mode 100644 (file)
index 0000000..2229d46
Binary files /dev/null and b/app/EQC_G5.jks differ
diff --git a/app/FotaBrand8541e/debug/FotaBrand8541e_V1_202101251756_debug.apk b/app/FotaBrand8541e/debug/FotaBrand8541e_V1_202101251756_debug.apk
new file mode 100644 (file)
index 0000000..1c87ee8
Binary files /dev/null and b/app/FotaBrand8541e/debug/FotaBrand8541e_V1_202101251756_debug.apk differ
diff --git a/app/FotaBrand8541e/debug/output-metadata.json b/app/FotaBrand8541e/debug/output-metadata.json
new file mode 100644 (file)
index 0000000..25d61d3
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "version": 2,
+  "artifactType": {
+    "type": "APK",
+    "kind": "Directory"
+  },
+  "applicationId": "com.ic.icfotaclient",
+  "variantName": "processFotaBrand8541eDebugResources",
+  "elements": [
+    {
+      "type": "SINGLE",
+      "filters": [],
+      "versionCode": 1,
+      "versionName": "1.0",
+      "outputFile": "FotaBrand8541e_V1_202101252023_debug.apk"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/app/FotaBrand8541e/release/FotaBrand8541e_V1_202101260957_release.apk b/app/FotaBrand8541e/release/FotaBrand8541e_V1_202101260957_release.apk
new file mode 100644 (file)
index 0000000..8bc451b
Binary files /dev/null and b/app/FotaBrand8541e/release/FotaBrand8541e_V1_202101260957_release.apk differ
diff --git a/app/FotaBrand8541e/release/output-metadata.json b/app/FotaBrand8541e/release/output-metadata.json
new file mode 100644 (file)
index 0000000..d1b542f
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "version": 2,
+  "artifactType": {
+    "type": "APK",
+    "kind": "Directory"
+  },
+  "applicationId": "com.ic.icfotaclient",
+  "variantName": "processFotaBrand8541eReleaseResources",
+  "elements": [
+    {
+      "type": "SINGLE",
+      "filters": [],
+      "versionCode": 1,
+      "versionName": "1.0",
+      "outputFile": "FotaBrand8541e_V1_202101260957_release.apk"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644 (file)
index 0000000..af1a36b
--- /dev/null
@@ -0,0 +1,120 @@
+plugins {
+    id 'com.android.application'
+    id 'kotlin-android'
+    id 'kotlin-android-extensions'
+}
+
+android {
+    compileSdkVersion 29
+
+
+    def currentVersionCode = getVersionCode()
+
+
+    defaultConfig {
+        applicationId "com.ic.icfotaclient"
+        minSdkVersion 19
+        targetSdkVersion 29
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+    }
+
+    signingConfigs {
+        key {
+            storeFile file("EQC_G5.jks")
+            storePassword "123456"
+            keyAlias "watch_g5"
+            keyPassword "123456"
+        }
+    }
+
+    productFlavors {
+
+        FotaBrand8521e {
+            flavorDimensions "FotaBrand21e"
+            buildConfigField "String", "brandName", "\"FotaBrand21e\""
+        }
+        FotaBrand8541e {
+            flavorDimensions "FotaBrand41e"
+            buildConfigField "String", "brandName", "\"FotaBrand41e\""
+        }
+    }
+
+    buildTypes {
+        release {
+            zipAlignEnabled true     // zip 对齐优化
+            shrinkResources true       // 移除未使用的资源文件
+            minifyEnabled true
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+            renderscriptDebuggable = false
+            debuggable = false
+            manifestPlaceholders = [:]
+            signingConfig signingConfigs.key
+            buildConfigField "String", "token", "\"fYldTgHDa7\""
+        }
+        debug {
+            signingConfig signingConfigs.key
+            buildConfigField "String", "token", "\"fYldTgHDa7\""
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+    kotlinOptions {
+        jvmTarget = '1.8'
+    }
+
+    android.applicationVariants.all { variant ->
+        variant.outputs.all {
+            if (variant.buildType.name == "release") {
+                outputFileName = "${variant.getFlavorName()}_V${currentVersionCode}_${releaseTime()}_release.apk"
+            } else {
+                outputFileName = "${variant.getFlavorName()}_V${currentVersionCode}_${releaseTime()}_debug.apk"
+            }
+        }
+    }
+}
+
+static def releaseTime() {
+    return new Date().format("yyyyMMddHHmm", TimeZone.getTimeZone("GMT+08:00"))
+}
+
+def getVersionCode() {
+    def versionCodeFile = file('version_code.properties')
+    if (versionCodeFile.canRead()) {
+        Properties properties = new Properties()
+        properties.load(new FileInputStream(versionCodeFile))
+        def versionCode = properties['VERSION_CODE'].toInteger()//读取version_code.properties文件存放的版本号。
+        def runTasks = gradle.startParameter.taskNames
+        def task = ':app:assembleRelease'
+        if (task in runTasks) {
+            properties['VERSION_CODE'] = (++versionCode).toString()
+            properties.store(versionCodeFile.newWriter(), null)
+        }
+        return versionCode
+    } else {
+        throw new GradleException("无法读取version_code.properties文件!")
+    }
+}
+
+dependencies {
+    def lifecycle_version = "2.2.0"
+    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
+    implementation files('libs/ota.da-sdk-v3.2.1.aar')
+    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+    implementation 'androidx.core:core-ktx:1.3.2'
+    implementation 'androidx.appcompat:appcompat:1.2.0'
+    implementation 'com.google.android.material:material:1.2.1'
+    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+    testImplementation 'junit:junit:4.+'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+    implementation("com.squareup.okhttp3:okhttp:3.12.0")
+    implementation files('libs/android-logging-log4j-1.0.3.jar',
+            'libs/ftp4j-1.7.2.jar',
+            'libs/log4j-1.2.17.jar')
+    implementation 'com.google.code.gson:gson:2.8.6'
+}
\ No newline at end of file
diff --git a/app/libs/android-logging-log4j-1.0.3.jar b/app/libs/android-logging-log4j-1.0.3.jar
new file mode 100644 (file)
index 0000000..7d4146f
Binary files /dev/null and b/app/libs/android-logging-log4j-1.0.3.jar differ
diff --git a/app/libs/ftp4j-1.7.2.jar b/app/libs/ftp4j-1.7.2.jar
new file mode 100644 (file)
index 0000000..fca26ec
Binary files /dev/null and b/app/libs/ftp4j-1.7.2.jar differ
diff --git a/app/libs/log4j-1.2.17.jar b/app/libs/log4j-1.2.17.jar
new file mode 100644 (file)
index 0000000..068867e
Binary files /dev/null and b/app/libs/log4j-1.2.17.jar differ
diff --git a/app/libs/okio-2.4.3.jar b/app/libs/okio-2.4.3.jar
new file mode 100644 (file)
index 0000000..6289cf6
Binary files /dev/null and b/app/libs/okio-2.4.3.jar differ
diff --git a/app/libs/ota.da-sdk-v3.2.1.aar b/app/libs/ota.da-sdk-v3.2.1.aar
new file mode 100644 (file)
index 0000000..ccca184
Binary files /dev/null and b/app/libs/ota.da-sdk-v3.2.1.aar differ
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644 (file)
index 0000000..41f2508
--- /dev/null
@@ -0,0 +1,88 @@
+# 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
+
+# 保留四大组件,自定义的Application等等这些类不被混淆
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Application
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.app.backup.BackupAgentHelper
+-keep public class * extends android.preference.Preference
+-keep public class * extends android.view.View
+
+-keep class android.support.** { *; }
+-keep public class * extends android.support.v4.**
+-keep public class * extends android.support.v7.**
+-keep public class * extends android.support.annotation.**
+-keep public class * extends android.app.Fragment
+-keep class com.google.** { *; }
+
+
+# 保留R下面的资源
+-keep class **.R$* {*;}
+-keepclassmembers class **.R$* {
+    public static <fields>;
+}
+
+# 保留在Activity中的方法参数是view的方法,
+# 这样以来我们在layout中写的onClick就不会被影响
+-keepclassmembers class * extends android.app.Activity{
+    public void *(android.view.View);
+}
+
+# 保留我们自定义控件(继承自View)不被混淆
+-keep public class * extends android.view.View{
+    *** get*();
+    void set*(***);
+    public <init>(android.content.Context);
+    public <init>(android.content.Context, android.util.AttributeSet);
+    public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+
+################################## 项目类 ##################################
+
+-keep class com.facebook.** { *; }
+-keep class com.mediatek.** { *; }
+-keep class org.apache.** { *; }
+-keep class okio.Okio.** { *; }
+-keep class java.nio.** { *; }
+-keep class com.android.** { *; }
+-keep class android.** { *; }
+-keep class android.ddm.** { *; }
+-keep class com.alibaba.fastjson.** { *; }
+-keep class com.eqc.watch.launcher.WatchApplication { *; }
+#-keep class com.enqualcomm.support.EqcAlgorithmNative { *; }
+-dontwarn com.facebook.**
+-dontwarn com.mediatek.**
+-dontwarn java.nio.**
+-dontwarn com.alibaba.fastjson.**
+-dontwarn android.**
+-dontwarn com.android.**
+-dontwarn rx.internal.**
+
+-keep class com.ic.** { *; }
+-keep class com.enqualcomm.** { *; }
+-keep class com.sina.util.dnscache.** { *; }
+-keep class rx.internal.**  { *; }
+-keep class java.util.concurrent.**  { *; }
+-keep class se.wetcat.qatia.**  { *; }
\ No newline at end of file
diff --git a/app/src/androidTest/java/com/ic/icfotaclient/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/ic/icfotaclient/ExampleInstrumentedTest.kt
new file mode 100644 (file)
index 0000000..1472a7c
--- /dev/null
@@ -0,0 +1,24 @@
+package com.ic.icfotaclient
+
+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.ic.icfotaclient", appContext.packageName)
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..bfc13b6
--- /dev/null
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.ic.icfotaclient"
+    android:sharedUserId="android.uid.system">
+
+    <uses-permission android:name="android.permission.REBOOT" />
+    <uses-permission android:name="android.permission.RECOVERY" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
+    <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <uses-permission android:name="android.permission.BROADCAST_STICKY" />
+    <uses-permission android:name="android.permission.ACCESS_MTP" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.Manifest.permission.RECOVERY" />
+
+
+    <application
+        android:name=".App"
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:supportsRtl="true"
+        android:theme="@style/Theme.ICFotaClient">
+        <activity android:name=".view.SystemUpdateActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="com.ic.ota.action.UPDATE_PAGE" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".view.ApiTestActivity" />
+
+        <receiver
+            android:name=".presenter.OtaReceiver"
+            android:enabled="true"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.BOOT_COMPLETED" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.MEDIA_MOUNTED" />
+                <data android:scheme="file" />
+            </intent-filter>
+        </receiver>
+        <service
+            android:name=".presenter.OtaService"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="com.ic.ota.action.CHECK_OTA" />
+            </intent-filter>
+        </service>
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/app/src/main/aidl/com/ic/icfotaclient/IOtaCheckCallback.aidl b/app/src/main/aidl/com/ic/icfotaclient/IOtaCheckCallback.aidl
new file mode 100644 (file)
index 0000000..03d0d90
--- /dev/null
@@ -0,0 +1,12 @@
+// IOtaCheckCallback.aidl
+package com.ic.icfotaclient;
+
+import com.ic.icfotaclient.bean.OtaInfo;
+
+interface IOtaCheckCallback {
+    void onCheckResult(in OtaInfo otaInfo);
+
+    void onImeiCodeGet(in String imeiCode);
+
+    void onVersionCodeGet(in String versionCode);
+}
\ No newline at end of file
diff --git a/app/src/main/aidl/com/ic/icfotaclient/IOtaCheckService.aidl b/app/src/main/aidl/com/ic/icfotaclient/IOtaCheckService.aidl
new file mode 100644 (file)
index 0000000..ecef6de
--- /dev/null
@@ -0,0 +1,16 @@
+// IOtaCheckService.aidl
+package com.ic.icfotaclient;
+
+import com.ic.icfotaclient.IOtaCheckCallback;
+
+interface IOtaCheckService {
+
+    oneway void checkHasNewVersion();
+
+    void setCallback(in IOtaCheckCallback callback);
+
+    oneway void getImeiCode();
+
+    oneway void getVersionCode();
+
+}
\ No newline at end of file
diff --git a/app/src/main/aidl/com/ic/icfotaclient/bean/OtaInfo.aidl b/app/src/main/aidl/com/ic/icfotaclient/bean/OtaInfo.aidl
new file mode 100644 (file)
index 0000000..31d503d
--- /dev/null
@@ -0,0 +1,6 @@
+// OtaInfo.aidl
+package com.ic.icfotaclient.bean;
+
+// Declare any non-default types here with import statements
+
+parcelable OtaInfo;
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/icfotaclient/App.kt b/app/src/main/java/com/ic/icfotaclient/App.kt
new file mode 100644 (file)
index 0000000..c48846b
--- /dev/null
@@ -0,0 +1,23 @@
+package com.ic.icfotaclient
+
+import android.app.Application
+import android.content.Context
+import android.os.Handler
+import android.os.HandlerThread
+
+class App : Application() {
+
+    companion object {
+        lateinit var sContext: Context
+        lateinit var sHandler: Handler
+    }
+
+    override fun onCreate() {
+        super.onCreate()
+        sContext = applicationContext
+        val otaHandlerThread = HandlerThread("ic:ota-handler-thread")
+        otaHandlerThread.start()
+        sHandler = Handler(otaHandlerThread.looper)
+    }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/icfotaclient/base/AbstractOtaRepository.kt b/app/src/main/java/com/ic/icfotaclient/base/AbstractOtaRepository.kt
new file mode 100644 (file)
index 0000000..a943fc3
--- /dev/null
@@ -0,0 +1,43 @@
+package com.ic.icfotaclient.base
+
+import com.ic.icfotaclient.bean.DownloadInfo
+import com.ic.icfotaclient.bean.OtaInfo
+import com.ic.icfotaclient.repository.IOtaRepositoryCallback
+
+abstract class AbstractOtaRepository : IOtaBaseRepository {
+
+
+    var mNextRepository: IOtaBaseRepository? = null
+
+    override suspend fun setNextRepository(nextRepository: IOtaBaseRepository) {
+        this.mNextRepository = nextRepository
+    }
+
+    override suspend fun checkHasNewVersion(): OtaInfo? {
+        return mNextRepository?.checkHasNewVersion()
+    }
+
+    override suspend fun downloadPackage(callback: IOtaRepositoryCallback): DownloadInfo? {
+        return mNextRepository?.downloadPackage(callback)
+    }
+
+    override suspend fun reportInstallResult(isSuccess: Boolean) {
+        mNextRepository?.reportInstallResult(isSuccess)
+    }
+
+    override suspend fun reportResult(versionCode: String) {
+        mNextRepository?.reportResult(versionCode)
+    }
+
+    override suspend fun getPackagePath(): String? {
+        return mNextRepository?.getPackagePath()
+    }
+
+    override suspend fun deletePackage() {
+        mNextRepository?.deletePackage()
+    }
+
+    override suspend fun pauseDownload() {
+        mNextRepository?.pauseDownload()
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/icfotaclient/base/IOtaBaseRepository.kt b/app/src/main/java/com/ic/icfotaclient/base/IOtaBaseRepository.kt
new file mode 100644 (file)
index 0000000..26a6ab9
--- /dev/null
@@ -0,0 +1,50 @@
+package com.ic.icfotaclient.base
+
+import com.ic.icfotaclient.bean.DownloadInfo
+import com.ic.icfotaclient.bean.OtaInfo
+import com.ic.icfotaclient.repository.IOtaRepositoryCallback
+
+
+interface IOtaBaseRepository {
+    /**
+     * 检查是否有新的版本
+     */
+    suspend fun checkHasNewVersion(): OtaInfo?
+
+    /**
+     * 下载升级包
+     */
+    suspend fun downloadPackage(callback: IOtaRepositoryCallback): DownloadInfo?
+
+    /**
+     * 通知升级结果
+     */
+    suspend fun reportInstallResult(isSuccess: Boolean);
+
+    /**
+     * 通知更新成功,让服务器收费
+     */
+    suspend fun reportResult(versionCode: String)
+
+    /**
+     * 获取升级包的路径
+     */
+    suspend fun getPackagePath(): String?
+
+    /**
+     * 删除升级包
+     */
+    suspend fun deletePackage()
+
+    /**
+     * 设置下一级委托的Repository
+     */
+    suspend fun setNextRepository(nextRepository: IOtaBaseRepository)
+
+
+    /**
+     * 暂停下载
+     */
+    suspend fun pauseDownload()
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/icfotaclient/bean/DownloadInfo.kt b/app/src/main/java/com/ic/icfotaclient/bean/DownloadInfo.kt
new file mode 100644 (file)
index 0000000..bd39c93
--- /dev/null
@@ -0,0 +1,7 @@
+package com.ic.icfotaclient.bean
+
+data class DownloadInfo(var path: String, val size: Long) {
+    override fun toString(): String {
+        return "DownloadInfo(path='$path', size=$size)"
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/icfotaclient/bean/ICOtaInfo.kt b/app/src/main/java/com/ic/icfotaclient/bean/ICOtaInfo.kt
new file mode 100644 (file)
index 0000000..2ab0aab
--- /dev/null
@@ -0,0 +1,13 @@
+package com.ic.icfotaclient.bean
+
+data class ICOtaInfo(
+    val AutoUpdate: Boolean,
+    val OtaPackageLength: Int,
+    val OtaPackageName: String,
+    val OtaPackageVersion: String,
+    val SystemVersion: String,
+    val UptoDate: Boolean,
+    val description: String,
+    val md5sum: String,
+    val url: String
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/icfotaclient/bean/OtaInfo.kt b/app/src/main/java/com/ic/icfotaclient/bean/OtaInfo.kt
new file mode 100644 (file)
index 0000000..ce544df
--- /dev/null
@@ -0,0 +1,59 @@
+package com.ic.icfotaclient.bean
+
+import android.os.Parcel
+import android.os.Parcelable
+
+/**
+ * 升级信息
+ */
+data class OtaInfo(
+    //是否强制升级
+    val isForce: Boolean,
+    //更新信息
+    val updateDesc: String?,
+    //版本号
+    val versionCode: String?,
+    //包的大小
+    val packageSize: Long,
+    //发布时间
+    val publishTime: Long,
+) : Parcelable {
+    constructor(parcel: Parcel) : this(
+        parcel.readByte() != 0.toByte(),
+        parcel.readString(),
+        parcel.readString(),
+        parcel.readLong(),
+        parcel.readLong(),
+    ) {
+    }
+
+    override fun toString(): String {
+        return "OtaInfo(isForce=$isForce," +
+                " updateDesc='$updateDesc'," +
+                " versionCode='$versionCode'," +
+                " packageSize=$packageSize," +
+                " publishTime=$publishTime)"
+    }
+
+    override fun writeToParcel(parcel: Parcel, flags: Int) {
+        parcel.writeByte(if (isForce) 1 else 0)
+        parcel.writeString(updateDesc)
+        parcel.writeString(versionCode)
+        parcel.writeLong(packageSize)
+        parcel.writeLong(publishTime)
+    }
+
+    override fun describeContents(): Int {
+        return 0
+    }
+
+    companion object CREATOR : Parcelable.Creator<OtaInfo> {
+        override fun createFromParcel(parcel: Parcel): OtaInfo {
+            return OtaInfo(parcel)
+        }
+
+        override fun newArray(size: Int): Array<OtaInfo?> {
+            return arrayOfNulls(size)
+        }
+    }
+}
diff --git a/app/src/main/java/com/ic/icfotaclient/presenter/OtaReceiver.kt b/app/src/main/java/com/ic/icfotaclient/presenter/OtaReceiver.kt
new file mode 100644 (file)
index 0000000..9a77823
--- /dev/null
@@ -0,0 +1,35 @@
+package com.ic.icfotaclient.presenter
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.util.Log
+import com.ic.icfotaclient.utils.Constants
+
+/**
+ * 广播监听者:
+ * 监听开机广播,去检查是否有升级
+ * 其他广播//TODO:
+ */
+class OtaReceiver : BroadcastReceiver() {
+    companion object {
+        const val TAG = "OtaReceiver"
+    }
+
+    override fun onReceive(context: Context?, intent: Intent?) {
+        Log.d(TAG, "OtaReceiver...")
+        if (intent == null) {
+            return
+        }
+        val action = intent.action
+        Log.d(TAG, "action is == > $action")
+        when (action) {
+            Intent.ACTION_BOOT_COMPLETED -> {
+                //开机广播,拉起服务
+                val serviceIntent = Intent(context, OtaService::class.java)
+                serviceIntent.action = Constants.CMD_ACTION_BOOT_COMPLETED
+                context?.startService(serviceIntent)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/icfotaclient/presenter/OtaService.kt b/app/src/main/java/com/ic/icfotaclient/presenter/OtaService.kt
new file mode 100644 (file)
index 0000000..d60a162
--- /dev/null
@@ -0,0 +1,128 @@
+package com.ic.icfotaclient.presenter
+
+import android.app.Service
+import android.content.Intent
+import android.os.IBinder
+import android.text.TextUtils
+import android.util.Log
+import androidx.lifecycle.*
+import com.ic.icfotaclient.App
+import com.ic.icfotaclient.IOtaCheckCallback
+import com.ic.icfotaclient.IOtaCheckService
+import com.ic.icfotaclient.utils.Constants
+import com.ic.icfotaclient.utils.OtaViewModelCreator
+import com.ic.icfotaclient.utils.VersionCheckFrom
+import com.ic.icfotaclient.utils.VersionInfoUtils
+
+/**
+ * 这个是专门给远程使用的,如果远程需要知道是否有更新的内容,可以通过AIDL通讯
+ * 绑定过来,检查是否有更新。
+ */
+class OtaService : Service(), ViewModelStoreOwner, LifecycleOwner {
+
+    companion object{
+        const val TAG = "OtaService"
+    }
+
+    override fun onBind(intent: Intent?): IBinder? {
+        return mServiceForRemote
+    }
+
+    override fun onCreate() {
+        super.onCreate()
+        Log.d(TAG,"on service created...")
+        OtaViewModelCreator.initOtaViewModelStoreOwner(this)
+        //观察数据变化
+        otaViewModel?.otaInfo?.observe(this, Observer {
+            Log.d(TAG,"on ota info change...$it")
+            //更新数据
+            mRemoteCallback?.onCheckResult(it)
+        })
+    }
+
+    private val otaViewModel by lazy {
+        OtaViewModelCreator.getOtaViewModel(null)
+    }
+
+    private var mRemoteCallback: IOtaCheckCallback? = null
+
+
+    private val mServiceForRemote = object : IOtaCheckService.Stub() {
+        //检查是否有新版本
+        override fun checkHasNewVersion() {
+            checkVersion(VersionCheckFrom.REMOTE_CHECK)
+        }
+
+        override fun setCallback(callback: IOtaCheckCallback?) {
+            //远程回调接口
+            mRemoteCallback = callback
+        }
+
+        override fun getImeiCode() {
+            val imei = VersionInfoUtils.getIMEI()
+            Log.d(TAG,"imei == > $imei")
+            mRemoteCallback?.onImeiCodeGet(imei)
+        }
+
+        override fun getVersionCode() {
+            val versionCode = VersionInfoUtils.getSystemVersion()
+            Log.d(TAG,"versionCode == > $versionCode")
+            mRemoteCallback?.onVersionCodeGet(versionCode)
+        }
+    }
+
+
+    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+        //判断指令是啥
+        val action = intent?.action
+        Log.d(TAG,"onStartCommand action -- > $action")
+        if (TextUtils.isEmpty(action)) {
+            return START_NOT_STICKY
+        }
+        //根据action做出处理
+        when (action) {
+            Constants.CMD_ACTION_BOOT_COMPLETED -> {
+                //开机完,创建一个ViewModel,生命周期与此服务生命周期挂钩
+                //以下动作,需要延时执行,因为IMEI号,网络这些还没准备好
+                //检查网络,如果网络okay了,就去检查版本更新
+                //如果网络不okay,那么就等待网络连接的广播,在连接的广播里去检查更新
+                App.sHandler.postDelayed({
+                    checkVersion(VersionCheckFrom.BOOT_CHECK)
+                }, Constants.TIME_DELAY_BOOT)
+                //如果已经更新了,那么通知一下服务器
+                otaViewModel?.checkHasUpdated()
+            }
+        }
+
+        return super.onStartCommand(intent, flags, startId)
+    }
+
+    private fun checkVersion(from: VersionCheckFrom) {
+        val checkNetWork = otaViewModel?.checkNetWork()
+        Log.d(TAG,"checkNetWork == > $checkNetWork")
+        if (checkNetWork == true) {
+            //连接上了,那么就去检查更新
+            otaViewModel?.checkUpdate(from)
+        }
+    }
+
+    /**
+     * 释放资源
+     */
+    override fun onDestroy() {
+        super.onDestroy()
+        viewModel.clear()
+    }
+
+    private val viewModel by lazy {
+        ViewModelStore()
+    }
+
+    override fun getViewModelStore(): ViewModelStore {
+        return viewModel
+    }
+
+    override fun getLifecycle(): Lifecycle {
+        return LifecycleRegistry(this)
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/icfotaclient/presenter/OtaViewModel.kt b/app/src/main/java/com/ic/icfotaclient/presenter/OtaViewModel.kt
new file mode 100644 (file)
index 0000000..164561f
--- /dev/null
@@ -0,0 +1,386 @@
+package com.ic.icfotaclient.presenter
+
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.BatteryManager
+import android.os.RecoverySystem
+import android.text.TextUtils
+import android.util.Log
+import android.view.Gravity
+import android.view.View
+import android.view.WindowManager
+import android.widget.Button
+import android.widget.TextView
+import android.widget.Toast
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.ic.icfotaclient.App
+import com.ic.icfotaclient.R
+import com.ic.icfotaclient.bean.DownloadInfo
+import com.ic.icfotaclient.bean.OtaInfo
+import com.ic.icfotaclient.repository.CarOtaRepository
+import com.ic.icfotaclient.repository.ICOtaRepository
+import com.ic.icfotaclient.repository.IOtaRepositoryCallback
+import com.ic.icfotaclient.utils.*
+import com.ic.icfotaclient.view.SystemUpdateActivity
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import java.io.File
+
+
+/**
+ * 逻辑层,全局只有一个,生命周期与服务挂钩吧,检查完了就可以停掉了,不需要一直跑着。
+ * 如果有升级就去做事情,如果没有升级就自己退出,释放所有资源。
+ */
+class OtaViewModel : ViewModel() {
+
+    companion object {
+        const val TAG = "OtaViewModel"
+    }
+
+
+    private val mFirstRepository by lazy {
+        //Repository是责任链模式,所以要根据顺序排好
+        val carOtaRepository = CarOtaRepository()
+        val icOtaRepository = ICOtaRepository()
+        icOtaRepository.mNextRepository = carOtaRepository
+        icOtaRepository
+    }
+
+    //关心的数据
+    //升级数据
+    val otaInfo = MutableLiveData<OtaInfo?>()
+
+    //下载进度,百分比
+    val downloadProgress = MutableLiveData<Int>()
+
+    //是否下载完成
+    val currentActionState by lazy {
+        val mutableLiveData = MutableLiveData<UpdateActionState>()
+        mutableLiveData.postValue(UpdateActionState.NONE)
+        mutableLiveData
+    }
+
+    private var wifiConnected = false
+
+    //删除包的状态
+    fun checkNetWork(): Boolean {
+        var result: Boolean
+        wifiConnected = NetworkUtils.isWifiConnected(App.sContext)
+        Log.d(TAG, "WIFI CONNECTED == > $wifiConnected")
+        result = if (wifiConnected) {
+            isTipsWifiConnect.postValue(false)
+            wifiConnected
+        } else {
+            val isNetConnected = NetworkUtils.isNetConnected(App.sContext)
+            Log.d(TAG, "mobile net is connected == > $isNetConnected")
+            isNetConnected
+        }
+        if (!result) {
+            //无网络
+            currentActionState.postValue(UpdateActionState.NO_NETWORK)
+        }
+        return result
+    }
+
+    //检查更新,需要在子线程种完成
+    fun checkUpdate(from: VersionCheckFrom) {
+        if (currentActionState.value == UpdateActionState.CHECKING) {
+            Log.d(TAG, "checking....")
+            return
+        }
+        if (!checkNetWork()) {
+            currentActionState.value = UpdateActionState.NO_NETWORK
+            return
+        }
+        currentActionState.postValue(UpdateActionState.CHECKING)
+        viewModelScope.launch {
+            try {
+                //检查更新
+                val result: OtaInfo? = withContext(Dispatchers.IO) {
+                    mFirstRepository.checkHasNewVersion()
+                }
+                //结果
+                Log.d(TAG, "check result ==> $result")
+                otaInfo.postValue(result)
+                //如果不为null,弹出一个系统级的dialog,也就是在任何地方都可以弹出的
+                currentActionState.postValue(
+                    if (result == null) {
+                        //已经是最新版本了
+                        UpdateActionState.LAST_VERSION
+                    } else {
+                        //如果是强制升级的,不等通知用户结果了直接升级
+                        if (result.isForce) {
+                            Log.d(TAG, "force download data...")
+                            //直接调用下载
+                            downloadPackage()
+                        } else {
+                            //不是UI来的,可以创建一个弹窗,否则不弹窗,非强制升级
+                            if (from != VersionCheckFrom.UI_CHECK) {
+                                createNewVersionDialog(result.updateDesc ?: "无描述")
+                            }
+                        }
+                        //有新版本
+                        UpdateActionState.NEW_VERSION
+                    }
+                )
+            } catch (e: Exception) {
+                Log.e(TAG, e.toString())
+            }
+        }
+    }
+
+    val downInfoResult = MutableLiveData<DownloadInfo>()
+
+
+    /**
+     * 暂停下载
+     */
+    fun pauseDownload() {
+        if (currentActionState.value != UpdateActionState.DOWNLOADING) {
+            Log.d(TAG, "current is not download...")
+            return
+        }
+        currentActionState.postValue(UpdateActionState.DOWNLOAD_PAUSE)
+        viewModelScope.launch {
+            withContext(Dispatchers.IO) {
+                Log.d(TAG, "download pause...")
+                mFirstRepository.pauseDownload()
+            }
+        }
+    }
+
+    val isTipsWifiConnect = MutableLiveData<Boolean>()
+
+
+    fun downloadPackage() {
+        if (currentActionState.value == UpdateActionState.DOWNLOADING) {
+            Log.d(TAG, "downloading...")
+            return
+        }
+        //判断size是否大于100M,如果大于100M的话那么就提示连接wifi进行下载
+        try {
+            if (otaInfo.value?.packageSize!! > 100000000L && !wifiConnected) {
+                isTipsWifiConnect.postValue(true)
+                //提示用户连接wifi下载
+                Log.d(TAG, "size == > ${otaInfo.value?.packageSize} please in wifi download...")
+                return
+            }
+        } catch (e: Exception) {
+            Log.d(TAG, "e", e)
+        }
+        currentActionState.postValue(UpdateActionState.DOWNLOADING)
+        viewModelScope.launch {
+            try {
+                val downResult = withContext(Dispatchers.IO) {
+                    mFirstRepository.downloadPackage(mDownloadCallback)
+                }
+                if (downResult == null) {
+                    currentActionState.postValue(UpdateActionState.ERROR)
+                } else {
+                    downInfoResult.value = downResult
+                    //下载成功以后,如果是强制升级的,直接走升级流程
+                    if (otaInfo.value?.isForce == true) {
+                        Log.d(TAG, "download completed and force to update....")
+                        //直接调用升级
+                        install()
+                    } else {
+                        currentActionState.postValue(UpdateActionState.DOWNLOADED)
+                    }
+                }
+            } catch (e: java.lang.Exception) {
+                Log.e("e", e.toString())
+            }
+        }
+    }
+
+    /**
+     * 下载的回调
+     */
+    private val mDownloadCallback = object : IOtaRepositoryCallback {
+
+        override fun onDownloadProgressChange(currentProgress: Long, totalProgress: Long) {
+            //处理一下进度
+            val progress: Int = (currentProgress * 100.0f / totalProgress + 0.5f).toInt()
+            Log.d(TAG, "progress == > $progress")
+            downloadProgress.postValue(progress)
+        }
+
+        override fun onDownloadError(errorCode: Int, msg: String) {
+            Toast.makeText(App.sContext, "errorCode:$errorCode", Toast.LENGTH_LONG).show()
+            Log.d(TAG, "onDownloadError -- > $errorCode == > $msg")
+            if (errorCode == 0) {
+                //停止下载了,修改UI为继续下载
+                currentActionState.postValue(UpdateActionState.DOWNLOAD_PAUSE)
+            } else {
+                //其他错误,都提示用户重启
+                currentActionState.postValue(UpdateActionState.ERROR)
+            }
+        }
+
+        override fun onDownloadSuccess() {
+            Log.d(TAG, "onDownloadSuccess...")
+            //下载完成了
+            currentActionState.postValue(UpdateActionState.DOWNLOADED)
+        }
+
+        override fun onDeletePackageSuccess() {
+            //
+        }
+
+        override fun onDeletePackageFailed(errorCode: Int, msg: String) {
+            //
+        }
+    }
+
+    /**
+     * 创建一个弹窗,直接添加到窗口中.
+     */
+    private fun createNewVersionDialog(desc: String) {
+        val manager = App.sContext.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+        val layoutParams = WindowManager.LayoutParams()
+        layoutParams.gravity = Gravity.CENTER
+        layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
+        layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT
+        layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT
+        layoutParams.alpha = 1.0f
+        layoutParams.packageName = App.sContext.getPackageName()
+        layoutParams.buttonBrightness = 1f
+        layoutParams.windowAnimations = android.R.style.Animation_Dialog
+
+        val view: View =
+            View.inflate(App.sContext, R.layout.dialog_tips, null)
+        val descText: TextView = view.findViewById(R.id.description)
+        if (TextUtils.isEmpty(desc)) {
+            descText.text = "没有更新说明..."
+        } else {
+            descText.text = desc
+        }
+        val yesButton: Button = view.findViewById(R.id.button_ok) as Button
+        val noButton: Button = view.findViewById(R.id.button_cancel) as Button
+        yesButton.setOnClickListener {
+            //下载安装包
+            downloadPackage()
+            //跳转到下载界面
+            val targetIntent = Intent(view.context, SystemUpdateActivity::class.java)
+            targetIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+            view.context.startActivity(targetIntent)
+            manager.removeView(view)
+        }
+        noButton.setOnClickListener { manager.removeView(view) }
+        manager.addView(view, layoutParams)
+    }
+
+    /**
+     * 获取当前的版本号
+     */
+    fun getCurrentVersionCode(): String? {
+        return VersionInfoUtils.getSystemVersion()
+    }
+
+    fun getImeiCode(): String? {
+        return VersionInfoUtils.getIMEI()
+    }
+
+    /**
+     * 更新版本
+     */
+    fun install() {
+        viewModelScope.launch {
+            try {
+                //判断电量多少,如果低于50%不进行安装
+                val batteryStatus: Intent? =
+                    IntentFilter(Intent.ACTION_BATTERY_CHANGED).let { filter ->
+                        App.sContext.registerReceiver(null, filter)
+                    }
+                val batteryPct: Float? = batteryStatus?.let { intent ->
+                    val level: Int = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
+                    val scale: Int = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
+                    level * 100f / scale.toFloat()
+                }
+                Log.d(TAG, "batteryPct == > $batteryPct")
+                if (batteryPct!! >= 50.0f) {
+                    Log.d(TAG, "otaInfo.value!!.versionCode == > " + otaInfo.value!!.versionCode)
+                    Log.d(TAG, "save target update version.")
+                    //保存一个SP值,升级完了检查,用于通知服务器更新成功了.
+                    val sharePreference = SPUtil.getSharePreference()
+                    sharePreference.edit()
+                        .putString(SPUtil.KEY_TARGET_VERSION_CODE, otaInfo.value!!.versionCode)
+                        .apply()
+                    //这里后面要改成我们自己的升级,而不是apk的升级方式
+                    withContext(Dispatchers.IO) {
+                        //进行电量检查
+                        val path = downInfoResult.value!!.path;
+                        //val path = "/data/media/0/sl8541e_1h10_oversea-ota-04210.zip"
+                        val tempFile = File(path)
+                        Log.d(TAG, "tempFile.exists() == > ${tempFile.exists()}")
+                        Log.d(TAG, "install path is == > $path")
+                        //先验证,再安装
+                        try {
+                            RecoverySystem.verifyPackage(File(path), null, null)
+                            Log.d(TAG, "verifyPackageFinished...")
+                            RecoverySystem.installPackage(
+                                App.sContext,
+                                File(path)
+                            )
+                        } catch (e: Exception) {
+                            e.printStackTrace()
+                        }
+                    }
+                } else {
+                    Log.d(TAG, "battery level is not enough...")
+                    //电量不够,不进行升级
+                    currentActionState.postValue(UpdateActionState.BATTERY_LEVEL_NOT_ENOUGH)
+                }
+            } catch (e: Exception) {
+                Log.e("e", e.toString())
+            }
+        }
+    }
+
+    /**
+     * 检查有没有更新成了,通过检查SP即可
+     */
+    fun checkHasUpdated() {
+        val sharePreference = SPUtil.getSharePreference()
+        val versionRecord = sharePreference.getString(SPUtil.KEY_TARGET_VERSION_CODE, "")
+        //获取当前的系统版本号
+        val systemVersion = VersionInfoUtils.getSystemVersion()
+        Log.d(TAG, "system version code is == > $systemVersion")
+        Log.d(TAG, "versionRecord code is == > $versionRecord")
+        if (TextUtils.isEmpty(versionRecord)) {
+            if (versionRecord == systemVersion) {
+                try {
+                    viewModelScope.launch {
+                        withContext(Dispatchers.IO) {
+                            Log.d(TAG, "post install result..success.")
+                            mFirstRepository.reportInstallResult(true)
+                            mFirstRepository.reportResult(versionRecord!!)
+                            //删除文件
+                            mFirstRepository.deletePackage()
+                        }
+                    }
+                } catch (e: Exception) {
+                    e.printStackTrace()
+                }
+            } else {
+                try {
+                    viewModelScope.launch {
+                        withContext(Dispatchers.IO) {
+                            Log.d(TAG, "post install result..failed.")
+                            mFirstRepository.reportInstallResult(false)
+                            mFirstRepository.reportResult(versionRecord!!)
+                        }
+                    }
+                } catch (e: Exception) {
+                    e.printStackTrace()
+                }
+            }
+            sharePreference.edit().putString(SPUtil.KEY_TARGET_VERSION_CODE, "").apply()
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/icfotaclient/repository/CarOtaRepository.kt b/app/src/main/java/com/ic/icfotaclient/repository/CarOtaRepository.kt
new file mode 100644 (file)
index 0000000..a391552
--- /dev/null
@@ -0,0 +1,118 @@
+package com.ic.icfotaclient.repository
+
+import android.os.Bundle
+import android.util.Log
+import com.ic.icfotaclient.BuildConfig
+import com.ic.icfotaclient.base.IOtaBaseRepository
+import com.ic.icfotaclient.bean.DownloadInfo
+import com.ic.icfotaclient.bean.OtaInfo
+import com.ic.icfotaclient.presenter.OtaViewModel
+import com.ic.icfotaclient.utils.CarOtaApiUtils
+import com.ic.icfotaclient.utils.VersionInfoUtils
+import com.ota.fw.IDownloadEvent
+import com.ota.fw.SysInfo
+import java.io.BufferedInputStream
+import java.io.BufferedOutputStream
+import java.io.FileInputStream
+import java.io.FileOutputStream
+
+/**
+ * 科洛达的Repository
+ */
+class CarOtaRepository : IOtaBaseRepository {
+
+    companion object {
+        const val TAG = "CarOtaRepository"
+    }
+
+    private var mCallback: IOtaRepositoryCallback? = null
+
+    override suspend fun checkHasNewVersion(): OtaInfo? {
+        val systemInfo = SysInfo(
+            BuildConfig.token,
+            VersionInfoUtils.getIMEI(),
+            VersionInfoUtils.getSystemVersion(),
+            BuildConfig.brandName,
+            VersionInfoUtils.getOtaProductName()
+        )
+        val checkResult = CarOtaApiUtils.mClient.check(systemInfo) ?: return null
+        Log.d(TAG, "checkResult == > $checkResult")
+        //转成我们的bean类
+        return OtaInfo(
+            checkResult.mode != 0,//0表示用户执行,1/2表示强制下载/强制更新
+            checkResult.releaseNote,
+            checkResult.targetVersion,
+            checkResult.size,
+            System.currentTimeMillis()//科洛达的没有时间这项
+        )
+    }
+
+    override suspend fun downloadPackage(callback: IOtaRepositoryCallback): DownloadInfo? {
+        mCallback = callback
+        val download = CarOtaApiUtils.mClient.download(object : IDownloadEvent {
+            override fun onDownloading(downloaded: Long, total: Long, extra: Bundle?) {
+                Log.d(TAG, "onDownloading...$downloaded -- >  $total")
+                mCallback?.onDownloadProgressChange(downloaded, total)
+            }
+
+            override fun onDownloaded(extra: Bundle?) {
+                Log.d(TAG, "onDownloaded...$extra")
+                mCallback?.onDownloadSuccess()
+            }
+
+            override fun onError(errCode: Int, extra: Bundle?) {
+                Log.d(TAG, "onError...$errCode")
+                mCallback?.onDownloadError(errCode, "下载出错")
+            }
+
+            override fun onRetry(code: Int) {
+                Log.d(TAG, "onRetry...$code")
+            }
+
+        }) ?: return null
+        //转一下
+        Log.d(TAG, "download == > ${download.file}")
+        val targetFile = VersionInfoUtils.getFlashPath()
+        Log.d(OtaViewModel.TAG, "copy finished to target path == > $targetFile")
+        val fis = FileInputStream(targetFile);
+        val fos = FileOutputStream(targetFile)
+        //复制到对应的目录下进行
+        val bis = BufferedInputStream(fis)
+        val bos = BufferedOutputStream(fos)
+        val buf = ByteArray(1024)
+        var len = 0;
+        while (true) {
+            len = bis.read(buf)
+            if (len == -1) break;
+            bos.write(buf, 0, len)
+        }
+        fis.close()
+        fos.close()
+        val downloadInfo = DownloadInfo(download.file.path, 0L)
+        downloadInfo.path = targetFile
+        return downloadInfo
+    }
+
+    override suspend fun reportInstallResult(isSuccess: Boolean) {
+        CarOtaApiUtils.mClient.reportUpdateResult(isSuccess, 0)
+        Log.d(TAG, "report result == isSuccess == > $isSuccess")
+    }
+
+    override suspend fun reportResult(versionCode: String) {
+        CarOtaApiUtils.mClient.reportUpdateResultByVersion(versionCode)
+        Log.d(TAG, "report == versionCode == > $versionCode")
+    }
+
+    override suspend fun getPackagePath(): String? {
+        return null
+    }
+
+    override suspend fun deletePackage() {
+        //传说他们自己会删除的,通报了成功以后
+    }
+
+    override suspend fun pauseDownload() {
+        Log.d(TAG, "pauseDownload...")
+        CarOtaApiUtils.mClient.stopDownload()
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/icfotaclient/repository/ICOtaRepository.kt b/app/src/main/java/com/ic/icfotaclient/repository/ICOtaRepository.kt
new file mode 100644 (file)
index 0000000..ea4a896
--- /dev/null
@@ -0,0 +1,87 @@
+package com.ic.icfotaclient.repository
+
+import android.util.Log
+import android.widget.Toast
+import com.ic.icfotaclient.App
+import com.ic.icfotaclient.R
+import com.ic.icfotaclient.base.AbstractOtaRepository
+import com.ic.icfotaclient.bean.DownloadInfo
+import com.ic.icfotaclient.bean.OtaInfo
+import com.ic.icfotaclient.utils.ICOtaApiUtils
+import com.ic.icfotaclient.utils.VersionInfoUtils
+import java.io.File
+
+/**
+ * 有不服气,可以自己复写,默认是交给下一级Repository处理。
+ * 比如说英卡的OTA,目前需要去统计一下数据,去调用一下检查更新即可。不需要做其他的工作。
+ */
+class ICOtaRepository : AbstractOtaRepository() {
+
+
+    private var checkIcVersion: OtaInfo? = null
+
+    /**
+     * 检查是否有更新,英卡的服务器
+     */
+    override suspend fun checkHasNewVersion(): OtaInfo? {
+        try {
+            //调用一下英卡的接口
+            checkIcVersion = ICOtaApiUtils.checkIcVersion()
+            if (checkIcVersion != null) {
+                return checkIcVersion
+            }
+        } catch (e: Exception) {
+            Log.e("e==>", e.toString())
+        }
+        //如果没有则走下一级服务器
+        return super.checkHasNewVersion()
+    }
+
+
+    override suspend fun pauseDownload() {
+        if (checkIcVersion != null) {
+            Toast.makeText(
+                App.sContext,
+                App.sContext.getString(R.string.unsupport_pause_download),
+                Toast.LENGTH_LONG
+            ).show()
+        }
+        super.pauseDownload()
+    }
+
+
+    override suspend fun downloadPackage(callback: IOtaRepositoryCallback): DownloadInfo? {
+        //下载安装包,直接当前线程调用即可
+        //如果这个有安装包就不需要往下去下载了。
+        if (checkIcVersion != null) {
+            //说明是走英卡的服务器更新的,从英卡服务器下载更新包
+            return ICOtaApiUtils.downloadPackage(callback)
+        }
+        return super.downloadPackage(callback)
+    }
+
+    override suspend fun reportResult(versionCode: String) {
+        if (checkIcVersion != null) {
+            return
+        }
+        super.reportResult(versionCode)
+    }
+
+
+    override suspend fun reportInstallResult(isSuccess: Boolean) {
+        if (checkIcVersion != null) {
+            return
+        }
+        super.reportInstallResult(isSuccess)
+    }
+
+    override suspend fun deletePackage() {
+        //删除flash_root下的
+        val file = File(VersionInfoUtils.getFlashPath())
+        if (file.exists()) {
+            file.delete()
+        }
+        super.deletePackage()
+    }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/icfotaclient/repository/IOtaRepositoryCallback.kt b/app/src/main/java/com/ic/icfotaclient/repository/IOtaRepositoryCallback.kt
new file mode 100644 (file)
index 0000000..0eab794
--- /dev/null
@@ -0,0 +1,29 @@
+package com.ic.icfotaclient.repository
+
+interface IOtaRepositoryCallback {
+
+    /**
+     * 下载进度
+     */
+    fun onDownloadProgressChange(currentProgress: Long, totalProgress: Long)
+
+    /**
+     * 0-停止下载 -1-连接错误 -2-下载中断 -3-下载文件校验失败 -4-其他错误 -5-文件校验失败
+     */
+    fun onDownloadError(errorCode: Int, msg: String)
+
+    /**
+     * 下载成功
+     */
+    fun onDownloadSuccess()
+
+    /**
+     * 删除升级包成功
+     */
+    fun onDeletePackageSuccess()
+
+    /**
+     * 删除升级包失败
+     */
+    fun onDeletePackageFailed(errorCode: Int, msg: String)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/icfotaclient/utils/CarOtaApiUtils.kt b/app/src/main/java/com/ic/icfotaclient/utils/CarOtaApiUtils.kt
new file mode 100644 (file)
index 0000000..21ae589
--- /dev/null
@@ -0,0 +1,19 @@
+package com.ic.icfotaclient.utils
+
+import android.util.Log
+import com.ic.icfotaclient.App
+import com.ota.fw.FotaClient
+import com.ota.fw.IHookInstall
+
+object CarOtaApiUtils {
+
+    val mClient by lazy {
+        FotaClient.buildBasic(App.sContext, Constants.OTA_URL, null, IHookInstall { context, file ->
+            Log.d(
+                "CarOtaApiUtils", "install success == > " + file.path
+            )
+            1//这个值是自定义值,在install的时候会返回
+        })
+    }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/icfotaclient/utils/Constants.kt b/app/src/main/java/com/ic/icfotaclient/utils/Constants.kt
new file mode 100644 (file)
index 0000000..2230469
--- /dev/null
@@ -0,0 +1,12 @@
+package com.ic.icfotaclient.utils
+
+object Constants {
+    const val CMD_ACTION_BOOT_COMPLETED = "cmd_action_boot_completed"
+    const val CMD_ACTION_ACTIVITY_START = "cmd_action_activity_start"
+    const val OTA_URL = "https://api.ota.limios.net/conn/api/v1/"
+
+    //收到开机广播后,10秒钟以后,再去检查系统更新
+    const val TIME_DELAY_BOOT = 10 * 1000L
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/icfotaclient/utils/ICOtaApiUtils.kt b/app/src/main/java/com/ic/icfotaclient/utils/ICOtaApiUtils.kt
new file mode 100644 (file)
index 0000000..551cb1c
--- /dev/null
@@ -0,0 +1,187 @@
+package com.ic.icfotaclient.utils
+
+import android.util.Log
+import com.google.gson.Gson
+import com.ic.icfotaclient.App
+import com.ic.icfotaclient.R
+import com.ic.icfotaclient.bean.DownloadInfo
+import com.ic.icfotaclient.bean.ICOtaInfo
+import com.ic.icfotaclient.bean.OtaInfo
+import com.ic.icfotaclient.repository.IOtaRepositoryCallback
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import java.io.BufferedInputStream
+import java.io.BufferedOutputStream
+import java.io.FileOutputStream
+import java.net.HttpURLConnection
+import java.net.URI
+import java.security.SecureRandom
+import java.security.cert.X509Certificate
+import java.util.concurrent.TimeUnit
+import javax.net.ssl.SSLContext
+import javax.net.ssl.TrustManager
+import javax.net.ssl.X509TrustManager
+
+object ICOtaApiUtils {
+
+    val TAG = "ICOtaApiUtils"
+
+    fun checkIcVersion(): OtaInfo? {
+        val uri = URI(getRemoteUri())
+        val url = uri.toString()
+        return getUpdateInfo(url)
+    }
+
+    private fun initOkHttp() {
+        //首先创建OkHttpClient.build进行信任所有证书配置
+        //首先创建OkHttpClient.build进行信任所有证书配置
+        val okhttpClient = OkHttpClient().newBuilder()
+        //信任所有服务器地址
+        //信任所有服务器地址
+        okhttpClient.hostnameVerifier { s, sslSession -> //设置为true
+            true
+        }
+        //创建管理器
+        //创建管理器
+        val trustAllCerts =
+            arrayOf<TrustManager>(object : X509TrustManager {
+                override fun checkClientTrusted(
+                    x509Certificates: Array<X509Certificate>,
+                    s: String
+                ) {
+                }
+
+                override fun checkServerTrusted(
+                    x509Certificates: Array<X509Certificate>,
+                    s: String
+                ) {
+                }
+
+                override fun getAcceptedIssuers(): Array<X509Certificate> {
+                    return arrayOf()
+                }
+            })
+        try {
+            val sslContext = SSLContext.getInstance("TLS")
+            sslContext.init(null, trustAllCerts, SecureRandom())
+
+            //为OkHttpClient设置sslSocketFactory
+            okhttpClient.sslSocketFactory(sslContext.socketFactory)
+        } catch (e: Exception) {
+            e.printStackTrace()
+        }
+        okHttpClient = okhttpClient
+            .connectTimeout(100000, TimeUnit.SECONDS)
+            .writeTimeout(100000, TimeUnit.SECONDS)
+            .readTimeout(100000, TimeUnit.SECONDS)
+            .build()
+    }
+
+    private var okHttpClient: OkHttpClient? = null
+
+    private var otaInfo: ICOtaInfo? = null
+
+    private fun getUpdateInfo(
+        url: String?
+    ): OtaInfo? {
+        if (okHttpClient == null) {
+            initOkHttp()
+        }
+        val request: Request = Request.Builder().url(url).build()
+
+        val execute = okHttpClient?.newCall(request)?.execute()
+        //解析数据
+        if (execute != null && execute.code() == HttpURLConnection.HTTP_OK) {
+            val body = execute.body() ?: return null
+            val json = body.string()
+            Log.d(TAG, "result == > $json")
+            val gson = Gson()
+            otaInfo = gson.fromJson<ICOtaInfo>(json, ICOtaInfo::class.java)
+            if (otaInfo != null) {
+                return OtaInfo(
+                    otaInfo!!.AutoUpdate,
+                    otaInfo!!.description,
+                    otaInfo!!.OtaPackageVersion,
+                    otaInfo!!.OtaPackageLength.toLong(),
+                    System.currentTimeMillis()
+                )
+            }
+        }
+        return null
+    }
+
+    private fun getRemoteUri(): String? {
+        var server: String = getRemoteHost()
+        val productname: String? = VersionInfoUtils.getOtaProductName()
+        val pkgversion: String? = VersionInfoUtils.getSystemVersion()
+        val imei: String? = VersionInfoUtils.getIMEI()
+        if (server.startsWith("http://")) {
+            server = server.replace("http://", "")
+        }
+        //为了兼容之前的只设置ip和端口的设置方式
+        var actionUrl =
+            "http://$server/ota.jsp?product=$productname&pkgver=$pkgversion&imei=$imei"
+        if (server.contains("/")) { //此时说明不是ip地址,而是个url
+            actionUrl =
+                "http://$server?product=$productname&pkgver=$pkgversion&imei=$imei"
+        }
+        Log.d(TAG, "actionUrl == > $actionUrl")
+        return actionUrl
+    }
+
+
+    private fun getRemoteHost(): String {
+        var remoteHost: String? =
+            SysPropUtils.get("persist.sys.ota.host", null)
+        if (remoteHost == null || remoteHost.isEmpty() || remoteHost == "unknown") {
+            remoteHost = "icfota.snicker.com.cn:8089"
+        }
+        Log.d(TAG, "remoteHost == > $remoteHost")
+        return remoteHost
+    }
+
+    /**
+     * 下载更新包
+     */
+    fun downloadPackage(
+        callback: IOtaRepositoryCallback?
+    ): DownloadInfo? {
+        if (okHttpClient == null) {
+            initOkHttp()
+        }
+        if (otaInfo == null) {
+            return null
+        }
+        val request: Request = Request.Builder().url(
+            otaInfo!!.url
+        ).build()
+        try {
+            val total = otaInfo!!.OtaPackageLength
+            val response = okHttpClient?.newCall(request)?.execute()
+            if (response?.code() == HttpURLConnection.HTTP_OK) {
+                val inputStream = response.body()?.byteStream()
+                val outPutStream = FileOutputStream(VersionInfoUtils.getFlashPath())
+                val bis = BufferedInputStream(inputStream)
+                val bos = BufferedOutputStream(outPutStream)
+                val buf = ByteArray(1024)
+                var len = 0;
+                var current = 0;
+                while (true) {
+                    len = bis.read(buf)
+                    if (len == -1) break;
+                    current += len;
+                    bos.write(buf, 0, len)
+                    callback?.onDownloadProgressChange(current.toLong(), total.toLong())
+                }
+                inputStream?.close()
+                outPutStream.close()
+            }
+            callback?.onDownloadSuccess()
+            return DownloadInfo(VersionInfoUtils.getFlashPath(), total.toLong())
+        } catch (e: Exception) {
+            e.printStackTrace()
+            callback?.onDownloadError(0, App.sContext.getString(R.string.error_tips_text))
+        }
+        return null
+    }
+}
diff --git a/app/src/main/java/com/ic/icfotaclient/utils/NetworkUtils.kt b/app/src/main/java/com/ic/icfotaclient/utils/NetworkUtils.kt
new file mode 100644 (file)
index 0000000..cad9b16
--- /dev/null
@@ -0,0 +1,76 @@
+package com.ic.icfotaclient.utils
+
+import android.Manifest.permission.ACCESS_NETWORK_STATE
+import android.content.Context
+import android.net.ConnectivityManager
+import android.net.NetworkCapabilities
+import android.os.Build
+import androidx.annotation.RequiresPermission
+
+
+/**
+ * 网络状态工具类,用于获取网络状态
+ */
+object NetworkUtils {
+
+    /**
+     * 网络是否已连接
+     *
+     * @return true:已连接 false:未连接
+     */
+    fun isNetConnected(context: Context): Boolean {
+        val manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            val networkCapabilities = manager.getNetworkCapabilities(manager.activeNetwork)
+            if (networkCapabilities != null) {
+                return (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
+                        || networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
+                        || networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET))
+            }
+        } else {
+            val networkInfo = manager.activeNetworkInfo
+            return networkInfo != null && networkInfo.isConnected
+        }
+        return false
+    }
+
+    /**
+     * Wifi是否已连接
+     *
+     * @return true:已连接 false:未连接
+     */
+    fun isWifiConnected(context: Context): Boolean {
+        val manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+        if (manager != null) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                val networkCapabilities = manager.getNetworkCapabilities(manager.activeNetwork)
+                if (networkCapabilities != null) {
+                    return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
+                }
+            } else {
+                val networkInfo = manager.activeNetworkInfo
+                return networkInfo != null && networkInfo.isConnected && networkInfo.type == ConnectivityManager.TYPE_WIFI
+            }
+        }
+        return false
+    }
+
+    /**
+     * 是否为流量
+     */
+    fun isMobileData(context: Context): Boolean {
+        val manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+        if (manager != null) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                val networkCapabilities = manager.getNetworkCapabilities(manager.activeNetwork)
+                if (networkCapabilities != null) {
+                    return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
+                }
+            } else {
+                val networkInfo = manager.activeNetworkInfo
+                return networkInfo != null && networkInfo.isConnected && networkInfo.type == ConnectivityManager.TYPE_MOBILE
+            }
+        }
+        return false
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/icfotaclient/utils/OtaViewModelCreator.kt b/app/src/main/java/com/ic/icfotaclient/utils/OtaViewModelCreator.kt
new file mode 100644 (file)
index 0000000..6b544b6
--- /dev/null
@@ -0,0 +1,62 @@
+package com.ic.icfotaclient.utils
+
+import android.util.Log
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.ViewModelStoreOwner
+import com.ic.icfotaclient.presenter.OtaViewModel
+
+/**
+ * 获取OtaViewModel,生命周期与OtaService挂钩
+ * 可以理解为一个单例
+ */
+object OtaViewModelCreator {
+
+    const val TAG = "OtaViewModelCreator"
+
+    lateinit var mVmStore: ViewModelStoreOwner
+
+    private var viewModel: OtaViewModel? = null
+
+
+    /**
+     * 首先要把owner设置进来
+     */
+    fun initOtaViewModelStoreOwner(vmStore: ViewModelStoreOwner) {
+        mVmStore = vmStore
+        viewModel = ViewModelProvider(mVmStore).get(OtaViewModel::class.java)
+        for (onViewModelInitListener in callbackList) {
+            onViewModelInitListener.onViewModelInit(viewModel)
+        }
+        callbackList.clear()
+    }
+
+    private val callbackList = arrayListOf<OnViewModelInitListener>()
+
+    /**
+     * 获取OtaViewModel,有可能为null,如果OtaService退出的话,会清掉的
+     * callback可以为null,如果为null,则不通知结果,但是会返回。
+     * 建议界面调用的时候,通过接口获取viewModel
+     */
+    fun getOtaViewModel(callback: OnViewModelInitListener?): OtaViewModel? {
+        //可以传null
+        if (!this::mVmStore.isInitialized) {
+            Log.d(
+                TAG,
+                "you must call initOtaViewModelStoreOwner() first to setup ViewModelStoreOwner"
+            )
+            //没有初始化,如果callback不为null,添加到集合里
+            if (callback != null) {
+                callbackList.add(callback)
+            }
+        } else {
+            //如果有接口设置过来,那就接口也返回一下
+            callback?.onViewModelInit(viewModel)
+        }
+        return viewModel
+    }
+
+    interface OnViewModelInitListener {
+        fun onViewModelInit(otaViewModel: OtaViewModel?);
+    }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/icfotaclient/utils/SPUtil.kt b/app/src/main/java/com/ic/icfotaclient/utils/SPUtil.kt
new file mode 100644 (file)
index 0000000..a35e391
--- /dev/null
@@ -0,0 +1,17 @@
+package com.ic.icfotaclient.utils
+
+import android.content.Context
+import android.content.SharedPreferences
+import com.ic.icfotaclient.App
+
+object SPUtil {
+
+
+    const val SP_NAME = "update_record"
+    const val KEY_TARGET_VERSION_CODE = "key_target_version_code"
+
+    fun getSharePreference(): SharedPreferences {
+        return App.sContext.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
+    }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/icfotaclient/utils/SysPropUtils.kt b/app/src/main/java/com/ic/icfotaclient/utils/SysPropUtils.kt
new file mode 100644 (file)
index 0000000..92e2d41
--- /dev/null
@@ -0,0 +1,54 @@
+package com.ic.icfotaclient.utils
+
+import java.lang.reflect.InvocationTargetException
+import java.lang.reflect.Method
+
+/**
+ * 系统属性获取的工具
+ */
+object SysPropUtils {
+
+    private var sysPropGet: Method? = null
+    private var sysPropSet: Method? = null
+
+    init {
+        try {
+            val forName = Class.forName("android.os.SystemProperties")
+            val methods = forName.methods
+            for (method in methods) {
+                if ("get" == method.name) {
+                    sysPropGet = method
+                } else {
+                    sysPropSet = method
+                }
+            }
+        } catch (e: ClassNotFoundException) {
+            e.printStackTrace()
+        }
+    }
+
+    operator fun get(name: String?, default_value: String?): String? {
+        try {
+            return sysPropGet!!.invoke(null, name, default_value) as String
+        } catch (e: IllegalArgumentException) {
+            e.printStackTrace()
+        } catch (e: IllegalAccessException) {
+            e.printStackTrace()
+        } catch (e: InvocationTargetException) {
+            e.printStackTrace()
+        }
+        return default_value
+    }
+
+    operator fun set(name: String?, value: String?) {
+        try {
+            sysPropSet!!.invoke(null, name, value)
+        } catch (e: IllegalArgumentException) {
+            e.printStackTrace()
+        } catch (e: IllegalAccessException) {
+            e.printStackTrace()
+        } catch (e: InvocationTargetException) {
+            e.printStackTrace()
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/icfotaclient/utils/UpdateActionState.kt b/app/src/main/java/com/ic/icfotaclient/utils/UpdateActionState.kt
new file mode 100644 (file)
index 0000000..cafcb33
--- /dev/null
@@ -0,0 +1,14 @@
+package com.ic.icfotaclient.utils
+
+enum class UpdateActionState {
+    NONE,/*初始状态,显示:检查更新,isEnable = true*/
+    CHECKING,/*正在检查更新,显示:正在检查更新 isEnable = false*/
+    NEW_VERSION,/*显示:下载新版本 isEnable = true*/
+    LAST_VERSION,/*显示:已是最新版本,isEnable =  false*/
+    DOWNLOADING,/*显示暂停下载 isEnable = true*/
+    DOWNLOADED,/*显示:安装,isEnable = true*/
+    DOWNLOAD_PAUSE,/*显示继续下载,isEnable=true*/
+    NO_NETWORK,/*显示无网络,isEnable=false*/
+    ERROR,/*恢复默认,显示检查更新 isEnable=false*/
+    BATTERY_LEVEL_NOT_ENOUGH,/*电池电量不足50%,isEnable=false*/
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/icfotaclient/utils/VersionCheckFrom.kt b/app/src/main/java/com/ic/icfotaclient/utils/VersionCheckFrom.kt
new file mode 100644 (file)
index 0000000..78544c4
--- /dev/null
@@ -0,0 +1,12 @@
+package com.ic.icfotaclient.utils
+
+enum class VersionCheckFrom {
+    /*远程调用检查*/
+    REMOTE_CHECK,
+
+    /*UI检查更新时的check*/
+    UI_CHECK,
+
+    /*开机检查的check*/
+    BOOT_CHECK
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/icfotaclient/utils/VersionInfoUtils.kt b/app/src/main/java/com/ic/icfotaclient/utils/VersionInfoUtils.kt
new file mode 100644 (file)
index 0000000..e7b21b4
--- /dev/null
@@ -0,0 +1,98 @@
+package com.ic.icfotaclient.utils
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.Build
+import android.telephony.TelephonyManager
+import android.text.TextUtils
+import android.util.Log
+import com.ic.icfotaclient.App
+import java.io.File
+
+/**
+ * 工具类
+ * 获取IMEI号
+ * 获取版本号
+ * 获取版本名称
+ */
+object VersionInfoUtils {
+
+    const val TAG = "VersionInfoUtils"
+
+    private const val KeyIMEI = "persist.sys.protocol_IMEI"
+
+    /**
+     * 获取IMEI号
+     */
+    //获取手机IMEI号
+    @SuppressLint("MissingPermission", "HardwareIds")
+    fun getIMEI(): String? {
+        //优先从Pro中获取,有可能通过短信修改了IMEI号
+        var imei: String? = SysPropUtils[KeyIMEI, ""]
+        if (TextUtils.isEmpty(imei)) {
+            imei = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+                (App.sContext.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager).imei
+            } else {
+                (App.sContext.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager).deviceId
+            }
+        }
+        Log.d("imei", "imei = >  $imei")
+        return imei
+    }
+
+
+    fun getOtaProductName(): String? {
+        var productName: String =
+            //C41S-D-EU-P2
+            SysPropUtils.get("ro.product.model", "") as String
+        if (productName.contains(" ")) {
+            productName = productName.replace(" ".toRegex(), "")
+        }
+        Log.d(TAG, "product name -=  > $productName")
+        return productName
+    }
+
+    fun getSystemVersion(): String? {
+        var version: String =
+            //C41S-D-EU-P2-V0.3.37.20210112.184146
+            SysPropUtils.get("ro.build.display.id", "") as String
+        if (version.isEmpty()) {
+            version = "1.0.0"
+        }
+        Log.d(TAG, "system version is == > $version")
+        return version
+    }
+
+
+    private fun getMultiUserState(): Boolean {
+        var multiUser: String? =
+            SysPropUtils.get("ro.factory.hasUMS", "")
+        if (multiUser != null && multiUser.isNotEmpty()) {
+            return multiUser != "true"
+        }
+        multiUser = SysPropUtils.get("ro.factory.storage_policy", "")
+        if (multiUser != null && multiUser.isNotEmpty()) {
+            return multiUser == "1"
+        }
+        val property = SysPropUtils.get("ro.boot.hardware", "")
+        return property != null && property.contains("sl8541e")
+    }
+
+
+    var flash_root =
+        "/data" //Environment.getExternalStorageDirectory().getAbsolutePath();
+
+    var DATA_ROOT = "/data/media/0"
+
+    private const val updateFileName = "update.zip"
+
+
+    fun getFlashPath(): String {
+        //移动到对应的位置上
+        if (getMultiUserState()) {
+            flash_root = DATA_ROOT
+        }
+
+        return flash_root + File.separator + updateFileName
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/icfotaclient/view/ApiTestActivity.kt b/app/src/main/java/com/ic/icfotaclient/view/ApiTestActivity.kt
new file mode 100644 (file)
index 0000000..a297703
--- /dev/null
@@ -0,0 +1,109 @@
+package com.ic.icfotaclient.view
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import com.ic.icfotaclient.BuildConfig
+import com.ic.icfotaclient.R
+
+import com.ic.icfotaclient.utils.VersionInfoUtils
+import com.ota.fw.FotaClient
+import com.ota.fw.IDownloadEvent
+import com.ota.fw.IUpdateInfo
+import com.ota.fw.SysInfo
+
+class ApiTestActivity : AppCompatActivity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_api_test)
+        //测试api
+        val client =
+            FotaClient.buildBasic(this, "http://api.ota.limios.net/conn/api/v1/", {
+                //网络是否可用?
+                true
+            }, { context, file ->
+                //context-上下文
+                //path-安装包路径
+                //调用 FotaClient. install 后,经过包二次校验,状态回报等操作后,就会调用到此接口来进行升级操
+                //作。返回值可以自定义,最终通过 FotaClient. install 返回
+                println("context == > $context")
+                println("file == > $file")
+                0//这个值会返回到install里
+            })
+
+        //SysInfo(String projectToken, String serialNum, String srcVer, String brand, String model, Bundle extra)
+        //serialNum-设备唯一识别码(IMEI 或 sn 号或其他参数)
+        //srcVer-设备当前版本号
+        //brand-设备 brand
+        //model-设备 model
+        //extra-设备额外信息,不设置可传 null
+        //projectToken-项目 token,后端生成,由科络达分配给客户
+        //检查更新
+        val sysInfo = SysInfo(
+            BuildConfig.token, VersionInfoUtils.getIMEI(),
+            VersionInfoUtils.getSystemVersion(), "JX_Dev", "JX_Default", null
+        )
+        //如果这里面不为null
+        /**
+         * public interface IUpdateInfo {
+         *       String getReleaseNote(); //获取更新包升级信息
+         *       String getTargetVersion(); //获取目标版本号
+         *       long getSize(); //获取升级包大小
+         * }
+         */
+        val check: IUpdateInfo = client.check(sysInfo)
+
+
+        //下载
+        client.download(object : IDownloadEvent {
+            override fun onDownloading(downloaded: Long, total: Long, extra: Bundle?) {
+                //下载中
+            }
+
+            override fun onDownloaded(extra: Bundle?) {
+                //下载完成
+            }
+
+            /**
+             *  0-停止下载
+             *  1-连接错误
+             *  2-下载中断
+             *  3-下载文件校验失败
+             *  4-其他错误
+             *  5-文件校验失败
+             */
+            override fun onError(errCode: Int, extra: Bundle?) {
+                //下载出错
+            }
+
+            override fun onRetry(p0: Int) {
+
+            }
+
+        })
+
+        //停止下载
+        client.stopDownload()
+
+        //安装
+        //返回值-根据升级方法的实现,该方法可能无法返回。默认返回值如下:
+        //-101-设备调用重启时发生异常
+        //-1001-一般错误
+        //-1004-升级包 MD5 校验失败
+        //其他值-IHookInstall.installPackage 返回值
+        //启动升级包安装流程,同步接口,需要在非主线程执行
+        val install = client.install()
+
+        //通知服务器安装结果
+        //TODO:
+
+        //success-升级是否成功
+        //code-升级失败时候的错误码,默认传 0
+        client.reportUpdateResult(true, 0)
+
+        //curVer-当前版本号
+        client.reportUpdateResultByVersion(VersionInfoUtils.getSystemVersion())
+
+
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/icfotaclient/view/SystemUpdateActivity.kt b/app/src/main/java/com/ic/icfotaclient/view/SystemUpdateActivity.kt
new file mode 100644 (file)
index 0000000..4c8ea59
--- /dev/null
@@ -0,0 +1,170 @@
+package com.ic.icfotaclient.view
+
+import android.content.Intent
+import android.os.Bundle
+import android.text.TextUtils
+import android.util.Log
+import android.view.View
+import androidx.appcompat.app.AppCompatActivity
+import androidx.lifecycle.Observer
+import com.ic.icfotaclient.R
+import com.ic.icfotaclient.presenter.OtaService
+import com.ic.icfotaclient.presenter.OtaViewModel
+import com.ic.icfotaclient.utils.Constants
+import com.ic.icfotaclient.utils.OtaViewModelCreator
+import com.ic.icfotaclient.utils.UpdateActionState
+import com.ic.icfotaclient.utils.VersionCheckFrom
+import kotlinx.android.synthetic.main.activity_main.*
+
+class SystemUpdateActivity : AppCompatActivity() {
+
+    override fun onBackPressed() {
+        //不让退出了哇
+        val checkNetWork = OtaViewModelCreator.getOtaViewModel(null)?.checkNetWork()
+        val currentActionState = OtaViewModelCreator.getOtaViewModel(null)?.currentActionState
+        val tipsWifiConnect = OtaViewModelCreator.getOtaViewModel(null)?.isTipsWifiConnect
+        if (checkNetWork != true ||
+            currentActionState?.value == UpdateActionState.LAST_VERSION
+            || tipsWifiConnect?.value == false
+        ) {
+            super.onBackPressed()
+        }
+    }
+
+    override fun onResume() {
+        super.onResume()
+        //检查一下网络更新
+        val otaViewModel = OtaViewModelCreator.getOtaViewModel(null)
+        otaViewModel?.checkNetWork()
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_main)
+        val intent = Intent(this, OtaService::class.java)
+        intent.action = Constants.CMD_ACTION_ACTIVITY_START
+        startService(intent)
+        checkUpdate.setOnClickListener {
+            val otaViewModel = OtaViewModelCreator.getOtaViewModel(null)
+            if (otaViewModel != null) {
+                //判断状态,执行不同的动作
+                when (otaViewModel.currentActionState.value) {
+                    UpdateActionState.NONE -> {
+                        //显示检查更新,isEnable=true
+                        //检查更新
+                        otaViewModel.checkUpdate(VersionCheckFrom.UI_CHECK)
+                    }
+                    UpdateActionState.NEW_VERSION -> {
+                        //去下载
+                        otaViewModel.downloadPackage()
+                    }
+                    UpdateActionState.DOWNLOADED -> {
+                        //去安装
+                        otaViewModel.install()
+                    }
+                    UpdateActionState.DOWNLOADING -> {
+                        //暂停下载
+                        otaViewModel.pauseDownload()
+                    }
+                    UpdateActionState.DOWNLOAD_PAUSE -> {
+                        //去下载
+                        otaViewModel.downloadPackage()
+                    }
+                    else -> {
+                        //NONE
+                    }
+                }
+            }
+        }
+
+        OtaViewModelCreator.getOtaViewModel(
+            object : OtaViewModelCreator.OnViewModelInitListener {
+                override fun onViewModelInit(otaViewModel: OtaViewModel?) {
+                    initObserver(otaViewModel)
+                }
+            })
+    }
+
+    private fun initObserver(otaViewModel: OtaViewModel?) {
+        otaViewModel?.apply {
+            val that = this@SystemUpdateActivity
+            otaInfo.observe(that, Observer {
+                if (it != null) {
+                    updateText.text = getString(R.string.update_tips_text) + it.updateDesc
+
+                }
+            })
+            isTipsWifiConnect.observe(that, Observer {
+                if (it) {
+                    checkUpdate.text = getString(R.string.tips_wifi_connect)
+                    checkUpdate.isEnabled = false
+                }
+            })
+            currentActionState.observe(that, Observer { it ->
+                Log.d("SystemUpdateActivity", "current state == > $it")
+                when (it) {
+                    UpdateActionState.NONE -> {
+                        //显示检查更新,isEnable=true
+                        checkUpdate.text = getString(R.string.check_has_update_text)
+                        checkUpdate.isEnabled = true
+                    }
+                    UpdateActionState.CHECKING -> {
+                        checkUpdate.text = getString(R.string.try_to_checking)
+                        checkUpdate.isEnabled = false
+                    }
+                    UpdateActionState.NEW_VERSION -> {
+                        checkUpdate.text = getString(R.string.download_package)
+                        checkUpdate.isEnabled = true
+                    }
+                    UpdateActionState.LAST_VERSION -> {
+                        checkUpdate.text = getString(R.string.last_version_text)
+                        checkUpdate.isEnabled = false
+                    }
+                    UpdateActionState.DOWNLOADING -> {
+                        checkUpdate.text = getString(R.string.pause_download_text)
+                        checkUpdate.isEnabled = true
+                    }
+                    UpdateActionState.DOWNLOAD_PAUSE -> {
+                        checkUpdate.text = getString(R.string.continue_download_text)
+                        checkUpdate.isEnabled = true
+                    }
+                    UpdateActionState.DOWNLOADED -> {
+                        checkUpdate.text = getString(R.string.update_text)
+                        checkUpdate.isEnabled = true
+                    }
+                    UpdateActionState.NO_NETWORK -> {
+                        checkUpdate.text = getString(R.string.no_network_text)
+                        checkUpdate.isEnabled = false
+                    }
+                    UpdateActionState.BATTERY_LEVEL_NOT_ENOUGH -> {
+                        checkUpdate.text = getString(R.string.battery_level_tips_text)
+                        checkUpdate.isEnabled = false
+                    }
+                    UpdateActionState.ERROR -> {
+                        checkUpdate.text = getString(R.string.error_tips_text)
+                        checkUpdate.isEnabled = false
+                    }
+                    else -> {
+                        //NONE
+                    }
+                }
+            })
+            downloadProgress.observe(that, Observer {
+                downloadProgressBar.visibility = View.VISIBLE
+                //更新进度条,此为百分比,转成int,max=100
+                downloadProgressBar.progress = it
+            })
+        }
+        val currentVersionCode = otaViewModel?.getCurrentVersionCode()
+        if (!TextUtils.isEmpty(currentVersionCode)) {
+            versionTitle.visibility = View.VISIBLE
+            versionTitle.text = getString(R.string.version_tips) + currentVersionCode
+        }
+        val imeiCode = otaViewModel?.getImeiCode()
+        if (!TextUtils.isEmpty(imeiCode)) {
+            imeiCodeTv.visibility = View.VISIBLE
+            imeiCodeTv.text = getString(R.string.imei_text) + imeiCode
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644 (file)
index 0000000..2b068d1
--- /dev/null
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
+        <aapt:attr name="android:fillColor">
+            <gradient
+                android:endX="85.84757"
+                android:endY="92.4963"
+                android:startX="42.9492"
+                android:startY="49.59793"
+                android:type="linear">
+                <item
+                    android:color="#44000000"
+                    android:offset="0.0" />
+                <item
+                    android:color="#00000000"
+                    android:offset="1.0" />
+            </gradient>
+        </aapt:attr>
+    </path>
+    <path
+        android:fillColor="#FFFFFF"
+        android:fillType="nonZero"
+        android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
+        android:strokeWidth="1"
+        android:strokeColor="#00000000" />
+</vector>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644 (file)
index 0000000..07d5da9
--- /dev/null
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <path
+        android:fillColor="#3DDC84"
+        android:pathData="M0,0h108v108h-108z" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M9,0L9,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,0L19,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,0L29,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,0L39,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,0L49,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,0L59,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,0L69,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,0L79,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M89,0L89,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M99,0L99,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,9L108,9"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,19L108,19"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,29L108,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,39L108,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,49L108,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,59L108,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,69L108,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,79L108,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,89L108,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,99L108,99"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,29L89,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,39L89,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,49L89,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,59L89,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,69L89,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,79L89,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,19L29,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,19L39,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,19L49,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,19L59,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,19L69,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,19L79,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+</vector>
diff --git a/app/src/main/res/drawable/selector_btn_bg.xml b/app/src/main/res/drawable/selector_btn_bg.xml
new file mode 100644 (file)
index 0000000..b7e9829
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/shape_btn_disable" android:state_enabled="false" />
+    <item android:drawable="@drawable/shape_btn_press" android:state_pressed="true" />
+    <item android:drawable="@drawable/shape_btn_normal" android:state_enabled="true" />
+    <item android:drawable="@drawable/shape_btn_normal"  />
+
+</selector>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_btn_disable.xml b/app/src/main/res/drawable/shape_btn_disable.xml
new file mode 100644 (file)
index 0000000..54db3c6
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+
+    <corners android:radius="5dp" />
+    <solid android:color="#6a6a6a" />
+
+    <size
+        android:width="200dp"
+        android:height="40dp" />
+
+</shape>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_btn_normal.xml b/app/src/main/res/drawable/shape_btn_normal.xml
new file mode 100644 (file)
index 0000000..a6853b5
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+
+    <corners android:radius="5dp" />
+    <solid android:color="#00aaff" />
+
+    <size
+        android:width="200dp"
+        android:height="40dp" />
+
+</shape>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_btn_press.xml b/app/src/main/res/drawable/shape_btn_press.xml
new file mode 100644 (file)
index 0000000..b4e7579
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+
+    <corners android:radius="5dp" />
+    <solid android:color="#0044ff" />
+
+    <size
+        android:width="200dp"
+        android:height="40dp" />
+
+</shape>
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_api_test.xml b/app/src/main/res/layout/activity_api_test.xml
new file mode 100644 (file)
index 0000000..61a4490
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644 (file)
index 0000000..4095cf0
--- /dev/null
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center_horizontal"
+    android:orientation="vertical"
+    android:padding="5dp"
+    tools:context=".view.SystemUpdateActivity">
+
+    <ScrollView
+        android:id="@+id/text_container"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1">
+
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_marginTop="24dp"
+            android:orientation="vertical"
+            app:layout_constraintTop_toTopOf="parent"
+            tools:layout_editor_absoluteX="5dp">
+
+            <!--IMEI code-->
+            <TextView
+                android:id="@+id/imeiCodeTv"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="left"
+                android:textColor="@color/black"
+                android:textSize="18sp"
+                app:layout_constraintTop_toTopOf="parent"
+                tools:layout_editor_absoluteX="5dp" />
+
+            <!--current version code.-->
+            <TextView
+                android:id="@+id/versionTitle"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="20dp"
+                android:gravity="left"
+                android:textColor="@color/black"
+                android:textSize="18sp" />
+
+            <TextView
+                android:id="@+id/updateText"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:textColor="@color/black"
+                android:textSize="18sp" />
+        </LinearLayout>
+
+    </ScrollView>
+    <!--下载进度条-->
+    <ProgressBar
+        android:id="@+id/downloadProgressBar"
+        style="?android:attr/progressBarStyleHorizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/text_container"
+        android:layout_gravity="center_vertical"
+        android:layout_marginTop="20dp"
+        android:layout_marginBottom="20dp"
+        android:max="100"
+        android:visibility="gone" />
+
+    <!--检查是否有更新-->
+    <TextView
+        android:id="@+id/checkUpdate"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/downloadProgressBar"
+        android:layout_marginBottom="20dp"
+        android:background="@drawable/selector_btn_bg"
+        android:gravity="center"
+        android:padding="10dp"
+        android:text="@string/check_has_update_text"
+        android:textColor="@color/white"
+        android:textSize="24sp" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_tips.xml b/app/src/main/res/layout/dialog_tips.xml
new file mode 100644 (file)
index 0000000..0039973
--- /dev/null
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/grey"
+    android:gravity="center_horizontal"
+    android:orientation="vertical"
+    android:padding="4dp">
+
+
+    <TextView
+        android:id="@+id/notify"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="5dp"
+        android:gravity="center_vertical|center_horizontal"
+        android:text="有新版本更新啦!"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textColor="@color/white"
+        android:textIsSelectable="true" />
+
+
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="5dp"
+        android:paddingBottom="5dp">
+
+        <TextView
+            android:id="@+id/description"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="left"
+            android:padding="4dp"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textColor="@color/white"
+            android:textIsSelectable="true" />
+    </ScrollView>
+
+    <View
+        android:layout_width="200dp"
+        android:layout_height="1px"
+        android:background="@color/white" />
+
+    <!-- Alert dialog style buttons along the bottom. -->
+    <LinearLayout
+        style="?android:attr/buttonBarStyle"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:measureWithLargestChild="true">
+
+        <Button
+            android:id="@+id/button_cancel"
+            style="?android:attr/buttonBarButtonStyle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="@string/IFIA_btn_no"
+            android:textColor="@color/white" />
+
+        <Button
+            android:id="@+id/button_ok"
+            style="?android:attr/buttonBarButtonStyle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="@string/IFIA_btn_yes"
+            android:textColor="@color/white" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644 (file)
index 0000000..eca70cf
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644 (file)
index 0000000..eca70cf
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..a571e60
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644 (file)
index 0000000..61da551
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..c41dd28
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644 (file)
index 0000000..db5080a
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..6dba46d
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644 (file)
index 0000000..da31a87
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..15ac681
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644 (file)
index 0000000..b216f2d
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..f25a419
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644 (file)
index 0000000..e96783c
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644 (file)
index 0000000..10166a7
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="purple_200">#FFBB86FC</color>
+    <color name="purple_500">#FF6200EE</color>
+    <color name="purple_700">#FF3700B3</color>
+    <color name="teal_200">#FF03DAC5</color>
+    <color name="teal_700">#FF018786</color>
+    <color name="black">#FF000000</color>
+    <color name="white">#FFFFFFFF</color>
+    <color name="grey">#6d6d6d</color>
+</resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644 (file)
index 0000000..598c92a
--- /dev/null
@@ -0,0 +1,22 @@
+<resources>
+    <string name="app_name">ICFotaClient</string>
+    <string name="IFIA_btn_yes">是</string>
+    <string name="IFIA_btn_no">否</string>
+    <string name="pb_title_downloading">正在下载升级包</string>
+    <string name="check_has_update_text">检查是否有更新</string>
+    <string name="update_text">马上升级</string>
+    <string name="no_network_text">无法触及到服务器</string>
+    <string name="battery_level_tips_text">请确保电量50%以上</string>
+    <string name="error_tips_text">资源准备失败,请重启尝试</string>
+    <string name="last_version_text">已经是最新版本</string>
+    <string name="download_package">下载更新包</string>
+    <string name="try_to_downloading">正在玩命下载中…</string>
+    <string name="pause_download_text">暂停下载</string>
+    <string name="continue_download_text">继续下载</string>
+    <string name="try_to_checking">正在玩命检查新版本…</string>
+    <string name="version_tips">当前版本:</string>
+    <string name="imei_text">IMEI号:</string>
+    <string name="update_tips_text">更新说明:\n</string>
+    <string name="tips_wifi_connect">请连接WI-FI下载</string>
+    <string name="unsupport_pause_download">该服务器不支持暂停下载</string>
+</resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
new file mode 100644 (file)
index 0000000..107fe50
--- /dev/null
@@ -0,0 +1,16 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <!-- Base application theme. -->
+    <style name="Theme.ICFotaClient" parent="Theme.MaterialComponents.DayNight.NoActionBar">
+        <!-- Primary brand color. -->
+        <item name="colorPrimary">@color/purple_500</item>
+        <item name="colorPrimaryVariant">@color/purple_700</item>
+        <item name="colorOnPrimary">@color/white</item>
+        <!-- Secondary brand color. -->
+        <item name="colorSecondary">@color/teal_200</item>
+        <item name="colorSecondaryVariant">@color/teal_700</item>
+        <item name="colorOnSecondary">@color/black</item>
+        <!-- Status bar color. -->
+        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
+        <!-- Customize your theme here. -->
+    </style>
+</resources>
\ No newline at end of file
diff --git a/app/src/test/java/com/ic/icfotaclient/ExampleUnitTest.kt b/app/src/test/java/com/ic/icfotaclient/ExampleUnitTest.kt
new file mode 100644 (file)
index 0000000..9031987
--- /dev/null
@@ -0,0 +1,17 @@
+package com.ic.icfotaclient
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+    @Test
+    fun addition_isCorrect() {
+        assertEquals(4, 2 + 2)
+    }
+}
\ No newline at end of file
diff --git a/app/version_code.properties b/app/version_code.properties
new file mode 100644 (file)
index 0000000..baca364
--- /dev/null
@@ -0,0 +1,2 @@
+#Thu Jul 16 16:46:05 CST 2020
+VERSION_CODE=1
diff --git a/build.gradle b/build.gradle
new file mode 100644 (file)
index 0000000..82a7c2c
--- /dev/null
@@ -0,0 +1,30 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+    ext.kotlin_version = "1.4.21"
+    repositories {
+        maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
+        maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter' }
+        google()
+        jcenter()
+    }
+    dependencies {
+        classpath "com.android.tools.build:gradle:4.1.1"
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+allprojects {
+    repositories {
+        maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
+        maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter' }
+        google()
+        jcenter()
+    }
+}
+
+task clean(type: Delete) {
+    delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
new file mode 100644 (file)
index 0000000..98bed16
--- /dev/null
@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644 (file)
index 0000000..f6b961f
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644 (file)
index 0000000..e2900aa
--- /dev/null
@@ -0,0 +1,6 @@
+#Fri Jan 08 09:56:30 CST 2021
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
diff --git a/gradlew b/gradlew
new file mode 100644 (file)
index 0000000..cccdd3d
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644 (file)
index 0000000..f955316
--- /dev/null
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644 (file)
index 0000000..cf68854
--- /dev/null
@@ -0,0 +1,2 @@
+include ':app'
+rootProject.name = "ICFotaClient"
\ No newline at end of file