瀏覽代碼

Apex & drivers
拆分,新建app 工程 apex & drivers cn 和 apex & drivers i

Ray Zhang 7 年之前
父節點
當前提交
957ffd7c3d
共有 100 個文件被更改,包括 17252 次插入100 次删除
  1. 28 29
      ApexDrivers/apexdriverscn/apexdriverscn.iml
  2. 32 8
      ApexDrivers/apexdriverscn/build.gradle
  3. 6 3
      ApexDrivers/apexdriverscn/src/main/AndroidManifest.xml
  4. 15 0
      ApexDrivers/apexdriverscn/src/main/java/com/usai/apex/apexdriverscn/ApplicationCN.java
  5. 3 2
      ApexDrivers/apexdriverscn/src/main/java/com/usai/apex/apexdriverscn/MainActivityCN.java
  6. 27 27
      ApexDrivers/apexdriversi/apexdriversi.iml
  7. 21 7
      ApexDrivers/apexdriversi/build.gradle
  8. 7 3
      ApexDrivers/apexdriversi/src/main/AndroidManifest.xml
  9. 15 0
      ApexDrivers/apexdriversi/src/main/java/com/usai/apex/apexdriversi/ApplicationI.java
  10. 3 3
      ApexDrivers/apexdriversi/src/main/java/com/usai/apex/apexdriversi/MainActivityI.java
  11. 0 18
      ApexDrivers/apexdriversi/src/main/res/layout/activity_main.xml
  12. 140 0
      ApexDrivers/apexdriverslib/apexdriverslib.iml
  13. 66 0
      ApexDrivers/apexdriverslib/build.gradle
  14. 二進制
      ApexDrivers/apexdriverslib/libs/core-2.3.0.jar
  15. 21 0
      ApexDrivers/apexdriverslib/proguard-rules.pro
  16. 26 0
      ApexDrivers/apexdriverslib/src/androidTest/java/com/usai/redant/apexdrivers/ExampleInstrumentedTest.java
  17. 142 0
      ApexDrivers/apexdriverslib/src/main/AndroidManifest.xml
  18. 二進制
      ApexDrivers/apexdriverslib/src/main/ic_launcher-web.png
  19. 752 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/ApexDriverApplication.java
  20. 84 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/ApexDriversBackgroundService.java
  21. 659 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/MainActivity.java
  22. 149 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/badgeview/BadgeView.java
  23. 74 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/base/BasicActivity.java
  24. 105 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/base/BasicDialog.java
  25. 110 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/base/BasicObject.java
  26. 112 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/camera/AutoFocusManager.java
  27. 306 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/camera/CameraConfigurationManager.java
  28. 310 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/camera/CameraManager.java
  29. 43 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/camera/FrontLightMode.java
  30. 56 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/camera/PreferencesActivity.java
  31. 73 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/camera/PreferencesFragment.java
  32. 56 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/camera/PreviewCallback.java
  33. 62 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/camera/open/OpenCameraInterface.java
  34. 87 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/AmbientLightManager.java
  35. 131 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/BeepManager.java
  36. 884 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/CaptureActivity.java
  37. 168 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/CaptureActivityHandler.java
  38. 102 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/DecodeFormatManager.java
  39. 121 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/DecodeHandler.java
  40. 236 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/DecodeHintManager.java
  41. 105 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/DecodeThread.java
  42. 49 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/FinishListener.java
  43. 116 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/InactivityTimer.java
  44. 261 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/Intents.java
  45. 35 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/ViewfinderResultPointCallback.java
  46. 189 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/ViewfinderView.java
  47. 744 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/DetailActivity.java
  48. 758 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/DetailAdapter.java
  49. 143 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/DetailSectionModel.java
  50. 104 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailActionModel.java
  51. 9 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailActionSelectionModel.java
  52. 46 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailBaseModel.java
  53. 14 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailLocationModel.java
  54. 33 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailMapModel.java
  55. 12 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailMultipleLineModel.java
  56. 98 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailPhotoModel.java
  57. 88 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailSignatureModel.java
  58. 13 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailSingleLineModel.java
  59. 79 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailSubActionModel.java
  60. 480 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/filter/OrderFilterActivity.java
  61. 417 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/filter/OrderFilterAdapter.java
  62. 122 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/home/HomeCellLayout.java
  63. 819 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/home/HomeFragment.java
  64. 200 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/home/HomeHeaderView.java
  65. 665 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/home/HomeMoreActivity.java
  66. 125 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/home/HomeOrderModel.java
  67. 375 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/login/LoginFragment.java
  68. 363 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/login/RetrievePasswordActivity.java
  69. 504 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/message/MessageActivity.java
  70. 69 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/message/model/MessageModel.java
  71. 412 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/network/Network.java
  72. 214 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/receiver/ApexDriverAlarmReceiver.java
  73. 72 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/receiver/ApexDriverBootCompleteReceiver.java
  74. 120 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/roundCornerImageView/RoundCornerImageView.java
  75. 33 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/scaleimageview/ScaleImageView.java
  76. 290 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/SettingActivity.java
  77. 410 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/SettingAdapter.java
  78. 213 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/changepassword/ChangePasswordDialog.java
  79. 52 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/model/AboutModel.java
  80. 49 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/model/ActionModel.java
  81. 32 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/model/BaseModel.java
  82. 6 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/model/LinkModel.java
  83. 105 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/model/OptionModel.java
  84. 77 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/model/SectionModel.java
  85. 9 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/model/SettingOption.java
  86. 7 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/model/SwitchModel.java
  87. 311 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/option/OptionActivity.java
  88. 143 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/signature/SignatureActivity.java
  89. 167 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/signature/SignatureView.java
  90. 110 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/update/PhotoPreviewActivity.java
  91. 1135 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/update/UpdateActivity.java
  92. 552 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/update/UpdateAdapter.java
  93. 132 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/update/UpdateSectionModel.java
  94. 58 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/update/model/UpdateBaseModel.java
  95. 19 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/update/model/UpdateImageBaseModel.java
  96. 37 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/update/model/UpdateInputModel.java
  97. 26 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/update/model/UpdateLabelModel.java
  98. 33 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/update/model/UpdateMultInputModel.java
  99. 86 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/update/model/UpdatePhotoModel.java
  100. 75 0
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/update/model/UpdateSignatureModel.java

+ 28 - 29
ApexDrivers/apexdriverscn/apexdriverscn.iml

@@ -84,46 +84,45 @@
       <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
       <sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
       <sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/check-manifest" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaPrecompile" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/prebuild" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/splits-support" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
       <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
+      <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
     </content>
-    <orderEntry type="jdk" jdkName="Android API 27 Platform" jdkType="Android SDK" />
+    <orderEntry type="jdk" jdkName="Android API 26 Platform" jdkType="Android SDK" />
     <orderEntry type="sourceFolder" forTests="false" />
-    <orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test:runner-1.0.2" level="project" />
-    <orderEntry type="library" name="Gradle: android.arch.lifecycle:common:1.1.0@jar" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:support-annotations:27.1.1@jar" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:animated-vector-drawable-27.1.1" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:support-compat-27.1.1" level="project" />
-    <orderEntry type="library" name="Gradle: android.arch.lifecycle:viewmodel-1.1.0" level="project" />
-    <orderEntry type="library" scope="TEST" name="Gradle: com.squareup:javawriter:2.1.1@jar" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:support-vector-drawable-27.1.1" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:support-core-ui-27.1.1" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:support-core-utils-27.1.1" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support.constraint:constraint-layout-1.1.3" level="project" />
-    <orderEntry type="library" scope="TEST" name="Gradle: com.google.code.findbugs:jsr305:2.0.1@jar" level="project" />
-    <orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test.espresso:espresso-core-3.0.2" level="project" />
-    <orderEntry type="library" scope="TEST" name="Gradle: javax.inject:javax.inject:1@jar" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:support-fragment-27.1.1" level="project" />
-    <orderEntry type="library" scope="TEST" name="Gradle: junit:junit:4.12@jar" level="project" />
-    <orderEntry type="library" name="Gradle: android.arch.core:runtime-1.1.0" level="project" />
-    <orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-core:1.3@jar" level="project" />
-    <orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test:monitor-1.0.2" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:appcompat-v7-27.1.1" level="project" />
-    <orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test.espresso:espresso-idling-resource-3.0.2" level="project" />
-    <orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata-core-1.1.0" level="project" />
-    <orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-library:1.3@jar" level="project" />
-    <orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-integration:1.3@jar" level="project" />
-    <orderEntry type="library" name="Gradle: android.arch.core:common:1.1.0@jar" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support.constraint:constraint-layout-solver:1.1.3@jar" level="project" />
-    <orderEntry type="library" scope="TEST" name="Gradle: net.sf.kxml:kxml2:2.3.0@jar" level="project" />
-    <orderEntry type="library" name="Gradle: android.arch.lifecycle:runtime-1.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-base-16.0.1" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:animated-vector-drawable-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-tasks-16.0.1" level="project" />
+    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-places-placereport-16.0.0" level="project" />
+    <orderEntry type="library" name="Gradle: android.arch.lifecycle:common:1.0.0@jar" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-vector-drawable-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-core-ui-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-core-utils-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:appcompat-v7-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-fragment-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: android.arch.core:common:1.0.0@jar" level="project" />
+    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-basement-16.0.1" level="project" />
+    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-location-16.0.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-media-compat-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-annotations:26.1.0@jar" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-compat-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-v4-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: android.arch.lifecycle:runtime-1.0.0" level="project" />
+    <orderEntry type="module" module-name="apexdriverslib" />
+    <orderEntry type="module" module-name="RAUtilsLibrary" />
   </component>
 </module>

+ 32 - 8
ApexDrivers/apexdriverscn/build.gradle

@@ -1,14 +1,14 @@
 apply plugin: 'com.android.application'
 
 android {
-    compileSdkVersion 27
+    compileSdkVersion 26
 
 
 
     defaultConfig {
         applicationId "com.usai.apex.apexdriverscn"
         minSdkVersion 23
-        targetSdkVersion 27
+        targetSdkVersion 25
         versionCode 1
         versionName "1.0"
 
@@ -22,15 +22,39 @@ android {
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
         }
     }
+    gradle.projectsEvaluated {
+        tasks.withType(JavaCompile) {
+//            options.compilerArgs << "-Xlint:deprecation"
+//            options.compilerArgs << "--stacktrace"
+//            options.compilerArgs << "--info"
+//            options.compilerArgs << "--debug"
+//            options.compilerArgs << "--scan"
+            options.compilerArgs << "-Xlint:unchecked"
 
+        }
+    }
 }
 
 dependencies {
-    implementation fileTree(dir: 'libs', include: ['*.jar'])
 
-    implementation 'com.android.support:appcompat-v7:27.1.1'
-    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
-    testImplementation 'junit:junit:4.12'
-    androidTestImplementation 'com.android.support.test:runner:1.0.2'
-    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+//    api project(':apexdriverslib')
+//    api project(':RAUtilsLibrary')
+//    implementation fileTree(dir: 'libs', include: ['*.jar'])
+//
+//    implementation 'com.android.support:appcompat-v7:27.1.1'
+//
+//    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
+//    testImplementation 'junit:junit:4.12'
+//    androidTestImplementation 'com.android.support.test:runner:1.0.2'
+//    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+    implementation 'com.android.support:appcompat-v7:26.1.0'
+//    implementation 'com.android.support:appcompat-v7:27.1.1'
+//    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
+//    testImplementation 'junit:junit:4.12'
+//    androidTestImplementation 'com.android.support.test:runner:1.0.2'
+//    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+    api project(':RAUtilsLibrary')
+    api project(':apexdriverslib')
 }

+ 6 - 3
ApexDrivers/apexdriverscn/src/main/AndroidManifest.xml

@@ -1,15 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     package="com.usai.apex.apexdriverscn">
 
     <application
+        android:name=".ApplicationCN"
         android:allowBackup="true"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_name"
-        android:roundIcon="@mipmap/ic_launcher_round"
+
         android:supportsRtl="true"
-        android:theme="@style/AppTheme">
-        <activity android:name=".MainActivity">
+
+        tools:replace="android:name">
+        <activity android:name=".MainActivityCN">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
 

+ 15 - 0
ApexDrivers/apexdriverscn/src/main/java/com/usai/apex/apexdriverscn/ApplicationCN.java

@@ -0,0 +1,15 @@
+package com.usai.apex.apexdriverscn;
+
+import com.usai.redant.apexdrivers.ApexDriverApplication;
+
+public class ApplicationCN extends ApexDriverApplication {
+    @Override
+    public void onCreate()
+    {
+        bUseGoogleLocation = false;
+        CHANNEL_ID = "Apex & Drivers CN";
+        CHANNEL_NAME = "Apex & Drivers CN";
+
+        super.onCreate();
+    }
+}

+ 3 - 2
ApexDrivers/apexdriverscn/src/main/java/com/usai/apex/apexdriverscn/MainActivity.java → ApexDrivers/apexdriverscn/src/main/java/com/usai/apex/apexdriverscn/MainActivityCN.java

@@ -1,9 +1,10 @@
 package com.usai.apex.apexdriverscn;
 
-import android.support.v7.app.AppCompatActivity;
 import android.os.Bundle;
 
-public class MainActivity extends AppCompatActivity {
+import com.usai.redant.apexdrivers.MainActivity;
+
+public class MainActivityCN extends MainActivity {
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {

+ 27 - 27
ApexDrivers/apexdriversi/apexdriversi.iml

@@ -84,45 +84,45 @@
       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/check-manifest" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaPrecompile" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/prebuild" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/splits-support" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
       <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
       <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
     </content>
-    <orderEntry type="jdk" jdkName="Android API 27 Platform" jdkType="Android SDK" />
+    <orderEntry type="jdk" jdkName="Android API 26 Platform" jdkType="Android SDK" />
     <orderEntry type="sourceFolder" forTests="false" />
-    <orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test:runner-1.0.2" level="project" />
-    <orderEntry type="library" name="Gradle: android.arch.lifecycle:common:1.1.0@jar" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:support-annotations:27.1.1@jar" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:animated-vector-drawable-27.1.1" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:support-compat-27.1.1" level="project" />
-    <orderEntry type="library" name="Gradle: android.arch.lifecycle:viewmodel-1.1.0" level="project" />
-    <orderEntry type="library" scope="TEST" name="Gradle: com.squareup:javawriter:2.1.1@jar" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:support-vector-drawable-27.1.1" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:support-core-ui-27.1.1" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:support-core-utils-27.1.1" level="project" />
-    <orderEntry type="library" scope="TEST" name="Gradle: com.google.code.findbugs:jsr305:2.0.1@jar" level="project" />
-    <orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test.espresso:espresso-core-3.0.2" level="project" />
-    <orderEntry type="library" scope="TEST" name="Gradle: javax.inject:javax.inject:1@jar" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:support-fragment-27.1.1" level="project" />
-    <orderEntry type="library" scope="TEST" name="Gradle: junit:junit:4.12@jar" level="project" />
-    <orderEntry type="library" name="Gradle: android.arch.core:runtime-1.1.0" level="project" />
-    <orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-core:1.3@jar" level="project" />
-    <orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test:monitor-1.0.2" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:appcompat-v7-27.1.1" level="project" />
-    <orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test.espresso:espresso-idling-resource-3.0.2" level="project" />
-    <orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata-core-1.1.0" level="project" />
-    <orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-library:1.3@jar" level="project" />
-    <orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-integration:1.3@jar" level="project" />
-    <orderEntry type="library" name="Gradle: android.arch.core:common:1.1.0@jar" level="project" />
-    <orderEntry type="library" scope="TEST" name="Gradle: net.sf.kxml:kxml2:2.3.0@jar" level="project" />
-    <orderEntry type="library" name="Gradle: android.arch.lifecycle:runtime-1.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-base-16.0.1" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:animated-vector-drawable-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-tasks-16.0.1" level="project" />
+    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-places-placereport-16.0.0" level="project" />
+    <orderEntry type="library" name="Gradle: android.arch.lifecycle:common:1.0.0@jar" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-vector-drawable-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-core-ui-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-core-utils-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:appcompat-v7-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-fragment-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: android.arch.core:common:1.0.0@jar" level="project" />
+    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-basement-16.0.1" level="project" />
+    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-location-16.0.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-media-compat-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-annotations:26.1.0@jar" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-compat-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-v4-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: android.arch.lifecycle:runtime-1.0.0" level="project" />
+    <orderEntry type="module" module-name="apexdriverslib" />
+    <orderEntry type="module" module-name="RAUtilsLibrary" />
   </component>
 </module>

+ 21 - 7
ApexDrivers/apexdriversi/build.gradle

@@ -1,14 +1,14 @@
 apply plugin: 'com.android.application'
 
 android {
-    compileSdkVersion 27
+    compileSdkVersion 26
 
 
 
     defaultConfig {
         applicationId "com.usai.apex.apexdriversi"
         minSdkVersion 23
-        targetSdkVersion 27
+        targetSdkVersion 26
         versionCode 1
         versionName "1.0"
 
@@ -22,15 +22,29 @@ android {
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
         }
     }
+    gradle.projectsEvaluated {
+        tasks.withType(JavaCompile) {
+//            options.compilerArgs << "-Xlint:deprecation"
+//            options.compilerArgs << "--stacktrace"
+//            options.compilerArgs << "--info"
+//            options.compilerArgs << "--debug"
+//            options.compilerArgs << "--scan"
+            options.compilerArgs << "-Xlint:unchecked"
 
+        }
+    }
 }
 
 dependencies {
+
     implementation fileTree(dir: 'libs', include: ['*.jar'])
+    implementation 'com.android.support:appcompat-v7:26.1.0'
+//    implementation 'com.android.support:appcompat-v7:27.1.1'
+//    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
+//    testImplementation 'junit:junit:4.12'
+//    androidTestImplementation 'com.android.support.test:runner:1.0.2'
+//    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+    api project(':RAUtilsLibrary')
+    api project(':apexdriverslib')
 
-    implementation 'com.android.support:appcompat-v7:27.1.1'
-    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
-    testImplementation 'junit:junit:4.12'
-    androidTestImplementation 'com.android.support.test:runner:1.0.2'
-    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
 }

+ 7 - 3
ApexDrivers/apexdriversi/src/main/AndroidManifest.xml

@@ -1,21 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     package="com.usai.apex.apexdriversi">
 
     <application
+        android:name=".ApplicationI"
         android:allowBackup="true"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_name"
-        android:roundIcon="@mipmap/ic_launcher_round"
+
         android:supportsRtl="true"
-        android:theme="@style/AppTheme">
-        <activity android:name=".MainActivity">
+
+        tools:replace="android:name">
+        <activity android:name=".MainActivityI">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
 
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+
     </application>
 
 </manifest>

+ 15 - 0
ApexDrivers/apexdriversi/src/main/java/com/usai/apex/apexdriversi/ApplicationI.java

@@ -0,0 +1,15 @@
+package com.usai.apex.apexdriversi;
+
+import com.usai.redant.apexdrivers.ApexDriverApplication;
+
+public class ApplicationI extends ApexDriverApplication {
+
+    @Override
+    public void onCreate()
+    {
+        super.onCreate();
+        CHANNEL_ID = "Apex & Drivers I";
+        CHANNEL_NAME = "Apex & Drivers I";
+    }
+
+}

+ 3 - 3
ApexDrivers/apexdriversi/src/main/java/com/usai/apex/apexdriversi/MainActivity.java → ApexDrivers/apexdriversi/src/main/java/com/usai/apex/apexdriversi/MainActivityI.java

@@ -1,13 +1,13 @@
 package com.usai.apex.apexdriversi;
 
-import android.support.v7.app.AppCompatActivity;
 import android.os.Bundle;
 
-public class MainActivity extends AppCompatActivity {
+import com.usai.redant.apexdrivers.MainActivity;
+
+public class MainActivityI extends MainActivity {
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_main);
     }
 }

+ 0 - 18
ApexDrivers/apexdriversi/src/main/res/layout/activity_main.xml

@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<android.support.constraint.ConstraintLayout 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"
-    tools:context=".MainActivity">
-
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="Hello World!"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintLeft_toLeftOf="parent"
-        app:layout_constraintRight_toRightOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
-
-</android.support.constraint.ConstraintLayout>

+ 140 - 0
ApexDrivers/apexdriverslib/apexdriverslib.iml

@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.id=":apexdriverslib" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" type="JAVA_MODULE" version="4">
+  <component name="FacetManager">
+    <facet type="android-gradle" name="Android-Gradle">
+      <configuration>
+        <option name="GRADLE_PROJECT_PATH" value=":apexdriverslib" />
+      </configuration>
+    </facet>
+    <facet type="android" name="Android">
+      <configuration>
+        <option name="SELECTED_BUILD_VARIANT" value="debug" />
+        <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
+        <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
+        <afterSyncTasks>
+          <task>generateDebugSources</task>
+        </afterSyncTasks>
+        <option name="ALLOW_USER_CONFIGURATION" value="false" />
+        <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
+        <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
+        <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
+        <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
+        <option name="PROJECT_TYPE" value="1" />
+      </configuration>
+    </facet>
+  </component>
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7">
+    <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
+    <output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" />
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/test/debug" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/res" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/resources" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/assets" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/aidl" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/java" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/rs" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/shaders" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/annotations" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/attr" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/check-manifest" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/extractedTypedefs" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/intermediate-jars" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaPrecompile" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/library_and_local_jars_jni" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/packaged-aidl" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/packaged-classes" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/packagedAssets" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/packaged_res" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/public_res" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
+      <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
+      <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
+    </content>
+    <orderEntry type="jdk" jdkName="Android API 26 Platform" jdkType="Android SDK" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-base-16.0.1" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:recyclerview-v7-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:animated-vector-drawable-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-tasks-16.0.1" level="project" />
+    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-places-placereport-16.0.0" level="project" />
+    <orderEntry type="library" name="Gradle: android.arch.lifecycle:common:1.0.0@jar" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-vector-drawable-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-core-ui-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: __local_aars__:/Users/ray/Documents/code_ERPSuiteAndroid/ApexDrivers/apexdriverslib/libs/core-2.3.0.jar:unspecified@jar" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:transition-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-core-utils-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support.constraint:constraint-layout-solver:1.1.2@jar" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:appcompat-v7-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-fragment-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: android.arch.core:common:1.0.0@jar" level="project" />
+    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-basement-16.0.1" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support.constraint:constraint-layout-1.1.2" level="project" />
+    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-location-16.0.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-media-compat-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-annotations:26.1.0@jar" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:design-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-v4-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: com.android.support:support-compat-26.1.0" level="project" />
+    <orderEntry type="library" name="Gradle: android.arch.lifecycle:runtime-1.0.0" level="project" />
+    <orderEntry type="module" module-name="RAUtilsLibrary" />
+  </component>
+</module>

+ 66 - 0
ApexDrivers/apexdriverslib/build.gradle

@@ -0,0 +1,66 @@
+apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion 26
+    defaultConfig {
+//        applicationId "com.usai.redant.apexdrivers"
+
+        minSdkVersion 23
+        targetSdkVersion 26
+
+        versionCode 1
+        versionName "1.0"
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+    libraryVariants.all { variant ->
+//        if (variant.buildType.name == 'release') {
+        variant.assemble.doLast {
+            variant.outputs.each { output ->
+                def outputFile = output.outputFile
+//                    if (outputFile != null && outputFile.name.endsWith('release.aar')) {
+                def fileName = "${project.name}"
+                println(fileName)
+                def outputPath = "../output"
+                copy {
+                    from outputFile
+                    into outputPath
+                    rename { fileName + ".aar" }
+                }
+//                    }
+            }
+        }
+//        }
+    }
+
+    gradle.projectsEvaluated {
+        tasks.withType(JavaCompile) {
+//            options.compilerArgs << "-Xlint:deprecation"
+//            options.compilerArgs << "--stacktrace"
+//            options.compilerArgs << "--info"
+//            options.compilerArgs << "--debug"
+//            options.compilerArgs << "--scan"
+            options.compilerArgs << "-Xlint:unchecked"
+
+        }
+    }
+}
+
+dependencies {
+
+    implementation 'com.android.support:appcompat-v7:26.1.0'
+    implementation 'com.android.support.constraint:constraint-layout:1.1.2'
+    implementation 'com.android.support:design:26.1.0'
+//    testImplementation 'junit:junit:4.12'
+//    androidTestImplementation 'com.android.support.test:runner:1.0.2'
+//    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+    implementation files('libs/core-2.3.0.jar')
+//    api 'com.google.android.gms:play-services-maps:11.4.2'
+//    api 'com.google.android.gms:play-services-location:11.4.2'
+    api project(':RAUtilsLibrary')
+}

二進制
ApexDrivers/apexdriverslib/libs/core-2.3.0.jar


+ 21 - 0
ApexDrivers/apexdriverslib/proguard-rules.pro

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

+ 26 - 0
ApexDrivers/apexdriverslib/src/androidTest/java/com/usai/redant/apexdrivers/ExampleInstrumentedTest.java

@@ -0,0 +1,26 @@
+package com.usai.redant.apexdrivers;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getTargetContext();
+
+        assertEquals("com.usai.redant.apexdrivers", appContext.getPackageName());
+    }
+}

+ 142 - 0
ApexDrivers/apexdriverslib/src/main/AndroidManifest.xml

@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:tools="http://schemas.android.com/tools"
+          package="com.usai.redant.apexdrivers">
+
+    <!-- External storage for log and cache. -->
+    <!-- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> -->
+    <!-- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> -->
+
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
+    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
+    <uses-permission android:name="android.permission.CAMERA"/>
+    <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    <uses-permission android:name="android.permission.VIBRATE"/>
+    <uses-permission android:name="android.permission.FLASHLIGHT"/>
+    <uses-permission android:name="android.alarm.permission.SET_ALARM"/>
+    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+    <uses-permission android:name="android.permission.INTERNET"/>
+
+    <!-- 调用硬件相机权限 -->
+    <uses-feature android:name="android.hardware.camera"/>
+    <uses-feature android:name="android.hardware.camera.autofocus"/>
+    <!-- 文件读写权限 -->
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+
+    <application
+        android:name=".ApexDriverApplication"
+        android:allowBackup="true"
+        android:label="@string/app_name"
+        android:icon="@mipmap/ic_launcher"
+        android:roundIcon="@mipmap/ic_launcher"
+        android:supportsRtl="true"
+        android:theme="@style/ApexDriverTheme"
+        tools:replace="android:theme,android:icon">
+        <provider
+            android:name="android.support.v4.content.FileProvider"
+            android:authorities="com.usai.apex.driver.fileprovider"
+            android:exported="false"
+            android:grantUriPermissions="true">
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/download_dir"/>
+        </provider>
+
+        <activity
+            android:name=".MainActivity"
+            android:launchMode="singleTop"
+            android:screenOrientation="portrait">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+
+        <service
+            android:name=".ApexDriversBackgroundService"
+            android:enabled="true"
+            android:exported="true"
+            android:directBootAware="true"
+            android:label="ApexDrivers background service">
+
+            <!-- <intent-filter> -->
+            <!-- <action android:name="com.usai.redant.apexdrivers.ApexDriversBackgroundService"/> -->
+            <!-- </intent-filter> -->
+        </service>
+
+        <receiver
+            android:name=".receiver.ApexDriverBootCompleteReceiver"
+            android:directBootAware="true"
+            android:enabled="true"
+            android:exported="false"
+            android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
+            <intent-filter>
+                <action android:name="android.intent.action.BOOT_COMPLETED"/>
+                <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED"/>
+            </intent-filter>
+        </receiver>
+
+        <activity
+            android:name=".detail.DetailActivity"
+            android:screenOrientation="portrait"/>
+        <activity
+            android:name=".update.UpdateActivity"
+            android:screenOrientation="portrait"/>
+        <activity
+            android:name=".camera.PreferencesActivity"
+            android:screenOrientation="portrait"/>
+        <activity
+            android:name=".codescanner.CaptureActivity"
+            android:clearTaskOnLaunch="true"
+            android:label="Scan"
+            android:screenOrientation="sensorLandscape"
+            android:stateNotNeeded="true"
+            android:theme="@style/CaptureTheme"
+            android:windowSoftInputMode="stateAlwaysHidden"/>
+        <activity
+            android:name=".update.PhotoPreviewActivity"
+            android:screenOrientation="portrait"/>
+        <activity
+            android:name=".login.RetrievePasswordActivity"
+            android:screenOrientation="portrait"/>
+        <activity
+            android:name=".home.HomeMoreActivity"
+            android:screenOrientation="portrait"/>
+        <activity
+            android:name=".upload.UploadListActivity"
+            android:screenOrientation="portrait"/>
+        <activity
+            android:name=".upload.TaskActivity"
+            android:screenOrientation="portrait"/>
+        <activity android:name=".filter.OrderFilterActivity"/>
+        <activity android:name=".signature.SignatureActivity"/>
+
+        <receiver
+            android:name=".receiver.ApexDriverAlarmReceiver"
+            android:directBootAware="true"
+            android:enabled="true"
+            android:exported="true"
+            android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
+            >
+            <intent-filter>
+
+                <!-- <action android:name="REDANT.BROADCAST.ACTION_REDANT_INIT_ALARM" /> -->
+                <!-- <action android:name="REDANT.BROADCAST.ACTION_REDANT_ALARM" /> -->
+                <action android:name="android.intent.action.BOOT_COMPLETED"/>
+                <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED"/>
+            </intent-filter>
+        </receiver>
+
+        <activity android:name=".message.MessageActivity">
+        </activity>
+        <activity android:name=".setting.SettingActivity">
+        </activity>
+        <activity android:name=".setting.option.OptionActivity">
+        </activity>
+        <activity android:name=".base.BasicActivity">
+        </activity>
+    </application>
+
+</manifest>

二進制
ApexDrivers/apexdriverslib/src/main/ic_launcher-web.png


+ 752 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/ApexDriverApplication.java

@@ -0,0 +1,752 @@
+package com.usai.redant.apexdrivers;
+
+import android.app.Activity;
+import android.app.Application;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.graphics.BitmapFactory;
+import android.location.Location;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.provider.Settings;
+import android.support.annotation.Nullable;
+import android.support.v4.app.NotificationCompat;
+import android.support.v7.app.AlertDialog;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.usai.redant.apexdrivers.detail.DetailActivity;
+import com.usai.redant.apexdrivers.network.Network;
+import com.usai.redant.apexdrivers.receiver.ApexDriverAlarmReceiver;
+import com.usai.redant.apexdrivers.utils.OperationQueue;
+import com.usai.redant.rautils.receiver.RABroadcast;
+import com.usai.redant.rautils.utils.AESUtil;
+
+import org.json.JSONObject;
+
+import java.lang.reflect.Field;
+
+import static android.app.Notification.VISIBILITY_PUBLIC;
+import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
+
+public class ApexDriverApplication extends Application {
+
+    public final static String secretKey = "usai";
+    public final static String preferencesKey = "Apex";
+    public static  String CHANNEL_ID = "Apex & Drivers";
+    public static  String CHANNEL_NAME = "Apex & Drivers";
+    public static boolean bUseGoogleLocation = true;
+
+    private static ApexDriverApplication mApp;
+
+    public String user;
+    public String password;
+    private volatile long notificationID;
+
+    public boolean isbackground=true;
+
+    public final static int BackgroundReportTypeNone = 0;
+    public final static int BackgroundReportTypeReject = 1;
+    public final static int BackgroundReportTypeAlways = 2;
+    public final static int BackgroundReportTypeAllow = 3;
+
+    public int backgroundReportType; // getValueForKeyPath/setValueForKeyPath 变量需公开
+
+
+    private ServiceConnection mServiceConnection;
+    private ApexDriversBackgroundService mService;
+    private boolean mRequiredLocation = false;
+
+    private Activity mCurActivity;
+
+
+    private OperationQueue networkQueue;
+
+    public void initLocation()
+    {
+        if(mService!=null)
+            mService.initLocation();
+    }
+//    private BroadcastReceiver screen_event_receiver = new BroadcastReceiver() {
+//        @Override
+//        public void onReceive(Context context, Intent intent) {
+////            if (isbackground) {
+//                isbackground = true;
+//
+////            }
+//        }
+//    };
+
+    private ActivityLifecycleCallbacks activitylcCallbacks =new ActivityLifecycleCallbacks() {
+        private int activityStartCount = 0;
+        @Override
+        public void onActivityCreated(Activity activity, Bundle bundle) {
+
+        }
+
+        @Override
+        public void onActivityStarted(Activity activity) {
+            activityStartCount++;
+            mCurActivity = activity;
+        }
+
+        @Override
+        public void onActivityResumed(Activity activity) {
+
+            if(isbackground==true)
+            {
+                isbackground = false;
+                //notification
+                Log.d("ApexDriverApplication", "onCreate: SEND" + RABroadcast.ACTION_REDANT_INIT_ALARM);
+                Intent bintent = new Intent(RABroadcast.ACTION_REDANT_INIT_ALARM);
+                bintent.setClass(ApexDriverApplication.this, ApexDriverAlarmReceiver.class);
+//        bintent.putExtra("msg", msg.toString());
+                sendBroadcast(bintent);
+            }
+//            isbackground = false;
+        }
+
+        @Override
+        public void onActivityPaused(Activity activity) {
+
+        }
+
+        @Override
+        public void onActivityStopped(Activity activity) {
+            activityStartCount--;
+            if(activityStartCount==0)
+            {
+                isbackground = true;
+                Log.d("ApexDriverApplication", "onActivityStopped: app go background");
+            }
+        }
+
+        @Override
+        public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
+
+        }
+
+        @Override
+        public void onActivityDestroyed(Activity activity) {
+
+        }
+    };
+
+//    @Override
+//    public void onTrimMemory(int level)
+//    {
+//        super.onTrimMemory(level);
+//        if (level == TRIM_MEMORY_UI_HIDDEN) {
+//            isbackground = true;
+//
+//        }
+//    }
+    @Override
+    public void onCreate() {
+        Log.d("ApexDriverApplication", "onCreate");
+        super.onCreate();
+
+//        IntentFilter screenOffFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
+//        registerReceiver(screen_event_receiver, screenOffFilter);
+
+        registerActivityLifecycleCallbacks(activitylcCallbacks);
+        mApp = this;
+
+        try {
+            user = savedUser();
+            password = savedPassword();
+
+            loadBackgroundReportType();
+
+//            if (user != null) {
+//                // 上传Token
+//                Network.uploadToken(RAUtil.getDeviceId(getApplicationContext()));
+//            }
+
+            Log.d("ApexDriverApplication", "onCreate: u:"+user+" p:"+password);
+            mServiceConnection = new ServiceConnection() {
+                @Override
+                public void onServiceConnected(ComponentName name, IBinder service) {
+
+                    ApexDriversBackgroundService.MyBinder binder = (ApexDriversBackgroundService.MyBinder)service;
+                    mService = (ApexDriversBackgroundService)binder.getService();
+
+                }
+
+                @Override
+                public void onServiceDisconnected(ComponentName name) {
+
+                    mService = null;
+                }
+            };
+//
+            Intent serviceintent = new Intent();
+            serviceintent.setClass(this, ApexDriversBackgroundService.class);
+
+            ComponentName cn ;
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+                cn =this.startForegroundService(serviceintent);
+            } else
+            {
+                cn =this.startService(serviceintent);
+            }
+
+
+            Intent intent = new Intent(getApplicationContext(),ApexDriversBackgroundService.class);
+            bindService(intent,mServiceConnection, Context.BIND_AUTO_CREATE);
+
+            networkQueue = new OperationQueue();
+
+
+            Log.d("ApexDriverApplication", "onCreate: launch service successful");
+
+        }catch (Exception e)
+        {
+
+        }
+
+
+        initAlarm();
+
+    }
+
+    void initAlarm()
+    {
+        Log.d("ApexDriverApplication", "onCreate: SEND" + RABroadcast.ACTION_REDANT_INIT_ALARM);
+        Intent bintent = new Intent(RABroadcast.ACTION_REDANT_INIT_ALARM);
+        bintent.setClass(this, ApexDriverAlarmReceiver.class);
+//        bintent.putExtra("msg", msg.toString());
+        sendBroadcast(bintent);
+    }
+
+    @Override
+    public void onTerminate() {
+        super.onTerminate();
+
+        unregisterActivityLifecycleCallbacks(activitylcCallbacks);
+        unbindService(mServiceConnection);
+    }
+
+    public OperationQueue getNetworkQueue() {
+        return networkQueue;
+    }
+
+    public void setRequiredLocation(boolean requiredLocation) {
+        mRequiredLocation = requiredLocation;
+
+        if (mService != null) {
+            if (mRequiredLocation) {
+
+                mService.startLocation();
+
+            } else {
+
+                mService.stopLocation();
+
+            }
+        }
+    }
+
+    public boolean getRequiredLocation() {
+        return mRequiredLocation;
+    }
+
+    public Location getCurrentLocation() {
+        return mService.RequestCachedLocation();
+    }
+
+    public boolean isLogin() {
+        return user != null && user.length() > 0 && password != null && password.length() > 0;
+    }
+
+    public void login(String user,String password) {
+        this.user = user;
+        this.password = password;
+
+
+        SharedPreferences pref = sharedPreferences();
+
+        SharedPreferences.Editor editor = pref.edit();
+        try {
+
+            editor.putString("user", encryptString(user));
+            editor.putString("password", encryptString(password));
+
+        } catch (Exception e) {
+            editor.putString("user", null);
+            editor.putString("password", null);
+            e.printStackTrace();
+        }
+        editor.commit();
+    }
+
+    public void updatePassword(String password) {
+        this.password = password;
+        SharedPreferences pref = sharedPreferences();
+        SharedPreferences.Editor editor = pref.edit();
+        try {
+
+            editor.putString("password", encryptString(password));
+
+        } catch (Exception e) {
+            editor.putString("password", null);
+            e.printStackTrace();
+        }
+        editor.commit();
+    }
+
+    public String savedUser() {
+
+        SharedPreferences pref=sharedPreferences();
+
+        String user = pref.getString("user", null);
+        if (user != null) {
+            user = decryptString(user);
+        }
+        return user;
+    }
+
+    public String savedPassword() {
+
+
+        SharedPreferences pref=sharedPreferences();
+
+        String p = pref.getString("password", null);
+        if (p != null) {
+            p = decryptString(p);
+        }
+        return p;
+    }
+
+    public void setNotificationID(long id) {
+        notificationID = id;
+
+        if (user == null) {
+            return;
+        }
+
+        SharedPreferences pref = sharedPreferences();
+        SharedPreferences.Editor editor = pref.edit();
+        String key = user + "_notification_id";
+        try {
+
+            editor.putLong(key, notificationID);
+
+        } catch (Exception e) {
+            editor.putString(key, null);
+            e.printStackTrace();
+        }
+        editor.commit();
+    }
+
+    public long getNotificationID() {
+
+        if (notificationID == 0) {
+
+            SharedPreferences pref=sharedPreferences();
+            String key = user + "_notification_id";
+            long id = pref.getLong(key, 0);
+            notificationID = id;
+        }
+
+        return notificationID;
+    }
+
+    public String encryptPassword() {
+         if (password != null) {
+             return encryptString(password);
+         }
+         return null;
+    }
+
+    public String encryptUser() {
+        if (user != null) {
+            return encryptString(user);
+        }
+        return null;
+    }
+
+    public String encryptString(String string) {
+        if (string == null) {
+            return null;
+        }
+        return AESUtil.encrypt256(secretKey,string);
+    }
+
+    public String decryptString(String string) {
+        if (string == null) {
+            return null;
+        }
+        return AESUtil.decrypt256(secretKey, string);
+    }
+
+    private SharedPreferences sharedPreferences() {
+
+        SharedPreferences pref=null;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+        {
+            Context c= this.createDeviceProtectedStorageContext();
+            pref = c.getSharedPreferences(ApexDriverApplication.preferencesKey, 0);
+        }
+        else
+        {
+            pref = getSharedPreferences(ApexDriverApplication.preferencesKey, 0);
+        }
+        return pref;
+    }
+
+    public void logout() {
+        user = null;
+        password = null;
+        SharedPreferences pref = sharedPreferences();
+
+        SharedPreferences.Editor editor = pref.edit();
+        editor.remove("user");
+        editor.remove("password");
+        editor.commit();
+
+    }
+
+    public Object getValueForKeyPath(String keyPath) {
+        if (TextUtils.isEmpty(keyPath)) {
+            return null;
+        }
+
+        try {
+
+            Field field = getClass().getField(keyPath);
+            if (field != null) {
+                Object obj = field.get(this);
+                return obj;
+            } else {
+                return null;
+            }
+
+        } catch (NoSuchFieldException e) {
+            e.printStackTrace();
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public void setValueForKeyPath(String keyPath,Object value) {
+
+        if (TextUtils.isEmpty(keyPath)) {
+            return;
+        }
+
+        try {
+
+            Field field = getClass().getField(keyPath);
+            if (field != null) {
+                field.set(this,value);
+            }
+
+        } catch (NoSuchFieldException e) {
+            e.printStackTrace();
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        }
+
+    }
+
+    public static ApexDriverApplication sharedApplication() {
+        return mApp;
+    }
+
+    public void setBackgroundReportType(int backgroundReportType) {
+        this.backgroundReportType = backgroundReportType;
+
+        SharedPreferences pref = sharedPreferences();
+        SharedPreferences.Editor editor = pref.edit();
+        try {
+
+            editor.putInt("backgroundReportType", backgroundReportType);
+
+        } catch (Exception e) {
+            editor.putInt("backgroundReportType", BackgroundReportTypeNone);
+            e.printStackTrace();
+        }
+        editor.commit();
+    }
+
+    public int getBackgroundReportType() {
+        return backgroundReportType;
+    }
+
+    private void loadBackgroundReportType() {
+        SharedPreferences pref = sharedPreferences();
+        int reportType = pref.getInt("backgroundReportType",BackgroundReportTypeNone);
+        this.backgroundReportType = reportType;
+    }
+
+    public void reportLocationForOrder(@Nullable final String location, final String orderId) {
+
+        getNetworkQueue().addOperationTask(new OperationQueue.OperationBackgroundCallBack() {
+            @Override
+            public Object operationDoInBackground() {
+
+                Network.reportLocation(location,orderId);
+
+                return null;
+            }
+        },null,null);
+
+    }
+
+    public void rejectReportLocation(final String reason, final String orderId) {
+
+        getNetworkQueue().addOperationTask(new OperationQueue.OperationBackgroundCallBack() {
+            @Override
+            public Object operationDoInBackground() {
+
+                Network.rejectReportLocation(reason,orderId);
+
+                return null;
+            }
+        },null,null);
+
+    }
+
+    public void reportLocationForOrder(final String orderId) {
+
+        switch (backgroundReportType) {
+            case BackgroundReportTypeNone: {
+
+            }
+            break;
+            case BackgroundReportTypeReject: {
+
+                String reason = "Driver " + user +" rejected to report location";
+                rejectReportLocation(reason,orderId);
+            }
+            break;
+            case BackgroundReportTypeAlways: {
+
+                if (mCurActivity != null) {
+                    new AlertDialog.Builder(mCurActivity)
+                            .setTitle("Warning")
+                            .setMessage("Report Location For Order:" + orderId)
+                            .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
+                                @Override
+                                public void onClick(DialogInterface dialog, int which) {
+
+                                    String location = null;
+                                    reportLocationForOrder(location,orderId);
+                                }
+                            })
+                            .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+                                @Override
+                                public void onClick(DialogInterface dialog, int which) {
+
+                                    String reason = "Driver " + user +" cancel to report location";
+                                    rejectReportLocation(reason,orderId);
+                                }
+                            })
+                            .show();
+                }
+
+            }
+            break;
+            case BackgroundReportTypeAllow: {
+
+                Location location = getCurrentLocation();
+                String latlon = "-999,-999";
+                if (location != null) {
+                    latlon = location.getLatitude() + "," + location.getLongitude();
+                }
+                reportLocationForOrder(latlon,orderId);
+            }
+            break;
+        }
+    }
+
+    public void receiveNotificationOnMainThread(final JSONObject notification) {
+        new Handler(Looper.getMainLooper()).post(new Runnable() {
+            @Override
+            public void run() {
+                receiveNotification(notification);
+            }
+        });
+    }
+
+    private void receiveNotification(JSONObject notification) {
+
+        if (notification != null) {
+
+            JSONObject aps = notification.optJSONObject("aps");
+            if (aps != null) {
+
+                int report_location = aps.optInt("report-location",0);
+                final String orderID = aps.optString("order-id");
+
+                if (report_location != 0) {
+
+                    reportLocationForOrder(orderID);
+
+                } else {
+
+                    int is_order = aps.optInt("is-order",0);
+                    if (is_order != 0) {
+
+//                        sendBroadcast(new Intent(HomeFragment.HomeReloadBroadcastAction));
+
+                        boolean isActive = !isbackground; // 程序是否在前台
+                         if (isActive) {
+                            // 弹窗提示
+
+                            final int orderType = aps.optInt("order-type");
+                            final String orderType2 = aps.optString("order-type2");
+                            final String statusNo = aps.optString("status-no");
+                            int required = aps.optInt("required");
+                            String msg = aps.optString("message");
+                            if (TextUtils.isEmpty(msg)) {
+                                msg = orderID + " status changed,view detail?";
+                            }
+
+                            AlertDialog.Builder builder = new AlertDialog.Builder(mCurActivity);
+                            builder.setTitle("Message");
+                            builder.setMessage(msg);
+                            if (required == 0) {
+                                builder.setNegativeButton("Cancel",null);
+                            }
+                            builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
+                                @Override
+                                public void onClick(DialogInterface dialog, int which) {
+                                    showDetail(orderID,orderType,orderType2,statusNo);
+                                }
+                            });
+                            builder.show();
+
+                        } else {
+                            // 发通知
+                            popLocalNotification(notification);
+                        }
+
+                    }
+
+                }
+
+            }
+
+        }
+
+    }
+
+    public void popLocalNotification(JSONObject notification) {
+
+        if (notification == null) {
+            return;
+        }
+        JSONObject aps = notification.optJSONObject("aps");
+        if (aps == null) {
+            return;
+        }
+
+        int id = aps.optInt("id");
+        JSONObject alert = aps.optJSONObject("alert");
+        String title = null,msg = null;
+        if (alert != null) {
+
+            title = alert.optString("title");
+            msg = alert.optString("body");
+        }
+
+        if (TextUtils.isEmpty(title) || TextUtils.isEmpty(msg)) {
+            return;
+        }
+
+//        final String orderID = aps.optString("order-id");
+//        final int orderType = aps.optInt("order-type");
+//        final String orderType2 = aps.optString("order-type2");
+//        final String statusNo = aps.optString("status-no");
+//
+//        Intent intent = DetailActivity.build(getApplicationContext(),orderID,orderType,orderType2,statusNo);
+//        intent.putExtra("goHome",true);
+        Intent intent = new Intent(getApplicationContext(),MainActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); // getIntent可能是null
+        intent.putExtra("aps",aps.toString()); // 程序在后台的情况下,点击通知将程序唤醒到前台时,并不能取得extra
+
+        /**
+         *
+         * requestCode: 需要保证不同,否则id不通的intent取到的extra也是同一个
+         * */
+        int requestCode = id;
+        PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), requestCode, intent,FLAG_UPDATE_CURRENT);
+
+        //1.获取系统通知的管理者
+        NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+
+        Notification noti = null;
+        long[] vibrates = { 0, 1000, 1000, 1000 };
+
+//        Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
+        Uri soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+
+            NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_NONE);
+            // 震动
+            channel.enableVibration(true);
+            channel.setVibrationPattern(vibrates);
+
+            channel.enableLights(true);
+
+            channel.setSound(soundUri, Notification.AUDIO_ATTRIBUTES_DEFAULT);
+
+            nm.createNotificationChannel(channel);
+
+            noti = new NotificationCompat.Builder(this, CHANNEL_ID)
+                    .setContentTitle(title)
+                    .setContentText(msg)
+                    .setSmallIcon(R.drawable.small_icon_clear)
+                    .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.large_notification_icon_clear))
+                    .setContentIntent(contentIntent)
+                    .setVisibility(VISIBILITY_PUBLIC)
+                    .setSound(soundUri)
+                    .setLights(0xff00bbff,500,200)
+                    .build();
+        } else {
+
+            noti = new Notification.Builder(this)
+                    .setContentTitle(title)
+                    .setContentText(msg)
+                    .setSmallIcon(R.drawable.small_icon_clear)
+                    .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.large_notification_icon_clear))
+                    .setContentIntent(contentIntent)
+                    .setVisibility(VISIBILITY_PUBLIC)
+                    .setSound(soundUri)
+                    .setLights(0xff00bbff,500,20)
+                    .build();
+
+        }
+
+        /**
+         * vibrate属性是一个长整型的数组,用于设置手机静止和振动的时长,以毫秒为单位。
+         * 参数中下标为0的值表示手机静止的时长,下标为1的值表示手机振动的时长, 下标为2的值又表示手机静止的时长,以此类推。
+         */
+        noti.vibrate = vibrates;
+
+        noti.flags |= Notification.FLAG_AUTO_CANCEL;
+        nm.notify(id, noti);
+    }
+
+    public void showDetail(String orderId, int type, String type2, String statusNo) {
+
+        Intent intent = DetailActivity.build(getApplicationContext(),orderId,type,type2,statusNo);
+        startActivity(intent);
+    }
+}

+ 84 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/ApexDriversBackgroundService.java

@@ -0,0 +1,84 @@
+package com.usai.redant.apexdrivers;
+
+import android.app.Notification;
+import android.content.Intent;
+import android.location.Location;
+import android.util.Log;
+
+import com.usai.redant.rautils.receiver.RABroadcast;
+import com.usai.redant.rautils.service.RAService;
+import com.usai.redant.rautils.utils.dbgUtil;
+
+import org.json.JSONObject;
+
+public class ApexDriversBackgroundService extends RAService implements RAService.ServiceLocation, RAService.ServicePushNotification,RAService.ServiceUpload{
+
+    //负责上传及定位
+//    public RAUploadManager uploadManager = new RAUploadManager(ApexDriverApplication.sharedApplication().getApplicationContext());
+
+    public ApexDriversBackgroundService() {
+
+    }
+
+    @Override
+    protected void Setup() {
+//        service_flag=FLAG_SERVICE_UPLOAD|FLAG_SERVICE_NOTIFICATION|FLAG_SERVICE_LOCATION;
+
+        CHANNEL_ID=ApexDriverApplication.sharedApplication().CHANNEL_ID;
+        CHANNEL_NAME=ApexDriverApplication.sharedApplication().CHANNEL_NAME;
+        initServiceLocation(this);
+//        initServiceNotification(this,"replace this string with notification checking url");
+        initServiceUpload(this);
+
+        dbgUtil.fileLog(this,"ApexDriversBackgroundService Setup()");
+    }
+
+//    @Override
+//    public void RequestLocation_ByBroadcast(String receiverID) {
+//        request_location(receiverID);
+//    }
+
+    @Override
+    public Location RequestCachedLocation() {
+        return request_cachedlocation();
+    }
+
+    @Override
+    public void onLocationChanged(Location location) {
+
+        Log.d("ApexDriversB..Service", "onLocationChanged");
+        Log.d("ApexDriversB..Service", "request single location");
+        request_location("123");
+//
+//        if(location==null)
+//            Log.d("ApexDriversB..Service", "onLocationChanged: null");
+//        else
+//            Log.d("ApexDriversB..Service", "onLocationChanged: "+location.getLongitude()+" , "+location.getLatitude());
+    }
+
+    @Override
+    public Notification prepareNotification(JSONObject msg) {
+        return null;
+    }
+
+    @Override
+    public void handleSilenceMessage(JSONObject msg) {
+
+    }
+
+    public void startLocation() {
+        sendBroadcast(new Intent(RABroadcast.ACTION_LOCATION_ENABLE_TRACING));
+    }
+
+    public void stopLocation() {
+        sendBroadcast(new Intent(RABroadcast.ACTION_LOCATION_DISABLE_TRACING));
+    }
+
+
+//    @Override
+//    public IBinder onBind(Intent intent) {
+//        // TODO: Return the communication channel to the service.
+////        throw new UnsupportedOperationException("Not yet implemented");
+//        return super.onBind(intent);
+//    }
+}

+ 659 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/MainActivity.java

@@ -0,0 +1,659 @@
+package com.usai.redant.apexdrivers;
+
+
+import android.Manifest;
+import android.app.AlertDialog;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.app.NotificationManagerCompat;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.RelativeLayout;
+
+import com.usai.redant.apexdrivers.base.BasicActivity;
+import com.usai.redant.apexdrivers.detail.DetailActivity;
+import com.usai.redant.apexdrivers.home.HomeFragment;
+import com.usai.redant.apexdrivers.login.LoginFragment;
+import com.usai.redant.apexdrivers.network.Network;
+import com.usai.redant.apexdrivers.upload.TaskActivity;
+import com.usai.redant.rautils.utils.ImageUtil;
+import com.usai.redant.rautils.utils.RAUtil;
+import com.usai.redant.rautils.utils.dbgUtil;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.lang.ref.WeakReference;
+import java.util.Iterator;
+
+//import android.location.Location;
+
+
+public class MainActivity extends BasicActivity implements LoginFragment.LoginCallBack {
+
+    private RelativeLayout mRootContainer;
+    private final static String FragmentTag = "ContentFragmentTag";
+
+    private static WeakReference<MainActivity> weakSelf = null;
+
+    public static MainActivity currentMainActivity() {
+        if (weakSelf != null) {
+            return weakSelf.get();
+        }
+        return null;
+    }
+
+    void Test()
+    {
+        dbgUtil.fileLog(this,"lib import successful");
+
+
+        RAUtil.LibTest();
+
+
+        RAUtil.LibTest1();
+
+
+//        if(true)
+//            return;
+//
+////        String en1=AESUtil.encrypt256("usai1234usai1234usai1234usai1234","ABCDE$3&))@!_*FGHIJ^%KLMNOPQRSTUVWXYZ--ABCDE$3&))@!_*FGHIJ^%KLMNOPQRSTUVWXYZABCDE$3&))@!_*FGHIJ^%KLMNOPQRSTUVWXYZABCDE$3&))@!_*FGHIJ^%KLMNOPQRSTUVWXYZ");
+////        String de1=AESUtil.decrypt256("usai1234usai1234usai1234usai1234",en1);
+////        Log.d("AES256", "EN: "+en1+"   DE: "+de1);
+//
+////        String en2=AESUtil.encrypt256("usai1234","ABCDE$3&))@!_*FGHIJ^%KLMNOPQRSTUVWXYZ--ABCDE$3&))@!_*FGHIJ^%KLMNOPQRSTUVWXYZ");
+////        String de2=AESUtil.decrypt256("usai1234",en2);
+////        Log.d("AES256", "EN: "+en2+"   DE: "+de2);
+////
+////
+////        String en3=AESUtil.encrypt256("usai1234","ABCDE$3&))@!_*FGHIJ^%KLMNOPQRSTUVWXYZ--ABCDE$3&))@!_*FGHIJ^%KLMNOPQRSTUVWXYZABCDE$3&))@!_*FGHIJ^%KLMNOPQRSTUVWXYZABCDE$3&))@!_*FGHIJ^%KLMNOPQRSTUVWXYZ");
+////        String de3=AESUtil.decrypt256("usai1234",en3);
+////        Log.d("AES256", "EN: "+en3+"   DE: "+de3);
+//
+
+
+
+
+//        if(true)
+//            return;
+//        ServiceConnection serviceConnection= new ServiceConnection(){
+//
+//            @Override
+//            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+//                ApexDriversBackgroundService.MyBinder binder;
+//                binder = (ApexDriversBackgroundService.MyBinder)iBinder;
+//                ApexDriversBackgroundService service = (ApexDriversBackgroundService)binder.getService();
+//
+//                Location l=service.RequestCachedLocation();
+//                if(l!=null)
+//                    Log.d("", "RequestLocation: "+l.toString());
+//
+//            }
+//
+//            @Override
+//            public void onServiceDisconnected(ComponentName componentName) {
+//
+//            }
+//        };
+//
+//        Intent intent = new Intent(MainActivity.this, ApexDriversBackgroundService.class);
+//
+//        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
+//
+//
+//
+//
+//
+//
+//        int FLAG_LOCATION_NONE = 0 ;
+//
+//        int FLAG_LOCATION_SERVICE = 1 << 1;
+//        int FLAG_NOTIFICATION_SERVICE = 1 << 2;
+//        int FLAG_UPLOAD_SERVICE = 1 << 3;
+//
+//        int a = 0;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+
+//        if (ApexDriverApplication.sharedApplication().isLogin()) {
+//            setTheme(R.style.ApexDriverTheme);
+//        } else {
+//            setTheme(R.style.ApexDriverThemeWhite);
+//            autoSetupStatusBar = false;
+//        }
+        weakSelf = new WeakReference<>(this);
+
+
+
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        mRootContainer = findViewById(R.id.root_container);
+
+        initView();
+
+//        checkPowerManagement();
+
+        Test();
+
+        checkPermissions();
+
+        checkNotificationEnable();
+        checkNotification();
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+
+        setIntent(intent);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        checkNotification();
+    }
+
+    /**
+     * 检查是否点击通知栏激活应用到前台
+     * */
+    private void checkNotification() {
+
+        if (ApexDriverApplication.sharedApplication().isLogin()) {
+
+            Intent intent = getIntent();
+            String apsStr = null;
+            boolean is_alert = false;
+            // 从Intent获取aps
+            if (intent != null) {
+
+                is_alert = intent.getBooleanExtra("is_alert",false);
+                apsStr = intent.getStringExtra("aps");
+
+            }
+
+            // 若aps不为空,则程序是从点击通知进入的
+            if (!TextUtils.isEmpty(apsStr)) {
+
+                intent.removeExtra("aps");
+
+                try {
+
+                    JSONObject aps = new JSONObject(apsStr);
+                    if (is_alert) {
+                        showDetailByAsk(aps);
+                    } else {
+                        showDetailByNotification(aps);
+                    }
+
+                } catch (JSONException e) {
+                    e.printStackTrace();
+                }
+
+                cancelAllNotification();
+
+                return;
+            }
+
+            // 如果aps为空,则点击appicon进入程序,从通知列表中取出最后一个发送,并且清除所有通知
+            NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+
+            /**
+             * Recover a list of active notifications: ones that have been posted by the calling app that
+             * have not yet been dismissed by the user or {@link #cancel(String, int)}ed by the app.
+             */
+            StatusBarNotification notifications[] = nm.getActiveNotifications();
+//            for (int i = notifications.length - 1; i >= 0; i--) {
+//                StatusBarNotification notification = notifications[i];
+//
+//            }
+            if (notifications.length > 0) {
+                StatusBarNotification statusBarNotification = notifications[0];
+                Notification notification = statusBarNotification.getNotification();
+                if (notification != null) {
+                    try {
+                        Intent immutIntent = new Intent();
+                        immutIntent.putExtra("is_alert",true);
+                        PendingIntent contentIntent = notification.contentIntent;
+                       if (contentIntent != null) {
+                           contentIntent.send(this,statusBarNotification.getId(),immutIntent);
+                       }
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+
+        }
+    }
+
+    private void cancelAllNotification() {
+        NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+        StatusBarNotification notifications[] = nm.getActiveNotifications();
+
+        for (StatusBarNotification statusBarNotification : notifications) {
+            int id = statusBarNotification.getId();
+            nm.cancel(id);
+        }
+    }
+
+    private void showDetailByNotification(JSONObject aps) {
+
+        if (aps != null) {
+            final String orderID = aps.optString("order-id");
+            final int orderType = aps.optInt("order-type");
+            final String orderType2 = aps.optString("order-type2");
+            final String statusNo = aps.optString("status-no");
+
+            Intent detailIntent = DetailActivity.build(getApplicationContext(),orderID,orderType,orderType2,statusNo);
+
+            startActivity(detailIntent);
+        }
+    }
+
+    private void showDetailByAsk(final JSONObject aps) {
+
+        if (aps != null) {
+
+            final String orderID = aps.optString("order-id");
+            int required = aps.optInt("required");
+            String msg = aps.optString("message");
+            if (TextUtils.isEmpty(msg)) {
+                msg = orderID + " status changed,view detail?";
+            }
+
+            android.support.v7.app.AlertDialog.Builder builder = new android.support.v7.app.AlertDialog.Builder(this);
+            builder.setTitle("Message");
+            builder.setMessage(msg);
+            if (required == 0) {
+                builder.setNegativeButton("Cancel",null);
+            }
+            builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                    showDetailByNotification(aps);
+                }
+            });
+            builder.show();
+
+        }
+
+
+    }
+
+    void checkPowerManagement()
+    {
+        Intent intent = new Intent();
+        String packageName = getPackageName();
+        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+
+        if (Build.VERSION.SDK_INT >= 23)
+        {
+            if (!pm.isIgnoringBatteryOptimizations(packageName))
+            {
+                intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
+                intent.setData(Uri.parse("package:" + packageName));
+            }
+//                intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
+//            else {
+//                intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
+//                intent.setData(Uri.parse("package:" + packageName));
+//            }
+            startActivity(intent);
+
+        }
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
+    {
+
+        if (requestCode == RAUtil.MY_PERMISSIONS_REQUEST)
+        {
+            boolean missing=false;
+            boolean request = false;
+            for(int i=0;i<grantResults.length;i++)
+            {
+
+                if(grantResults[i]!= PackageManager.PERMISSION_GRANTED)
+                    missing=true;
+                boolean bshow= ActivityCompat.shouldShowRequestPermissionRationale(this,permissions[i]);
+                if(bshow)
+                    request = true;
+            }
+
+            String msg=null;
+            if(request)
+                msg="Apex & Drivers needs some essential permissions.";
+            else
+                msg="Apex & Drivers will quit because missing some essential permissions.\nPlease check your system setting.";
+
+            if(missing) {
+                final boolean finalRequest = request;
+                new AlertDialog.Builder(this)
+                        .setTitle("Warning")
+                        .setMessage(msg)
+                        .setPositiveButton("OK", new DialogInterface.OnClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which) {
+//                                checkAllPermission();
+                                if (finalRequest)
+                                    checkPermissions();
+                                else
+                                    finish();
+                            }
+                        })
+
+                        .show();
+            }
+            else
+            {
+                ApexDriverApplication.sharedApplication().initLocation();
+            }
+
+            return;
+        }
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+    }
+
+    private void checkPermissions() {
+
+//        String[] permissions = {Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE};
+
+        String[] permissions = {
+                Manifest.permission.CAMERA,
+//                Manifest.permission.VIBRATE,
+//                Manifest.permission.READ_CONTACTS,
+//                Manifest.permission.READ_PHONE_STATE,
+                Manifest.permission.WRITE_EXTERNAL_STORAGE,
+                Manifest.permission.READ_EXTERNAL_STORAGE,
+                Manifest.permission.RECEIVE_BOOT_COMPLETED,
+//                Manifest.permission.ACCESS_NETWORK_STATE,
+//                Manifest.permission.ACCESS_WIFI_STATE,
+                Manifest.permission.INTERNET,
+                Manifest.permission.ACCESS_FINE_LOCATION,
+                Manifest.permission.ACCESS_COARSE_LOCATION
+//
+//                Manifest.permission.CHANGE_CONFIGURATION
+
+
+
+        };
+
+        RAUtil.checkPermissions(this,permissions);
+    }
+
+    private void checkNotificationEnable() {
+
+        boolean enable = NotificationManagerCompat.from(getApplicationContext()).areNotificationsEnabled();
+        if (!enable) {
+
+            new AlertDialog.Builder(this)
+                    .setTitle("Warning")
+                    .setMessage("you should enable notification")
+                    .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            openSystemSetting();
+                        }
+                    })
+                    .show();
+
+        }
+
+    }
+
+    private void openSystemSetting() {
+        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+        intent.setData(Uri.parse("package:" + getPackageName()));
+        startActivity(intent);
+    }
+
+    private void restart() {
+
+        finish();
+
+        startActivity(new Intent(this,MainActivity.class));
+
+    }
+
+    private void setupLoginAppearance() {
+
+//        changeStatusBarGradient();
+
+        android.support.v7.app.ActionBar actionBar = getSupportActionBar();
+        if(actionBar != null){
+            actionBar.setElevation(0); // 5.0以上隐藏分割线
+
+            // 将图像渲染成白色
+            Drawable drawable = getResources().getDrawable(R.drawable.apex_logo);
+//            ImageUtil.renderingDrawable(drawable,getResources(),R.color.ApexDriverWhite);
+
+            actionBar.setHomeAsUpIndicator(drawable);
+            actionBar.setDisplayHomeAsUpEnabled(true);
+
+        }
+
+        setTitle(null);
+
+        invalidateOptionsMenu();
+
+        setTitle(ApexDriverApplication.sharedApplication().user);
+    }
+
+    private void setupLogoutAppearance() {
+
+//        changeStatusBarNormal();
+
+        android.support.v7.app.ActionBar actionBar = getSupportActionBar();
+        if(actionBar != null){
+            actionBar.setElevation(1); // 5.0以上隐藏分割线
+
+            Drawable drawable = getResources().getDrawable(R.drawable.apex_logo);
+//            ImageUtil.clearDrawableRendering(drawable);
+
+            actionBar.setHomeAsUpIndicator(drawable);
+            actionBar.setDisplayHomeAsUpEnabled(true);
+
+        }
+
+        invalidateOptionsMenu();
+
+        setTitle("Login");
+    }
+
+    private void initView() {
+
+        FragmentManager manager = getSupportFragmentManager();
+        FragmentTransaction transaction = manager.beginTransaction();
+
+        Fragment oldFragment = manager.findFragmentByTag(FragmentTag);
+
+        if (oldFragment != null) { // 屏幕旋转会导致Fragment重叠
+            return;
+        }
+
+        if (ApexDriverApplication.sharedApplication().isLogin()) {
+
+            HomeFragment homeFragment = new HomeFragment();
+            transaction.add(R.id.root_container, homeFragment,FragmentTag);
+
+            setupLoginAppearance();
+
+        } else {
+
+            LoginFragment loginFragment = new LoginFragment();
+            loginFragment.setCallBack(this);
+            transaction.add(R.id.root_container, loginFragment,FragmentTag);
+
+            setupLogoutAppearance();
+        }
+        transaction.commit();
+
+        invalidateOptionsMenu();
+    }
+
+    // 登陆
+
+    @Override
+    public void onLogin() {
+
+        HomeFragment homeFragment = new HomeFragment();
+
+        FragmentManager manager = getSupportFragmentManager();
+        FragmentTransaction transaction = manager.beginTransaction();
+
+        transaction.replace(R.id.root_container,homeFragment,FragmentTag);
+
+        transaction.commit();
+
+        setupLoginAppearance();
+
+//        restart();
+
+        // notification token
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                Network.uploadToken(RAUtil.getDeviceId(getApplicationContext()));
+            }
+        }).start();
+
+    }
+
+
+    // d登出
+    private void showLogin() {
+
+        FragmentManager manager = getSupportFragmentManager();
+        FragmentTransaction transaction = manager.beginTransaction();
+
+        LoginFragment loginFragment = new LoginFragment();
+        loginFragment.setCallBack(this);
+        transaction.replace(R.id.root_container, loginFragment,FragmentTag);
+
+        transaction.commit();
+
+        setupLogoutAppearance();
+
+//        restart();
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        super.onPrepareOptionsMenu(menu);
+
+        menu.clear();
+
+        if (ApexDriverApplication.sharedApplication().isLogin()) {
+
+            MenuItem uploadItem = menu.add(0, 0, 0, "Upload List");
+
+            Drawable drawable = getResources().getDrawable(R.drawable.upload_list);
+            ImageUtil.renderingDrawable(drawable,getResources(),R.color.ApexDriverWhite);
+            uploadItem.setIcon(drawable);
+
+            uploadItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+
+//            menu.add(0, 1, 0, "Logout");
+
+        } else {
+
+        }
+
+        return true;
+    }
+
+
+//    @Override
+//    public boolean onCreateOptionsMenu(Menu menu) {
+//        getMenuInflater().inflate(R.menu.home_menu,menu);
+//
+//        return true;
+//    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+
+        switch (item.getItemId()) {
+            case 0: {
+
+                Intent intent = new Intent(this, TaskActivity.class);
+                startActivity(intent);
+            }
+            break;
+            case 1: {
+                logout();
+            }
+            break;
+//            case R.id.home_upload_list: {
+//                Intent intent = new Intent(this, TaskActivity.class);
+//                startActivity(intent);
+//            }
+//            break;
+        }
+
+        return true;
+    }
+
+    private ProgressDialog mProgressDialog;
+    private void showProgressDialog() {
+        if (mProgressDialog == null) {
+            mProgressDialog = new ProgressDialog(this);
+            mProgressDialog.setMessage("loading...");
+            mProgressDialog.setCancelable(false);
+            mProgressDialog.show();
+        }
+    }
+
+    private void dismissProgressDialog() {
+        if (mProgressDialog != null && mProgressDialog.isShowing()) {
+            mProgressDialog.dismiss();
+        }
+        mProgressDialog = null;
+    }
+
+    public void logout() {
+
+        showProgressDialog();
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                Network.logout();
+
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        dismissProgressDialog();
+                        ApexDriverApplication.sharedApplication().logout();
+                        showLogin();
+                    }
+                });
+            }
+        }).start();
+    }
+
+}

+ 149 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/badgeview/BadgeView.java

@@ -0,0 +1,149 @@
+package com.usai.redant.apexdrivers.badgeview;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.RoundRectShape;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+
+
+public class BadgeView extends android.support.v7.widget.AppCompatTextView {
+
+
+    private int mBadgeNumber;
+    private int mTextW,mTextH;
+    private int backgroundColor = Color.RED;
+
+    public BadgeView(Context context) {
+        this(context,null);
+    }
+
+    public BadgeView(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs,android.R.attr.textViewStyle);
+    }
+
+    public BadgeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        init();
+    }
+
+    private void init() {
+
+        setTextColor(Color.WHITE);
+        setTypeface(Typeface.DEFAULT);
+        setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
+        setGravity(Gravity.CENTER);
+
+        setBadgeCount(0);
+
+    }
+
+
+    public void setBackground(int dipRadius, int badgeColor) {
+        int radius = dip2Px(dipRadius);
+        float[] radiusArray = new float[] { radius, radius, radius, radius, radius, radius, radius, radius };
+
+        RoundRectShape roundRect = new RoundRectShape(radiusArray, null, null);
+        ShapeDrawable bgDrawable = new ShapeDrawable(roundRect);
+        bgDrawable.getPaint().setColor(badgeColor);
+//        setBackground(bgDrawable);
+
+        setBackgroundDrawable(bgDrawable);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        if (mTextW == 0 && mTextH == 0) { // constraintLayout 会调用一次,显示正确。否则RelativeLayout会调用多次,导致Max + 20多次。
+
+            int w = getMeasuredWidth();
+            int h = getMeasuredHeight();
+
+            mTextW = w;
+            mTextH = h;
+
+            int max = Math.max(w,h);
+
+            getLayoutParams().height = max;
+            getLayoutParams().width = max;
+
+            Integer count = getBadgeCount();
+            if (count != null && count >= 10) {
+                getLayoutParams().width = max + 20;
+            }
+
+            setLayoutParams(getLayoutParams());
+
+            setBackground(max / 2, backgroundColor);
+        }
+
+    }
+
+    @Override
+    public void setText(CharSequence text, BufferType type) {
+        if (text == null || text.toString().equalsIgnoreCase("0") || text.length() == 0) {
+            setVisibility(View.GONE);
+        } else {
+            setVisibility(View.VISIBLE);
+        }
+
+
+        super.setText(text, type);
+
+        clear();
+        requestLayout();
+    }
+
+    private void clear() {
+
+
+        if (getLayoutParams() != null) {
+            getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
+            getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
+            setLayoutParams(getLayoutParams());
+        }
+
+        mTextW = 0;
+        mTextH = 0;
+    }
+
+    public void setBadgeCount(int count) {
+        mBadgeNumber = count;
+        if (count > 99) {
+            setText("99+");
+        } else {
+            setText(String.valueOf(count));
+        }
+    }
+
+    public Integer getBadgeCount() {
+        return mBadgeNumber;
+    }
+
+
+    /*
+     * converts dip to px
+     */
+    private int dip2Px(float dip) {
+        return (int) (dip * getContext().getResources().getDisplayMetrics().density + 0.5f);
+    }
+
+    public static int px2dp(Context context, float pxValue) {
+        float scale = context.getResources().getDisplayMetrics().density;
+        return (int) (pxValue / scale + 0.5f);
+    }
+
+
+    public void setBackgroundColor(int color) {
+        backgroundColor = color;
+    }
+
+}

+ 74 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/base/BasicActivity.java

@@ -0,0 +1,74 @@
+package com.usai.redant.apexdrivers.base;
+
+import android.graphics.Color;
+import android.os.Build;
+import android.os.Looper;
+import android.os.MessageQueue;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.View;
+
+import com.usai.redant.apexdrivers.R;
+
+public class BasicActivity extends AppCompatActivity {
+
+    public boolean autoSetupStatusBar = true;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+//        setContentView(R.layout.activity_basic);
+
+//        Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
+//            @Override
+//            public boolean queueIdle() {
+//                if (true) {
+//                    initStatusBar();
+//                    getWindow().getDecorView().addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+//                        @Override
+//                        public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
+//                            initStatusBar();
+//                        }
+//                    });
+//                }
+//                return false;
+//            }
+//        });
+
+        if (autoSetupStatusBar) {
+            getWindow().getDecorView().addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+                @Override
+                public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                    changeStatusBarGradient();
+                }
+            });
+        }
+
+    }
+
+
+    private void initStatusBar(int resId) {
+        View statusBarView = null;
+        if (statusBarView == null) {
+            //android系统级的资源id得这么拿,不然拿不到
+            int identifier = getResources().getIdentifier("statusBarBackground", "id", "android");
+            statusBarView = getWindow().findViewById(identifier);
+        }
+        if (statusBarView != null) {
+            statusBarView.setBackgroundResource(resId);
+        }
+    }
+
+    public void changeStatusBarGradient() {
+        initStatusBar(R.drawable.gradient_color);
+    }
+
+    public void changeStatusBarNormal() {
+//        initStatusBar(R.drawable.normal_color);
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+        }
+    }
+}

+ 105 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/base/BasicDialog.java

@@ -0,0 +1,105 @@
+package com.usai.redant.apexdrivers.base;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.view.Gravity;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+
+import com.usai.redant.apexdrivers.R;
+
+
+public class BasicDialog extends Dialog {
+
+    private Context mCtx;
+    private View mRootView;
+    private BasicDialogSetupCallBack mCallBack;
+
+    public final static int BasicDialogContentGravityTop = 0;
+    public final static int BasicDialogContentGravityCenter = 1;
+    public final static int BasicDialogContentGravityBottom = 2;
+
+    public BasicDialog(@NonNull Context context, BasicDialogSetupCallBack callBack) {
+        this(context);
+
+        mCallBack = callBack;
+
+        mCtx = context;
+        init();
+    }
+
+    private BasicDialog(@NonNull Context context) {
+        this(context, R.style.RedAntDialog);
+    }
+
+    private BasicDialog(@NonNull Context context, int themeResId) {
+        super(context, themeResId);
+    }
+
+    private void init() {
+        if (mCallBack != null) {
+            mRootView = mCallBack.createContentView(this);
+            if (mRootView != null) {
+                setContentView(mRootView);
+            }
+        }
+    }
+
+    @Override
+    public void show() {
+
+        if (mRootView == null) {
+            super.show();
+            return;
+        }
+
+        Window dialogWindow = getWindow();
+        if (dialogWindow != null) {
+
+            int gravity = BasicDialogContentGravityCenter;
+            if (mCallBack != null) {
+                gravity = mCallBack.dialogGravity(this);
+            }
+
+            switch (gravity) {
+                case BasicDialogContentGravityTop: {
+                    dialogWindow.setGravity(Gravity.TOP);
+                }
+                break;
+                case BasicDialogContentGravityCenter: {
+                    dialogWindow.setGravity(Gravity.CENTER);
+                }
+                break;
+                case BasicDialogContentGravityBottom: {
+                    dialogWindow.setGravity(Gravity.BOTTOM);
+                }
+                break;
+                default: {
+                    dialogWindow.setGravity(Gravity.CENTER);
+                }
+                break;
+            }
+
+            WindowManager.LayoutParams layoutParams = dialogWindow.getAttributes(); // 获取对话框当前的参数值
+            layoutParams.x = 0; // 新位置X坐标
+            layoutParams.y = 0; // 新位置Y坐标
+            layoutParams.width = (int) mCtx.getResources().getDisplayMetrics().widthPixels; // 宽度
+            mRootView.measure(0, 0);
+            layoutParams.height = mRootView.getMeasuredHeight();
+
+            layoutParams.alpha = 9f; // 透明度
+            dialogWindow.setAttributes(layoutParams);
+        }
+
+        super.show();
+    }
+
+    public interface BasicDialogSetupCallBack {
+
+        View createContentView(final BasicDialog dialog);
+        int dialogGravity(final BasicDialog dialog);
+
+    }
+}

+ 110 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/base/BasicObject.java

@@ -0,0 +1,110 @@
+package com.usai.redant.apexdrivers.base;
+
+import org.json.JSONObject;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class BasicObject {
+
+    public void setValuesForKeysWithJSON(JSONObject json) {
+
+        if (json == null || json.length() == 0) {
+            return;
+        }
+
+
+        try {
+            Class cls = getClass();
+            while (cls != null && cls != Class.class) {
+
+                Field[] fields = cls.getFields();
+                for (Field f : fields) {
+
+                    String key = f.getName();
+
+                    Object value = json.opt(key);
+
+                    if (value == null) {
+                        continue;
+                    }
+
+                    Class type = f.getType();
+                    if (type == value.getClass() || ((type == int.class || type == long.class) && value.getClass() == Integer.class) || ((type == float.class || type == double.class) && (value.getClass() == Double.class || value.getClass() == Float.class))) {
+
+
+                        boolean success = invokeSetter(cls,f,type,key,value);
+                        if (!success) {
+                            f.set(this,value);
+                        }
+
+                    } else {
+
+                        if (value.getClass() == String.class) {
+
+                            String string = (String)value;
+
+                            if (type == Integer.class || type == int.class || type == long.class) {
+
+                                value = Integer.valueOf(string);
+
+
+                            } else if (type == Double.class || type == double.class || type == float.class) {
+
+                                value = Double.valueOf(string);
+
+                            } else if (type == Boolean.class || type == boolean.class) {
+
+                                value = Boolean.valueOf(string);
+                            }
+
+                            boolean success = invokeSetter(cls,f,type,key,value);
+                            if (!success) {
+                                f.set(this,value);
+                            }
+
+                        }
+
+                    }
+
+                }
+                cls = cls.getSuperclass();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    public boolean invokeSetter(Class cls, Field f, Class type,String key,Object value) {
+
+        String setter = "set" + toUpperCaseFirstOne(key);
+        try {
+
+
+            Class<?> myClassType = Class.forName(cls.getName());
+//            Class<?>[] types = new Class[] { File.class };
+//            Constructor<?> cons = myClassType.getConstructor(types);
+
+            Method method = myClassType.getMethod(setter,type);
+            if (method != null) {
+
+                method.invoke(this,value);
+
+                return true;
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return false;
+    }
+
+    public static String toUpperCaseFirstOne(String s){
+        if(Character.isUpperCase(s.charAt(0)))
+            return s;
+        else
+            return (new StringBuilder()).append(Character.toUpperCase(s.charAt(0))).append(s.substring(1)).toString();
+    }
+}

+ 112 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/camera/AutoFocusManager.java

@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2012 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.usai.redant.apexdrivers.camera;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.hardware.Camera;
+import android.os.AsyncTask;
+import android.preference.PreferenceManager;
+import android.util.Log;
+
+import com.usai.redant.apexdrivers.camera.PreferencesActivity;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+final class AutoFocusManager implements Camera.AutoFocusCallback {
+
+  private static final String TAG = AutoFocusManager.class.getSimpleName();
+
+  private static final long AUTO_FOCUS_INTERVAL_MS = 2000L;
+  private static final Collection<String> FOCUS_MODES_CALLING_AF;
+  static {
+    FOCUS_MODES_CALLING_AF = new ArrayList<String>(2);
+    FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_AUTO);
+    FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_MACRO);
+  }
+
+  private boolean active;
+  private final boolean useAutoFocus;
+  private final Camera camera;
+  private AsyncTask<?,?,?> outstandingTask;
+
+  AutoFocusManager(Context context, Camera camera) {
+    this.camera = camera;
+    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
+    String currentFocusMode = camera.getParameters().getFocusMode();
+    useAutoFocus =
+        sharedPrefs.getBoolean(PreferencesActivity.KEY_AUTO_FOCUS, true) &&
+        FOCUS_MODES_CALLING_AF.contains(currentFocusMode);
+    Log.i(TAG, "Current focus mode '" + currentFocusMode + "'; use auto focus? " + useAutoFocus);
+    start();
+  }
+
+  @Override
+  public synchronized void onAutoFocus(boolean success, Camera theCamera) {
+    if (active) {
+      outstandingTask = new AutoFocusTask();
+      outstandingTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+    }
+  }
+
+  synchronized void start() {
+    if (useAutoFocus) {
+      active = true;
+      try {
+        camera.autoFocus(this);
+      } catch (RuntimeException re) {
+        // Have heard RuntimeException reported in Android 4.0.x+; continue?
+        Log.w(TAG, "Unexpected exception while focusing", re);
+      }
+    }
+  }
+
+  synchronized void stop() {
+    if (useAutoFocus) {
+      try {
+        camera.cancelAutoFocus();
+      } catch (RuntimeException re) {
+        // Have heard RuntimeException reported in Android 4.0.x+; continue?
+        Log.w(TAG, "Unexpected exception while cancelling focusing", re);
+      }
+    }
+    if (outstandingTask != null) {
+      outstandingTask.cancel(true);
+      outstandingTask = null;
+    }
+    active = false;
+  }
+
+  private final class AutoFocusTask extends AsyncTask<Object,Object,Object> {
+    @Override
+    protected Object doInBackground(Object... voids) {
+      try {
+        Thread.sleep(AUTO_FOCUS_INTERVAL_MS);
+      } catch (InterruptedException e) {
+        // continue
+      }
+      synchronized (AutoFocusManager.this) {
+        if (active) {
+          start();
+        }
+      }
+      return null;
+    }
+  }
+
+}

+ 306 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/camera/CameraConfigurationManager.java

@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.usai.redant.apexdrivers.camera;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.graphics.Point;
+import android.hardware.Camera;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import android.view.Display;
+import android.view.WindowManager;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A class which deals with reading, parsing, and setting the camera parameters which are used to
+ * configure the camera hardware.
+ */
+final class CameraConfigurationManager {
+
+  private static final String TAG = "CameraConfiguration";
+
+  // This is bigger than the size of a small screen, which is still supported. The routine
+  // below will still select the default (presumably 320x240) size for these. This prevents
+  // accidental selection of very low resolution on some devices.
+  private static final int MIN_PREVIEW_PIXELS = 480 * 320; // normal screen
+  //private static final float MAX_EXPOSURE_COMPENSATION = 1.5f;
+  //private static final float MIN_EXPOSURE_COMPENSATION = 0.0f;
+  private static final double MAX_ASPECT_DISTORTION = 0.15;
+
+  private final Context context;
+  private Point screenResolution;
+  private Point cameraResolution;
+
+  CameraConfigurationManager(Context context) {
+    this.context = context;
+  }
+
+  /**
+   * Reads, one time, values from the camera that are needed by the app.
+   */
+  void initFromCameraParameters(Camera camera) {
+    Camera.Parameters parameters = camera.getParameters();
+    WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+    Display display = manager.getDefaultDisplay();
+    Point theScreenResolution = new Point();
+    display.getSize(theScreenResolution);
+    screenResolution = theScreenResolution;
+    Log.i(TAG, "Screen resolution: " + screenResolution);
+    cameraResolution = findBestPreviewSizeValue(parameters, screenResolution);
+    Log.i(TAG, "Camera resolution: " + cameraResolution);
+  }
+
+  void setDesiredCameraParameters(Camera camera, boolean safeMode) {
+    Camera.Parameters parameters = camera.getParameters();
+
+    if (parameters == null) {
+      Log.w(TAG, "Device error: no camera parameters are available. Proceeding without configuration.");
+      return;
+    }
+
+    Log.i(TAG, "Initial camera parameters: " + parameters.flatten());
+
+    if (safeMode) {
+      Log.w(TAG, "In camera config safe mode -- most settings will not be honored");
+    }
+
+    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+
+    initializeTorch(parameters, prefs, safeMode);
+
+    String focusMode = null;
+    if (prefs.getBoolean(PreferencesActivity.KEY_AUTO_FOCUS, true)) {
+      if (safeMode || prefs.getBoolean(PreferencesActivity.KEY_DISABLE_CONTINUOUS_FOCUS, false)) {
+        focusMode = findSettableValue(parameters.getSupportedFocusModes(),
+                                      Camera.Parameters.FOCUS_MODE_AUTO);
+      } else {
+        focusMode = findSettableValue(parameters.getSupportedFocusModes(),
+                                      Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE,
+                                      Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO,
+                                      Camera.Parameters.FOCUS_MODE_AUTO);
+      }
+    }
+    // Maybe selected auto-focus but not available, so fall through here:
+    if (!safeMode && focusMode == null) {
+      focusMode = findSettableValue(parameters.getSupportedFocusModes(),
+                                    Camera.Parameters.FOCUS_MODE_MACRO,
+                                    Camera.Parameters.FOCUS_MODE_EDOF);
+    }
+    if (focusMode != null) {
+      parameters.setFocusMode(focusMode);
+    }
+
+    if (prefs.getBoolean(PreferencesActivity.KEY_INVERT_SCAN, false)) {
+      String colorMode = findSettableValue(parameters.getSupportedColorEffects(),
+                                           Camera.Parameters.EFFECT_NEGATIVE);
+      if (colorMode != null) {
+        parameters.setColorEffect(colorMode);
+      }
+    }
+
+    parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
+    camera.setParameters(parameters);
+
+    Camera.Parameters afterParameters = camera.getParameters();
+    Camera.Size afterSize = afterParameters.getPreviewSize();
+    if (afterSize!= null && (cameraResolution.x != afterSize.width || cameraResolution.y != afterSize.height)) {
+      Log.w(TAG, "Camera said it supported preview size " + cameraResolution.x + 'x' + cameraResolution.y +
+                 ", but after setting it, preview size is " + afterSize.width + 'x' + afterSize.height);
+      cameraResolution.x = afterSize.width;
+      cameraResolution.y = afterSize.height;
+    }
+  }
+
+  Point getCameraResolution() {
+    return cameraResolution;
+  }
+
+  Point getScreenResolution() {
+    return screenResolution;
+  }
+
+  boolean getTorchState(Camera camera) {
+    if (camera != null) {
+      Camera.Parameters parameters = camera.getParameters();
+      if (parameters != null) {
+        String flashMode = camera.getParameters().getFlashMode();
+        return flashMode != null &&
+            (Camera.Parameters.FLASH_MODE_ON.equals(flashMode) ||
+             Camera.Parameters.FLASH_MODE_TORCH.equals(flashMode));
+      }
+    }
+    return false;
+  }
+
+  void setTorch(Camera camera, boolean newSetting) {
+    Camera.Parameters parameters = camera.getParameters();
+    doSetTorch(parameters, newSetting, false);
+    camera.setParameters(parameters);
+  }
+
+  private void initializeTorch(Camera.Parameters parameters, SharedPreferences prefs, boolean safeMode) {
+    boolean currentSetting = FrontLightMode.readPref(prefs) == FrontLightMode.ON;
+    doSetTorch(parameters, currentSetting, safeMode);
+  }
+
+  private void doSetTorch(Camera.Parameters parameters, boolean newSetting, boolean safeMode) {
+    String flashMode;
+    if (newSetting) {
+      flashMode = findSettableValue(parameters.getSupportedFlashModes(),
+                                    Camera.Parameters.FLASH_MODE_TORCH,
+                                    Camera.Parameters.FLASH_MODE_ON);
+    } else {
+      flashMode = findSettableValue(parameters.getSupportedFlashModes(),
+                                    Camera.Parameters.FLASH_MODE_OFF);
+    }
+    if (flashMode != null) {
+      parameters.setFlashMode(flashMode);
+    }
+
+    /*
+    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+    if (!prefs.getBoolean(PreferencesActivity.KEY_DISABLE_EXPOSURE, false)) {
+      if (!safeMode) {
+        int minExposure = parameters.getMinExposureCompensation();
+        int maxExposure = parameters.getMaxExposureCompensation();
+        if (minExposure != 0 || maxExposure != 0) {
+          float step = parameters.getExposureCompensationStep();
+          int desiredCompensation;
+          if (newSetting) {
+            // Light on; set low exposue compensation
+            desiredCompensation = Math.max((int) (MIN_EXPOSURE_COMPENSATION / step), minExposure);
+          } else {
+            // Light off; set high compensation
+            desiredCompensation = Math.min((int) (MAX_EXPOSURE_COMPENSATION / step), maxExposure);
+          }
+          Log.i(TAG, "Setting exposure compensation to " + desiredCompensation + " / " + (step * desiredCompensation));
+          parameters.setExposureCompensation(desiredCompensation);
+        } else {
+          Log.i(TAG, "Camera does not support exposure compensation");
+        }
+      }
+    }
+     */
+  }
+
+  private Point findBestPreviewSizeValue(Camera.Parameters parameters, Point screenResolution) {
+
+    List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes();
+    if (rawSupportedSizes == null) {
+      Log.w(TAG, "Device returned no supported preview sizes; using default");
+      Camera.Size defaultSize = parameters.getPreviewSize();
+      return new Point(defaultSize.width, defaultSize.height);
+    }
+
+    // Sort by size, descending
+    List<Camera.Size> supportedPreviewSizes = new ArrayList<Camera.Size>(rawSupportedSizes);
+    Collections.sort(supportedPreviewSizes, new Comparator<Camera.Size>() {
+      @Override
+      public int compare(Camera.Size a, Camera.Size b) {
+        int aPixels = a.height * a.width;
+        int bPixels = b.height * b.width;
+        if (bPixels < aPixels) {
+          return -1;
+        }
+        if (bPixels > aPixels) {
+          return 1;
+        }
+        return 0;
+      }
+    });
+
+    if (Log.isLoggable(TAG, Log.INFO)) {
+      StringBuilder previewSizesString = new StringBuilder();
+      for (Camera.Size supportedPreviewSize : supportedPreviewSizes) {
+        previewSizesString.append(supportedPreviewSize.width).append('x')
+            .append(supportedPreviewSize.height).append(' ');
+      }
+      Log.i(TAG, "Supported preview sizes: " + previewSizesString);
+    }
+
+    double screenAspectRatio = (double) screenResolution.x / (double) screenResolution.y;
+
+    // Remove sizes that are unsuitable
+    Iterator<Camera.Size> it = supportedPreviewSizes.iterator();
+    while (it.hasNext()) {
+      Camera.Size supportedPreviewSize = it.next();
+      int realWidth = supportedPreviewSize.width;
+      int realHeight = supportedPreviewSize.height;
+      if (realWidth * realHeight < MIN_PREVIEW_PIXELS) {
+        it.remove();
+        continue;
+      }
+
+      boolean isCandidatePortrait = realWidth < realHeight;
+      int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth;
+      int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight;
+      double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight;
+      double distortion = Math.abs(aspectRatio - screenAspectRatio);
+      if (distortion > MAX_ASPECT_DISTORTION) {
+        it.remove(); 
+        continue;
+      }
+
+      if (maybeFlippedWidth == screenResolution.x && maybeFlippedHeight == screenResolution.y) {
+        Point exactPoint = new Point(realWidth, realHeight);
+        Log.i(TAG, "Found preview size exactly matching screen size: " + exactPoint);
+        return exactPoint;
+      }
+    }
+
+    // If no exact match, use largest preview size. This was not a great idea on older devices because
+    // of the additional computation needed. We're likely to get here on newer Android 4+ devices, where
+    // the CPU is much more powerful.
+    if (!supportedPreviewSizes.isEmpty()) {
+      Camera.Size largestPreview = supportedPreviewSizes.get(0);
+      Point largestSize = new Point(largestPreview.width, largestPreview.height);
+      Log.i(TAG, "Using largest suitable preview size: " + largestSize);
+      return largestSize;
+    }
+
+    // If there is nothing at all suitable, return current preview size
+    Camera.Size defaultPreview = parameters.getPreviewSize();
+    Point defaultSize = new Point(defaultPreview.width, defaultPreview.height);
+    Log.i(TAG, "No suitable preview sizes, using default: " + defaultSize);
+    return defaultSize;
+  }
+
+  private static String findSettableValue(Collection<String> supportedValues,
+                                          String... desiredValues) {
+    Log.i(TAG, "Supported values: " + supportedValues);
+    String result = null;
+    if (supportedValues != null) {
+      for (String desiredValue : desiredValues) {
+        if (supportedValues.contains(desiredValue)) {
+          result = desiredValue;
+          break;
+        }
+      }
+    }
+    Log.i(TAG, "Settable value: " + result);
+    return result;
+  }
+
+}

+ 310 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/camera/CameraManager.java

@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.usai.redant.apexdrivers.camera;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.Camera;
+import android.os.Handler;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+import com.google.zxing.PlanarYUVLuminanceSource;
+import com.usai.redant.apexdrivers.camera.open.OpenCameraInterface;
+
+
+import java.io.IOException;
+
+/**
+ * This object wraps the Camera service object and expects to be the only one talking to it. The
+ * implementation encapsulates the steps needed to take preview-sized images, which are used for
+ * both preview and decoding.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class CameraManager {
+
+  private static final String TAG = CameraManager.class.getSimpleName();
+
+  private static final int MIN_FRAME_WIDTH = 240;
+  private static final int MIN_FRAME_HEIGHT = 240;
+  private static final int MAX_FRAME_WIDTH = 1200; // = 5/8 * 1920
+  private static final int MAX_FRAME_HEIGHT = 675; // = 5/8 * 1080
+
+  private final Context context;
+  private final CameraConfigurationManager configManager;
+  private Camera camera;
+  private AutoFocusManager autoFocusManager;
+  private Rect framingRect;
+  private Rect framingRectInPreview;
+  private boolean initialized;
+  private boolean previewing;
+  private int requestedFramingRectWidth;
+  private int requestedFramingRectHeight;
+  /**
+   * Preview frames are delivered here, which we pass on to the registered handler. Make sure to
+   * clear the handler so it will only receive one message.
+   */
+  private final PreviewCallback previewCallback;
+
+  public CameraManager(Context context) {
+    this.context = context;
+    this.configManager = new CameraConfigurationManager(context);
+    previewCallback = new PreviewCallback(configManager);
+  }
+
+  /**
+   * Opens the camera driver and initializes the hardware parameters.
+   *
+   * @param holder The surface object which the camera will draw preview frames into.
+   * @throws IOException Indicates the camera driver failed to open.
+   */
+  public synchronized void openDriver(SurfaceHolder holder) throws IOException {
+    Camera theCamera = camera;
+    if (theCamera == null) {
+      theCamera = OpenCameraInterface.open();
+      if (theCamera == null) {
+        throw new IOException();
+      }
+      camera = theCamera;
+    }
+    theCamera.setPreviewDisplay(holder);
+
+    if (!initialized) {
+      initialized = true;
+      configManager.initFromCameraParameters(theCamera);
+      if (requestedFramingRectWidth > 0 && requestedFramingRectHeight > 0) {
+        setManualFramingRect(requestedFramingRectWidth, requestedFramingRectHeight);
+        requestedFramingRectWidth = 0;
+        requestedFramingRectHeight = 0;
+      }
+    }
+
+    Camera.Parameters parameters = theCamera.getParameters();
+    String parametersFlattened = parameters == null ? null : parameters.flatten(); // Save these, temporarily
+    try {
+      configManager.setDesiredCameraParameters(theCamera, false);
+    } catch (RuntimeException re) {
+      // Driver failed
+      Log.w(TAG, "Camera rejected parameters. Setting only minimal safe-mode parameters");
+      Log.i(TAG, "Resetting to saved camera params: " + parametersFlattened);
+      // Reset:
+      if (parametersFlattened != null) {
+        parameters = theCamera.getParameters();
+        parameters.unflatten(parametersFlattened);
+        try {
+          theCamera.setParameters(parameters);
+          configManager.setDesiredCameraParameters(theCamera, true);
+        } catch (RuntimeException re2) {
+          // Well, darn. Give up
+          Log.w(TAG, "Camera rejected even safe-mode parameters! No configuration");
+        }
+      }
+    }
+
+  }
+
+  public synchronized boolean isOpen() {
+    return camera != null;
+  }
+
+  /**
+   * Closes the camera driver if still in use.
+   */
+  public synchronized void closeDriver() {
+    if (camera != null) {
+      camera.release();
+      camera = null;
+      // Make sure to clear these each time we close the camera, so that any scanning rect
+      // requested by intent is forgotten.
+      framingRect = null;
+      framingRectInPreview = null;
+    }
+  }
+
+  /**
+   * Asks the camera hardware to begin drawing preview frames to the screen.
+   */
+  public synchronized void startPreview() {
+    Camera theCamera = camera;
+    if (theCamera != null && !previewing) {
+      theCamera.startPreview();
+      previewing = true;
+      autoFocusManager = new AutoFocusManager(context, camera);
+    }
+  }
+
+  /**
+   * Tells the camera to stop drawing preview frames.
+   */
+  public synchronized void stopPreview() {
+    if (autoFocusManager != null) {
+      autoFocusManager.stop();
+      autoFocusManager = null;
+    }
+    if (camera != null && previewing) {
+      camera.stopPreview();
+      previewCallback.setHandler(null, 0);
+      previewing = false;
+    }
+  }
+
+
+  public synchronized void setTorch(boolean newSetting) {
+    if (newSetting != configManager.getTorchState(camera)) {
+      if (camera != null) {
+        if (autoFocusManager != null) {
+          autoFocusManager.stop();
+        }
+        configManager.setTorch(camera, newSetting);
+        if (autoFocusManager != null) {
+          autoFocusManager.start();
+        }
+      }
+    }
+  }
+
+  /**
+   * A single preview frame will be returned to the handler supplied. The data will arrive as byte[]
+   * in the message.obj field, with width and height encoded as message.arg1 and message.arg2,
+   * respectively.
+   *
+   * @param handler The handler to send the message to.
+   * @param message The what field of the message to be sent.
+   */
+  public synchronized void requestPreviewFrame(Handler handler, int message) {
+    Camera theCamera = camera;
+    if (theCamera != null && previewing) {
+      previewCallback.setHandler(handler, message);
+      theCamera.setOneShotPreviewCallback(previewCallback);
+    }
+  }
+
+  /**
+   * Calculates the framing rect which the UI should draw to show the user where to place the
+   * barcode. This target helps with alignment as well as forces the user to hold the device
+   * far enough away to ensure the image will be in focus.
+   *
+   * @return The rectangle to draw on screen in window coordinates.
+   */
+  public synchronized Rect getFramingRect() {
+    if (framingRect == null) {
+      if (camera == null) {
+        return null;
+      }
+      Point screenResolution = configManager.getScreenResolution();
+      if (screenResolution == null) {
+        // Called early, before init even finished
+        return null;
+      }
+
+      int width = findDesiredDimensionInRange(screenResolution.x, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH);
+      int height = findDesiredDimensionInRange(screenResolution.y, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT);
+
+      int leftOffset = (screenResolution.x - width) / 2;
+      int topOffset = (screenResolution.y - height) / 2;
+      framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);
+      Log.d(TAG, "Calculated framing rect: " + framingRect);
+    }
+    return framingRect;
+  }
+  
+  private static int findDesiredDimensionInRange(int resolution, int hardMin, int hardMax) {
+    int dim = 5 * resolution / 8; // Target 5/8 of each dimension
+    if (dim < hardMin) {
+      return hardMin;
+    }
+    if (dim > hardMax) {
+      return hardMax;
+    }
+    return dim;
+  }
+
+  /**
+   * Like {@link #getFramingRect} but coordinates are in terms of the preview frame,
+   * not UI / screen.
+   */
+  public synchronized Rect getFramingRectInPreview() {
+    if (framingRectInPreview == null) {
+      Rect framingRect = getFramingRect();
+      if (framingRect == null) {
+        return null;
+      }
+      Rect rect = new Rect(framingRect);
+      Point cameraResolution = configManager.getCameraResolution();
+      Point screenResolution = configManager.getScreenResolution();
+      if (cameraResolution == null || screenResolution == null) {
+        // Called early, before init even finished
+        return null;
+      }
+      rect.left = rect.left * cameraResolution.x / screenResolution.x;
+      rect.right = rect.right * cameraResolution.x / screenResolution.x;
+      rect.top = rect.top * cameraResolution.y / screenResolution.y;
+      rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;
+      framingRectInPreview = rect;
+    }
+    return framingRectInPreview;
+  }
+
+  /**
+   * Allows third party apps to specify the scanning rectangle dimensions, rather than determine
+   * them automatically based on screen resolution.
+   *
+   * @param width The width in pixels to scan.
+   * @param height The height in pixels to scan.
+   */
+  public synchronized void setManualFramingRect(int width, int height) {
+    if (initialized) {
+      Point screenResolution = configManager.getScreenResolution();
+      if (width > screenResolution.x) {
+        width = screenResolution.x;
+      }
+      if (height > screenResolution.y) {
+        height = screenResolution.y;
+      }
+      int leftOffset = (screenResolution.x - width) / 2;
+      int topOffset = (screenResolution.y - height) / 2;
+      framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);
+      Log.d(TAG, "Calculated manual framing rect: " + framingRect);
+      framingRectInPreview = null;
+    } else {
+      requestedFramingRectWidth = width;
+      requestedFramingRectHeight = height;
+    }
+  }
+
+  /**
+   * A factory method to build the appropriate LuminanceSource object based on the format
+   * of the preview buffers, as described by Camera.Parameters.
+   *
+   * @param data A preview frame.
+   * @param width The width of the image.
+   * @param height The height of the image.
+   * @return A PlanarYUVLuminanceSource instance.
+   */
+  public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {
+    Rect rect = getFramingRectInPreview();
+    if (rect == null) {
+      return null;
+    }
+    // Go ahead and assume it's YUV rather than die.
+    return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,
+                                        rect.width(), rect.height(), false);
+  }
+
+}

+ 43 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/camera/FrontLightMode.java

@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.usai.redant.apexdrivers.camera;
+
+import android.content.SharedPreferences;
+
+import com.usai.redant.apexdrivers.camera.PreferencesActivity;
+
+/**
+ * Enumerates settings of the prefernce controlling the front light.
+ */
+public enum FrontLightMode {
+
+  /** Always on. */
+  ON,
+  /** On only when ambient light is low. */
+  AUTO,
+  /** Always off. */
+  OFF;
+
+  private static FrontLightMode parse(String modeString) {
+    return modeString == null ? OFF : valueOf(modeString);
+  }
+
+  public static FrontLightMode readPref(SharedPreferences sharedPrefs) {
+    return parse(sharedPrefs.getString(PreferencesActivity.KEY_FRONT_LIGHT_MODE, null));
+  }
+
+}

+ 56 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/camera/PreferencesActivity.java

@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.usai.redant.apexdrivers.camera;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * The main settings activity.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ * @author Sean Owen
+ */
+public final class PreferencesActivity extends Activity {
+
+  public static final String KEY_DECODE_1D = "preferences_decode_1D";
+  public static final String KEY_DECODE_QR = "preferences_decode_QR";
+  public static final String KEY_DECODE_DATA_MATRIX = "preferences_decode_Data_Matrix";
+  public static final String KEY_CUSTOM_PRODUCT_SEARCH = "preferences_custom_product_search";
+
+  public static final String KEY_PLAY_BEEP = "preferences_play_beep";
+  public static final String KEY_VIBRATE = "preferences_vibrate";
+  public static final String KEY_COPY_TO_CLIPBOARD = "preferences_copy_to_clipboard";
+  public static final String KEY_FRONT_LIGHT_MODE = "preferences_front_light_mode";
+  public static final String KEY_BULK_MODE = "preferences_bulk_mode";
+  public static final String KEY_REMEMBER_DUPLICATES = "preferences_remember_duplicates";
+  public static final String KEY_SUPPLEMENTAL = "preferences_supplemental";
+  public static final String KEY_AUTO_FOCUS = "preferences_auto_focus";
+  public static final String KEY_INVERT_SCAN = "preferences_invert_scan";
+  public static final String KEY_SEARCH_COUNTRY = "preferences_search_country";
+
+  public static final String KEY_DISABLE_CONTINUOUS_FOCUS = "preferences_disable_continuous_focus";
+  //public static final String KEY_DISABLE_EXPOSURE = "preferences_disable_exposure";
+
+  @Override
+  protected void onCreate(Bundle icicle) {
+    super.onCreate(icicle);
+    getFragmentManager().beginTransaction().replace(android.R.id.content, new PreferencesFragment()).commit();
+
+  }
+
+}

+ 73 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/camera/PreferencesFragment.java

@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.usai.redant.apexdrivers.camera;
+
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceScreen;
+
+import com.usai.redant.apexdrivers.R;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public final class PreferencesFragment 
+    extends PreferenceFragment
+    implements SharedPreferences.OnSharedPreferenceChangeListener {
+  
+  private CheckBoxPreference decode1D;
+  private CheckBoxPreference decodeQR;
+  private CheckBoxPreference decodeDataMatrix;
+  
+  @Override
+  public void onCreate(Bundle icicle) {
+    super.onCreate(icicle);
+    addPreferencesFromResource(R.xml.preferences);
+    
+    PreferenceScreen preferences = getPreferenceScreen();
+    preferences.getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
+    decode1D = (CheckBoxPreference) preferences.findPreference(PreferencesActivity.KEY_DECODE_1D);
+    decodeQR = (CheckBoxPreference) preferences.findPreference(PreferencesActivity.KEY_DECODE_QR);
+    decodeDataMatrix = (CheckBoxPreference) preferences.findPreference(PreferencesActivity.KEY_DECODE_DATA_MATRIX);
+    disableLastCheckedPref();
+  }
+  
+  @Override
+  public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+    disableLastCheckedPref();
+  }
+
+  private void disableLastCheckedPref() {
+    Collection<CheckBoxPreference> checked = new ArrayList<CheckBoxPreference>(3);
+    if (decode1D.isChecked()) {
+      checked.add(decode1D);
+    }
+    if (decodeQR.isChecked()) {
+      checked.add(decodeQR);
+    }
+    if (decodeDataMatrix.isChecked()) {
+      checked.add(decodeDataMatrix);
+    }
+    boolean disable = checked.size() < 2;
+    CheckBoxPreference[] checkBoxPreferences = {decode1D, decodeQR, decodeDataMatrix};
+    for (CheckBoxPreference pref : checkBoxPreferences) {
+      pref.setEnabled(!(disable && checked.contains(pref)));
+    }
+  }
+}

+ 56 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/camera/PreviewCallback.java

@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.usai.redant.apexdrivers.camera;
+
+import android.graphics.Point;
+import android.hardware.Camera;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+final class PreviewCallback implements Camera.PreviewCallback {
+
+  private static final String TAG = PreviewCallback.class.getSimpleName();
+
+  private final CameraConfigurationManager configManager;
+  private Handler previewHandler;
+  private int previewMessage;
+
+  PreviewCallback(CameraConfigurationManager configManager) {
+    this.configManager = configManager;
+  }
+
+  void setHandler(Handler previewHandler, int previewMessage) {
+    this.previewHandler = previewHandler;
+    this.previewMessage = previewMessage;
+  }
+
+  @Override
+  public void onPreviewFrame(byte[] data, Camera camera) {
+    Point cameraResolution = configManager.getCameraResolution();
+    Handler thePreviewHandler = previewHandler;
+    if (cameraResolution != null && thePreviewHandler != null) {
+      Message message = thePreviewHandler.obtainMessage(previewMessage, cameraResolution.x,
+          cameraResolution.y, data);
+      message.sendToTarget();
+      previewHandler = null;
+    } else {
+      Log.d(TAG, "Got preview callback, but no handler or resolution available");
+    }
+  }
+
+}

+ 62 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/camera/open/OpenCameraInterface.java

@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.usai.redant.apexdrivers.camera.open;
+
+import android.hardware.Camera;
+import android.util.Log;
+
+public final class OpenCameraInterface {
+
+  private static final String TAG = OpenCameraInterface.class.getName();
+
+  private OpenCameraInterface() {
+  }
+
+  /**
+   * Opens a rear-facing camera with {@link Camera#open(int)}, if one exists, or opens camera 0.
+   */
+  public static Camera open() {
+    
+    int numCameras = Camera.getNumberOfCameras();
+    if (numCameras == 0) {
+      Log.w(TAG, "No cameras!");
+      return null;
+    }
+
+    int index = 0;
+    while (index < numCameras) {
+      Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
+      Camera.getCameraInfo(index, cameraInfo);
+      if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
+        break;
+      }
+      index++;
+    }
+    
+    Camera camera;
+    if (index < numCameras) {
+      Log.i(TAG, "Opening camera #" + index);
+      camera = Camera.open(index);
+    } else {
+      Log.i(TAG, "No camera facing back; returning camera #0");
+      camera = Camera.open(0);
+    }
+
+    return camera;
+  }
+
+}

+ 87 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/AmbientLightManager.java

@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.usai.redant.apexdrivers.codescanner;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.preference.PreferenceManager;
+
+import com.usai.redant.apexdrivers.camera.CameraManager;
+import com.usai.redant.apexdrivers.camera.FrontLightMode;
+
+/**
+ * Detects ambient light and switches on the front light when very dark, and off again when sufficiently light.
+ *
+ * @author Sean Owen
+ * @author Nikolaus Huber
+ */
+final class AmbientLightManager implements SensorEventListener {
+
+  private static final float TOO_DARK_LUX = 45.0f;
+  private static final float BRIGHT_ENOUGH_LUX = 450.0f;
+
+  private final Context context;
+  private CameraManager cameraManager;
+  private Sensor lightSensor;
+
+  AmbientLightManager(Context context) {
+    this.context = context;
+  }
+
+  void start(CameraManager cameraManager) {
+    this.cameraManager = cameraManager;
+    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
+    if (FrontLightMode.readPref(sharedPrefs) == FrontLightMode.AUTO) {
+      SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+      lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
+      if (lightSensor != null) {
+        sensorManager.registerListener(this, lightSensor, SensorManager.SENSOR_DELAY_NORMAL);
+      }
+    }
+  }
+
+  void stop() {
+    if (lightSensor != null) {
+      SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+      sensorManager.unregisterListener(this);
+      cameraManager = null;
+      lightSensor = null;
+    }
+  }
+
+  @Override
+  public void onSensorChanged(SensorEvent sensorEvent) {
+    float ambientLightLux = sensorEvent.values[0];
+    if (cameraManager != null) {
+      if (ambientLightLux <= TOO_DARK_LUX) {
+        cameraManager.setTorch(true);
+      } else if (ambientLightLux >= BRIGHT_ENOUGH_LUX) {
+        cameraManager.setTorch(false);
+      }
+    }
+  }
+
+  @Override
+  public void onAccuracyChanged(Sensor sensor, int accuracy) {
+    // do nothing
+  }
+
+}

+ 131 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/BeepManager.java

@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.usai.redant.apexdrivers.codescanner;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.os.Vibrator;
+import android.preference.PreferenceManager;
+import android.util.Log;
+
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.apexdrivers.camera.PreferencesActivity;
+
+import java.io.IOException;
+
+/**
+ * Manages beeps and vibrations for {@link CaptureActivity}.
+ */
+final class BeepManager implements MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener {
+
+  private static final String TAG = BeepManager.class.getSimpleName();
+
+  private static final float BEEP_VOLUME = 0.10f;
+  private static final long VIBRATE_DURATION = 200L;
+
+  private final Activity activity;
+  private MediaPlayer mediaPlayer;
+  private boolean playBeep;
+  private boolean vibrate;
+
+  BeepManager(Activity activity) {
+    this.activity = activity;
+    this.mediaPlayer = null;
+    updatePrefs();
+  }
+
+  synchronized void updatePrefs() {
+    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
+    playBeep = shouldBeep(prefs, activity);
+    vibrate = prefs.getBoolean(PreferencesActivity.KEY_VIBRATE, false);
+    if (playBeep && mediaPlayer == null) {
+      // The volume on STREAM_SYSTEM is not adjustable, and users found it too loud,
+      // so we now play on the music stream.
+      activity.setVolumeControlStream(AudioManager.STREAM_MUSIC);
+      mediaPlayer = buildMediaPlayer(activity);
+    }
+  }
+
+  synchronized void playBeepSoundAndVibrate() {
+	  Log.i(TAG,"playBeep=========>"+playBeep);
+	  Log.i(TAG,"mediaPlayer=========>"+mediaPlayer==null?"null":"ok");
+	  Log.i(TAG,"vibrate=========>"+vibrate);
+    if (playBeep && mediaPlayer != null) {
+      mediaPlayer.start();
+    }
+    if (vibrate) {
+      Vibrator vibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE);
+      vibrator.vibrate(VIBRATE_DURATION);
+    }
+  }
+
+  private static boolean shouldBeep(SharedPreferences prefs, Context activity) {
+    boolean shouldPlayBeep = prefs.getBoolean(PreferencesActivity.KEY_PLAY_BEEP, true);
+    if (shouldPlayBeep) {
+      // See if sound settings overrides this
+      AudioManager audioService = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
+      if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
+        shouldPlayBeep = false;
+      }
+    }
+    return shouldPlayBeep;
+  }
+
+  private MediaPlayer buildMediaPlayer(Context activity) {
+    MediaPlayer mediaPlayer = new MediaPlayer();
+    mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+    mediaPlayer.setOnCompletionListener(this);
+    mediaPlayer.setOnErrorListener(this);
+
+    AssetFileDescriptor file = activity.getResources().openRawResourceFd(R.raw.beep);
+    try {
+      mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength());
+      file.close();
+      mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);
+      mediaPlayer.prepare();
+    } catch (IOException ioe) {
+      Log.w(TAG, ioe);
+      mediaPlayer = null;
+    }
+    return mediaPlayer;
+  }
+
+  @Override
+  public void onCompletion(MediaPlayer mp) {
+    // When the beep has finished playing, rewind to queue up another one.      
+    mp.seekTo(0);
+  }
+
+  @Override
+  public synchronized boolean onError(MediaPlayer mp, int what, int extra) {
+    if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
+      // we are finished, so put up an appropriate error toast if required and finish
+      activity.finish();
+    } else {
+      // possibly media player error, so release and recreate
+      mp.release();
+      mediaPlayer = null;
+      updatePrefs();
+    }
+    return true;
+  }
+
+}

+ 884 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/CaptureActivity.java

@@ -0,0 +1,884 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.usai.redant.apexdrivers.codescanner;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.Result;
+import com.google.zxing.ResultPoint;
+import com.google.zxing.client.result.ResultParser;
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.apexdrivers.camera.CameraManager;
+import com.usai.redant.rautils.utils.dbgUtil;
+
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+
+//import android.content.SharedPreferences;
+
+/**
+ * This activity opens the camera and does the actual scanning on a background
+ * thread. It draws a viewfinder to help the user place the barcode correctly,
+ * shows feedback as the image processing is happening, and then overlays the
+ * results when a scan is successful.
+ * 
+ * @author dswitkin@google.com (Daniel Switkin)
+ * @author Sean Owen
+ */
+public final class CaptureActivity extends Activity implements
+		SurfaceHolder.Callback
+{
+
+	private static final String TAG	= CaptureActivity.class
+													.getSimpleName();
+
+	// private static final long DEFAULT_INTENT_RESULT_DURATION_MS = 1500L;
+	// private static final long BULK_MODE_SCAN_DELAY_MS = 1000L;
+
+	// private static final String[] ZXING_URLS = {
+	// "http://zxing.appspot.com/scan", "zxing://scan/" };
+
+	// public static final int HISTORY_REQUEST_CODE = 0x0000bacc;
+
+	// private static final Collection<ResultMetadataType>
+	// DISPLAYABLE_METADATA_TYPES =
+	// EnumSet.of(ResultMetadataType.ISSUE_NUMBER,
+	// ResultMetadataType.SUGGESTED_PRICE,
+	// ResultMetadataType.ERROR_CORRECTION_LEVEL,
+	// ResultMetadataType.POSSIBLE_COUNTRY);
+
+	private CameraManager				cameraManager;
+	private CaptureActivityHandler		handler;
+	private Result						savedResultToShow;
+	private ViewfinderView				viewfinderView;
+	private TextView statusView;
+	// private View resultView;
+	private Result						lastResult;
+	private boolean						hasSurface;
+	// private boolean copyToClipboard;
+	// private IntentSource source;
+	// private String sourceUrl;
+	// private ScanFromWebPageManager scanFromWebPageManager;
+	private Collection<BarcodeFormat> decodeFormats;
+	private Map<DecodeHintType, ?> decodeHints;
+	private String characterSet;
+	// private HistoryManager historyManager;
+	private InactivityTimer				inactivityTimer;
+	private BeepManager					beepManager;
+	private AmbientLightManager			ambientLightManager;
+
+
+
+	ViewfinderView getViewfinderView()
+	{
+		return viewfinderView;
+	}
+
+	public Handler getHandler()
+	{
+		return handler;
+	}
+
+	CameraManager getCameraManager()
+	{
+		return cameraManager;
+	}
+
+	Button swith_button;
+
+	@Override
+	public void onCreate(Bundle icicle)
+	{
+		dbgUtil.Logd(TAG, "==============>CaptureActivity created!");
+
+		super.onCreate(icicle);
+
+		Window window = getWindow();
+		window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+
+		setContentView(R.layout.capture);
+
+		swith_button = (Button) findViewById(R.id.btn_switch);
+		swith_button.setTag("false");
+		swith_button.setBackgroundColor(getResources().getColor(
+				R.color.message_fail));
+		swith_button.setOnClickListener(new OnClickListener()
+		{
+
+			@Override
+			public void onClick(View v)
+			{
+
+				// inactivityTimer.onPause();
+
+				if (Boolean.parseBoolean(v.getTag().toString()) == false)
+				{
+					// inactivityTimer.onResume();
+					v.setTag("true");
+					v.setBackgroundColor(getResources().getColor(
+							R.color.message_success));
+				}
+				else
+				{
+
+					// inactivityTimer.onPause();
+					v.setTag("false");
+					v.setBackgroundColor(getResources().getColor(
+							R.color.message_fail));
+				}
+
+			}
+		});
+
+		// setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+		hasSurface = false;
+		// historyManager = new HistoryManager(this);
+		// historyManager.trimHistory();
+		inactivityTimer = new InactivityTimer(this);
+		beepManager = new BeepManager(this);
+		ambientLightManager = new AmbientLightManager(this);
+
+		// PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
+
+
+		Log.d(TAG, "finish oncreate");
+	}
+	
+	@Override
+	protected void onResume()
+	{
+		super.onResume();
+
+		// CameraManager must be initialized here, not in onCreate(). This is
+		// necessary because we don't
+		// want to open the camera driver and measure the screen size if we're
+		// going to show the help on
+		// first launch. That led to bugs where the scanning rectangle was the
+		// wrong size and partially
+		// off screen.
+		cameraManager = new CameraManager(getApplication());
+
+		viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
+		viewfinderView.setCameraManager(cameraManager);
+
+		// resultView = findViewById(R.id.result_view);
+		statusView = (TextView) findViewById(R.id.status_view);
+
+		handler = null;
+		lastResult = null;
+
+		resetStatusView();
+
+		SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
+		SurfaceHolder surfaceHolder = surfaceView.getHolder();
+		if (hasSurface)
+		{
+			// The activity was paused but not stopped, so the surface still
+			// exists. Therefore
+			// surfaceCreated() won't be called, so init the camera here.
+			initCamera(surfaceHolder);
+		}
+		else
+		{
+			// Install the callback and wait for surfaceCreated() to init the
+			// camera.
+			surfaceHolder.addCallback(this);
+		}
+
+		beepManager.updatePrefs();
+		ambientLightManager.start(cameraManager);
+
+		inactivityTimer.onResume();
+
+		Intent intent = getIntent();
+
+		// SharedPreferences prefs =
+		// PreferenceManager.getDefaultSharedPreferences(this);
+		// copyToClipboard =
+		// prefs.getBoolean(PreferencesActivity.KEY_COPY_TO_CLIPBOARD, true)
+		// && (intent == null ||
+		// intent.getBooleanExtra(Intents.Scan.SAVE_HISTORY, true));
+
+		// source = IntentSource.NONE;
+		decodeFormats = null;
+		characterSet = null;
+
+		if (intent != null)
+		{
+
+			String action = intent.getAction();
+			// String dataString = intent.getDataString();
+
+			if (Intents.Scan.ACTION.equals(action))
+			{
+
+				// Scan the formats the intent requested, and return the result
+				// to the calling activity.
+				// source = IntentSource.NATIVE_APP_INTENT;
+				decodeFormats = DecodeFormatManager.parseDecodeFormats(intent);
+				decodeHints = DecodeHintManager.parseDecodeHints(intent);
+
+				if (intent.hasExtra(Intents.Scan.WIDTH)
+						&& intent.hasExtra(Intents.Scan.HEIGHT))
+				{
+					int width = intent.getIntExtra(Intents.Scan.WIDTH, 0);
+					int height = intent.getIntExtra(Intents.Scan.HEIGHT, 0);
+					if (width > 0 && height > 0)
+					{
+						cameraManager.setManualFramingRect(width, height);
+					}
+				}
+
+				String customPromptMessage = intent
+						.getStringExtra(Intents.Scan.PROMPT_MESSAGE);
+				if (customPromptMessage != null)
+				{
+					statusView.setText(customPromptMessage);
+				}
+
+			}
+			// else if (dataString != null &&
+			// dataString.contains("http://www.google") &&
+			// dataString.contains("/m/products/scan")) {
+			//
+			// // Scan only products and send the result to mobile Product
+			// Search.
+			// // source = IntentSource.PRODUCT_SEARCH_LINK;
+			// sourceUrl = dataString;
+			// decodeFormats = DecodeFormatManager.PRODUCT_FORMATS;
+			//
+			// }
+			// else if (isZXingURL(dataString)) {
+			//
+			// // Scan formats requested in query string (all formats if none
+			// specified).
+			// // If a return URL is specified, send the results there.
+			// Otherwise, handle it ourselves.
+			// source = IntentSource.ZXING_LINK;
+			// sourceUrl = dataString;
+			// Uri inputUri = Uri.parse(dataString);
+			// scanFromWebPageManager = new ScanFromWebPageManager(inputUri);
+			// decodeFormats = DecodeFormatManager.parseDecodeFormats(inputUri);
+			// // Allow a sub-set of the hints to be specified by the caller.
+			// decodeHints = DecodeHintManager.parseDecodeHints(inputUri);
+			//
+			// }
+
+			characterSet = intent.getStringExtra(Intents.Scan.CHARACTER_SET);
+
+		}
+	}
+
+	// private static boolean isZXingURL(String dataString) {
+	// if (dataString == null) {
+	// return false;
+	// }
+	// for (String url : ZXING_URLS) {
+	// if (dataString.startsWith(url)) {
+	// return true;
+	// }
+	// }
+	// return false;
+	// }
+
+	@Override
+	protected void onPause()
+	{
+		if (handler != null)
+		{
+			handler.quitSynchronously();
+			handler = null;
+		}
+		inactivityTimer.onPause();
+		ambientLightManager.stop();
+		cameraManager.closeDriver();
+		if (!hasSurface)
+		{
+			SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
+			SurfaceHolder surfaceHolder = surfaceView.getHolder();
+			surfaceHolder.removeCallback(this);
+		}
+		super.onPause();
+	}
+
+	@Override
+	protected void onDestroy()
+	{
+		inactivityTimer.shutdown();
+		super.onDestroy();
+	}
+
+	@Override
+	public boolean onKeyDown(int keyCode, KeyEvent event)
+	{
+		switch (keyCode)
+		{
+		// case KeyEvent.KEYCODE_BACK:
+		// if (source == IntentSource.NATIVE_APP_INTENT) {
+		// setResult(RESULT_CANCELED);
+		// finish();
+		// return true;
+		// }
+		// if ((source == IntentSource.NONE || source ==
+		// IntentSource.ZXING_LINK) && lastResult != null) {
+		// restartPreviewAfterDelay(0L);
+		// return true;
+		// }
+		// break;
+			case KeyEvent.KEYCODE_FOCUS:
+			case KeyEvent.KEYCODE_CAMERA:
+				// Handle these events so they don't launch the Camera app
+				return true;
+				// Use volume up/down to turn on light
+			case KeyEvent.KEYCODE_VOLUME_DOWN:
+				cameraManager.setTorch(false);
+				return true;
+			case KeyEvent.KEYCODE_VOLUME_UP:
+				cameraManager.setTorch(true);
+				return true;
+		}
+		return super.onKeyDown(keyCode, event);
+	}
+
+//	@Override
+//	public boolean onCreateOptionsMenu(Menu menu)
+//	{
+//		MenuInflater menuInflater = getMenuInflater();
+//		menuInflater.inflate(R.menu.capture, menu);
+//		return super.onCreateOptionsMenu(menu);
+//	}
+//
+//	@Override
+//	public boolean onOptionsItemSelected(MenuItem item)
+//	{
+//		Intent intent = new Intent(Intent.ACTION_VIEW);
+//		intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+//		switch (item.getItemId())
+//		{
+//		// case R.id.menu_share:
+//		// intent.setClassName(this, ShareActivity.class.getName());
+//		// startActivity(intent);
+//		// break;
+//		// case R.id.menu_history:
+//		// intent.setClassName(this, HistoryActivity.class.getName());
+//		// startActivityForResult(intent, HISTORY_REQUEST_CODE);
+//		// break;
+//			case R.id.menu_settings:
+//				intent.setClassName(this, PreferencesActivity.class.getName());
+//				startActivity(intent);
+//				break;
+//			// case R.id.menu_help:
+//			// intent.setClassName(this, HelpActivity.class.getName());
+//			// startActivity(intent);
+//			// break;
+//			default:
+//				return super.onOptionsItemSelected(item);
+//		}
+//		return true;
+//	}
+
+	// @Override
+	// public void onActivityResult(int requestCode, int resultCode, Intent
+	// intent) {
+	// if (resultCode == RESULT_OK) {
+	// // if (requestCode == HISTORY_REQUEST_CODE)
+	// // {
+	// // int itemNumber = intent.getIntExtra(Intents.History.ITEM_NUMBER, -1);
+	// // if (itemNumber >= 0) {
+	// // HistoryItem historyItem = historyManager.buildHistoryItem(itemNumber);
+	// // decodeOrStoreSavedBitmap(null, historyItem.getResult());
+	// // }
+	// // }
+	// }
+	// }
+
+	private void decodeOrStoreSavedBitmap(Bitmap bitmap, Result result)
+	{
+		// Bitmap isn't used yet -- will be used soon
+		if (handler == null)
+		{
+			savedResultToShow = result;
+		}
+		else
+		{
+			if (result != null)
+			{
+				savedResultToShow = result;
+			}
+			if (savedResultToShow != null)
+			{
+				Message message = Message.obtain(handler,
+						R.id.decode_succeeded, savedResultToShow);
+				handler.sendMessage(message);
+			}
+			savedResultToShow = null;
+		}
+	}
+
+	@Override
+	public void surfaceCreated(SurfaceHolder holder)
+	{
+		if (holder == null)
+		{
+			Log.d(TAG,
+					"*** WARNING *** surfaceCreated() gave us a null surface!");
+		}
+		if (!hasSurface)
+		{
+			hasSurface = true;
+			initCamera(holder);
+		}
+	}
+
+	@Override
+	public void surfaceDestroyed(SurfaceHolder holder)
+	{
+		hasSurface = false;
+	}
+
+	@Override
+	public void surfaceChanged(SurfaceHolder holder, int format, int width,
+                               int height)
+	{
+
+	}
+
+	/**
+	 * A valid barcode has been found, so give an indication of success and show
+	 * the results.
+	 * 
+	 * @param rawResult
+	 *            The contents of the barcode.
+	 * @param scaleFactor
+	 *            amount by which thumbnail was scaled
+	 * @param barcode
+	 *            A greyscale bitmap of the camera data which was decoded.
+	 */
+	public void handleDecode(Result rawResult, Bitmap barcode, float scaleFactor)
+	{
+		String tag = swith_button.getTag().toString();
+		if (Boolean.parseBoolean(tag) == false)
+		{
+			restartPreviewAfterDelay(0L);
+			return;
+		}
+		inactivityTimer.onActivity();
+		lastResult = rawResult;
+		TextView debugTextView = (TextView) findViewById(R.id.textView1);
+		debugTextView.setText(rawResult.getText());
+		// ResultHandler resultHandler =
+		// ResultHandlerFactory.makeResultHandler(this, rawResult);
+		Log.i(TAG, "RESULT=========>" + rawResult.getText().toString());
+		// ParsedResult result = parseResult(rawResult);
+		Log.i(TAG, "HANDLER========>"
+				+ ResultParser.parseResult(rawResult).getType().toString());
+		boolean fromLiveScan = barcode != null;
+		if (fromLiveScan)
+		{
+			// historyManager.addHistoryItem(rawResult, resultHandler);
+			// Then not from history, so beep/vibrate and we have an image to
+			// draw on
+			beepManager.playBeepSoundAndVibrate();
+			// drawResultPoints(barcode, scaleFactor, rawResult);
+		}
+
+		Intent result = new Intent();
+		result.putExtra("pid", rawResult.getText());
+		setResult(Activity.RESULT_OK, result);
+		finish();
+		// restartPreviewAfterDelay(1000l);
+		// finish();
+		//
+		// switch (source) {
+		// case NATIVE_APP_INTENT:
+		// case PRODUCT_SEARCH_LINK:
+		// handleDecodeExternally(rawResult, resultHandler, barcode);
+		// break;
+		// case ZXING_LINK:
+		// if (scanFromWebPageManager == null ||
+		// !scanFromWebPageManager.isScanFromWebPage()) {
+		// handleDecodeInternally(rawResult, resultHandler, barcode);
+		// } else {
+		// handleDecodeExternally(rawResult, resultHandler, barcode);
+		// }
+		// break;
+		// case NONE:
+		// SharedPreferences prefs =
+		// PreferenceManager.getDefaultSharedPreferences(this);
+		// if (fromLiveScan &&
+		// prefs.getBoolean(PreferencesActivity.KEY_BULK_MODE, false)) {
+		// Toast.makeText(getApplicationContext(),
+		// getResources().getString(R.string.msg_bulk_mode_scanned) + " (" +
+		// rawResult.getText() + ')',
+		// Toast.LENGTH_SHORT).show();
+		// // Wait a moment or else it will scan the same barcode continuously
+		// about 3 times
+		// restartPreviewAfterDelay(BULK_MODE_SCAN_DELAY_MS);
+		// } else {
+		// handleDecodeInternally(rawResult, resultHandler, barcode);
+		// }
+		// break;
+		// }
+	}
+
+	/**
+	 * Superimpose a line for 1D or dots for 2D to highlight the key features of
+	 * the barcode.
+	 *
+	 * @param scaleFactor
+	 *            amount by which thumbnail was scaled
+	 */
+	// private void drawResultPoints(Bitmap barcode, float scaleFactor,
+	// Result rawResult)
+	// {
+	// ResultPoint[] points = rawResult.getResultPoints();
+	// if (points != null && points.length > 0)
+	// {
+	// Canvas canvas = new Canvas(barcode);
+	// Paint paint = new Paint();
+	// paint.setColor(getResources().getColor(R.color.result_points));
+	// if (points.length == 2)
+	// {
+	// paint.setStrokeWidth(4.0f);
+	// drawLine(canvas, paint, points[0], points[1], scaleFactor);
+	// }
+	// else if (points.length == 4
+	// && (rawResult.getBarcodeFormat() == BarcodeFormat.UPC_A || rawResult
+	// .getBarcodeFormat() == BarcodeFormat.EAN_13))
+	// {
+	// // Hacky special case -- draw two lines, for the barcode and
+	// // metadata
+	// drawLine(canvas, paint, points[0], points[1], scaleFactor);
+	// drawLine(canvas, paint, points[2], points[3], scaleFactor);
+	// }
+	// else
+	// {
+	// paint.setStrokeWidth(10.0f);
+	// for (ResultPoint point : points)
+	// {
+	// if (point != null)
+	// {
+	// canvas.drawPoint(scaleFactor * point.getX(),
+	// scaleFactor * point.getY(), paint);
+	// }
+	// }
+	// }
+	// }
+	// }
+
+	private static void drawLine(Canvas canvas, Paint paint, ResultPoint a,
+                                 ResultPoint b, float scaleFactor)
+	{
+		if (a != null && b != null)
+		{
+			canvas.drawLine(scaleFactor * a.getX(), scaleFactor * a.getY(),
+					scaleFactor * b.getX(), scaleFactor * b.getY(), paint);
+		}
+	}
+
+	// Put up our own UI for how to handle the decoded contents.
+	// private void handleDecodeInternally(Result rawResult, ResultHandler
+	// resultHandler, Bitmap barcode) {
+	// statusView.setVisibility(View.GONE);
+	// viewfinderView.setVisibility(View.GONE);
+	// // resultView.setVisibility(View.VISIBLE);
+	// Log.i(TAG, "RESULT=========>"+rawResult.getText().toString());
+	// Log.i(TAG, "HANDLER========>"+resultHandler.getType().toString());
+
+	// ImageView barcodeImageView = (ImageView)
+	// findViewById(R.id.barcode_image_view);
+	// if (barcode == null) {
+	// barcodeImageView.setImageBitmap(BitmapFactory.decodeResource(getResources(),
+	// R.drawable.ic_launcher));
+	// } else {
+	// barcodeImageView.setImageBitmap(barcode);
+	// }
+	//
+	// TextView formatTextView = (TextView) findViewById(R.id.format_text_view);
+	// formatTextView.setText(rawResult.getBarcodeFormat().toString());
+	//
+	// TextView typeTextView = (TextView) findViewById(R.id.type_text_view);
+	// typeTextView.setText(resultHandler.getType().toString());
+	//
+	// DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.SHORT,
+	// DateFormat.SHORT);
+	// TextView timeTextView = (TextView) findViewById(R.id.time_text_view);
+	// timeTextView.setText(formatter.format(new
+	// Date(rawResult.getTimestamp())));
+	//
+	//
+	// TextView metaTextView = (TextView) findViewById(R.id.meta_text_view);
+	// View metaTextViewLabel = findViewById(R.id.meta_text_view_label);
+	// metaTextView.setVisibility(View.GONE);
+	// metaTextViewLabel.setVisibility(View.GONE);
+	// Map<ResultMetadataType,Object> metadata = rawResult.getResultMetadata();
+	// if (metadata != null) {
+	// StringBuilder metadataText = new StringBuilder(20);
+	// for (Map.Entry<ResultMetadataType,Object> entry : metadata.entrySet()) {
+	// if (DISPLAYABLE_METADATA_TYPES.contains(entry.getKey())) {
+	// metadataText.append(entry.getValue()).append('\n');
+	// }
+	// }
+	// if (metadataText.length() > 0) {
+	// metadataText.setLength(metadataText.length() - 1);
+	// metaTextView.setText(metadataText);
+	// metaTextView.setVisibility(View.VISIBLE);
+	// metaTextViewLabel.setVisibility(View.VISIBLE);
+	// }
+	// }
+	//
+	// TextView contentsTextView = (TextView)
+	// findViewById(R.id.contents_text_view);
+	// CharSequence displayContents = resultHandler.getDisplayContents();
+	// contentsTextView.setText(displayContents);
+	// // Crudely scale betweeen 22 and 32 -- bigger font for shorter text
+	// int scaledSize = Math.max(22, 32 - displayContents.length() / 4);
+	// contentsTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, scaledSize);
+	//
+	// TextView supplementTextView = (TextView)
+	// findViewById(R.id.contents_supplement_text_view);
+	// supplementTextView.setText("");
+	// supplementTextView.setOnClickListener(null);
+	// // if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(
+	// // PreferencesActivity.KEY_SUPPLEMENTAL, true)) {
+	// // SupplementalInfoRetriever.maybeInvokeRetrieval(supplementTextView,
+	// // resultHandler.getResult(),
+	// // historyManager,
+	// // this);
+	// // }
+	//
+	// int buttonCount = resultHandler.getButtonCount();
+	// ViewGroup buttonView = (ViewGroup) findViewById(R.id.result_button_view);
+	// buttonView.requestFocus();
+	// for (int x = 0; x < ResultHandler.MAX_BUTTON_COUNT; x++) {
+	// TextView button = (TextView) buttonView.getChildAt(x);
+	// if (x < buttonCount) {
+	// button.setVisibility(View.VISIBLE);
+	// button.setText(resultHandler.getButtonText(x));
+	// button.setOnClickListener(new ResultButtonListener(resultHandler, x));
+	// } else {
+	// button.setVisibility(View.GONE);
+	// }
+	// }
+	//
+	// if (copyToClipboard && !resultHandler.areContentsSecure()) {
+	// ClipboardInterface.setText(displayContents, this);
+	// }
+	// }
+
+	// // Briefly show the contents of the barcode, then handle the result
+	// outside Barcode Scanner.
+	// private void handleDecodeExternally(Result rawResult, ResultHandler
+	// resultHandler, Bitmap barcode) {
+	//
+	// if (barcode != null) {
+	// viewfinderView.drawResultBitmap(barcode);
+	// }
+	//
+	// long resultDurationMS;
+	// if (getIntent() == null) {
+	// resultDurationMS = DEFAULT_INTENT_RESULT_DURATION_MS;
+	// } else {
+	// resultDurationMS =
+	// getIntent().getLongExtra(Intents.Scan.RESULT_DISPLAY_DURATION_MS,
+	// DEFAULT_INTENT_RESULT_DURATION_MS);
+	// }
+	//
+	// if (resultDurationMS > 0) {
+	// String rawResultString = String.valueOf(rawResult);
+	// if (rawResultString.length() > 32) {
+	// rawResultString = rawResultString.substring(0, 32) + " ...";
+	// }
+	// statusView.setText(getString(resultHandler.getDisplayTitle()) + " : " +
+	// rawResultString);
+	// }
+	//
+	// if (copyToClipboard && !resultHandler.areContentsSecure()) {
+	// CharSequence text = resultHandler.getDisplayContents();
+	// ClipboardInterface.setText(text, this);
+	// }
+	//
+	// if (source == IntentSource.NATIVE_APP_INTENT) {
+	//
+	// // Hand back whatever action they requested - this can be changed to
+	// Intents.Scan.ACTION when
+	// // the deprecated intent is retired.
+	// Intent intent = new Intent(getIntent().getAction());
+	// intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+	// intent.putExtra(Intents.Scan.RESULT, rawResult.toString());
+	// intent.putExtra(Intents.Scan.RESULT_FORMAT,
+	// rawResult.getBarcodeFormat().toString());
+	// byte[] rawBytes = rawResult.getRawBytes();
+	// if (rawBytes != null && rawBytes.length > 0) {
+	// intent.putExtra(Intents.Scan.RESULT_BYTES, rawBytes);
+	// }
+	// Map<ResultMetadataType,?> metadata = rawResult.getResultMetadata();
+	// if (metadata != null) {
+	// if (metadata.containsKey(ResultMetadataType.UPC_EAN_EXTENSION)) {
+	// intent.putExtra(Intents.Scan.RESULT_UPC_EAN_EXTENSION,
+	// metadata.get(ResultMetadataType.UPC_EAN_EXTENSION).toString());
+	// }
+	// Number orientation = (Number)
+	// metadata.get(ResultMetadataType.ORIENTATION);
+	// if (orientation != null) {
+	// intent.putExtra(Intents.Scan.RESULT_ORIENTATION, orientation.intValue());
+	// }
+	// String ecLevel = (String)
+	// metadata.get(ResultMetadataType.ERROR_CORRECTION_LEVEL);
+	// if (ecLevel != null) {
+	// intent.putExtra(Intents.Scan.RESULT_ERROR_CORRECTION_LEVEL, ecLevel);
+	// }
+	// @SuppressWarnings("unchecked")
+	// Iterable<byte[]> byteSegments = (Iterable<byte[]>)
+	// metadata.get(ResultMetadataType.BYTE_SEGMENTS);
+	// if (byteSegments != null) {
+	// int i = 0;
+	// for (byte[] byteSegment : byteSegments) {
+	// intent.putExtra(Intents.Scan.RESULT_BYTE_SEGMENTS_PREFIX + i,
+	// byteSegment);
+	// i++;
+	// }
+	// }
+	// }
+	// sendReplyMessage(R.id.return_scan_result, intent, resultDurationMS);
+	//
+	// } else if (source == IntentSource.PRODUCT_SEARCH_LINK) {
+	//
+	// // Reformulate the URL which triggered us into a query, so that the
+	// request goes to the same
+	// // TLD as the scan URL.
+	// int end = sourceUrl.lastIndexOf("/scan");
+	// String replyURL = sourceUrl.substring(0, end) + "?q=" +
+	// resultHandler.getDisplayContents() + "&source=zxing";
+	// sendReplyMessage(R.id.launch_product_query, replyURL, resultDurationMS);
+	//
+	// } else if (source == IntentSource.ZXING_LINK) {
+	//
+	// if (scanFromWebPageManager != null &&
+	// scanFromWebPageManager.isScanFromWebPage()) {
+	// String replyURL = scanFromWebPageManager.buildReplyURL(rawResult,
+	// resultHandler);
+	// sendReplyMessage(R.id.launch_product_query, replyURL, resultDurationMS);
+	// }
+	//
+	// }
+	// }
+
+	// private void sendReplyMessage(int id, Object arg, long delayMS) {
+	// if (handler != null) {
+	// Message message = Message.obtain(handler, id, arg);
+	// if (delayMS > 0L) {
+	// handler.sendMessageDelayed(message, delayMS);
+	// } else {
+	// handler.sendMessage(message);
+	// }
+	// }
+	// }
+
+	private void initCamera(SurfaceHolder surfaceHolder)
+	{
+		if (surfaceHolder == null)
+		{
+			throw new IllegalStateException("No SurfaceHolder provided");
+		}
+		if (cameraManager.isOpen())
+		{
+			Log.w(TAG,
+					"initCamera() while already open -- late SurfaceView callback?");
+			return;
+		}
+		try
+		{
+			cameraManager.openDriver(surfaceHolder);
+			// Creating the handler starts the preview, which can also throw a
+			// RuntimeException.
+			if (handler == null)
+			{
+				handler = new CaptureActivityHandler(this, decodeFormats,
+						decodeHints, characterSet, cameraManager);
+			}
+			decodeOrStoreSavedBitmap(null, null);
+		}
+		catch (IOException ioe)
+		{
+			Log.w(TAG, ioe);
+			displayFrameworkBugMessageAndExit();
+		}
+		catch (RuntimeException e)
+		{
+			// Barcode Scanner has seen crashes in the wild of this variety:
+			// java.?lang.?RuntimeException: Fail to connect to camera service
+			Log.w(TAG, "Unexpected error initializing camera", e);
+			displayFrameworkBugMessageAndExit();
+		}
+	}
+
+	private void displayFrameworkBugMessageAndExit()
+	{
+		AlertDialog.Builder builder = new AlertDialog.Builder(this);
+		builder.setTitle(getString(R.string.app_name));
+		builder.setMessage(getString(R.string.msg_camera_framework_bug));
+		builder.setPositiveButton(R.string.button_ok, new FinishListener(this));
+		builder.setOnCancelListener(new FinishListener(this));
+		builder.show();
+	}
+
+	public void restartPreviewAfterDelay(long delayMS)
+	{
+		if (handler != null)
+		{
+			handler.sendEmptyMessageDelayed(R.id.decode_failed, delayMS);
+		}
+		resetStatusView();
+	}
+
+	private void resetStatusView()
+	{
+		// resultView.setVisibility(View.GONE);
+		statusView.setText(R.string.msg_default_status);
+		statusView.setVisibility(View.VISIBLE);
+		viewfinderView.setVisibility(View.VISIBLE);
+		lastResult = null;
+	}
+
+	public void drawViewfinder()
+	{
+		viewfinderView.drawViewfinder();
+	}
+}

+ 168 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/CaptureActivityHandler.java

@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.usai.redant.apexdrivers.codescanner;
+
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.provider.Browser;
+import android.util.Log;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.Result;
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.apexdrivers.camera.CameraManager;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * This class handles all the messaging which comprises the state machine for capture.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class CaptureActivityHandler extends Handler {
+
+  private static final String TAG = CaptureActivityHandler.class.getSimpleName();
+
+  private final CaptureActivity activity;
+  private final DecodeThread decodeThread;
+  private State state;
+  private final CameraManager cameraManager;
+
+  private enum State {
+    PREVIEW,
+    SUCCESS,
+    DONE
+  }
+
+  CaptureActivityHandler(CaptureActivity activity,
+                         Collection<BarcodeFormat> decodeFormats,
+                         Map<DecodeHintType,?> baseHints,
+                         String characterSet,
+                         CameraManager cameraManager) {
+    this.activity = activity;
+    decodeThread = new DecodeThread(activity, decodeFormats, baseHints, characterSet,
+        new ViewfinderResultPointCallback(activity.getViewfinderView()));
+    decodeThread.start();
+    state = State.SUCCESS;
+
+    // Start ourselves capturing previews and decoding.
+    this.cameraManager = cameraManager;
+    cameraManager.startPreview();
+    restartPreviewAndDecode();
+  }
+
+  @Override
+  public void handleMessage(Message message) {
+      if (message.what == R.id.restart_preview) {
+          Log.d(TAG, "Got restart preview message");
+          restartPreviewAndDecode();
+
+      } else if (message.what == R.id.decode_succeeded) {
+          Log.d(TAG, "Got decode succeeded message");
+          state = State.SUCCESS;
+          Bundle bundle = message.getData();
+          Bitmap barcode = null;
+          float scaleFactor = 1.0f;
+          if (bundle != null) {
+              byte[] compressedBitmap = bundle.getByteArray(DecodeThread.BARCODE_BITMAP);
+              if (compressedBitmap != null) {
+                  barcode = BitmapFactory.decodeByteArray(compressedBitmap, 0, compressedBitmap.length, null);
+                  // Mutable copy:
+                  barcode = barcode.copy(Bitmap.Config.ARGB_8888, true);
+              }
+              scaleFactor = bundle.getFloat(DecodeThread.BARCODE_SCALED_FACTOR);
+          }
+          activity.handleDecode((Result) message.obj, barcode, scaleFactor);
+
+      } else if (message.what == R.id.decode_failed) {// We're decoding as fast as possible, so when one decode fails, start another.
+          state = State.PREVIEW;
+          cameraManager.requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
+
+      } else if (message.what == R.id.return_scan_result) {
+          Log.d(TAG, "Got return scan result message");
+          activity.setResult(Activity.RESULT_OK, (Intent) message.obj);
+          activity.finish();
+
+      } else if (message.what == R.id.launch_product_query) {
+          Log.d(TAG, "Got product query message");
+          String url = (String) message.obj;
+
+          Intent intent = new Intent(Intent.ACTION_VIEW);
+          intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+          intent.setData(Uri.parse(url));
+
+          ResolveInfo resolveInfo =
+                  activity.getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
+          String browserPackageName = null;
+          if (resolveInfo != null && resolveInfo.activityInfo != null) {
+              browserPackageName = resolveInfo.activityInfo.packageName;
+              Log.d(TAG, "Using browser in package " + browserPackageName);
+          }
+
+          // Needed for default Android browser / Chrome only apparently
+          if ("com.android.browser".equals(browserPackageName) || "com.android.chrome".equals(browserPackageName)) {
+              intent.setPackage(browserPackageName);
+              intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+              intent.putExtra(Browser.EXTRA_APPLICATION_ID, browserPackageName);
+          }
+
+          try {
+              activity.startActivity(intent);
+          } catch (ActivityNotFoundException ignored) {
+              Log.w(TAG, "Can't find anything to handle VIEW of URI " + url);
+          }
+
+      }
+  }
+
+  public void quitSynchronously() {
+    state = State.DONE;
+    cameraManager.stopPreview();
+    Message quit = Message.obtain(decodeThread.getHandler(), R.id.quit);
+    quit.sendToTarget();
+    try {
+      // Wait at most half a second; should be enough time, and onPause() will timeout quickly
+      decodeThread.join(500L);
+    } catch (InterruptedException e) {
+      // continue
+    }
+
+    // Be absolutely sure we don't send any queued up messages
+    removeMessages(R.id.decode_succeeded);
+    removeMessages(R.id.decode_failed);
+  }
+
+  private void restartPreviewAndDecode() {
+    if (state == State.SUCCESS) {
+      state = State.PREVIEW;
+      cameraManager.requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
+      activity.drawViewfinder();
+    }
+  }
+
+}

+ 102 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/DecodeFormatManager.java

@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.usai.redant.apexdrivers.codescanner;
+
+import android.content.Intent;
+import android.net.Uri;
+
+import com.google.zxing.BarcodeFormat;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.regex.Pattern;
+
+final class DecodeFormatManager {
+
+  private static final Pattern COMMA_PATTERN = Pattern.compile(",");
+
+  static final Collection<BarcodeFormat> PRODUCT_FORMATS;
+  static final Collection<BarcodeFormat> ONE_D_FORMATS;
+  static final Collection<BarcodeFormat> QR_CODE_FORMATS = EnumSet.of(BarcodeFormat.QR_CODE);
+  static final Collection<BarcodeFormat> DATA_MATRIX_FORMATS = EnumSet.of(BarcodeFormat.DATA_MATRIX);
+  static {
+    PRODUCT_FORMATS = EnumSet.of(BarcodeFormat.UPC_A,
+                                 BarcodeFormat.UPC_E,
+                                 BarcodeFormat.EAN_13,
+                                 BarcodeFormat.EAN_8,
+                                 BarcodeFormat.RSS_14,
+                                 BarcodeFormat.RSS_EXPANDED);
+    ONE_D_FORMATS = EnumSet.of(BarcodeFormat.CODE_39,
+                               BarcodeFormat.CODE_93,
+                               BarcodeFormat.CODE_128,
+                               BarcodeFormat.ITF,
+                               BarcodeFormat.CODABAR);
+    ONE_D_FORMATS.addAll(PRODUCT_FORMATS);
+  }
+
+  private DecodeFormatManager() {}
+
+  static Collection<BarcodeFormat> parseDecodeFormats(Intent intent) {
+    Iterable<String> scanFormats = null;
+    CharSequence scanFormatsString = intent.getStringExtra(Intents.Scan.FORMATS);
+    if (scanFormatsString != null) {
+      scanFormats = Arrays.asList(COMMA_PATTERN.split(scanFormatsString));
+    }
+    return parseDecodeFormats(scanFormats, intent.getStringExtra(Intents.Scan.MODE));
+  }
+
+  static Collection<BarcodeFormat> parseDecodeFormats(Uri inputUri) {
+    List<String> formats = inputUri.getQueryParameters(Intents.Scan.FORMATS);
+    if (formats != null && formats.size() == 1 && formats.get(0) != null){
+      formats = Arrays.asList(COMMA_PATTERN.split(formats.get(0)));
+    }
+    return parseDecodeFormats(formats, inputUri.getQueryParameter(Intents.Scan.MODE));
+  }
+
+  private static Collection<BarcodeFormat> parseDecodeFormats(Iterable<String> scanFormats,
+                                                              String decodeMode) {
+    if (scanFormats != null) {
+      Collection<BarcodeFormat> formats = EnumSet.noneOf(BarcodeFormat.class);
+      try {
+        for (String format : scanFormats) {
+          formats.add(BarcodeFormat.valueOf(format));
+        }
+        return formats;
+      } catch (IllegalArgumentException iae) {
+        // ignore it then
+      }
+    }
+    if (decodeMode != null) {
+      if (Intents.Scan.PRODUCT_MODE.equals(decodeMode)) {
+        return PRODUCT_FORMATS;
+      }
+      if (Intents.Scan.QR_CODE_MODE.equals(decodeMode)) {
+        return QR_CODE_FORMATS;
+      }
+      if (Intents.Scan.DATA_MATRIX_MODE.equals(decodeMode)) {
+        return DATA_MATRIX_FORMATS;
+      }
+      if (Intents.Scan.ONE_D_MODE.equals(decodeMode)) {
+        return ONE_D_FORMATS;
+      }
+    }
+    return null;
+  }
+
+}

+ 121 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/DecodeHandler.java

@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.usai.redant.apexdrivers.codescanner;
+
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.MultiFormatReader;
+import com.google.zxing.PlanarYUVLuminanceSource;
+import com.google.zxing.ReaderException;
+import com.google.zxing.Result;
+import com.google.zxing.common.HybridBinarizer;
+import com.usai.redant.apexdrivers.R;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Map;
+
+final class DecodeHandler extends Handler {
+
+  private static final String TAG = DecodeHandler.class.getSimpleName();
+
+  private final CaptureActivity activity;
+  private final MultiFormatReader multiFormatReader;
+  private boolean running = true;
+
+  DecodeHandler(CaptureActivity activity, Map<DecodeHintType,Object> hints) {
+    multiFormatReader = new MultiFormatReader();
+    multiFormatReader.setHints(hints);
+    this.activity = activity;
+  }
+
+  @Override
+  public void handleMessage(Message message) {
+    if (!running) {
+      return;
+    }
+      if (message.what == R.id.decode) {
+          decode((byte[]) message.obj, message.arg1, message.arg2);
+
+      } else if (message.what == R.id.quit) {
+          running = false;
+          Looper.myLooper().quit();
+
+      }
+  }
+
+  /**
+   * Decode the data within the viewfinder rectangle, and time how long it took. For efficiency,
+   * reuse the same reader objects from one decode to the next.
+   *
+   * @param data   The YUV preview frame.
+   * @param width  The width of the preview frame.
+   * @param height The height of the preview frame.
+   */
+  private void decode(byte[] data, int width, int height) {
+    long start = System.currentTimeMillis();
+    Result rawResult = null;
+    PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(data, width, height);
+    if (source != null) {
+      BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
+      try {
+        rawResult = multiFormatReader.decodeWithState(bitmap);
+      } catch (ReaderException re) {
+        // continue
+      } finally {
+        multiFormatReader.reset();
+      }
+    }
+
+    Handler handler = activity.getHandler();
+    if (rawResult != null) {
+      // Don't log the barcode contents for security.
+      long end = System.currentTimeMillis();
+      Log.d(TAG, "Found barcode in " + (end - start) + " ms");
+      if (handler != null) {
+        Message message = Message.obtain(handler, R.id.decode_succeeded, rawResult);
+        Bundle bundle = new Bundle();
+        bundleThumbnail(source, bundle);        
+        message.setData(bundle);
+        message.sendToTarget();
+      }
+    } else {
+      if (handler != null) {
+        Message message = Message.obtain(handler, R.id.decode_failed);
+        message.sendToTarget();
+      }
+    }
+  }
+
+  private static void bundleThumbnail(PlanarYUVLuminanceSource source, Bundle bundle) {
+    int[] pixels = source.renderThumbnail();
+    int width = source.getThumbnailWidth();
+    int height = source.getThumbnailHeight();
+    Bitmap bitmap = Bitmap.createBitmap(pixels, 0, width, width, height, Bitmap.Config.ARGB_8888);
+    ByteArrayOutputStream out = new ByteArrayOutputStream();
+    bitmap.compress(Bitmap.CompressFormat.JPEG, 50, out);
+    bundle.putByteArray(DecodeThread.BARCODE_BITMAP, out.toByteArray());
+    bundle.putFloat(DecodeThread.BARCODE_SCALED_FACTOR, (float) width / source.getWidth());
+  }
+
+}

+ 236 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/DecodeHintManager.java

@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2013 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.usai.redant.apexdrivers.codescanner;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.google.zxing.DecodeHintType;
+
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * @author Lachezar Dobrev
+ */
+final class DecodeHintManager {
+  
+  private static final String TAG = DecodeHintManager.class.getSimpleName();
+
+  // This pattern is used in decoding integer arrays.
+  private static final Pattern COMMA = Pattern.compile(",");
+
+  private DecodeHintManager() {}
+
+  /**
+   * <p>Split a query string into a list of name-value pairs.</p>
+   * 
+   * <p>This is an alternative to the {@link Uri#getQueryParameterNames()} and
+   * {@link Uri#getQueryParameters(String)}, which are quirky and not suitable
+   * for exist-only Uri parameters.</p>
+   * 
+   * <p>This method ignores multiple parameters with the same name and returns the
+   * first one only. This is technically incorrect, but should be acceptable due
+   * to the method of processing Hints: no multiple values for a hint.</p>
+   * 
+   * @param query query to split
+   * @return name-value pairs
+   */
+  private static Map<String,String> splitQuery(String query) {
+    Map<String,String> map = new HashMap<String,String>();
+    int pos = 0;
+    while (pos < query.length()) {
+      if (query.charAt(pos) == '&') {
+        // Skip consecutive ampersand separators.
+        pos ++;
+        continue;
+      }
+      int amp = query.indexOf('&', pos);
+      int equ = query.indexOf('=', pos);
+      if (amp < 0) {
+        // This is the last element in the query, no more ampersand elements.
+        String name;
+        String text;
+        if (equ < 0) {
+          // No equal sign
+          name = query.substring(pos);
+          name = name.replace('+', ' '); // Preemptively decode +
+          name = Uri.decode(name);
+          text = "";
+        } else {
+          // Split name and text.
+          name = query.substring(pos, equ);
+          name = name.replace('+', ' '); // Preemptively decode +
+          name = Uri.decode(name);
+          text = query.substring(equ + 1);
+          text = text.replace('+', ' '); // Preemptively decode +
+          text = Uri.decode(text);
+        }
+        if (!map.containsKey(name)) {
+          map.put(name, text);
+        }
+        break;
+      }
+      if (equ < 0 || equ > amp) {
+        // No equal sign until the &: this is a simple parameter with no value.
+        String name = query.substring(pos, amp);
+        name = name.replace('+', ' '); // Preemptively decode +
+        name = Uri.decode(name);
+        if (!map.containsKey(name)) {
+          map.put(name, "");
+        }
+        pos = amp + 1;
+        continue;
+      }
+      String name = query.substring(pos, equ);
+      name = name.replace('+', ' '); // Preemptively decode +
+      name = Uri.decode(name);
+      String text = query.substring(equ+1, amp);
+      text = text.replace('+', ' '); // Preemptively decode +
+      text = Uri.decode(text);
+      if (!map.containsKey(name)) {
+        map.put(name, text);
+      }
+      pos = amp + 1;
+    }
+    return map;
+  }
+
+  static Map<DecodeHintType,?> parseDecodeHints(Uri inputUri) {
+    String query = inputUri.getEncodedQuery();
+    if (query == null || query.isEmpty()) {
+      return null;
+    }
+
+    // Extract parameters
+    Map<String, String> parameters = splitQuery(query);
+
+    Map<DecodeHintType, Object> hints = new EnumMap<DecodeHintType, Object>(DecodeHintType.class);
+
+    for (DecodeHintType hintType: DecodeHintType.values()) {
+
+      if (hintType == DecodeHintType.CHARACTER_SET ||
+          hintType == DecodeHintType.NEED_RESULT_POINT_CALLBACK ||
+          hintType == DecodeHintType.POSSIBLE_FORMATS) {
+        continue; // This hint is specified in another way
+      }
+
+      String parameterName = hintType.name();
+      String parameterText = parameters.get(parameterName);
+      if (parameterText == null) {
+        continue;
+      }
+      if (hintType.getValueType().equals(Object.class)) {
+        // This is an unspecified type of hint content. Use the value as is.
+        // TODO: Can we make a different assumption on this?
+        hints.put(hintType, parameterText);
+        continue;
+      }
+      if (hintType.getValueType().equals(Void.class)) {
+        // Void hints are just flags: use the constant specified by DecodeHintType
+        hints.put(hintType, Boolean.TRUE);
+        continue;
+      }
+      if (hintType.getValueType().equals(String.class)) {
+        // A string hint: use the decoded value.
+        hints.put(hintType, parameterText);
+        continue;
+      }
+      if (hintType.getValueType().equals(Boolean.class)) {
+        // A boolean hint: a few values for false, everything else is true.
+        // An empty parameter is simply a flag-style parameter, assuming true
+        if (parameterText.isEmpty()) {
+          hints.put(hintType, Boolean.TRUE);
+        } else if ("0".equals(parameterText) || 
+                   "false".equalsIgnoreCase(parameterText) || 
+                   "no".equalsIgnoreCase(parameterText)) {
+          hints.put(hintType, Boolean.FALSE);
+        } else {
+          hints.put(hintType, Boolean.TRUE);
+        }
+
+        continue;
+      }
+      if (hintType.getValueType().equals(int[].class)) {
+        // An integer array. Used to specify valid lengths.
+        // Strip a trailing comma as in Java style array initialisers.
+        if (!parameterText.isEmpty() && parameterText.charAt(parameterText.length() - 1) == ',') {
+          parameterText = parameterText.substring(0, parameterText.length() - 1);
+        }
+        String[] values = COMMA.split(parameterText);
+        int[] array = new int[values.length];
+        for (int i = 0; i < values.length; i++) {
+          try {
+            array[i] = Integer.parseInt(values[i]);
+          } catch (NumberFormatException ignored) {
+            Log.w(TAG, "Skipping array of integers hint " + hintType + " due to invalid numeric value: '" + values[i] + '\'');
+            array = null;
+            break;
+          }
+        }
+        if (array != null) {
+          hints.put(hintType, array);
+        }
+        continue;
+      } 
+      Log.w(TAG, "Unsupported hint type '" + hintType + "' of type " + hintType.getValueType());
+    }
+
+    Log.i(TAG, "Hints from the URI: " + hints);
+    return hints;
+  }
+
+  static Map<DecodeHintType, Object> parseDecodeHints(Intent intent) {
+    Bundle extras = intent.getExtras();
+    if (extras == null || extras.isEmpty()) {
+      return null;
+    }
+    Map<DecodeHintType,Object> hints = new EnumMap<DecodeHintType,Object>(DecodeHintType.class);
+
+    for (DecodeHintType hintType: DecodeHintType.values()) {
+
+      if (hintType == DecodeHintType.CHARACTER_SET ||
+          hintType == DecodeHintType.NEED_RESULT_POINT_CALLBACK ||
+          hintType == DecodeHintType.POSSIBLE_FORMATS) {
+        continue; // This hint is specified in another way
+      }
+
+      String hintName = hintType.name();
+      if (extras.containsKey(hintName)) {
+        if (hintType.getValueType().equals(Void.class)) {
+          // Void hints are just flags: use the constant specified by the DecodeHintType
+          hints.put(hintType, Boolean.TRUE);
+        } else {
+          Object hintData = extras.get(hintName);
+          if (hintType.getValueType().isInstance(hintData)) {
+            hints.put(hintType, hintData);
+          } else {
+            Log.w(TAG, "Ignoring hint " + hintType + " because it is not assignable from " + hintData);
+          }
+        }
+      }
+    }
+
+    Log.i(TAG, "Hints from the Intent: " + hints);
+    return hints;
+  }
+
+}

+ 105 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/DecodeThread.java

@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.usai.redant.apexdrivers.codescanner;
+
+import android.content.SharedPreferences;
+import android.os.Handler;
+import android.os.Looper;
+import android.preference.PreferenceManager;
+import android.util.Log;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.ResultPointCallback;
+import com.usai.redant.apexdrivers.camera.PreferencesActivity;
+
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * This thread does all the heavy lifting of decoding the images.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+final class DecodeThread extends Thread {
+
+  public static final String BARCODE_BITMAP = "barcode_bitmap";
+  public static final String BARCODE_SCALED_FACTOR = "barcode_scaled_factor";
+
+  private final CaptureActivity activity;
+  private final Map<DecodeHintType,Object> hints;
+  private Handler handler;
+  private final CountDownLatch handlerInitLatch;
+
+  DecodeThread(CaptureActivity activity,
+               Collection<BarcodeFormat> decodeFormats,
+               Map<DecodeHintType,?> baseHints,
+               String characterSet,
+               ResultPointCallback resultPointCallback) {
+
+    this.activity = activity;
+    handlerInitLatch = new CountDownLatch(1);
+
+    hints = new EnumMap<DecodeHintType,Object>(DecodeHintType.class);
+    if (baseHints != null) {
+      hints.putAll(baseHints);
+    }
+
+    // The prefs can't change while the thread is running, so pick them up once here.
+    if (decodeFormats == null || decodeFormats.isEmpty()) {
+      SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
+      decodeFormats = EnumSet.noneOf(BarcodeFormat.class);
+      if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_1D, false)) {
+        decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);
+      }
+      if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_QR, false)) {
+        decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
+      }
+      if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_DATA_MATRIX, false)) {
+        decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
+      }
+    }
+    hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
+
+    if (characterSet != null) {
+      hints.put(DecodeHintType.CHARACTER_SET, characterSet);
+    }
+    hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK, resultPointCallback);
+    Log.i("DecodeThread", "Hints: " + hints);
+  }
+
+  Handler getHandler() {
+    try {
+      handlerInitLatch.await();
+    } catch (InterruptedException ie) {
+      // continue?
+    }
+    return handler;
+  }
+
+  @Override
+  public void run() {
+    Looper.prepare();
+    handler = new DecodeHandler(activity, hints);
+    handlerInitLatch.countDown();
+    Looper.loop();
+  }
+
+}

+ 49 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/FinishListener.java

@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.usai.redant.apexdrivers.codescanner;
+
+import android.app.Activity;
+import android.content.DialogInterface;
+
+/**
+ * Simple listener used to exit the app in a few cases.
+ *
+ * @author Sean Owen
+ */
+public final class FinishListener implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
+
+  private final Activity activityToFinish;
+
+  public FinishListener(Activity activityToFinish) {
+    this.activityToFinish = activityToFinish;
+  }
+
+  @Override
+  public void onCancel(DialogInterface dialogInterface) {
+    run();
+  }
+
+  @Override
+  public void onClick(DialogInterface dialogInterface, int i) {
+    run();
+  }
+
+  private void run() {
+    activityToFinish.finish();
+  }
+
+}

+ 116 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/InactivityTimer.java

@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.usai.redant.apexdrivers.codescanner;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncTask;
+import android.os.BatteryManager;
+import android.util.Log;
+
+/**
+ * Finishes an activity after a period of inactivity if the device is on battery power.
+ */
+final class InactivityTimer {
+
+  private static final String TAG = InactivityTimer.class.getSimpleName();
+
+  private static final long INACTIVITY_DELAY_MS = 5 * 60 * 1000L;
+
+  private final Activity activity;
+  private final BroadcastReceiver powerStatusReceiver;
+  private boolean registered;
+  private AsyncTask<?,?,?> inactivityTask;
+
+  InactivityTimer(Activity activity) {
+    this.activity = activity;
+    powerStatusReceiver = new PowerStatusReceiver();
+    registered = false;
+    onActivity();
+  }
+
+  synchronized void onActivity() {
+    cancel();
+    inactivityTask = new InactivityAsyncTask();
+    inactivityTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+  }
+
+  public synchronized void onPause() {
+    cancel();
+    if (registered) {
+      activity.unregisterReceiver(powerStatusReceiver);
+      registered = false;
+    } else {
+      Log.w(TAG, "PowerStatusReceiver was never registered?");
+    }
+  }
+
+  public synchronized void onResume() {
+    if (registered) {
+      Log.w(TAG, "PowerStatusReceiver was already registered?");
+    } else {
+      activity.registerReceiver(powerStatusReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+      registered = true;
+    }
+    onActivity();
+  }
+
+  private synchronized void cancel() {
+    AsyncTask<?,?,?> task = inactivityTask;
+    if (task != null) {
+      task.cancel(true);
+      inactivityTask = null;
+    }
+  }
+
+  void shutdown() {
+    cancel();
+  }
+
+  private final class PowerStatusReceiver extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent){
+      if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
+        // 0 indicates that we're on battery
+        boolean onBatteryNow = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) <= 0;
+        if (onBatteryNow) {
+          InactivityTimer.this.onActivity();
+        } else {
+          InactivityTimer.this.cancel();
+        }
+      }
+    }
+  }
+
+  private final class InactivityAsyncTask extends AsyncTask<Object,Object,Object> {
+    @Override
+    protected Object doInBackground(Object... objects) {
+      try {
+        Thread.sleep(INACTIVITY_DELAY_MS);
+        Log.i(TAG, "Finishing activity due to inactivity");
+        activity.finish();
+      } catch (InterruptedException e) {
+        // continue without killing
+      }
+      return null;
+    }
+  }
+
+}

+ 261 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/Intents.java

@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.usai.redant.apexdrivers.codescanner;
+
+/**
+ * This class provides the constants to use when sending an Intent to Barcode Scanner.
+ * These strings are effectively API and cannot be changed.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class Intents {
+  private Intents() {
+  }
+
+  public static final class Scan {
+    /**
+     * Send this intent to open the Barcodes app in scanning mode, find a barcode, and return
+     * the results.
+     */
+    public static final String ACTION = "com.google.zxing.client.android.SCAN";
+
+    /**
+     * By default, sending this will decode all barcodes that we understand. However it
+     * may be useful to limit scanning to certain formats. Use
+     * {@link android.content.Intent#putExtra(String, String)} with one of the values below.
+     *
+     * Setting this is effectively shorthand for setting explicit formats with {@link #FORMATS}.
+     * It is overridden by that setting.
+     */
+    public static final String MODE = "SCAN_MODE";
+
+    /**
+     * Decode only UPC and EAN barcodes. This is the right choice for shopping apps which get
+     * prices, reviews, etc. for products.
+     */
+    public static final String PRODUCT_MODE = "PRODUCT_MODE";
+
+    /**
+     * Decode only 1D barcodes.
+     */
+    public static final String ONE_D_MODE = "ONE_D_MODE";
+
+    /**
+     * Decode only QR codes.
+     */
+    public static final String QR_CODE_MODE = "QR_CODE_MODE";
+
+    /**
+     * Decode only Data Matrix codes.
+     */
+    public static final String DATA_MATRIX_MODE = "DATA_MATRIX_MODE";
+
+    /**
+     * Comma-separated list of formats to scan for. The values must match the names of
+     * {@link com.google.zxing.BarcodeFormat}s, e.g. {@link com.google.zxing.BarcodeFormat#EAN_13}.
+     * Example: "EAN_13,EAN_8,QR_CODE". This overrides {@link #MODE}.
+     */
+    public static final String FORMATS = "SCAN_FORMATS";
+
+    /**
+     * @see com.google.zxing.DecodeHintType#CHARACTER_SET
+     */
+    public static final String CHARACTER_SET = "CHARACTER_SET";
+
+    /**
+     * Optional parameters to specify the width and height of the scanning rectangle in pixels.
+     * The app will try to honor these, but will clamp them to the size of the preview frame.
+     * You should specify both or neither, and pass the size as an int.
+     */
+    public static final String WIDTH = "SCAN_WIDTH";
+    public static final String HEIGHT = "SCAN_HEIGHT";
+
+    /**
+     * Desired duration in milliseconds for which to pause after a successful scan before
+     * returning to the calling intent. Specified as a long, not an integer!
+     * For example: 1000L, not 1000.
+     */
+    public static final String RESULT_DISPLAY_DURATION_MS = "RESULT_DISPLAY_DURATION_MS";
+
+    /**
+     * Prompt to show on-screen when scanning by intent. Specified as a {@link String}.
+     */
+    public static final String PROMPT_MESSAGE = "PROMPT_MESSAGE";
+
+    /**
+     * If a barcode is found, Barcodes returns {@link android.app.Activity#RESULT_OK} to
+     * {@link android.app.Activity#onActivityResult(int, int, android.content.Intent)}
+     * of the app which requested the scan via
+     * {@link android.app.Activity#startActivityForResult(android.content.Intent, int)}
+     * The barcodes contents can be retrieved with
+     * {@link android.content.Intent#getStringExtra(String)}.
+     * If the user presses Back, the result code will be {@link android.app.Activity#RESULT_CANCELED}.
+     */
+    public static final String RESULT = "SCAN_RESULT";
+
+    /**
+     * Call {@link android.content.Intent#getStringExtra(String)} with {@link #RESULT_FORMAT}
+     * to determine which barcode format was found.
+     * See {@link com.google.zxing.BarcodeFormat} for possible values.
+     */
+    public static final String RESULT_FORMAT = "SCAN_RESULT_FORMAT";
+
+    /**
+     * Call {@link android.content.Intent#getStringExtra(String)} with {@link #RESULT_UPC_EAN_EXTENSION}
+     * to return the content of any UPC extension barcode that was also found. Only applicable
+     * to {@link com.google.zxing.BarcodeFormat#UPC_A} and {@link com.google.zxing.BarcodeFormat#EAN_13}
+     * formats.
+     */
+    public static final String RESULT_UPC_EAN_EXTENSION = "SCAN_RESULT_UPC_EAN_EXTENSION";
+
+    /**
+     * Call {@link android.content.Intent#getByteArrayExtra(String)} with {@link #RESULT_BYTES}
+     * to get a {@code byte[]} of raw bytes in the barcode, if available.
+     */
+    public static final String RESULT_BYTES = "SCAN_RESULT_BYTES";
+
+    /**
+     * Key for the value of {@link com.google.zxing.ResultMetadataType#ORIENTATION}, if available.
+     * Call {@link android.content.Intent#getIntArrayExtra(String)} with {@link #RESULT_ORIENTATION}.
+     */
+    public static final String RESULT_ORIENTATION = "SCAN_RESULT_ORIENTATION";
+
+    /**
+     * Key for the value of {@link com.google.zxing.ResultMetadataType#ERROR_CORRECTION_LEVEL}, if available.
+     * Call {@link android.content.Intent#getStringExtra(String)} with {@link #RESULT_ERROR_CORRECTION_LEVEL}.
+     */
+    public static final String RESULT_ERROR_CORRECTION_LEVEL = "SCAN_RESULT_ERROR_CORRECTION_LEVEL";
+
+    /**
+     * Prefix for keys that map to the values of {@link com.google.zxing.ResultMetadataType#BYTE_SEGMENTS},
+     * if available. The actual values will be set under a series of keys formed by adding 0, 1, 2, ...
+     * to this prefix. So the first byte segment is under key "SCAN_RESULT_BYTE_SEGMENTS_0" for example.
+     * Call {@link android.content.Intent#getByteArrayExtra(String)} with these keys.
+     */
+    public static final String RESULT_BYTE_SEGMENTS_PREFIX = "SCAN_RESULT_BYTE_SEGMENTS_";
+
+    /**
+     * Setting this to false will not save scanned codes in the history. Specified as a {@code boolean}.
+     */
+    public static final String SAVE_HISTORY = "SAVE_HISTORY";
+
+    private Scan() {
+    }
+  }
+
+  public static final class History {
+
+    public static final String ITEM_NUMBER = "ITEM_NUMBER";
+
+    private History() {
+    }
+  }
+
+  public static final class Encode {
+    /**
+     * Send this intent to encode a piece of data as a QR code and display it full screen, so
+     * that another person can scan the barcode from your screen.
+     */
+    public static final String ACTION = "com.google.zxing.client.android.ENCODE";
+
+    /**
+     * The data to encode. Use {@link android.content.Intent#putExtra(String, String)} or
+     * {@link android.content.Intent#putExtra(String, android.os.Bundle)},
+     * depending on the type and format specified. Non-QR Code formats should
+     * just use a String here. For QR Code, see Contents for details.
+     */
+    public static final String DATA = "ENCODE_DATA";
+
+    /**
+     * The type of data being supplied if the format is QR Code. Use
+
+     */
+    public static final String TYPE = "ENCODE_TYPE";
+
+    /**
+     * The barcode format to be displayed. If this isn't specified or is blank,
+     * it defaults to QR Code. Use {@link android.content.Intent#putExtra(String, String)}, where
+     * format is one of {@link com.google.zxing.BarcodeFormat}.
+     */
+    public static final String FORMAT = "ENCODE_FORMAT";
+
+    /**
+     * Normally the contents of the barcode are displayed to the user in a TextView. Setting this
+     * boolean to false will hide that TextView, showing only the encode barcode.
+     */
+    public static final String SHOW_CONTENTS = "ENCODE_SHOW_CONTENTS";
+
+    private Encode() {
+    }
+  }
+
+  public static final class SearchBookContents {
+    /**
+     * Use Google Book Search to search the contents of the book provided.
+     */
+    public static final String ACTION = "com.google.zxing.client.android.SEARCH_BOOK_CONTENTS";
+
+    /**
+     * The book to search, identified by ISBN number.
+     */
+    public static final String ISBN = "ISBN";
+
+    /**
+     * An optional field which is the text to search for.
+     */
+    public static final String QUERY = "QUERY";
+
+    private SearchBookContents() {
+    }
+  }
+
+  public static final class WifiConnect {
+    /**
+     * Internal intent used to trigger connection to a wi-fi network.
+     */
+    public static final String ACTION = "com.google.zxing.client.android.WIFI_CONNECT";
+
+    /**
+     * The network to connect to, all the configuration provided here.
+     */
+    public static final String SSID = "SSID";
+
+    /**
+     * The network to connect to, all the configuration provided here.
+     */
+    public static final String TYPE = "TYPE";
+
+    /**
+     * The network to connect to, all the configuration provided here.
+     */
+    public static final String PASSWORD = "PASSWORD";
+
+    private WifiConnect() {
+    }
+  }
+
+  public static final class Share {
+    /**
+     * Give the user a choice of items to encode as a barcode, then render it as a QR Code and
+     * display onscreen for a friend to scan with their phone.
+     */
+    public static final String ACTION = "com.google.zxing.client.android.SHARE";
+
+    private Share() {
+    }
+  }
+}

+ 35 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/ViewfinderResultPointCallback.java

@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2009 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.usai.redant.apexdrivers.codescanner;
+
+import com.google.zxing.ResultPoint;
+import com.google.zxing.ResultPointCallback;
+
+final class ViewfinderResultPointCallback implements ResultPointCallback {
+
+  private final ViewfinderView viewfinderView;
+
+  ViewfinderResultPointCallback(ViewfinderView viewfinderView) {
+    this.viewfinderView = viewfinderView;
+  }
+
+  @Override
+  public void foundPossibleResultPoint(ResultPoint point) {
+    viewfinderView.addPossibleResultPoint(point);
+  }
+
+}

+ 189 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/codescanner/ViewfinderView.java

@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.usai.redant.apexdrivers.codescanner;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.google.zxing.ResultPoint;
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.apexdrivers.camera.CameraManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This view is overlaid on top of the camera preview. It adds the viewfinder rectangle and partial
+ * transparency outside it, as well as the laser scanner animation and result points.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class ViewfinderView extends View {
+
+  private static final int[] SCANNER_ALPHA = {0, 64, 128, 192, 255, 192, 128, 64};
+  private static final long ANIMATION_DELAY = 80L;
+  private static final int CURRENT_POINT_OPACITY = 0xA0;
+  private static final int MAX_RESULT_POINTS = 20;
+  private static final int POINT_SIZE = 6;
+
+  private CameraManager cameraManager;
+  private final Paint paint;
+  private Bitmap resultBitmap;
+  private final int maskColor;
+  private final int resultColor;
+  private final int laserColor;
+  private final int resultPointColor;
+  private int scannerAlpha;
+  private List<ResultPoint> possibleResultPoints;
+  private List<ResultPoint> lastPossibleResultPoints;
+
+  // This constructor is used when the class is built from an XML resource.
+  public ViewfinderView(Context context, AttributeSet attrs) {
+    super(context, attrs);
+
+    // Initialize these once for performance rather than calling them every time in onDraw().
+    paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    Resources resources = getResources();
+    maskColor = resources.getColor(R.color.viewfinder_mask);
+    resultColor = resources.getColor(R.color.result_view);
+    laserColor = resources.getColor(R.color.viewfinder_laser);
+    resultPointColor = resources.getColor(R.color.possible_result_points);
+    scannerAlpha = 0;
+    possibleResultPoints = new ArrayList<ResultPoint>(5);
+    lastPossibleResultPoints = null;
+  }
+
+  public void setCameraManager(CameraManager cameraManager) {
+    this.cameraManager = cameraManager;
+  }
+
+  @Override
+  public void onDraw(Canvas canvas) {
+    if (cameraManager == null) {
+      return; // not ready yet, early draw before done configuring
+    }
+    Rect frame = cameraManager.getFramingRect();
+    Rect previewFrame = cameraManager.getFramingRectInPreview();
+    if (frame == null || previewFrame == null) {
+      return;
+    }
+    int width = canvas.getWidth();
+    int height = canvas.getHeight();
+
+    // Draw the exterior (i.e. outside the framing rect) darkened
+    paint.setColor(resultBitmap != null ? resultColor : maskColor);
+    canvas.drawRect(0, 0, width, frame.top, paint);
+    canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
+    canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint);
+    canvas.drawRect(0, frame.bottom + 1, width, height, paint);
+
+    if (resultBitmap != null) {
+      // Draw the opaque result bitmap over the scanning rectangle
+      paint.setAlpha(CURRENT_POINT_OPACITY);
+      canvas.drawBitmap(resultBitmap, null, frame, paint);
+    } else {
+
+      // Draw a red "laser scanner" line through the middle to show decoding is active
+      paint.setColor(laserColor);
+      paint.setAlpha(SCANNER_ALPHA[scannerAlpha]);
+      scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length;
+      int middle = frame.height() / 2 + frame.top;
+      canvas.drawRect(frame.left + 2, middle - 1, frame.right - 1, middle + 2, paint);
+      
+      float scaleX = frame.width() / (float) previewFrame.width();
+      float scaleY = frame.height() / (float) previewFrame.height();
+
+      List<ResultPoint> currentPossible = possibleResultPoints;
+      List<ResultPoint> currentLast = lastPossibleResultPoints;
+      int frameLeft = frame.left;
+      int frameTop = frame.top;
+      if (currentPossible.isEmpty()) {
+        lastPossibleResultPoints = null;
+      } else {
+        possibleResultPoints = new ArrayList<ResultPoint>(5);
+        lastPossibleResultPoints = currentPossible;
+        paint.setAlpha(CURRENT_POINT_OPACITY);
+        paint.setColor(resultPointColor);
+        synchronized (currentPossible) {
+          for (ResultPoint point : currentPossible) {
+            canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX),
+                              frameTop + (int) (point.getY() * scaleY),
+                              POINT_SIZE, paint);
+          }
+        }
+      }
+      if (currentLast != null) {
+        paint.setAlpha(CURRENT_POINT_OPACITY / 2);
+        paint.setColor(resultPointColor);
+        synchronized (currentLast) {
+          float radius = POINT_SIZE / 2.0f;
+          for (ResultPoint point : currentLast) {
+            canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX),
+                              frameTop + (int) (point.getY() * scaleY),
+                              radius, paint);
+          }
+        }
+      }
+
+      // Request another update at the animation interval, but only repaint the laser line,
+      // not the entire viewfinder mask.
+      postInvalidateDelayed(ANIMATION_DELAY,
+                            frame.left - POINT_SIZE,
+                            frame.top - POINT_SIZE,
+                            frame.right + POINT_SIZE,
+                            frame.bottom + POINT_SIZE);
+    }
+  }
+
+  public void drawViewfinder() {
+    Bitmap resultBitmap = this.resultBitmap;
+    this.resultBitmap = null;
+    if (resultBitmap != null) {
+      resultBitmap.recycle();
+    }
+    invalidate();
+  }
+
+  /**
+   * Draw a bitmap with the result points highlighted instead of the live scanning display.
+   *
+   * @param barcode An image of the decoded barcode.
+   */
+  public void drawResultBitmap(Bitmap barcode) {
+    resultBitmap = barcode;
+    invalidate();
+  }
+
+  public void addPossibleResultPoint(ResultPoint point) {
+    List<ResultPoint> points = possibleResultPoints;
+    synchronized (points) {
+      points.add(point);
+      int size = points.size();
+      if (size > MAX_RESULT_POINTS) {
+        // trim it
+        points.subList(0, size - MAX_RESULT_POINTS / 2).clear();
+      }
+    }
+  }
+
+}

+ 744 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/DetailActivity.java

@@ -0,0 +1,744 @@
+package com.usai.redant.apexdrivers.detail;
+
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.content.LocalBroadcastManager;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ExpandableListView;
+import android.widget.ListView;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.usai.redant.apexdrivers.ApexDriverApplication;
+import com.usai.redant.apexdrivers.base.BasicActivity;
+import com.usai.redant.apexdrivers.detail.model.DetailActionSelectionModel;
+import com.usai.redant.apexdrivers.detail.model.DetailLocationModel;
+import com.usai.redant.apexdrivers.detail.model.DetailMapModel;
+import com.usai.redant.apexdrivers.detail.model.DetailSubActionModel;
+import com.usai.redant.apexdrivers.home.HomeFragment;
+import com.usai.redant.apexdrivers.MainActivity;
+import com.usai.redant.apexdrivers.network.Network;
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.apexdrivers.update.UpdateActivity;
+import com.usai.redant.rautils.utils.RAUtil;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+
+import static com.usai.redant.apexdrivers.detail.DetailAdapter.MapLifCircleAction;
+import static com.usai.redant.apexdrivers.detail.model.DetailSubActionModel.DetailActionSubType.DetailActionSubTypeAccept;
+import static com.usai.redant.apexdrivers.detail.model.DetailSubActionModel.DetailActionSubType.DetailActionSubTypeEnum;
+import static com.usai.redant.apexdrivers.detail.model.DetailSubActionModel.DetailActionSubType.DetailActionSubTypeUpdate;
+import static com.usai.redant.apexdrivers.detail.model.DetailSubActionModel.DetailRemoteActionType.DetailRemoteActionTypeGoHome;
+import static com.usai.redant.apexdrivers.detail.model.DetailSubActionModel.DetailRemoteActionType.DetailRemoteActionTypeReload;
+import static com.usai.redant.rautils.utils.Network.RESULT_TRUE;
+
+public class DetailActivity extends BasicActivity implements DetailAdapter.DetailActionDelegate {
+
+
+    private final static String IntentOrderIDKey = "orderID";
+    private final static  String IntentOrderStatusKey = "orderStatus";
+    private final static  String IntentOrderType2Key = "orderType2";
+    private final static  String IntentStatusNoKey = "statusNo";
+
+    private final static  String SaveDataKey = "DetailSaveJSON";
+    private final static  String SaveOrderIDKey = "DetailSaveOrderID";
+    private final static  String SaveOrderStatusKey = "DetailSaveOrderStatus";
+    private final static  String SaveOrderType2Key = "DetailSaveOrderType2";
+    private final static  String SaveStatusNo2Key = "DetailSaveStatusNo";
+
+    public static Intent build(Context context, String orderID,int status,String type2,String statusNo) {
+
+        if (context == null) {
+            return null;
+        }
+
+        Intent intent = new Intent(context,DetailActivity.class);
+        if (orderID != null) {
+            intent.putExtra(IntentOrderIDKey,orderID);
+        }
+        if (type2 != null) {
+            intent.putExtra(IntentOrderType2Key,type2);
+        }
+        if (statusNo != null) {
+            intent.putExtra(IntentStatusNoKey,statusNo);
+        }
+
+        intent.putExtra(IntentOrderStatusKey,status);
+        return intent;
+    }
+
+    private String mOrderID;
+    private String mOrderType2;
+    private String mStatusNo;
+    private int mOrderStatus;
+
+    private Context mCtx = this;
+    private DetailActivity self = this;
+    private ExpandableListView mListView;
+    private SwipeRefreshLayout mRefresh;
+    private DetailAdapter mAdapter;
+    private ArrayList<DetailSectionModel> mSectionArray = new ArrayList<>();
+    private JSONObject mJson;
+
+    private DetailHandler mHandler;
+    private TextView mEmptyView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_detail);
+
+        android.support.v7.app.ActionBar actionBar = getSupportActionBar();
+        if(actionBar != null){
+            actionBar.setHomeButtonEnabled(true);
+            actionBar.setDisplayHomeAsUpEnabled(true);
+        }
+
+        setTitle("Order Detail");
+
+        if (getIntent() != null) {
+            mOrderID = getIntent().getStringExtra(IntentOrderIDKey);
+            mOrderType2 = getIntent().getStringExtra(IntentOrderType2Key);
+            mOrderStatus = getIntent().getIntExtra(IntentOrderStatusKey,0);
+            mStatusNo = getIntent().getStringExtra(IntentStatusNoKey);
+        }
+
+        mHandler = new DetailHandler(this);
+
+        mListView = findViewById(R.id.detail_list_view);
+        mAdapter = new DetailAdapter(mCtx,self,mSectionArray);
+
+        mListView.setAdapter(mAdapter);
+        mListView.setGroupIndicator(null);
+        mListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
+            @Override
+            public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
+                return true; // 不允许GroupView点击
+            }
+        });
+
+        mRefresh = findViewById(R.id.detail_refresh);
+        mRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
+            @Override
+            public void onRefresh() {
+                loadData();
+            }
+        });
+
+        mEmptyView = findViewById(R.id.detail_empty_btn);
+        mEmptyView.setText("There is no data\nPlease click to reload");
+        mEmptyView.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                loadData();
+            }
+        });
+        mListView.setEmptyView(findViewById(R.id.detail_empty_view));
+
+        if (savedInstanceState != null) {
+
+            String jsonStr = savedInstanceState.getString(SaveDataKey);
+            if (jsonStr != null) {
+
+                try {
+
+                    JSONObject json = new JSONObject(jsonStr);
+
+                    handleJson(json);
+                    changeData();
+
+                } catch (JSONException e) {
+                    e.printStackTrace();
+                }
+
+            }
+
+            mOrderID = savedInstanceState.getString(SaveOrderIDKey);
+            mOrderType2 = savedInstanceState.getString(SaveOrderType2Key);
+            mOrderStatus = savedInstanceState.getInt(SaveOrderStatusKey);
+            mStatusNo = savedInstanceState.getString(SaveStatusNo2Key);
+
+        } else {
+            loadData();
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home:
+                this.finish(); // back button
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        if (mJson != null) {
+            outState.putString(SaveDataKey,mJson.toString());
+        }
+        if (mOrderID != null) {
+            outState.putString(SaveOrderIDKey,mOrderID);
+        }
+        if (mOrderType2 != null) {
+            outState.putString(SaveOrderType2Key,mOrderType2);
+        }
+        outState.putInt(SaveOrderStatusKey,mOrderStatus);
+
+        if (mStatusNo != null) {
+            outState.putString(SaveStatusNo2Key,mStatusNo);
+        }
+
+        sendLifeCircleBroadCast(0);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        sendLifeCircleBroadCast(1);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        sendLifeCircleBroadCast(2);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        sendLifeCircleBroadCast(3);
+    }
+    @Override
+    protected void onPause() {
+        sendLifeCircleBroadCast(4);
+        super.onPause();
+    }
+
+    @Override
+    public void onLowMemory() {
+        super.onLowMemory();
+        sendLifeCircleBroadCast(5);
+    }
+
+    @Override
+    protected void onDestroy() {
+
+        dismissProgressDialog();
+
+        sendLifeCircleBroadCast(6);
+
+        super.onDestroy();
+    }
+
+    private void sendLifeCircleBroadCast(int life) {
+
+//        Intent intent = new Intent(MapLifCircleAction);
+//        intent.putExtra("Life",life);
+//
+//        LocalBroadcastManager.getInstance(mCtx).sendBroadcast(intent);
+    }
+
+    private void goHome() {
+        Intent intent = new Intent(self, MainActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        self.startActivity(intent);
+
+        sendBroadcast(new Intent(HomeFragment.HomeReloadBroadcastAction));
+    }
+
+    /**
+     * Data
+     * */
+
+    private ProgressDialog mProgressDialog;
+    private void showProgressDialog() {
+        if (mProgressDialog == null) {
+            mProgressDialog = new ProgressDialog(this);
+            mProgressDialog.setMessage("loading...");
+            mProgressDialog.setCancelable(false);
+            mProgressDialog.show();
+        }
+    }
+
+    private void dismissProgressDialog() {
+        if (mProgressDialog != null && mProgressDialog.isShowing()) {
+            mProgressDialog.dismiss();
+        }
+        mProgressDialog = null;
+    }
+
+    private void showWarningMsg(String msg) {
+        if (msg == null || msg.length() == 0) {
+            return;
+        }
+
+        new AlertDialog.Builder(mCtx).setTitle("Warning")
+                .setMessage(msg)
+                .setPositiveButton("Ok",null)
+                .show();
+    }
+
+    private void handleJson(JSONObject json) {
+
+        try {
+            JSONArray sectionArr = json.optJSONArray("sections");
+            if (sectionArr != null) {
+
+                mJson = json;
+                mSectionArray.clear();
+
+                for (int i = 0; i < sectionArr.length(); i++) {
+
+                    JSONObject section = sectionArr.getJSONObject(i);
+                    DetailSectionModel model = new DetailSectionModel(mCtx);
+
+                    model.title = section.optString("title");
+                    model.setValues(section.optJSONArray("values"));
+
+                    mSectionArray.add(model);
+                }
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+    }
+
+    private void changeData() {
+
+        /**
+         * 手动调用打开/关闭 Group,否则默认关闭
+         * */
+        if (mSectionArray.size() > 0) {
+            for (int i = 0; i < mSectionArray.size(); i++) {
+                mListView.expandGroup(i);
+            }
+        }
+
+        mAdapter.notifyDataSetChanged();
+
+    }
+
+    private void reload() {
+        loadData();
+    }
+
+    private void loadData() {
+
+        showProgressDialog();
+
+        if (mSectionArray != null) {
+            mSectionArray.clear();
+        }
+        if (mAdapter != null) {
+            mAdapter.notifyDataSetChanged();
+        }
+
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+
+                JSONObject json = com.usai.redant.apexdrivers.network.Network.requestOrderDetail(mCtx,mOrderID,mOrderStatus,mOrderType2,mStatusNo);
+
+                Message msg = new Message();
+                msg.what = DetailHandler.DetailActionReloadData;
+                msg.obj = json;
+                mHandler.sendMessage(msg);
+            }
+
+        }).start();
+    }
+
+    private void handleRemoteAction(final DetailSubActionModel model) {
+
+        showProgressDialog();
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+
+                JSONObject json = Network.report(model.url,model.params);
+
+                Message msg = new Message();
+                msg.what = DetailHandler.DetailActionRemote;
+                msg.obj = json;
+                msg.arg1 = model.remoteActionType;
+                mHandler.sendMessage(msg);
+
+            }
+        }).start();
+
+    }
+
+    private void handleEnumAction(View view,DetailSubActionModel actionModel) {
+
+        showWindow(view,actionModel.enums);
+    }
+
+    private void showUpdate(DetailSubActionModel model) {
+
+        Intent intent = UpdateActivity.build(mCtx,mOrderID,model.actionID,model.actionTitle,mOrderType2);
+        mCtx.startActivity(intent);
+    }
+
+    /**
+     * Detail Action
+     * */
+
+    @Override
+    public void performAction(final View view, final DetailSubActionModel actionModel) {
+
+        if (actionModel.alert) {
+
+            new AlertDialog.Builder(mCtx)
+                    .setTitle(actionModel.alertTitle)
+                    .setMessage(actionModel.alertMsg)
+                    .setNegativeButton("Cancel",null)
+                    .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            handleAction(view,actionModel);
+                        }
+                    })
+                    .show();
+
+        } else {
+            handleAction(view,actionModel);
+        }
+
+    }
+
+    @Override
+    public void navigationTo(DetailLocationModel locationModel) {
+
+        if (locationModel != null && TextUtils.isEmpty(locationModel.street)) {
+
+            Uri gmmIntentUri = Uri.parse("google.navigation:q=" + locationModel.street + "&mode=d");
+            Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri);
+            mapIntent.setPackage("com.google.android.apps.maps");
+            if (mapIntent.resolveActivity(getPackageManager()) != null) {
+                startActivity(mapIntent);
+            } else {
+                Toast.makeText(mCtx,"There is no google map",Toast.LENGTH_LONG).show();
+            }
+        }
+
+    }
+
+    @Override
+    public void showMap(DetailMapModel model) {
+        if (model != null && !TextUtils.isEmpty(model.getLat()) && !TextUtils.isEmpty(model.getLon())) {
+
+            Uri gmmIntentUri = Uri.parse("geo:" + model.getLat() + "," + model.getLon() + "?mode=d");
+            Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri);
+            mapIntent.setPackage("com.google.android.apps.maps");
+            if (mapIntent.resolveActivity(getPackageManager()) != null) {
+                startActivity(mapIntent);
+            } else {
+                Toast.makeText(mCtx,"There is no google map",Toast.LENGTH_LONG).show();
+            }
+
+        }
+    }
+
+    private void handleAction(View view, DetailSubActionModel actionModel) {
+
+        switch (actionModel.actionType) {
+            case DetailSubActionModel.DetailActionType.DetailActionTypeLocal: {
+
+                if (actionModel.actionSubType == DetailActionSubTypeEnum) {
+                    handleEnumAction(view,actionModel);
+                } else if (actionModel.actionSubType == DetailActionSubTypeUpdate) {
+                    showUpdate(actionModel);
+                }
+
+
+            }
+            break;
+            case DetailSubActionModel.DetailActionType.DetailActionTypeRemote: {
+
+                if (actionModel.actionSubType == DetailActionSubTypeAccept) {
+                    ApexDriverApplication.sharedApplication().setRequiredLocation(true);
+                }
+
+                handleRemoteAction(actionModel);
+            }
+            break;
+            default:
+                break;
+        }
+    }
+
+
+    /**
+     * Handler
+     * */
+
+    private static final class DetailHandler extends Handler {
+
+        static final int DetailActionReloadData = 0;
+        static final int DetailActionRemote = 1;
+
+        WeakReference<DetailActivity> mWeakDetail;
+
+        public DetailHandler(DetailActivity activity) {
+            mWeakDetail = new WeakReference<>(activity);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            super.handleMessage(msg);
+
+            DetailActivity activity = mWeakDetail.get();
+            activity.dismissProgressDialog();
+            switch (msg.what) {
+                case DetailActionReloadData: {
+
+                    if (activity.mRefresh.isRefreshing()) {
+                        activity.mRefresh.setRefreshing(false);
+                    }
+
+                    JSONObject json = (JSONObject) msg.obj;
+
+                    if (json != null) {
+
+                        try {
+                            int restul = json.getInt("result");
+                            if (restul == RESULT_TRUE) {
+
+                                activity.handleJson(json);
+
+                            } else {
+                                // error
+                                String errMsg = json.optString("err_msg");
+                                if (errMsg == null || errMsg.length() == 0) {
+                                    errMsg = "Sorry,there is something wrong";
+                                }
+                                activity.showWarningMsg(errMsg);
+                            }
+
+                        } catch (JSONException e) {
+                            e.printStackTrace();
+                            // error
+                            String errMsg = "Sorry,there is something wrong";
+                            activity.showWarningMsg(errMsg);
+                        }
+                    } else {
+                        // error
+                        String errMsg = "Sorry,there is something wrong";
+                        activity.showWarningMsg(errMsg);
+                    }
+                    activity.changeData();
+                }
+                break;
+                case DetailActionRemote: {
+
+                    JSONObject json = (JSONObject)msg.obj;
+                    if (json != null) {
+                        int result = json.optInt("result");
+                        if (result == RESULT_TRUE) {
+
+                            switch (msg.arg1) {
+                                case DetailRemoteActionTypeGoHome: {
+                                    activity.goHome();
+                                }
+                                break;
+                                case DetailRemoteActionTypeReload: {
+                                    activity.reload();
+                                }
+                                break;
+                            }
+                        } else {
+                            // error;
+                            String errMsg = json.optString("err_msg");
+                            if (errMsg == null || errMsg.length() == 0) {
+                                errMsg = "Sorry,there is something wrong";
+                            }
+                            activity.showWarningMsg(errMsg);
+                        }
+                    } else {
+                        // error
+                        String errMsg = "Sorry,there is something wrong";
+                        activity.showWarningMsg(errMsg);
+                    }
+
+                }
+                break;
+                default:{
+
+                }
+                break;
+            }
+        }
+    }
+
+    /**
+     * Pop
+     * */
+    private PopupWindow popupWindow;
+    private ListView popListView;
+    private PopAdapter popAdapter;
+    private View popView;
+    private void showWindow(View parent, final ArrayList<DetailActionSelectionModel> models) {
+
+        LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+        popView = layoutInflater.inflate(R.layout.pop_list_view, null);
+
+        popListView = (ListView) popView.findViewById(R.id.pop_list);
+
+        popAdapter = new PopAdapter(this, R.layout.pop_cell,models);
+        popListView.setAdapter(popAdapter);
+
+        // 创建一个PopuWidow对象
+        popupWindow = new PopupWindow(popView, RAUtil.dp2px(mCtx,250), RAUtil.dp2px(mCtx,300));
+
+        // 使其聚集
+        popupWindow.setFocusable(true);
+        // 设置允许在外点击消失
+        popupWindow.setOutsideTouchable(true);
+
+        popupWindow.update();
+
+        // 这个是为了点击“返回Back”也能使其消失,并且并不会影响你的背景
+        popupWindow.setBackgroundDrawable(new ColorDrawable(Color.RED));
+        WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
+        // 显示的位置
+        int xPos = windowManager.getDefaultDisplay().getWidth() / 2 - popupWindow.getWidth() / 2;
+
+        popupWindow.showAsDropDown(parent, xPos, 0);
+//        popupWindow.showAsDropDown(parent);
+
+        popListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+
+            @Override
+            public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
+
+                final DetailActionSelectionModel model = models.get(position);
+
+                if (position != 0) {
+
+                    DetailActionSelectionModel currentModel = models.get(0);
+
+                    new AlertDialog.Builder(mCtx)
+                            .setTitle("Warning")
+                            .setMessage("the " + currentModel.actionTitle + " is not done,are you sure to update " + model.actionTitle + " now?")
+                            .setNegativeButton("Cancel",null)
+                            .setPositiveButton("YES", new DialogInterface.OnClickListener() {
+                                @Override
+                                public void onClick(DialogInterface dialog, int which) {
+                                    if (popupWindow != null) {
+                                        popupWindow.dismiss();
+                                    }
+
+
+                                    Intent intent = UpdateActivity.build(mCtx,mOrderID,model.actionID,model.actionTitle,mOrderType2);
+                                    mCtx.startActivity(intent);
+                                }
+                            })
+                            .show();
+
+                } else {
+
+                    if (popupWindow != null) {
+                        popupWindow.dismiss();
+                    }
+
+
+                    Intent intent = UpdateActivity.build(mCtx,mOrderID,model.actionID,model.actionTitle,mOrderType2);
+                    mCtx.startActivity(intent);
+
+                }
+            }
+        });
+    }
+
+    private class PopAdapter extends ArrayAdapter<DetailActionSelectionModel> {
+
+        private Context ctx;
+        private int resId;
+        private ArrayList<DetailActionSelectionModel> selectionModels;
+
+        public PopAdapter(@NonNull Context context, int resource,ArrayList<DetailActionSelectionModel> selectionModels) {
+            super(context, resource);
+            ctx = context;
+            resId = resource;
+            this.selectionModels = selectionModels;
+        }
+
+        @Override
+        public int getCount() {
+
+            return selectionModels.size();
+        }
+
+        @NonNull
+        @Override
+        public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
+
+            PopHolder holder;
+            if (convertView == null) {
+
+                convertView = LayoutInflater.from(ctx).inflate(resId,null);
+                holder = new PopHolder(convertView);
+
+            } else {
+
+                holder = (PopHolder)convertView.getTag();
+            }
+
+            DetailActionSelectionModel model = selectionModels.get(position);
+            holder.bindPopModel(model);
+
+            return convertView;
+        }
+
+        private class PopHolder {
+
+            TextView titleTv;
+
+            PopHolder(View view) {
+
+                titleTv = view.findViewById(R.id.pop_title_tv);
+
+                view.setTag(this);
+            }
+
+            public void bindPopModel(DetailActionSelectionModel model) {
+                titleTv.setText(model.actionTitle);
+            }
+
+        }
+    }
+
+}

+ 758 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/DetailAdapter.java

@@ -0,0 +1,758 @@
+package com.usai.redant.apexdrivers.detail;
+
+import android.content.Context;
+import android.content.Intent;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseExpandableListAdapter;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.apexdrivers.detail.model.DetailActionModel;
+import com.usai.redant.apexdrivers.detail.model.DetailBaseModel;
+import com.usai.redant.apexdrivers.detail.model.DetailLocationModel;
+import com.usai.redant.apexdrivers.detail.model.DetailMapModel;
+import com.usai.redant.apexdrivers.detail.model.DetailMultipleLineModel;
+import com.usai.redant.apexdrivers.detail.model.DetailPhotoModel;
+import com.usai.redant.apexdrivers.detail.model.DetailSignatureModel;
+import com.usai.redant.apexdrivers.detail.model.DetailSingleLineModel;
+import com.usai.redant.apexdrivers.detail.model.DetailSubActionModel;
+import com.usai.redant.apexdrivers.update.PhotoPreviewActivity;
+import com.usai.redant.rautils.utils.RAUtil;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+//import com.google.android.gms.maps.GoogleMap;
+//import com.google.android.gms.maps.OnMapReadyCallback;
+
+public class DetailAdapter extends BaseExpandableListAdapter {
+
+    private ArrayList<DetailSectionModel> mSections;
+    private Context mCtx;
+    private WeakReference<DetailActionDelegate> mDelegate;
+
+    DetailAdapter(Context ctx, DetailActionDelegate delegate, ArrayList<DetailSectionModel> sections) {
+        mCtx = ctx;
+        mSections = sections;
+        mDelegate = new WeakReference<>(delegate);
+    }
+
+    @Override
+    public int getGroupCount() {
+        if (mSections == null) {
+            return 0;
+        }
+        return mSections.size();
+    }
+
+    @Override
+    public int getChildrenCount(int groupPosition) {
+
+        DetailSectionModel sectionModel = mSections.get(groupPosition);
+
+        return sectionModel.itemCount();
+    }
+
+    @Override
+    public Object getGroup(int groupPosition) {
+        return mSections.get(groupPosition);
+    }
+
+    @Override
+    public Object getChild(int groupPosition, int childPosition) {
+        DetailSectionModel sectionModel = mSections.get(groupPosition);
+        return sectionModel.itemAtIndex(childPosition);
+    }
+
+    @Override
+    public long getGroupId(int groupPosition) {
+        return groupPosition;
+    }
+
+    @Override
+    public long getChildId(int groupPosition, int childPosition) {
+        return childPosition;
+    }
+
+    @Override
+    public int getChildTypeCount() {
+
+        return DetailBaseModel.detailValueTypeCount();
+    }
+
+    @Override
+    public int getChildType(int groupPosition, int childPosition) {
+
+        DetailSectionModel sectionModel = mSections.get(groupPosition);
+        DetailBaseModel model = sectionModel.itemAtIndex(childPosition);
+
+        return model.type;
+    }
+
+    @Override
+    public boolean hasStableIds() {
+        return true;
+    }
+
+    @Override
+    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
+
+        if (mCtx == null) {
+            return null;
+        }
+
+        SectionHolder holder;
+        if (convertView == null) {
+
+            convertView = LayoutInflater.from(mCtx).inflate(R.layout.detail_section_header,null);
+            holder = new SectionHolder(convertView);
+
+        } else {
+            holder = (SectionHolder) convertView.getTag();
+        }
+        DetailSectionModel sectionModel = mSections.get(groupPosition);
+        holder.bindSecionModel(sectionModel);
+
+
+        return convertView;
+    }
+
+    @Override
+    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
+        if (mCtx == null) {
+            return null;
+        }
+
+        DetailSectionModel sectionModel = mSections.get(groupPosition);
+        DetailBaseModel model = sectionModel.itemAtIndex(childPosition);
+
+        switch (getChildType(groupPosition,childPosition)) {
+            case DetailBaseModel.OrderDetailValueType.OderDetailValueTypeSingleLine: {
+
+                SingleLineHolder holder;
+                if (convertView == null) {
+
+                    convertView = LayoutInflater.from(mCtx).inflate(R.layout.detail_single_line_cell,null);
+                    holder = new SingleLineHolder(convertView);
+
+                } else {
+
+                    holder = (SingleLineHolder) convertView.getTag();
+                }
+
+                DetailSingleLineModel singleLineModel = (DetailSingleLineModel)model;
+
+                holder.bindSingleLineModel(singleLineModel);
+
+            }
+            break;
+            case DetailBaseModel.OrderDetailValueType.OderDetailValueTypeMultipleLine: {
+
+                MultipleLineHolder holder;
+                if (convertView == null) {
+
+                    convertView = LayoutInflater.from(mCtx).inflate(R.layout.detail_mult_line_cell,null);
+                    holder = new MultipleLineHolder(convertView);
+
+                } else {
+
+                    holder = (MultipleLineHolder)convertView.getTag();
+                }
+
+                DetailMultipleLineModel multipleLineModel = (DetailMultipleLineModel)model;
+
+                holder.bindMultLineModel(multipleLineModel);
+
+            }
+            break;
+            case DetailBaseModel.OrderDetailValueType.OderDetailValueTypeAction: {
+
+                ActionHolder holder;
+                if (convertView == null) {
+
+                    convertView = LayoutInflater.from(mCtx).inflate(R.layout.detail_action_cell,null);
+                    holder = new ActionHolder(convertView);
+
+                } else {
+
+                    holder = (ActionHolder) convertView.getTag();
+                }
+                DetailActionModel actionModel = (DetailActionModel)model;
+
+                holder.bindActionModel(actionModel);
+
+            }
+            break;
+            case DetailBaseModel.OrderDetailValueType.OderDetailValueTypeLocation: {
+
+                LocationHolder holder;
+                if (convertView == null) {
+
+                    convertView = LayoutInflater.from(mCtx).inflate(R.layout.detail_location_cell,null);
+                    holder = new LocationHolder(convertView);
+
+                } else {
+
+                    holder = (LocationHolder)convertView.getTag();
+                }
+                DetailLocationModel locationModel = (DetailLocationModel)model;
+
+                holder.bindLocationModel(locationModel);
+            }
+            break;
+            case DetailBaseModel.OrderDetailValueType.OderDetailValueTypeMap: {
+                MapHolder holder;
+                if (convertView == null) {
+
+                    convertView = LayoutInflater.from(mCtx).inflate(R.layout.detail_map_cell,null);
+                    holder = new MapHolder(convertView);
+
+                } else {
+                    holder = (MapHolder)convertView.getTag();
+                }
+
+                DetailMapModel mapModel = (DetailMapModel)model;
+                holder.bindModel(mapModel);
+            }
+            break;
+            case DetailBaseModel.OrderDetailValueType.OderDetailValueTypePhoto: {
+                PhotoHolder holder;
+                if (convertView == null) {
+
+                    convertView = LayoutInflater.from(mCtx).inflate(R.layout.detail_photo_cell,null);
+                    holder = new PhotoHolder(convertView);
+
+                } else {
+                    holder = (PhotoHolder)convertView.getTag();
+                }
+
+                DetailPhotoModel mapModel = (DetailPhotoModel)model;
+                holder.bindModel(mapModel);
+            }
+            break;
+            case DetailBaseModel.OrderDetailValueType.OderDetailValueTypeSignature: {
+                SignatureHolder holder;
+                if (convertView == null) {
+
+                    convertView = LayoutInflater.from(mCtx).inflate(R.layout.detail_signature_cell,null);
+                    holder = new SignatureHolder(convertView);
+
+                } else {
+                    holder = (SignatureHolder)convertView.getTag();
+                }
+
+                DetailSignatureModel mapModel = (DetailSignatureModel)model;
+                holder.bindModel(mapModel);
+            }
+
+            default:
+                break;
+        }
+
+
+        return convertView;
+    }
+
+    @Override
+    public boolean isChildSelectable(int groupPosition, int childPosition) {
+        return false;
+    }
+
+    private class SectionHolder {
+
+        TextView titleTv;
+
+        SectionHolder(View view) {
+
+            titleTv = view.findViewById(R.id.header_title_tv);
+
+            view.setTag(this);
+        }
+
+        public void bindSecionModel(DetailSectionModel model) {
+            titleTv.setText(model.title);
+        }
+    }
+
+    private class SingleLineHolder {
+
+        TextView titleTv;
+        TextView valueTv;
+
+        SingleLineHolder(View view) {
+
+            titleTv = view.findViewById(R.id.detail_title_tv);
+            valueTv = view.findViewById(R.id.detail_value_tv);
+
+            view.setTag(this);
+        }
+
+        public void bindSingleLineModel(DetailSingleLineModel model) {
+
+            titleTv.setText(model.title);
+            valueTv.setText(model.value);
+            if (model.highlight) {
+                valueTv.setTextColor(mCtx.getResources().getColor(R.color.ApexDriverTextRedColor));
+            } else {
+                valueTv.setTextColor(mCtx.getResources().getColor(R.color.ApexDriverTextBlackColor));
+            }
+        }
+    }
+
+    private class MultipleLineHolder {
+
+        TextView valueTv;
+
+        MultipleLineHolder(View view) {
+
+            valueTv = view.findViewById(R.id.detail_multiple_tv);
+
+            view.setTag(this);
+        }
+
+        public void bindMultLineModel(DetailMultipleLineModel model) {
+            valueTv.setText(model.value);
+
+            if (model.highlight) {
+                valueTv.setTextColor(mCtx.getResources().getColor(R.color.ApexDriverTextRedColor));
+            } else {
+                valueTv.setTextColor(mCtx.getResources().getColor(R.color.ApexDriverTextBlackColor));
+            }
+        }
+    }
+
+    private class ActionHolder {
+
+        LinearLayout linearLayout;
+
+        ActionHolder(View view) {
+
+            linearLayout = (LinearLayout) view;
+
+            view.setTag(this);
+        }
+
+        public void bindActionModel(DetailActionModel model) {
+
+            linearLayout.removeAllViews();
+
+            int actionCount = model.actionCount();
+            if (actionCount == 0) {
+                return;
+            }
+
+            int colCount = 2;
+            int rowCount = actionCount / colCount;
+            if (actionCount % colCount != 0) {
+                rowCount++;
+            }
+
+
+            for (int row = 0; row < rowCount; row++) {
+
+                LinearLayout ll = new LinearLayout(mCtx);
+                for (int col = 0; col < colCount; col++) {
+
+                    final int index = row * colCount + col;
+
+                    RelativeLayout container = new RelativeLayout(mCtx);
+                    LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT,1);
+                    ll.addView(container,layoutParams);
+
+                    Button btn = new Button(mCtx);
+                    RelativeLayout.LayoutParams btnLayoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, RAUtil.dp2px(mCtx,50));
+                    btnLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
+
+                    int marginH = RAUtil.dp2px(mCtx,5);
+                    int marginT = RAUtil.dp2px(mCtx,2.5f);
+                    int marginB = RAUtil.dp2px(mCtx,2.5f);
+                    if (row == rowCount - 1) {
+                        marginB = RAUtil.dp2px(mCtx,5);
+                    } else if (row == 0) {
+                        marginT = RAUtil.dp2px(mCtx,5);
+                    }
+
+                    btnLayoutParams.setMargins(marginH,marginT,marginH,marginB);
+                    container.addView(btn,btnLayoutParams);
+
+                    final DetailSubActionModel subActionModel = model.actionModelForIndex(index);
+                    btn.setText(subActionModel.actionTitle);
+                    btn.setTextSize(TypedValue.COMPLEX_UNIT_SP,20);
+                    btn.setAllCaps(false);
+                    btn.setBackgroundResource(R.drawable.btn_bg);
+
+                    btn.setOnClickListener(new View.OnClickListener() {
+                        @Override
+                        public void onClick(View v) {
+                            if (mDelegate != null && mDelegate.get() != null) {
+                                mDelegate.get().performAction(v,subActionModel);
+                            }
+                        }
+                    });
+
+                    if (index + 1 ==  actionCount) {
+                        break;
+                    }
+
+                }
+
+                LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
+                linearLayout.addView(ll,layoutParams);
+            }
+
+        }
+    }
+
+    private class LocationHolder {
+
+        TextView titleTv;
+        TextView locationTv;
+        ImageButton navigationBtn;
+        WeakReference<DetailLocationModel> mWeakLocation;
+
+        LocationHolder(View view) {
+
+            titleTv = view.findViewById(R.id.detail_location_title_tv);
+            locationTv = view.findViewById(R.id.detail_location_value_tv);
+            navigationBtn = view.findViewById(R.id.detail_location_nav_btn);
+            navigationBtn.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    if (mWeakLocation != null && mDelegate != null) {
+                        if (mWeakLocation.get() != null && mDelegate.get() != null) {
+                            DetailLocationModel model = mWeakLocation.get();
+                            mDelegate.get().navigationTo(model);
+                        }
+                    }
+                }
+            });
+
+            view.setTag(this);
+        }
+
+        public void bindLocationModel(DetailLocationModel model) {
+
+            titleTv.setText(model.title);
+            locationTv.setText(model.location);
+
+            mWeakLocation = new WeakReference<>(model);
+
+            if (model.highlight) {
+                locationTv.setTextColor(mCtx.getResources().getColor(R.color.ApexDriverTextRedColor));
+            } else {
+                locationTv.setTextColor(mCtx.getResources().getColor(R.color.ApexDriverTextBlackColor));
+            }
+        }
+    }
+
+    public final static String MapLifCircleAction = "com.apex.driver.map_life_circle.action";
+
+    private class MapHolder implements DetailBaseModel.OrderDetailModelDelegate {
+        TextView titleTv;
+        TextView valueTv;
+//        com.google.android.gms.maps.MapView mapView;
+//        GoogleMap googleMap;
+//        MapBroadcastReceiver receiver;
+        ImageButton mapBtn;
+
+        WeakReference<DetailMapModel> weakMap;
+
+
+//        private class MapBroadcastReceiver extends BroadcastReceiver {
+//
+//            @Override
+//            public void onReceive(Context context, Intent intent) {
+//
+//                int life = intent.getIntExtra("life",0);
+//                switch (life) {
+//                    case 0:{
+//                        onActivitySaveInstanceState();
+//                    }
+//                    break;
+//                    case 1:{
+//                        onActivityResume();
+//                    }
+//                    break;
+//                    case 2:{
+//                        onActivityStart();
+//                    }
+//                    break;
+//                    case 3:{
+//                        onActivityStop();
+//                    }
+//                    break;
+//                    case 4:{
+//                        onActivityPause();
+//                    }
+//                    break;
+//                    case 5:{
+//                        onActivityLowMemory();
+//                    }
+//                    break;
+//                    case 6:{
+//                        onActivityDestroy();
+//                    }
+//                    break;
+//                }
+//            }
+//        }
+
+        MapHolder(View view) {
+
+            titleTv = view.findViewById(R.id.detail_map_title_tv);
+            valueTv = view.findViewById(R.id.detail_map_value_tv);
+//            mapView = view.findViewById(R.id.detail_map_view);
+//            mapView.getMapAsync(this);
+
+            mapBtn = view.findViewById(R.id.detail_map_view);
+            mapBtn.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+
+                    if (mDelegate != null && mDelegate.get() != null) {
+                        mDelegate.get().showMap(weakMap.get());
+                    }
+                }
+            });
+
+            view.setTag(this);
+
+//            receiver = new MapBroadcastReceiver();
+//
+//            IntentFilter  intentFilter = new IntentFilter();
+//            intentFilter.addAction(MapLifCircleAction);
+//
+//            LocalBroadcastManager.getInstance(mCtx).registerReceiver(receiver,intentFilter);
+        }
+
+//        private void onActivitySaveInstanceState() {
+//
+//            onActivityDestroy();
+//        }
+//
+//
+//        private void onActivityResume() {
+//            mapView.onResume();
+//        }
+//
+//
+//        private void onActivityStart() {
+//            mapView.onStart();
+//        }
+//
+//
+//        private void onActivityStop() {
+//            mapView.onStop();
+//        }
+//
+//        private void onActivityPause() {
+//            mapView.onPause();
+//        }
+//
+//        private void onActivityDestroy() {
+//
+//            mapView.onDestroy();
+//            LocalBroadcastManager.getInstance(mCtx).unregisterReceiver(receiver);
+//        }
+//
+//        private void onActivityLowMemory() {
+//            mapView.onLowMemory();
+//        }
+
+        public void bindModel(DetailMapModel model) {
+            if (weakMap != null && weakMap.get() != null) {
+                weakMap.get().setDelegate(null);
+            }
+            if (model != null) {
+                weakMap = new WeakReference<>(model);
+                weakMap.get().setDelegate(this);
+            } else {
+                weakMap = null;
+            }
+
+            refreshUI();
+        }
+
+//        @Override
+//        public void onMapReady(GoogleMap googleMap) {
+//            this.googleMap = googleMap;
+//
+//            refreshUI();
+//        }
+
+        @Override
+        public void refreshUI() {
+
+
+            if (weakMap != null) {
+                DetailMapModel model = weakMap.get();
+                if (model != null) {
+
+                    titleTv.setText(model.title);
+                    valueTv.setText(model.location);
+
+                    if (model.highlight) {
+                        valueTv.setTextColor(mCtx.getResources().getColor(R.color.ApexDriverTextRedColor));
+                    } else {
+                        valueTv.setTextColor(mCtx.getResources().getColor(R.color.ApexDriverTextBlackColor));
+                    }
+
+                    String lat = model.getLat();
+                    String lon = model.getLon();
+                    boolean showMap = lat != null && lat.length() > 0 && lon != null && lon.length() > 0;
+
+                    if (showMap) {
+
+                        if (lat.equals("-999") && lon.equals("-999")) {
+
+                            mapBtn.setEnabled(false);
+                            mapBtn.setImageResource(R.drawable.btn_map_no);
+                        } else {
+
+                            mapBtn.setEnabled(true);
+                            mapBtn.setImageResource(R.drawable.btn_map);
+                        }
+
+                    } else {
+
+                        mapBtn.setEnabled(false);
+                        mapBtn.setImageResource(R.drawable.btn_map_no);
+                    }
+
+
+                } else {
+
+                    titleTv.setText(null);
+                    valueTv.setText(null);
+                    mapBtn.setEnabled(false);
+                    mapBtn.setImageResource(R.drawable.btn_map_no);
+                }
+            } else {
+                titleTv.setText(null);
+                valueTv.setText(null);
+                mapBtn.setEnabled(false);
+                mapBtn.setImageResource(R.drawable.btn_map_no);
+            }
+        }
+    }
+
+    private class PhotoHolder implements DetailBaseModel.OrderDetailModelDelegate {
+        TextView titleTv;
+        ImageButton photoBtn;
+        WeakReference<DetailPhotoModel> weakPhoto;
+
+        PhotoHolder(View view) {
+
+            titleTv = view.findViewById(R.id.detail_photo_title_tv);
+            photoBtn = view.findViewById(R.id.detail_photo_btn);
+            photoBtn.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+
+                    if (weakPhoto != null && weakPhoto.get() != null) {
+                        String path = weakPhoto.get().getPhotoPath(mCtx);
+                        clickImage(path);
+                    }
+
+                }
+            });
+
+            view.setTag(this);
+        }
+
+        public void bindModel(DetailPhotoModel model) {
+            if (weakPhoto != null && weakPhoto.get() != null) {
+                weakPhoto.get().setDelegate(null);
+            }
+            if (model != null) {
+                weakPhoto = new WeakReference<>(model);
+                weakPhoto.get().setDelegate(this);
+            } else {
+                weakPhoto = null;
+            }
+        }
+
+        @Override
+        public void refreshUI() {
+            if (weakPhoto != null) {
+                DetailPhotoModel model = weakPhoto.get();
+                if (model != null) {
+
+                    titleTv.setText(model.getTitle());
+                    photoBtn.setImageBitmap(model.getPhoto());
+                }
+            }
+        }
+    }
+
+    private class SignatureHolder implements DetailBaseModel.OrderDetailModelDelegate {
+
+        TextView titleTv;
+        ImageView signatureView;
+        WeakReference<DetailSignatureModel> weakSignature;
+
+        SignatureHolder(View view) {
+
+            titleTv = view.findViewById(R.id.detail_signature_title_tv);
+            signatureView = view.findViewById(R.id.detail_signature_image_view);
+            signatureView.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+
+                    if (weakSignature != null && weakSignature.get() != null) {
+                        String path = weakSignature.get().getSignaturePath(mCtx);
+                        clickImage(path);
+                    }
+                }
+            });
+
+            view.setTag(this);
+        }
+
+        public void bindModel(DetailSignatureModel model) {
+            if (weakSignature != null && weakSignature.get() != null) {
+                weakSignature.get().setDelegate(null);
+            }
+            if (model != null) {
+                weakSignature = new WeakReference<>(model);
+                weakSignature.get().setDelegate(this);
+            } else {
+                weakSignature = null;
+            }
+        }
+
+        @Override
+        public void refreshUI() {
+            if (weakSignature != null) {
+                DetailSignatureModel model = weakSignature.get();
+                if (model != null) {
+
+                    titleTv.setText(model.getTitle());
+                    signatureView.setImageBitmap(model.getSignature());
+                }
+            }
+        }
+    }
+
+    public interface DetailActionDelegate {
+
+        void performAction(View view,DetailSubActionModel actionModel);
+        void navigationTo(DetailLocationModel locationModel);
+        void showMap(DetailMapModel model);
+    }
+
+    private void clickImage(String path) {
+
+        Intent intent = PhotoPreviewActivity.build(mCtx,path,false);
+        mCtx.startActivity(intent);
+
+    }
+}

+ 143 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/DetailSectionModel.java

@@ -0,0 +1,143 @@
+package com.usai.redant.apexdrivers.detail;
+
+import android.content.Context;
+
+import com.usai.redant.apexdrivers.detail.model.DetailActionModel;
+import com.usai.redant.apexdrivers.detail.model.DetailBaseModel;
+import com.usai.redant.apexdrivers.detail.model.DetailLocationModel;
+import com.usai.redant.apexdrivers.detail.model.DetailMapModel;
+import com.usai.redant.apexdrivers.detail.model.DetailMultipleLineModel;
+import com.usai.redant.apexdrivers.detail.model.DetailPhotoModel;
+import com.usai.redant.apexdrivers.detail.model.DetailSignatureModel;
+import com.usai.redant.apexdrivers.detail.model.DetailSingleLineModel;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+
+public class DetailSectionModel {
+
+    public ArrayList<DetailBaseModel> values = new ArrayList<>();
+    public String title;
+
+    private Context mCtx;
+    DetailSectionModel(Context context) {
+        mCtx = context;
+    }
+
+    public int itemCount() {
+        if (values == null) {
+            return 0;
+        }
+        return values.size();
+    }
+
+    public DetailBaseModel itemAtIndex(int index) {
+        if (values == null || index >= values.size()) {
+            return null;
+        }
+        return values.get(index);
+    }
+
+    public void setValues(JSONArray values) {
+        this.values.clear();
+        if (values == null) {
+            return;
+        } else {
+
+            for (int i = 0; i < values.length(); i++) {
+
+                JSONObject value = values.optJSONObject(i);
+                if (value != null) {
+                    int type = value.optInt("type",-1);
+                    boolean highlight = value.optBoolean("highlight");
+                    switch (type) {
+                        case DetailBaseModel.OrderDetailValueType.OderDetailValueTypeSingleLine: {
+                            DetailSingleLineModel model = new DetailSingleLineModel(mCtx);
+
+                            model.title = value.optString("title");
+                            model.value = value.optString("value");
+                            model.type = type;
+                            model.highlight = highlight;
+
+                            this.values.add(model);
+                        }
+                        break;
+                        case DetailBaseModel.OrderDetailValueType.OderDetailValueTypeMultipleLine: {
+                            DetailMultipleLineModel model = new DetailMultipleLineModel(mCtx);
+
+                            model.value = value.optString("value");
+                            model.type = type;
+                            model.highlight = highlight;
+
+                            this.values.add(model);
+                        }
+                        break;
+                        case DetailBaseModel.OrderDetailValueType.OderDetailValueTypeAction: {
+
+                            DetailActionModel model = new DetailActionModel(mCtx);
+
+                            model.setActions(value.optJSONArray("actions"));
+                            model.type = type;
+                            model.highlight = highlight;
+
+                            this.values.add(model);
+                        }
+                        break;
+                        case DetailBaseModel.OrderDetailValueType.OderDetailValueTypeLocation: {
+
+                            DetailLocationModel model = new DetailLocationModel(mCtx);
+
+                            model.title = value.optString("title");
+                            model.location = value.optString("location");
+                            model.street = value.optString("street");
+                            model.type = type;
+                            model.highlight = highlight;
+
+                            this.values.add(model);
+                        }
+                        break;
+                        case DetailBaseModel.OrderDetailValueType.OderDetailValueTypeMap: {
+
+                            DetailMapModel model = new DetailMapModel(mCtx);
+                            model.type = type;
+                            model.title = value.optString("title");
+                            model.location = value.optString("location");
+                            model.setLatlon(value.optString("latlon"));
+                            model.highlight = highlight;
+
+                            this.values.add(model);
+                        }
+                        break;
+                        case DetailBaseModel.OrderDetailValueType.OderDetailValueTypePhoto: {
+
+                            DetailPhotoModel model = new DetailPhotoModel(mCtx);
+                            model.setTitle(value.optString("title"));
+                            model.setPhotoURL(value.optString("photoURL"));
+                            model.type = type;
+                            model.highlight = highlight;
+
+                            this.values.add(model);
+                        }
+                        break;
+                        case DetailBaseModel.OrderDetailValueType.OderDetailValueTypeSignature: {
+
+                            DetailSignatureModel model = new DetailSignatureModel(mCtx);
+                            model.setTitle(value.optString("title"));
+                            model.setSignatureURL(value.optString("signatureURL"));
+                            model.type = type;
+                            model.highlight = highlight;
+
+                            this.values.add(model);
+                        }
+                        break;
+                        default:
+                            break;
+                    }
+                }
+            }
+        }
+    }
+
+}

+ 104 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailActionModel.java

@@ -0,0 +1,104 @@
+package com.usai.redant.apexdrivers.detail.model;
+
+import android.content.Context;
+
+import com.usai.redant.rautils.utils.RAUtil;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+
+public class DetailActionModel extends DetailBaseModel{
+
+    public ArrayList<DetailSubActionModel> actions = new ArrayList<>();
+
+    public DetailActionModel(Context context) {
+        super(context);
+    }
+
+    public int actionCount() {
+        if (actions == null) {
+            return 0;
+        }
+        return actions.size();
+    }
+
+    public DetailSubActionModel actionModelForIndex(int index) {
+        if (actions == null || index >= actions.size()) {
+            return null;
+        }
+        return actions.get(index);
+    }
+
+    public void setActions(JSONArray actions) {
+
+        this.actions.clear();
+        if (actions == null) {
+
+            return;
+        } else  {
+
+            for (int i = 0; i < actions.length(); i++) {
+                JSONObject action = actions.optJSONObject(i);
+                if (action != null) {
+                    DetailSubActionModel subActionModel = new DetailSubActionModel();
+
+                    int type = action.optInt("actionType");
+                    int subType = action.optInt("actionSubType");
+                    String title = action.optString("actionTitle");
+                    subActionModel.actionTitle = title;
+
+                    subActionModel.actionType = type;
+                    subActionModel.actionSubType = subType;
+
+                    if (type == DetailSubActionModel.DetailActionType.DetailActionTypeLocal) {
+
+                        if (subType == DetailSubActionModel.DetailActionSubType.DetailActionSubTypeEnum) {
+                            JSONArray enums = action.optJSONArray("enums");
+                            subActionModel.setEnums(enums);
+                        } else if (subType == DetailSubActionModel.DetailActionSubType.DetailActionSubTypeUpdate) {
+                            int actionID = action.optInt("actionID");
+                            subActionModel.actionID = actionID;
+                        }
+                    }
+
+                    if (type == DetailSubActionModel.DetailActionType.DetailActionTypeRemote) {
+
+                        String url = action.optString("url");
+                        subActionModel.url = url;
+
+                        JSONObject params = action.optJSONObject("params");
+                        if (params != null) {
+                            subActionModel.params = RAUtil.Json2Bundle(params);
+                        } else {
+                            subActionModel.params = null;
+                        }
+
+                        int remoteActionType = action.optInt("remoteActionType");
+                        subActionModel.remoteActionType = remoteActionType;
+                    }
+
+                    boolean alert = action.optBoolean("alert");
+                    if (alert) {
+
+                        String alertTitle = action.optString("alertTitle");
+                        String alertMsg = action.optString("alertMsg");
+
+                        subActionModel.alertTitle = alertTitle;
+                        subActionModel.alertMsg = alertMsg;
+                    }
+                    subActionModel.alert = alert;
+
+
+
+                    this.actions.add(subActionModel);
+                }
+            }
+        }
+
+
+
+    }
+
+}

+ 9 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailActionSelectionModel.java

@@ -0,0 +1,9 @@
+package com.usai.redant.apexdrivers.detail.model;
+
+public class DetailActionSelectionModel {
+
+    public String actionTitle;
+    public String detailActionTitle;
+    public int actionID;
+
+}

+ 46 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailBaseModel.java

@@ -0,0 +1,46 @@
+package com.usai.redant.apexdrivers.detail.model;
+
+import android.content.Context;
+
+public class DetailBaseModel {
+
+    public static class OrderDetailValueType {
+        public static final int OderDetailValueTypeSingleLine = 0;
+        public static final int OderDetailValueTypeMultipleLine = 1;
+        public static final int OderDetailValueTypeAction = 2;
+        public static final int OderDetailValueTypeLocation = 3;
+        public static final int OderDetailValueTypeMap = 4;
+        public static final int OderDetailValueTypePhoto = 5;
+        public static final int OderDetailValueTypeSignature = 6;
+    }
+
+    public static int detailValueTypeCount() {
+        return 7;
+    }
+
+    public interface OrderDetailModelDelegate {
+        void refreshUI();
+    }
+
+    public int type;
+    public boolean highlight;
+
+    private OrderDetailModelDelegate delegate;
+
+    Context mCtx;
+    public DetailBaseModel(Context context) {
+        mCtx = context;
+    }
+
+    public void setDelegate(OrderDetailModelDelegate delegate) {
+        this.delegate = delegate;
+
+        if (delegate != null) {
+            delegate.refreshUI();
+        }
+    }
+
+    public OrderDetailModelDelegate getDelegate() {
+        return delegate;
+    }
+}

+ 14 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailLocationModel.java

@@ -0,0 +1,14 @@
+package com.usai.redant.apexdrivers.detail.model;
+
+import android.content.Context;
+
+public class DetailLocationModel extends DetailBaseModel {
+
+    public String title;
+    public String location;
+    public String street;
+
+    public DetailLocationModel(Context context) {
+        super(context);
+    }
+}

+ 33 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailMapModel.java

@@ -0,0 +1,33 @@
+package com.usai.redant.apexdrivers.detail.model;
+
+import android.content.Context;
+
+public class DetailMapModel extends DetailBaseModel {
+
+    public String title;
+    public String location;
+    private String latlon;
+    private String lat;
+    private String lon;
+
+    public DetailMapModel(Context context) {
+        super(context);
+    }
+
+    public void setLatlon(String latlon) {
+        this.latlon = latlon;
+        if (latlon != null && latlon.contains(",")) {
+            String[] arr = latlon.split(",");
+            lat = arr[0];
+            lon = arr[1];
+        }
+    }
+
+    public String getLat() {
+        return lat;
+    }
+
+    public String getLon() {
+        return lon;
+    }
+}

+ 12 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailMultipleLineModel.java

@@ -0,0 +1,12 @@
+package com.usai.redant.apexdrivers.detail.model;
+
+import android.content.Context;
+
+public class DetailMultipleLineModel extends DetailBaseModel {
+
+    public String value;
+
+    public DetailMultipleLineModel(Context context) {
+        super(context);
+    }
+}

+ 98 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailPhotoModel.java

@@ -0,0 +1,98 @@
+package com.usai.redant.apexdrivers.detail.model;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.media.Image;
+import android.text.TextUtils;
+
+import com.usai.redant.apexdrivers.ApexDriverApplication;
+import com.usai.redant.apexdrivers.utils.OperationQueue;
+import com.usai.redant.rautils.utils.ImageUtil;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+public class DetailPhotoModel extends DetailBaseModel {
+
+    private String title;
+    private String photoURL;
+    private Bitmap photo;
+
+    public DetailPhotoModel(Context context) {
+        super(context);
+    }
+
+    private void setPhoto(Bitmap photo) {
+        this.photo = photo;
+
+        if (getDelegate() != null) {
+            getDelegate().refreshUI();
+        }
+    }
+
+    public Bitmap getPhoto() {
+        return photo;
+    }
+
+    public String getPhotoURL() {
+        return photoURL;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public void setPhotoURL(final String photoURL) {
+        this.photoURL = photoURL;
+
+        if (TextUtils.isEmpty(photoURL)) {
+            setPhoto(null);
+            return;
+        }
+
+        ApexDriverApplication.sharedApplication().getNetworkQueue().addOperationTask(new OperationQueue.OperationBackgroundCallBack() {
+            @Override
+            public Object operationDoInBackground() {
+
+                try {
+
+                    URI uri = new URI(photoURL);
+
+                    ImageUtil.loadImageFromURL(mCtx,uri,0, 0);
+
+                    String filename = ImageUtil.imageCachePath(mCtx,uri);
+                    Bitmap bitmap = ImageUtil.adjustPhotoOrientation(filename);
+
+                    return bitmap;
+
+                } catch (URISyntaxException e) {
+                    e.printStackTrace();
+                }
+
+                return null;
+            }
+        }, new OperationQueue.OperationCompletionCallBack() {
+            @Override
+            public void operationCompletion(Object object) {
+
+                Bitmap bitmap = (Bitmap)object;
+                setPhoto(bitmap);
+            }
+        }, new OperationQueue.OperationCancelCallBack() {
+            @Override
+            public void operationCancelled() {
+                setPhoto(null);
+            }
+        });
+    }
+
+    public String getPhotoPath(Context context) {
+
+        return ImageUtil.imageCachePathForURl(context,photoURL);
+    }
+
+}

+ 88 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailSignatureModel.java

@@ -0,0 +1,88 @@
+package com.usai.redant.apexdrivers.detail.model;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.text.TextUtils;
+
+import com.usai.redant.apexdrivers.ApexDriverApplication;
+import com.usai.redant.apexdrivers.utils.OperationQueue;
+import com.usai.redant.rautils.utils.ImageUtil;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+public class DetailSignatureModel extends DetailBaseModel {
+
+    private String title;
+    private String signatureURL;
+    private Bitmap signature;
+
+    public DetailSignatureModel(Context context) {
+        super(context);
+    }
+
+    private void setSignature(Bitmap signature) {
+        this.signature = signature;
+
+        if (getDelegate() != null) {
+            getDelegate().refreshUI();
+        }
+    }
+
+    public Bitmap getSignature() {
+        return signature;
+    }
+
+    public String getSignatureURL() {
+        return signatureURL;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public void setSignatureURL(final String signatureURL) {
+        this.signatureURL = signatureURL;
+
+        if (TextUtils.isEmpty(signatureURL)) {
+            setSignature(null);
+            return;
+        }
+
+        ApexDriverApplication.sharedApplication().getNetworkQueue().addOperationTask(new OperationQueue.OperationBackgroundCallBack() {
+            @Override
+            public Object operationDoInBackground() {
+
+                try {
+
+                    return ImageUtil.loadImageFromURL(mCtx,new URI(signatureURL),0, 0);
+
+                } catch (URISyntaxException e) {
+                    e.printStackTrace();
+                }
+
+                return null;
+            }
+        }, new OperationQueue.OperationCompletionCallBack() {
+            @Override
+            public void operationCompletion(Object object) {
+
+                Bitmap bitmap = (Bitmap)object;
+                setSignature(bitmap);
+            }
+        }, new OperationQueue.OperationCancelCallBack() {
+            @Override
+            public void operationCancelled() {
+                setSignature(null);
+            }
+        });
+    }
+
+    public String getSignaturePath(Context context) {
+        return ImageUtil.imageCachePathForURl(context,signatureURL);
+    }
+}

+ 13 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailSingleLineModel.java

@@ -0,0 +1,13 @@
+package com.usai.redant.apexdrivers.detail.model;
+
+import android.content.Context;
+
+public class DetailSingleLineModel extends DetailBaseModel {
+
+    public String title;
+    public String value;
+
+    public DetailSingleLineModel(Context context) {
+        super(context);
+    }
+}

+ 79 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailSubActionModel.java

@@ -0,0 +1,79 @@
+package com.usai.redant.apexdrivers.detail.model;
+
+import android.os.Bundle;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+
+public class DetailSubActionModel {
+
+    public static class DetailActionType {
+        public static final int DetailActionTypeRemote = 0;
+        public static final int DetailActionTypeLocal = 1;
+    }
+
+    public static class DetailActionSubType {
+        public static final int DetailActionSubTypeEnum = 0;
+        public static final int DetailActionSubTypeAccept = 1;
+        public static final int DetailActionSubTypeReject = 2;
+        public static final int DetailActionSubTypeUpdate = 3;
+        public static final int DetailActionSubTypeOther = 4;
+    }
+
+    public static class DetailRemoteActionType {
+
+        public static final int DetailRemoteActionTypeGoHome = 0;
+        public static final int DetailRemoteActionTypeReload = 1;
+
+    }
+
+    public int actionType;
+    public int actionSubType;
+    public String actionTitle;
+
+    /**
+     * actionType == RADetailActionTypeLocal && actionSubType == RADetailActionSubTypeEnum
+     * */
+    public ArrayList<DetailActionSelectionModel> enums = new ArrayList<DetailActionSelectionModel>();
+    public void setEnums(JSONArray enums) {
+
+        this.enums.clear();
+        if (enums == null) {
+            return;
+        }
+
+        for (int i = 0; i < enums.length(); i++) {
+            JSONObject enumObj = enums.optJSONObject(i);
+            if (enumObj != null) {
+                DetailActionSelectionModel model = new DetailActionSelectionModel();
+
+                model.actionID = enumObj.optInt("actionID");
+                model.actionTitle = enumObj.optString("actionTitle");
+                model.detailActionTitle = actionTitle;
+
+                this.enums.add(model);
+            }
+        }
+    }
+
+    /**
+     * actionType == RADetailActionTypeRemote
+     * */
+    public String url;
+    public Bundle params;
+    public int remoteActionType;
+
+    /**
+     * actionType == RADetailActionTypeLocal && actionSubType == DetailActionSubTypeUpdate
+     * */
+    public int actionID;
+
+    /**
+     * alert
+     * */
+    public boolean alert;
+    public String alertTitle;
+    public String alertMsg;
+}

+ 480 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/filter/OrderFilterActivity.java

@@ -0,0 +1,480 @@
+package com.usai.redant.apexdrivers.filter;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ExpandableListView;
+import android.widget.RelativeLayout;
+
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.apexdrivers.base.BasicActivity;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+public class OrderFilterActivity extends BasicActivity {
+
+    private static final String OrderFilterKey = "OrderFilterKey";
+    private static final String OrderFilterSavedKey = "OrderFilterSavedKey";
+
+    public static Intent filterActivityIntent(Context ctx, JSONObject filter) {
+
+        if (ctx == null) {
+            return null;
+        }
+
+        if (filter.length() == 0) {
+            new AlertDialog.Builder(ctx)
+                    .setTitle("Warning")
+                    .setMessage("There is no filter data")
+                    .setPositiveButton("Ok",null)
+                    .show();
+            return null;
+        }
+
+        Intent intent = new Intent(ctx,OrderFilterActivity.class);
+        intent.putExtra(OrderFilterKey,filter.toString());
+
+        return intent;
+    }
+
+    private Context mCtx = this;
+    private ExpandableListView mFilterListView;
+    private JSONObject mFilter;
+    private OrderFilterAdapter mAdapter;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_order_filter);
+
+        ActionBar actionBar = getSupportActionBar();
+        if (actionBar != null) {
+            actionBar.setHomeButtonEnabled(true);
+            actionBar.setDisplayHomeAsUpEnabled(true);
+        }
+
+        mFilterListView = findViewById(R.id.order_filter_listView);
+        mFilterListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
+            @Override
+            public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
+                return true;
+            }
+        });
+        mFilterListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
+            @Override
+            public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
+
+                BaseModel baseModel = (BaseModel) mAdapter.getChild(groupPosition,childPosition);
+                if (baseModel.type == ModelTypeStatus) {
+
+                    StatusModel model = (StatusModel)baseModel;
+                    model.setSelected(!model.selected);
+                }
+
+                return false;
+            }
+        });
+
+        RelativeLayout footer = (RelativeLayout) LayoutInflater.from(mCtx).inflate(R.layout.filter_search_footer,null);
+        mFilterListView.addFooterView(footer);
+        Button searchBtn = footer.findViewById(R.id.filter_search_btn);
+        searchBtn.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                searchClick();
+            }
+        });
+
+        mFilter = null;
+        if (getIntent() != null) {
+            String filterStr = getIntent().getStringExtra(OrderFilterKey);
+            if (filterStr != null && filterStr.length() > 0) {
+                try {
+                    mFilter = new JSONObject(filterStr);
+                } catch (JSONException e) {
+                    e.printStackTrace();
+                    mFilter = null;
+                }
+            } else {
+                mFilter = null;
+            }
+        }
+
+        if (savedInstanceState != null) {
+            String filterStr = savedInstanceState.getString(OrderFilterSavedKey);
+            if (filterStr != null && filterStr.length() > 0) {
+                try {
+                    mFilter = new JSONObject(filterStr);
+                } catch (JSONException e) {
+                    e.printStackTrace();
+                    mFilter = null;
+                }
+            } else {
+                mFilter = null;
+            }
+        }
+
+        prepareData();
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        if (mFilter != null) {
+            outState.putString(OrderFilterSavedKey,mFilter.toString());
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home: {
+                finish();
+                return true;
+            }
+        }
+
+        return super.onOptionsItemSelected(item);
+    }
+
+    /**
+     * Data
+     * */
+    private void prepareData() {
+
+        ArrayList<SectionModel> sectionModels = new ArrayList<>();
+
+        if (mFilter != null) {
+            JSONArray sections = mFilter.optJSONArray("sections");
+            for (int i = 0; i < sections.length(); i++) {
+
+                JSONObject section = sections.optJSONObject(i);
+                if (section != null) {
+
+                    SectionModel model = new SectionModel();
+                    model.setValuesWithJsonObject(section);
+
+                    sectionModels.add(model);
+                }
+
+            }
+        }
+
+        mAdapter = new OrderFilterAdapter(mCtx,sectionModels);
+        mFilterListView.setAdapter(mAdapter);
+
+        /**
+         * 手动调用打开/关闭 Group,否则默认关闭
+         * */
+        if (sectionModels.size() > 0) {
+            for (int i = 0; i < sectionModels.size(); i++) {
+                mFilterListView.expandGroup(i);
+            }
+        }
+    }
+
+    private void searchClick() {
+
+        String filter = prepareSearchParameters();
+
+        Intent intent = new Intent();
+        if (filter != null && filter.length() > 0) {
+            intent.putExtra("filter",filter);
+        }
+
+        setResult(RESULT_OK,intent);
+
+        finish();
+    }
+
+    private String prepareSearchParameters() {
+
+        JSONObject params = new JSONObject();
+
+        ArrayList<OrderFilterActivity.SectionModel> sectionModelArrayList = mAdapter.getSections();
+
+        if (sectionModelArrayList != null && sectionModelArrayList.size() > 0) {
+
+            for (SectionModel sectionModel : sectionModelArrayList) {
+               String key = sectionModel.key;
+               if (key != null && key.length() > 0) {
+                   ArrayList<String> values = new ArrayList<>();
+                   for (BaseModel model : sectionModel.values) {
+                       switch (model.type) {
+                           case ModelTypeStatus: {
+                               StatusModel statusModel = (StatusModel)model;
+                               if (statusModel.selected && statusModel.value != null && statusModel.value.length() > 0) {
+                                   values.add(statusModel.value);
+                               }
+                           }
+                           break;
+                           case ModelTypeDate: {
+                               DateModel dateModel = (DateModel)model;
+                               String from = dateModel.from;
+                               String to = dateModel.to;
+                               if ((from != null && from.length() > 0) || (to != null && to.length() > 0)) {
+                                   if (from == null || from.length() == 0) {
+                                       from = "";
+                                   }
+                                   if (to == null || to.length() == 0) {
+                                       to = "";
+                                   }
+                                   String date = from + "~" + to;
+                                   values.add(date);
+                               }
+                           }
+                           break;
+                           case ModelTypeInput: {
+                               InputModel inputModel = (InputModel)model;
+                               if (inputModel.value != null && inputModel.value.length() > 0) {
+                                   values.add(inputModel.value);
+                               }
+                           }
+                           break;
+                       }
+                   }
+
+                   String value = list2String(values,"<RASEPERATOR>");
+                   if (value != null && value.length() > 0) {
+                       try {
+                           params.put(key,value);
+                       } catch (JSONException e) {
+                           e.printStackTrace();
+                       }
+                   }
+               }
+            }
+
+        }
+
+        if (params.length() == 0) {
+            return null;
+        } else {
+            return params.toString();
+        }
+    }
+
+    private String list2String(List<String> list,String seperator) {
+
+        Objects.requireNonNull(seperator);
+
+        if (list == null || list.size() == 0) {
+            return "";
+        }
+        if (list.size() == 1) {
+            return list.get(0);
+        }
+
+        StringBuffer stringBuffer = new StringBuffer();
+        for (int i = 0; i < list.size(); i++) {
+            String e = list.get(i);
+
+            stringBuffer.append(e);
+
+            if (i != list.size() - 1) {
+                stringBuffer.append(seperator);
+            }
+        }
+        return stringBuffer.toString();
+    }
+
+    /**
+     * Model
+     * */
+
+    public static class SectionModel {
+        public String title;
+        public String key;
+        public ArrayList<BaseModel> values = new ArrayList<>();
+
+        public void setValuesWithJsonObject(JSONObject jsonObject) {
+            if (jsonObject != null) {
+
+                title = jsonObject.optString("title");
+                key = jsonObject.optString("key");
+                JSONArray values = jsonObject.optJSONArray("values");
+                setValues(values);
+            }
+        }
+
+        public void setValues(JSONArray values) {
+
+            this.values.clear();
+
+            if (values == null) {
+                return;
+            }
+
+            for (int i = 0; i < values.length(); i++) {
+                JSONObject value = values.optJSONObject(i);
+                if (value != null) {
+                    int type = value.optInt("type");
+                    switch (type) {
+                        case ModelTypeStatus: {
+                            StatusModel model = new StatusModel();
+                            model.setValuesWithJsonObject(value);
+
+                            this.values.add(model);
+                        }
+                        break;
+                        case ModelTypeDate: {
+                            DateModel model = new DateModel();
+                            model.setValuesWithJsonObject(value);
+
+                            this.values.add(model);
+                        }
+                        break;
+                        case ModelTypeInput: {
+                            InputModel model = new InputModel();
+                            model.setValuesWithJsonObject(value);
+
+                            this.values.add(model);
+                        }
+                        break;
+
+                    }
+                }
+            }
+
+        }
+
+        public BaseModel modelForRow(int row) {
+            return values.get(row);
+        }
+    }
+
+    public static final int ModelTypeStatus = 0;
+    public static final int ModelTypeInput = 1;
+    public static final int ModelTypeDate = 2;
+
+    public interface ModelDelegate {
+        void refreshUI();
+    }
+
+    public static class BaseModel {
+
+        public int type;
+        public ModelDelegate delegate;
+
+        public void setValuesWithJsonObject(JSONObject object) {
+            if (object != null) {
+                type = object.optInt("type");
+            }
+
+        }
+
+    }
+
+    public static class DateModel extends BaseModel {
+
+        public String from,to;
+
+        @Override
+        public void setValuesWithJsonObject(JSONObject object) {
+            super.setValuesWithJsonObject(object);
+
+            if (object != null) {
+                setFrom(object.optString("from"));
+                setTo(object.optString("to"));
+            } else {
+                setFrom(null);
+                setTo(null);
+            }
+        }
+
+        public void setFrom(String from) {
+            this.from = from;
+
+            if (delegate != null) {
+                delegate.refreshUI();
+            }
+        }
+
+        public void setTo(String to) {
+            this.to = to;
+
+            if (delegate != null) {
+                delegate.refreshUI();
+            }
+        }
+    }
+
+    public static class InputModel extends BaseModel {
+
+        public String value;
+        @Override
+        public void setValuesWithJsonObject(JSONObject object) {
+            super.setValuesWithJsonObject(object);
+
+            if (object != null) {
+                setValue(object.optString("value"));
+            } else {
+                setValue(null);
+            }
+        }
+
+        public void setValue(String value) {
+            this.value = value;
+
+            if (delegate != null) {
+                delegate.refreshUI();
+            }
+        }
+
+        public void updateValue(String value) {
+            this.value = value;
+        }
+    }
+
+    public static class StatusModel extends BaseModel {
+
+        public String value;
+        public boolean selected;
+
+        @Override
+        public void setValuesWithJsonObject(JSONObject object) {
+            super.setValuesWithJsonObject(object);
+
+            if (object != null) {
+                setValue(object.optString("value"));
+            } else {
+                setValue(null);
+            }
+        }
+
+        public void setValue(String value) {
+            this.value = value;
+
+            if (delegate != null) {
+                delegate.refreshUI();
+            }
+        }
+
+        public void setSelected(boolean selected) {
+            this.selected = selected;
+
+            if (delegate != null) {
+                delegate.refreshUI();
+            }
+        }
+    }
+
+    /**
+     * Adapter
+     * */
+
+}

+ 417 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/filter/OrderFilterAdapter.java

@@ -0,0 +1,417 @@
+package com.usai.redant.apexdrivers.filter;
+
+import android.app.DatePickerDialog;
+import android.content.Context;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseExpandableListAdapter;
+import android.widget.Button;
+import android.widget.DatePicker;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.usai.redant.apexdrivers.R;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Calendar;
+
+import static com.usai.redant.apexdrivers.filter.OrderFilterActivity.ModelTypeDate;
+import static com.usai.redant.apexdrivers.filter.OrderFilterActivity.ModelTypeInput;
+import static com.usai.redant.apexdrivers.filter.OrderFilterActivity.ModelTypeStatus;
+
+public class OrderFilterAdapter extends BaseExpandableListAdapter {
+
+    private Context mCtx;
+    private ArrayList<OrderFilterActivity.SectionModel> mSections;
+
+    OrderFilterAdapter(Context context, ArrayList<OrderFilterActivity.SectionModel> sections) {
+        mCtx = context;
+        mSections = sections;
+    }
+
+    public ArrayList<OrderFilterActivity.SectionModel> getSections() {
+        return mSections;
+    }
+
+    @Override
+    public int getGroupCount() {
+        if (mSections == null) {
+            return 0;
+        }
+        return mSections.size();
+    }
+
+    @Override
+    public int getChildrenCount(int groupPosition) {
+
+        OrderFilterActivity.SectionModel sectionModel = mSections.get(groupPosition);
+
+        return sectionModel.values.size();
+    }
+
+    @Override
+    public Object getGroup(int groupPosition) {
+
+        OrderFilterActivity.SectionModel sectionModel = mSections.get(groupPosition);
+
+        return sectionModel;
+    }
+
+    @Override
+    public Object getChild(int groupPosition, int childPosition) {
+
+        OrderFilterActivity.SectionModel sectionModel = mSections.get(groupPosition);
+
+        return sectionModel.modelForRow(childPosition);
+    }
+
+    @Override
+    public long getGroupId(int groupPosition) {
+        return groupPosition;
+    }
+
+    @Override
+    public long getChildId(int groupPosition, int childPosition) {
+        return childPosition;
+    }
+
+    @Override
+    public boolean hasStableIds() {
+        return false;
+    }
+
+    @Override
+    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
+
+        HeaderHolder holder = null;
+        if (convertView == null) {
+            convertView = LayoutInflater.from(mCtx).inflate(R.layout.filter_header,null);
+            holder = new HeaderHolder(convertView);
+        } else {
+            holder = (HeaderHolder)convertView.getTag();
+        }
+
+        OrderFilterActivity.SectionModel model = mSections.get(groupPosition);
+        holder.titleTv.setText(model.title);
+
+        return convertView;
+    }
+
+    @Override
+    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
+
+        OrderFilterActivity.SectionModel sectionModel = mSections.get(groupPosition);
+
+        OrderFilterActivity.BaseModel model = sectionModel.modelForRow(childPosition);
+
+        switch (model.type) {
+            case ModelTypeStatus: {
+
+                StatusHolder holder = null;
+                if (convertView == null) {
+                    convertView = LayoutInflater.from(mCtx).inflate(R.layout.filter_status_cell,null);
+                    holder = new StatusHolder(convertView);
+                } else {
+                    holder = (StatusHolder)convertView.getTag();
+                }
+
+                holder.setModel(model);
+            }
+            break;
+            case ModelTypeInput: {
+
+                InputHolder holder = null;
+                if (convertView == null) {
+                    convertView = LayoutInflater.from(mCtx).inflate(R.layout.filter_input_cell,null);
+                    holder = new InputHolder(convertView);
+                } else {
+                    holder = (InputHolder)convertView.getTag();
+                }
+                holder.setModel(model);
+            }
+            break;
+            case ModelTypeDate: {
+
+                DateHolder holder = null;
+                if (convertView == null) {
+                    convertView = LayoutInflater.from(mCtx).inflate(R.layout.filter_date_cell,null);
+                    holder = new DateHolder(convertView);
+                } else {
+                    holder = (DateHolder)convertView.getTag();
+                }
+                holder.setModel(model);
+            }
+            break;
+        }
+
+        return convertView;
+    }
+
+    @Override
+    public boolean isChildSelectable(int groupPosition, int childPosition) {
+        return true;
+    }
+
+    @Override
+    public int getChildTypeCount() {
+        return 3;
+    }
+
+    @Override
+    public int getChildType(int groupPosition, int childPosition) {
+        OrderFilterActivity.BaseModel model = (OrderFilterActivity.BaseModel) getChild(groupPosition,childPosition);
+        return model.type;
+    }
+
+    private class HeaderHolder {
+
+        TextView titleTv;
+
+        HeaderHolder(View view) {
+
+            titleTv = view.findViewById(R.id.filter_header_tv);
+            view.setTag(this);
+        }
+    }
+
+    private class BaseHolder implements OrderFilterActivity.ModelDelegate{
+
+        public OrderFilterActivity.BaseModel model;
+
+        public OrderFilterActivity.BaseModel getModel() {
+            return model;
+        }
+
+        public void setModel(OrderFilterActivity.BaseModel model) {
+            if (this.model != null) {
+                this.model.delegate = null;
+            }
+            this.model = model;
+            if (this.model != null) {
+                this.model.delegate = this;
+            }
+
+            refreshUI();
+        }
+
+        @Override
+        public void refreshUI() {
+
+        }
+    }
+
+    private class DateHolder extends BaseHolder {
+
+        Button fromBtn,toBtn;
+
+        DateHolder(View view) {
+
+            fromBtn = view.findViewById(R.id.filter_date_from_btn);
+            toBtn = view.findViewById(R.id.filter_date_to_btn);
+
+            fromBtn.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v)
+                {
+
+                    Calendar c = Calendar.getInstance();
+                    new DatePickerDialog(mCtx,
+                            new DatePickerDialog.OnDateSetListener()
+                            {
+
+                                @Override
+                                public void onDateSet(DatePicker view,
+                                                      int year, int monthOfYear,
+                                                      int dayOfMonth)
+                                {
+                                    String date = ++monthOfYear + "/" + dayOfMonth + "/" + year;
+
+                                    OrderFilterActivity.DateModel model = (OrderFilterActivity.DateModel)getModel();
+                                    model.setFrom(date);
+
+                                }
+
+                            }, c.get(Calendar.YEAR), c.get(Calendar.MONTH),
+                            c.get(Calendar.DAY_OF_MONTH)).show();
+
+                }
+//                {
+//
+//                    DatePickerDialog datePickerDialog = new DatePickerDialog(mCtx, 0);
+//                    datePickerDialog.setOnDateSetListener(new DatePickerDialog.OnDateSetListener() {
+//                        @Override
+//                        public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
+//
+//                            String date = ++month + "/" + dayOfMonth + "/" + year;
+//
+//                            OrderFilterActivity.DateModel model = (OrderFilterActivity.DateModel)getModel();
+//                            model.setFrom(date);
+//                        }
+//                    });
+//                    datePickerDialog.show();
+//
+//                }
+            });
+
+            toBtn.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v)
+                {
+
+                    Calendar c = Calendar.getInstance();
+                    new DatePickerDialog(mCtx,
+                            new DatePickerDialog.OnDateSetListener()
+                            {
+
+                                @Override
+                                public void onDateSet(DatePicker view,
+                                                      int year, int monthOfYear,
+                                                      int dayOfMonth)
+                                {
+                                    String date = ++monthOfYear + "/" + dayOfMonth + "/" + year;
+
+                                    OrderFilterActivity.DateModel model = (OrderFilterActivity.DateModel)getModel();
+                                    model.setTo(date);
+
+                                }
+
+                            }, c.get(Calendar.YEAR), c.get(Calendar.MONTH),
+                            c.get(Calendar.DAY_OF_MONTH)).show();
+
+                }
+
+//                {
+//                    DatePickerDialog datePickerDialog = new DatePickerDialog(mCtx, 0);
+//                    datePickerDialog.setOnDateSetListener(new DatePickerDialog.OnDateSetListener() {
+//                        @Override
+//                        public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
+//
+//                            String date = ++month + "/" + dayOfMonth + "/" + year;
+//
+//                            OrderFilterActivity.DateModel model = (OrderFilterActivity.DateModel)getModel();
+//                            model.setTo(date);
+//                        }
+//                    });
+//                    datePickerDialog.show();
+//                }
+            });
+
+            view.setTag(this);
+        }
+
+        @Override
+        public void refreshUI() {
+            super.refreshUI();
+
+            OrderFilterActivity.DateModel model = (OrderFilterActivity.DateModel)this.model;
+            String from = model.from;
+            if (from == null || from.length() == 0) {
+                from = "From";
+            }
+            fromBtn.setText(from);
+
+            String to = model.to;
+            if (to == null || to.length() == 0) {
+                to = "To";
+            }
+            toBtn.setText(to);
+        }
+    }
+
+    private class InputHolder extends BaseHolder {
+
+        EditText inputText;
+
+        OrderFilterTextWatcher textWatcher = new OrderFilterTextWatcher();
+
+        InputHolder(View view) {
+
+            inputText = view.findViewById(R.id.filter_input_tv);
+
+            inputText.addTextChangedListener(textWatcher);
+
+            view.setTag(this);
+        }
+
+        @Override
+        public void setModel(OrderFilterActivity.BaseModel model) {
+            super.setModel(model);
+
+            textWatcher.setInputModel(model);
+        }
+
+        @Override
+        public void refreshUI() {
+            super.refreshUI();
+
+            OrderFilterActivity.InputModel model = (OrderFilterActivity.InputModel)this.model;
+
+            inputText.setText(model.value);
+        }
+    }
+
+    private class StatusHolder extends BaseHolder {
+
+        TextView valueTv;
+        ImageView selectionView;
+
+        StatusHolder(View view) {
+
+            valueTv = view.findViewById(R.id.filter_status_title_tv);
+            selectionView = view.findViewById(R.id.filter_status_selection_view);
+            view.setTag(this);
+        }
+
+        @Override
+        public void refreshUI() {
+            super.refreshUI();
+
+            OrderFilterActivity.StatusModel model = (OrderFilterActivity.StatusModel)this.model;
+
+            valueTv.setText(model.value);
+            selectionView.setVisibility(model.selected ? View.VISIBLE : View.INVISIBLE);
+        }
+    }
+
+    private class OrderFilterTextWatcher implements TextWatcher {
+
+        private WeakReference<OrderFilterActivity.BaseModel> weakModel;
+
+        void setInputModel(OrderFilterActivity.BaseModel model) {
+            if (model != null) {
+                weakModel = new WeakReference<>(model);
+            } else {
+                weakModel = null;
+            }
+        }
+
+        @Override
+        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+        }
+
+        @Override
+        public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+            if (weakModel == null) {
+                return;
+            }
+
+            if (weakModel.get() instanceof OrderFilterActivity.InputModel) {
+
+               ((OrderFilterActivity.InputModel) weakModel.get()).updateValue(s.toString());
+            }
+
+        }
+
+        @Override
+        public void afterTextChanged(Editable s) {
+
+        }
+    }
+}

+ 122 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/home/HomeCellLayout.java

@@ -0,0 +1,122 @@
+package com.usai.redant.apexdrivers.home;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.usai.redant.apexdrivers.R;
+
+import java.lang.ref.WeakReference;
+
+public class HomeCellLayout extends RelativeLayout implements HomeOrderModel.OrderModelDelegate {
+
+    TextView titleTv, orderNoTv, containerNoTv, dateTv,orderType2Tv;
+    ImageView statusView, markView;
+    RelativeLayout contentContainer;
+
+    WeakReference<HomeOrderModel> weakModel;
+    private boolean initialed = false;
+
+    public HomeCellLayout(Context context) {
+        super(context);
+
+    }
+
+    public HomeCellLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+    }
+
+    public HomeCellLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+    }
+
+    public HomeCellLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+    }
+
+    private void init() {
+
+        contentContainer = findViewById(R.id.content_container);
+        titleTv = findViewById(R.id.title_tv);
+        orderNoTv = findViewById(R.id.order_no_tv);
+        containerNoTv = findViewById(R.id.container_no_tv);
+        dateTv = findViewById(R.id.date_tv);
+        statusView = findViewById(R.id.status_view);
+        markView = findViewById(R.id.order_mark_view);
+        orderType2Tv = findViewById(R.id.type2_tv);
+
+        initialed = true;
+    }
+
+    public void bindOrderModel(HomeOrderModel model) {
+
+        if (!initialed) {
+            init();
+        }
+
+        if (weakModel != null && weakModel.get() != null) {
+            try {
+                weakModel.get().delegate = null; // 会有null的情况
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        if (model != null) {
+            weakModel = new WeakReference<>(model);
+            weakModel.get().delegate = this;
+        } else {
+            weakModel = null;
+        }
+
+        refresh();
+
+    }
+
+    @Override
+    public void refresh() {
+
+        if (weakModel == null || weakModel.get() == null) {
+            return;
+        }
+
+        HomeOrderModel model = weakModel.get();
+        titleTv.setText(model.title);
+        orderNoTv.setText(model.orderNo);
+        containerNoTv.setText(model.containerNo);
+        dateTv.setText(model.date);
+        orderType2Tv.setText(model.order_type2);
+        statusView.setImageBitmap(model.getIcon());
+
+        if (model.backendFlag) {
+            markView.setVisibility(View.VISIBLE);
+        } else {
+            markView.setVisibility(View.GONE);
+        }
+
+        if (model.selected || isPressed()) {
+            contentContainer.setBackgroundResource(R.drawable.round_corner_selection_bg);
+        } else {
+            contentContainer.setBackgroundResource(R.drawable.round_corner_bg);
+        }
+
+    }
+
+    @Override
+    protected void drawableStateChanged() {
+        super.drawableStateChanged();
+
+        RelativeLayout contentContainer = findViewById(R.id.content_container);
+        if (contentContainer == null) {
+            return;
+        }
+
+        refresh();
+    }
+}

+ 819 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/home/HomeFragment.java

@@ -0,0 +1,819 @@
+package com.usai.redant.apexdrivers.home;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.app.AlertDialog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseExpandableListAdapter;
+import android.widget.Button;
+import android.widget.ExpandableListView;
+import android.widget.TextView;
+
+import com.usai.redant.apexdrivers.ApexDriverApplication;
+import com.usai.redant.apexdrivers.MainActivity;
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.apexdrivers.badgeview.BadgeView;
+import com.usai.redant.apexdrivers.detail.DetailActivity;
+import com.usai.redant.apexdrivers.message.MessageActivity;
+import com.usai.redant.apexdrivers.utils.OperationQueue;
+import com.usai.redant.rautils.utils.Network;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+//import com.usai.redant.apexdrivers.settings;
+
+public class HomeFragment extends Fragment implements HomeHeaderView.HomeHeaderDelegate {
+
+    public final static String HomeReloadBroadcastAction = "com.usai.redant.apexdriver.home_refresh";
+    private final static String HomeSaveDataKey = "HomeSaveDataKey";
+
+    private final static int REQUEST_MORE_CODE = 0;
+
+    private Context mCtx;
+    private HomeFragment self = this;
+    private ExpandableListView mListView;
+    private SwipeRefreshLayout mRefresh;
+    private HomeListAdapter mAdapter;
+    private ArrayList<HomeSectionModel> mSectionArray = new ArrayList<>();
+    private HomeHandler mHandler = new HomeHandler(this);
+    private HomeBroadCastReceiver mReceiver;
+    private JSONObject mJson;
+    private HomeOrderModel mSelectedModel;
+    private TextView mEmptyView;
+    private HomeHeaderView mHeaderView;
+
+    @Nullable
+    @Override
+    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+
+        /**
+         * 直接inflate并返回的,结果是看不到View
+         * */
+        View view = super.onCreateView(inflater,container,savedInstanceState);
+        if (view == null) {//
+            view = inflater.inflate(R.layout.fragment_home, container, false);
+        }
+        ViewGroup parent = (ViewGroup) view.getParent();
+        if (parent != null) {
+            parent.removeView(view);
+        }
+        return view;
+    }
+
+    @Override
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+
+        mListView = view.findViewById(R.id.home_list_view);
+
+        mAdapter = new HomeListAdapter();
+        mListView.setAdapter(mAdapter);
+        mListView.setGroupIndicator(null); // Section Header前面有一个小三角,移除。也可以设置自己的图片
+        mListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
+            @Override
+            public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
+
+                HomeSectionModel sectionModel = mSectionArray.get(groupPosition);
+                HomeOrderModel orderModel = sectionModel.orderModelForIndex(childPosition);
+
+                if (mSelectedModel != null) {
+                    mSelectedModel.setSelection(false);
+                }
+                mSelectedModel = orderModel;
+                if (mSelectedModel != null) {
+                    mSelectedModel.setSelection(true);
+                }
+
+                if (orderModel.getBackendFlag()) {
+                    orderModel.setBackendFlag(!orderModel.getBackendFlag());
+                    sectionModel.setBackendFlagCount(--sectionModel.backendFlagCount);
+                }
+
+                Intent intent = DetailActivity.build(mCtx,orderModel.orderID,orderModel.status,orderModel.order_type2,orderModel.status_no);
+                mCtx.startActivity(intent);
+
+                return false;
+            }
+        });
+        mListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
+            @Override
+            public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
+                return true;
+            }
+        });
+
+        mRefresh = view.findViewById(R.id.home_refresh);
+        mRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
+            @Override
+            public void onRefresh() {
+                loadData();
+            }
+        });
+
+        mEmptyView = view.findViewById(R.id.home_empty_btn);
+        mEmptyView.setText("There is no data\nPlease click to reload");
+        mEmptyView.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                loadData();
+            }
+        });
+        mListView.setEmptyView(view.findViewById(R.id.home_empty_view));
+
+        // header
+        mHeaderView = HomeHeaderView.headerView(mCtx);
+        mHeaderView.setDelegate(this);
+        mListView.addHeaderView(mHeaderView);
+    }
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mCtx = getActivity();
+
+        registBroadcastReceiver();
+
+        if (savedInstanceState != null) {
+
+            String jsonStr = savedInstanceState.getString(HomeSaveDataKey);
+            if (jsonStr != null) {
+                try {
+
+                    JSONObject json = new JSONObject(jsonStr);
+                    handleJson(json);
+
+                } catch (JSONException e) {
+                    e.printStackTrace();
+                }
+            }
+
+        } else {
+            loadData();
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    public void onSaveInstanceState(@NonNull Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        if (mJson != null) {
+            outState.putString(HomeSaveDataKey,mJson.toString());
+        }
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+
+        if (requestCode == REQUEST_MORE_CODE) {
+
+            if (requestCode == Activity.RESULT_OK && data != null) {
+
+                boolean reload = data.getBooleanExtra("reload",false);
+                if (reload) {
+                    loadData();
+                }
+            }
+        }
+
+
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+
+        mCtx.unregisterReceiver(mReceiver);
+        dismissProgressDialog();
+    }
+
+    private void registBroadcastReceiver() {
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(HomeReloadBroadcastAction);
+        mReceiver = new HomeBroadCastReceiver();
+        mCtx.registerReceiver(mReceiver,intentFilter);
+    }
+
+    private ProgressDialog mProgressDialog;
+    private void showProgressDialog() {
+        if (mProgressDialog == null) {
+            mProgressDialog = new ProgressDialog(mCtx);
+            mProgressDialog.setMessage("loading...");
+            mProgressDialog.setCancelable(false);
+            mProgressDialog.show();
+        }
+    }
+
+    private void dismissProgressDialog() {
+        if (mProgressDialog != null && mProgressDialog.isShowing()) {
+            mProgressDialog.dismiss();
+        }
+        mProgressDialog = null;
+    }
+
+    private void showWarningMsg(String msg) {
+        if (msg == null || msg.length() == 0) {
+            return;
+        }
+
+        new AlertDialog.Builder(mCtx).setTitle("Warning")
+                .setMessage(msg)
+                .setPositiveButton("Ok",null)
+                .show();
+    }
+
+    private void handleJson(JSONObject json) {
+
+        if (json != null) {
+
+            try {
+                JSONArray sectionArr = json.optJSONArray("sections");
+                if (sectionArr != null) {
+                    mJson = json;
+                    mSectionArray.clear();
+
+                    for (int i = 0; i < sectionArr.length(); i++) {
+
+                        JSONObject section = sectionArr.getJSONObject(i);
+                        HomeSectionModel model = new HomeSectionModel(mCtx);
+                        model.section = i;
+
+                        model.type = section.optInt("type");
+                        model.title = section.optString("title");
+                        model.setOrders(section.optJSONArray("orders"));
+                        model.totalCount = section.optInt("totalCount",0);
+                        model.backendFlagCount = section.optInt("backendFlagCount");
+
+                        mSectionArray.add(model);
+                    }
+                }
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+        }
+
+        Message msg = new Message();
+        msg.what = HomeHandler.HomeActionReloadData;
+        msg.obj = json;
+        mHandler.sendMessage(msg);
+    }
+
+    private void loadData() {
+
+        showProgressDialog();
+
+        if (mSectionArray != null) {
+            mSectionArray.clear();
+        }
+        if (mAdapter != null) {
+           mAdapter.notifyDataSetChanged();
+        }
+
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+
+                JSONObject json = com.usai.redant.apexdrivers.network.Network.requestOrderList(mCtx);
+                if (json != null) {
+                    try {
+
+                        int restul = json.getInt("result");
+                        if (restul == Network.RESULT_TRUE) {
+
+                            boolean requiredLocation = json.optBoolean("requiredLocation");
+                            ApexDriverApplication.sharedApplication().setRequiredLocation(requiredLocation);
+
+                            mSelectedModel = null;
+                            handleJson(json);
+
+                        } else {
+
+                            // error
+                            Message msg = new Message();
+                            msg.what = HomeHandler.HomeActionError;
+
+                            String errMsg = json.optString("err_msg");
+                            if (errMsg == null || errMsg.length() == 0) {
+                                errMsg = "Sorry,there is something wrong";
+                            }
+                            msg.obj = errMsg;
+
+                            mHandler.sendMessage(msg);
+
+                        }
+
+                    } catch (JSONException e) {
+                        e.printStackTrace();
+                        // error
+                        Message msg = new Message();
+                        msg.what = HomeHandler.HomeActionError;
+
+                        String errMsg = "Sorry,there is something wrong";
+                        msg.obj = errMsg;
+
+                        mHandler.sendMessage(msg);
+                    }
+                } else {
+                    // error;
+                    Message msg = new Message();
+                    msg.what = HomeHandler.HomeActionError;
+
+                    String errMsg = "Sorry,there is something wrong";
+                    msg.obj = errMsg;
+
+                    mHandler.sendMessage(msg);
+                }
+
+            }
+
+        }).start();
+
+    }
+
+    private void updateDriverAvailable(final boolean available) {
+
+        showProgressDialog();
+        ApexDriverApplication.sharedApplication().getNetworkQueue().addOperationTask(new OperationQueue.OperationBackgroundCallBack() {
+            @Override
+            public Object operationDoInBackground() {
+
+                return com.usai.redant.apexdrivers.network.Network.updateDriverAvailable(available);
+            }
+        }, new OperationQueue.OperationCompletionCallBack() {
+            @Override
+            public void operationCompletion(Object object) {
+
+                dismissProgressDialog();
+
+                JSONObject json = (JSONObject)object;
+
+                if (json != null) {
+
+                    int result = json.optInt("result");
+                    if (result == Network.RESULT_TRUE) {
+
+                        mHeaderView.setAvailable(available);
+
+                    } else {
+                        String errMsg = json.optString("err_msg");
+                        if (errMsg == null || errMsg.length() == 0) {
+                            errMsg = "Sorry,there is something wrong";
+                        }
+                        showWarningMsg(errMsg);
+                    }
+                } else {
+                    String errMsg = "Sorry,there is something wrong";
+
+                    showWarningMsg(errMsg);
+                }
+
+
+            }
+        },null);
+    }
+
+    @Override
+    public void signoutClick() {
+        ((MainActivity)getActivity()).logout();
+    }
+
+    @Override
+    public void settingClick() {
+        com.usai.redant.apexdrivers.setting.SettingActivity.startSettingActivity(mCtx);
+    }
+
+    @Override
+    public void availableClick() {
+
+        final boolean available = mHeaderView.isAvailable();
+        String msg = "are you sure to change status to " + (available ? "Unavailable" : "Available");
+        new AlertDialog.Builder(mCtx)
+                .setTitle("Warning")
+                .setMessage(msg)
+                .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        updateDriverAvailable(!available);
+                    }
+                })
+                .setNegativeButton("Cancel",null)
+                .show();
+
+    }
+
+    @Override
+    public void messageClick() {
+        MessageActivity.startMessageActivity(mCtx);
+    }
+
+    private static final class HomeHandler extends Handler {
+
+        static final int HomeActionReloadData = 0;
+        static final int HomeActionError = 1;
+
+        WeakReference<HomeFragment> mWeakHome;
+
+        public HomeHandler(HomeFragment fragment) {
+            mWeakHome = new WeakReference<>(fragment);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            super.handleMessage(msg);
+
+            HomeFragment fragment = mWeakHome.get();
+            fragment.dismissProgressDialog();
+            if (fragment.mRefresh.isRefreshing()) {
+                fragment.mRefresh.setRefreshing(false);
+            }
+
+            switch (msg.what) {
+                case HomeActionReloadData: {
+
+                    /**
+                     * 手动调用打开/关闭 Group,否则默认关闭
+                     * */
+                    for (int i = 0; i < fragment.mSectionArray.size(); i++) {
+                        HomeSectionModel sectionModel = fragment.mSectionArray.get(i);
+                        fragment.mListView.expandGroup(i);
+                    }
+
+                    fragment.mAdapter.notifyDataSetChanged();
+
+                    JSONObject json = (JSONObject) msg.obj;
+                    if (json != null) {
+                        boolean driver_available = json.optBoolean("driver_available");
+
+                        int new_count = json.optInt("new_count");
+                        int processing_count = json.optInt("processing_count");
+                        int finish_count = json.optInt("finish_count");
+                        int message_count = json.optInt("message_count");
+
+                        if (fragment.mHeaderView != null) {
+                            fragment.mHeaderView.setAvailable(driver_available);
+                            fragment.mHeaderView.setNewCount(new_count);
+                            fragment.mHeaderView.setProcessingCount(processing_count);
+                            fragment.mHeaderView.setFinishCount(finish_count);
+                            fragment.mHeaderView.setExistNewMessage(message_count > 0);
+                        }
+                    }
+                }
+                break;
+                case HomeActionError: {
+                    String errMsg = (String) msg.obj;
+                    fragment.showWarningMsg(errMsg);
+                }
+                default:{
+
+                }
+                break;
+            }
+        }
+    }
+
+
+    private static int HOME_CELL_TYPE_SECTION_HEADER    = 0;
+    private static int HOME_CELL_TYPE_ORDER             = 1;
+
+    private class HomeListAdapter extends BaseExpandableListAdapter {
+
+        private class SectionHeaderHolder implements View.OnClickListener,HomeSectionModel.SectionModelListener{
+
+            TextView titleTv;
+            BadgeView badgeView;
+            Button expandBtn;
+            WeakReference<HomeSectionModel> weakSectionModel;
+
+            SectionHeaderHolder(View view) {
+
+                titleTv = view.findViewById(R.id.header_title_tv);
+                badgeView = view.findViewById(R.id.header_badge_tv);
+                badgeView.setBackgroundColor(getResources().getColor(R.color.ApexDriverRedColor));
+                expandBtn = view.findViewById(R.id.section_switch_btn);
+                expandBtn.setOnClickListener(this);
+                expandBtn.setText("More");
+
+                view.setTag(this);
+            }
+
+            void bindSectionModel(HomeSectionModel model) {
+                if (weakSectionModel != null && weakSectionModel.get() != null) {
+                    weakSectionModel.get().listener = null;
+                }
+                if (model == null) {
+                    weakSectionModel = null;
+                } else {
+                    weakSectionModel = new WeakReference<>(model);
+                }
+                if (weakSectionModel != null) {
+                    weakSectionModel.get().listener = this;
+                }
+                refresh();
+            }
+
+            @Override
+            public void onClick(View v) {
+                HomeSectionModel sectionModel = weakSectionModel.get();
+
+                Intent intent = HomeMoreActivity.build(mCtx,sectionModel.type,sectionModel.title);
+
+                startActivityForResult(intent,REQUEST_MORE_CODE);
+            }
+
+            @Override
+            public void refresh() {
+
+                HomeSectionModel model = null;
+                if (weakSectionModel != null) {
+                    model = weakSectionModel.get();
+                }
+
+                if (model != null) {
+                    titleTv.setText(model.title);
+                    badgeView.setBadgeCount(model.backendFlagCount);
+                    if (model.hasMore()) {
+                        expandBtn.setVisibility(View.VISIBLE);
+                    } else {
+                        expandBtn.setVisibility(View.GONE);
+                    }
+                } else {
+                    titleTv.setText(null);
+                    expandBtn.setVisibility(View.GONE);
+                }
+
+
+            }
+        }
+
+        private class OrderCellHolder {
+
+            WeakReference<HomeCellLayout> weakCell;
+
+            OrderCellHolder(View view) {
+
+                view.setTag(this);
+                weakCell = new WeakReference<>((HomeCellLayout) view);
+            }
+
+            void bindOrderModel(HomeOrderModel model) {
+
+                if (weakCell != null && weakCell.get() != null) {
+                    weakCell.get().bindOrderModel(model);
+                }
+            }
+
+        }
+
+        @Override
+        public int getGroupCount() {
+            if (mSectionArray == null) {
+                return 0;
+            }
+            return mSectionArray.size();
+        }
+
+        @Override
+        public int getChildrenCount(int groupPosition) {
+
+            HomeSectionModel sectionModel = mSectionArray.get(groupPosition);
+
+            int childrenCount = sectionModel.orderCount();
+
+            return childrenCount;
+        }
+
+        @Override
+        public Object getGroup(int groupPosition) {
+            HomeSectionModel sectionModel = mSectionArray.get(groupPosition);
+            return sectionModel;
+        }
+
+        @Override
+        public Object getChild(int groupPosition, int childPosition) {
+
+            HomeSectionModel sectionModel = mSectionArray.get(groupPosition);
+            return sectionModel.orderModelForIndex(childPosition);
+        }
+
+        @Override
+        public int getChildType(int groupPosition, int childPosition) {
+            return 0;
+        }
+
+        @Override
+        public int getChildTypeCount() {
+            return 2;
+        }
+
+        @Override
+        public long getGroupId(int groupPosition) {
+            return groupPosition;
+        }
+
+        @Override
+        public long getChildId(int groupPosition, int childPosition) {
+            return childPosition;
+        }
+
+        @Override
+        public boolean hasStableIds() {
+            return true;
+        }
+
+        @Override
+        public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
+
+            View header;
+            SectionHeaderHolder holder;
+            if (convertView == null) {
+
+                header = LayoutInflater.from(mCtx).inflate(R.layout.section_header,null);
+                holder = new SectionHeaderHolder(header);
+
+            } else {
+                header = convertView;
+                holder = (SectionHeaderHolder)convertView.getTag();
+            }
+
+            HomeSectionModel sectionModel = mSectionArray.get(groupPosition);
+            holder.bindSectionModel(sectionModel);
+
+            return header;
+        }
+
+        @Override
+        public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
+
+
+            View cell;
+            OrderCellHolder holder;
+            if (convertView == null) {
+
+                cell = LayoutInflater.from(mCtx).inflate(R.layout.home_order_cell,null);
+                holder = new OrderCellHolder(cell);
+
+            } else {
+
+                cell = convertView;
+                holder = (OrderCellHolder)convertView.getTag();
+            }
+
+            HomeSectionModel sectionModel = mSectionArray.get(groupPosition);
+            HomeOrderModel orderModel = sectionModel.orderModelForIndex(childPosition);
+
+            holder.bindOrderModel(orderModel);
+
+            return cell;
+        }
+
+        @Override
+        public boolean isChildSelectable(int groupPosition, int childPosition) {
+            return true;
+        }
+
+        @Override
+        public void onGroupExpanded(int groupPosition) {
+            super.onGroupExpanded(groupPosition);
+        }
+
+        @Override
+        public void onGroupCollapsed(int groupPosition) {
+            super.onGroupCollapsed(groupPosition);
+        }
+    }
+
+    private static class HomeSectionModel {
+
+        public interface SectionModelListener {
+            void refresh();
+        }
+
+
+
+        int type; // OrderStatus
+        ArrayList<HomeOrderModel> orders;
+        String title;
+        int section;
+        long totalCount;
+        int backendFlagCount;
+        SectionModelListener listener;
+
+        private Context mContext;
+        public HomeSectionModel(Context context) {
+            mContext = context;
+        }
+
+        public void setBackendFlagCount(int count) {
+            backendFlagCount = count;
+
+            if (listener != null) {
+                listener.refresh();
+            }
+        }
+
+        int orderCount() {
+            if (orders == null) {
+                return 0;
+            }
+
+            return orders.size();
+        }
+
+        boolean hasMore() {
+            return totalCount > orderCount();
+        }
+
+        void setOrders(JSONArray orders) {
+            if (orders == null) {
+                this.orders = null;
+                return;
+            }
+            ArrayList<HomeOrderModel> orderArr = new ArrayList<>();
+            for (int i = 0; i < orders.length(); i++) {
+                JSONObject order = orders.optJSONObject(i);
+                if (order != null) {
+                    HomeOrderModel model = new HomeOrderModel(mContext);
+                    model.status = order.optInt("status",0);
+                    model.title = order.optString("title");
+                    model.orderNo = order.optString("orderNo");
+                    model.containerNo = order.optString("containerNo");
+                    model.date = order.optString("date");
+                    model.orderID = order.optString("orderID");
+                    model.order_type2 = order.optString("order_type2");
+                    model.setIconURL(order.optString("iconURL"));
+                    model.setBackendFlag(order.optBoolean("backendFlag"));
+                    model.status_no = order.optString("status_no");
+
+                    orderArr.add(model);
+                }
+            }
+            this.orders = orderArr;
+        }
+
+        HomeOrderModel orderModelForIndex(int index) {
+            if (index >= orders.size()) {
+                return null;
+            }
+
+            return orders.get(index);
+        }
+
+        int orderModelIndexForID(String id) {
+
+            if (id == null) {
+                return 0;
+            }
+
+            for (int i = 0; i < orders.size(); i++) {
+
+                HomeOrderModel model = orders.get(i);
+                if(model.orderID != null && model.orderID.equals(id)) {
+                    return i;
+                }
+            }
+            return 0;
+        }
+
+    }
+
+
+    /**
+     * Receiver
+     * */
+
+    private class HomeBroadCastReceiver extends BroadcastReceiver {
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+
+            if (intent.getAction() == HomeReloadBroadcastAction) {
+                loadData();
+            }
+
+        }
+    }
+}

+ 200 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/home/HomeHeaderView.java

@@ -0,0 +1,200 @@
+package com.usai.redant.apexdrivers.home;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.usai.redant.apexdrivers.R;
+
+public class HomeHeaderView extends RelativeLayout {
+
+    public interface HomeHeaderDelegate {
+        void signoutClick();
+        void settingClick();
+        void availableClick();
+        void messageClick();
+    }
+
+    private Context mCtx;
+
+    public static HomeHeaderView headerView(Context ctx) {
+        HomeHeaderView headerView = (HomeHeaderView) LayoutInflater.from(ctx).inflate(R.layout.home_header_view,null);
+        return headerView;
+    }
+
+    public HomeHeaderView(Context context) {
+        this(context,null);
+    }
+
+    public HomeHeaderView(Context context, AttributeSet attrs) {
+        this(context, attrs,0);
+    }
+
+    public HomeHeaderView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        mCtx = context;
+    }
+
+    private boolean initialized = false;
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        if (!initialized) {
+            init();
+        }
+    }
+
+    private TextView newCountTv,processingCountTv,finishCountTv,availableTv;
+    private ImageView signoutBtn,settingBtn,availableBtn,messageBtn;
+    private ImageView messageFlagView;
+
+    private boolean available,existNewMessage;
+    private HomeHeaderDelegate delegate;
+    private int newCount,processingCount,finishCount;
+
+
+    private void init() {
+
+        newCountTv = findViewById(R.id.home_header_new_count_tv);
+        processingCountTv = findViewById(R.id.home_header_processing_count_tv);
+        finishCountTv = findViewById(R.id.home_header_finish_count_tv);
+        availableTv = findViewById(R.id.home_header_available_tv);
+
+        signoutBtn = findViewById(R.id.home_header_sign_out_iv);
+        settingBtn = findViewById(R.id.home_header_setting_iv);
+        availableBtn = findViewById(R.id.home_header_available_iv);
+        messageBtn = findViewById(R.id.home_header_message_iv);
+
+        messageFlagView = findViewById(R.id.home_header_message_flag_view);
+
+        setAvailable(false);
+        setExistNewMessage(false);
+
+        signoutBtn.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (delegate != null) {
+                    delegate.signoutClick();
+                }
+            }
+        });
+
+        settingBtn.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (delegate != null) {
+                    delegate.settingClick();
+                }
+            }
+        });
+
+        availableBtn.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (delegate != null) {
+                    delegate.availableClick();
+                }
+            }
+        });
+
+        messageBtn.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (delegate != null) {
+                    delegate.messageClick();
+                }
+            }
+        });
+
+        initialized = true;
+    }
+
+    private String textFromCount(int count) {
+
+        if (count >= 0) {
+            return String.format("%d",count);
+        }
+
+        return null;
+    }
+
+    public void setNewCount(int newCount) {
+        this.newCount = newCount;
+
+        newCountTv.setText(textFromCount(newCount));
+    }
+
+    public void setProcessingCount(int processingCount) {
+        this.processingCount = processingCount;
+
+        processingCountTv.setText(textFromCount(processingCount));
+    }
+
+    public void setFinishCount(int finishCount) {
+        this.finishCount = finishCount;
+
+        finishCountTv.setText(textFromCount(finishCount));
+    }
+
+    public int getNewCount() {
+        return newCount;
+    }
+
+    public int getProcessingCount() {
+        return processingCount;
+    }
+
+    public int getFinishCount() {
+        return finishCount;
+    }
+
+    public void setAvailable(boolean available) {
+        this.available = available;
+
+        if (available) {
+
+            availableBtn.setImageResource(R.drawable.action_available);
+            availableTv.setText("Available");
+
+        } else {
+
+            availableBtn.setImageResource(R.drawable.action_unavailable);
+            availableTv.setText("Unavailable");
+        }
+    }
+
+    public boolean isAvailable() {
+        return available;
+    }
+
+    public void setExistNewMessage(boolean existNewMessage) {
+        this.existNewMessage = existNewMessage;
+
+        if (existNewMessage) {
+            messageBtn.setImageResource(R.drawable.action_message);
+            messageFlagView.setVisibility(VISIBLE);
+
+        } else {
+            messageBtn.setImageResource(R.drawable.action_message);
+            messageFlagView.setVisibility(INVISIBLE);
+        }
+    }
+
+    public boolean isExistNewMessage() {
+        return existNewMessage;
+    }
+
+    public void setDelegate(HomeHeaderDelegate delegate) {
+        this.delegate = delegate;
+    }
+
+    public HomeHeaderDelegate getDelegate() {
+        return delegate;
+    }
+}

+ 665 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/home/HomeMoreActivity.java

@@ -0,0 +1,665 @@
+package com.usai.redant.apexdrivers.home;
+
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.database.DataSetObserver;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AlertDialog;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.apexdrivers.base.BasicActivity;
+import com.usai.redant.apexdrivers.detail.DetailActivity;
+import com.usai.redant.apexdrivers.filter.OrderFilterActivity;
+import com.usai.redant.rautils.utils.Network;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+import static com.usai.redant.rautils.utils.RAUtil.dp2px;
+import static com.usai.redant.rautils.utils.RAUtil.sp2px;
+
+public class HomeMoreActivity extends BasicActivity implements AbsListView.OnScrollListener {
+
+    private final static String OrderTypeKey = "OrderTypeKey";
+    private final static String TitleKey = "TitleKey";
+
+    private final static String SavedJsonKey = "SavedJsonKey";
+    private final static String SavedOrderTypeKey = "SavedOrderTypeKey";
+    private final static String SavedTitleKey = "SavedTitleKey";
+
+
+    private final static int REQUEST_ORDER_FILTER_CODE = 0;
+
+    public static Intent build(Context context, int orderType, String title) {
+
+        if (context == null) {
+            return null;
+        }
+
+        Intent intent = new Intent(context,HomeMoreActivity.class);
+        intent.putExtra(OrderTypeKey,orderType);
+        if (title != null) {
+            intent.putExtra(TitleKey,title);
+        }
+
+        return intent;
+    }
+
+    private final static int limit = 20;
+
+    private ListView mListView;
+    private SwipeRefreshLayout mRefresh;
+    private TextView footer;
+    private Context mContext = this;
+    private ArrayList<HomeOrderModel> mOrders = new ArrayList<>();
+    private HomeMoreListAdapter mAdapter;
+    private HomeMoreHandler mHandler;
+    private int mType;
+    private String mTitle;
+    private HomeOrderModel mSelectedModel;
+
+    private JSONObject mFilter;
+    private String mFilterParams;
+
+    private boolean reloadHome = false;
+    private TextView mEmptyView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_home_more);
+
+        ActionBar actionBar = getSupportActionBar();
+        if (actionBar != null) {
+            actionBar.setHomeButtonEnabled(true);
+            actionBar.setDisplayHomeAsUpEnabled(true);
+        }
+
+        mHandler = new HomeMoreHandler(this);
+
+        mRefresh = findViewById(R.id.home_more_refresh);
+        mRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
+            @Override
+            public void onRefresh() {
+                reload();
+            }
+        });
+
+        mListView = findViewById(R.id.home_more_list_view);
+        configureListView();
+
+        /**
+         * Data
+         * */
+        Intent intent = getIntent();
+        if (intent != null) {
+            mType = intent.getIntExtra(OrderTypeKey,0);
+            mTitle = intent.getStringExtra(TitleKey);
+            setTitle(mTitle);
+        }
+
+        if (savedInstanceState != null) {
+            mType = savedInstanceState.getInt(SavedOrderTypeKey);
+            mTitle = savedInstanceState.getString(SavedTitleKey);
+
+            String jsonStr = savedInstanceState.getString(SavedJsonKey);
+            if (jsonStr != null) {
+                try {
+
+                    JSONObject json = new JSONObject(jsonStr);
+                    handleJson(json,LoadDataOptionInitial);
+
+                } catch (JSONException e){
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        if (mOrders.size() == 0) {
+            loadData();
+        }
+    }
+
+    @Override
+    public void finish() {
+
+        Intent data = new Intent();
+        data.putExtra("reload",reloadHome);
+        setResult(RESULT_OK,data);
+
+        super.finish();
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+
+        if (requestCode == REQUEST_ORDER_FILTER_CODE) {
+            if (resultCode == RESULT_OK) {
+                String filter = data.getStringExtra("filter");
+                mFilterParams = filter;
+
+                loadData(LoadDataOptionReload);
+            }
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        int i = item.getItemId();
+
+        if (i == android.R.id.home) {
+            finish();
+            return true;
+        } else if (i == R.id.filter_btn) {
+            showFilter();
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+
+        getMenuInflater().inflate(R.menu.order_filter_menu,menu);
+
+        return true;
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        outState.putInt(SavedOrderTypeKey,mType);
+        String json = prepareOrderForSave();
+        if (json != null) {
+            outState.putString(SavedJsonKey,json);
+        }
+        if (mTitle != null) {
+            outState.putString(SavedTitleKey,mTitle);
+        }
+
+    }
+
+    private void showFilter() {
+
+//        mFilter = com.usai.redant.apexdrivers.Network.Network.loadFakeData(this,R.raw.fake_order_filter);
+        Intent intent = OrderFilterActivity.filterActivityIntent(mContext,mFilter);
+
+        startActivityForResult(intent,REQUEST_ORDER_FILTER_CODE);
+    }
+
+    private String prepareOrderForSave() {
+
+        try {
+
+            JSONArray orders = new JSONArray();
+            for (HomeOrderModel model : mOrders) {
+
+                JSONObject json = new JSONObject();
+                json.put("status",model.status);
+                if (model.title != null) {
+                    json.put("title",model.title);
+                }
+                if (model.orderNo != null) {
+                    json.put("orderNo",model.orderNo);
+                }
+                if (model.orderID != null) {
+                    json.put("orderID",model.orderID);
+                }
+                if (model.containerNo != null) {
+                    json.put("containerNo",model.containerNo);
+                }
+                if (model.date != null) {
+                    json.put("date",model.date);
+                }
+                if (model.order_type2 != null) {
+                    json.put("order_type2",model.order_type2);
+                }
+                if (model.iconURL != null) {
+                    json.put("iconURL",model.iconURL);
+                }
+                orders.put(json);
+            }
+
+            JSONObject json = new JSONObject();
+            json.put("orders",orders);
+
+            if (mFilter != null) {
+                json.put("filter",mFilter);
+            }
+
+            return json.toString();
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        return null;
+    }
+
+    /**
+     * Configure
+     * */
+    private void configureListView() {
+
+        footer = new TextView(mContext);
+        footer.setBackgroundColor(Color.WHITE);
+        footer.setGravity(Gravity.CENTER);
+        footer.setText("loading...");
+        footer.setTextSize(sp2px(mContext,7));
+        footer.setTextColor(Color.BLACK);
+        footer.setVisibility(View.INVISIBLE);
+
+        AbsListView.LayoutParams footerLayoutParams = new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,dp2px(mContext,40));
+        footer.setLayoutParams(footerLayoutParams);
+
+        mListView.addFooterView(footer);
+        mListView.setOnScrollListener(this);
+
+        mAdapter = new HomeMoreListAdapter();
+        mListView.setAdapter(mAdapter);
+
+        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+
+                if (position >= mOrders.size()) {
+                    return;
+                }
+
+
+                HomeOrderModel orderModel = mOrders.get(position);
+
+                if (mSelectedModel != null) {
+                    mSelectedModel.setSelection(false);
+                }
+                mSelectedModel = orderModel;
+                if (mSelectedModel != null) {
+                    mSelectedModel.setSelection(true);
+                }
+
+                if (orderModel.getBackendFlag()) {
+                    orderModel.setBackendFlag(!orderModel.getBackendFlag());
+                    reloadHome = true;
+                }
+
+                Intent intent = DetailActivity.build(mContext,orderModel.orderID,orderModel.status,orderModel.order_type2,orderModel.status_no);
+                mContext.startActivity(intent);
+
+            }
+        });
+
+        mEmptyView = findViewById(R.id.home_more_empty_btn);
+        mEmptyView.setText("There is no data\nPlease click to reload");
+        mEmptyView.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                loadData();
+            }
+        });
+        mListView.setEmptyView(findViewById(R.id.home_more_empty_view));
+    }
+
+
+    /**
+     * Scroll Listener
+     * */
+    private int last_index;
+    private int total_index;
+    private boolean isLoading = false;//表示是否正处于加载状态
+
+    @Override
+    public void onScrollStateChanged(AbsListView view, int scrollState) {
+        if(last_index == total_index && (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE))
+        {
+            // 表示此时需要显示刷新视图界面进行新数据的加载(要等滑动停止)
+            if(!isLoading)
+            {
+                // 设置刷新界面可见
+                footer.setVisibility(View.VISIBLE);
+                loadMore();
+            }
+        }
+    }
+
+    @Override
+    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
+        last_index = firstVisibleItem+visibleItemCount;
+        total_index = totalItemCount;
+    }
+
+    /**
+     * Alert/Dialog
+     * */
+    private ProgressDialog mProgressDialog;
+    private void showProgressDialog() {
+        if (mProgressDialog == null) {
+            mProgressDialog = new ProgressDialog(mContext);
+            mProgressDialog.setMessage("loading...");
+            mProgressDialog.setCancelable(false);
+            mProgressDialog.show();
+        }
+    }
+
+    private void dismissProgressDialog() {
+        if (mProgressDialog != null && mProgressDialog.isShowing()) {
+            mProgressDialog.dismiss();
+        }
+        mProgressDialog = null;
+    }
+
+    private void showWarningMsg(String msg) {
+        if (msg == null || msg.length() == 0) {
+            return;
+        }
+
+        new AlertDialog.Builder(mContext).setTitle("Warning")
+                .setMessage(msg)
+                .setPositiveButton("Ok",null)
+                .show();
+    }
+
+    /**
+     * Handler
+     * */
+    private static final class HomeMoreHandler extends Handler {
+
+        static final int HomeActionLoadData = 0;
+        static final int HomeActionError = 1;
+
+        WeakReference<HomeMoreActivity> mWeakHome;
+
+        HomeMoreHandler(HomeMoreActivity activity) {
+            mWeakHome = new WeakReference<>(activity);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            super.handleMessage(msg);
+
+            HomeMoreActivity activity = mWeakHome.get();
+            activity.dismissProgressDialog();
+            switch (msg.what) {
+                case HomeActionLoadData: {
+
+                    if (activity.mRefresh.isRefreshing()) {
+                        activity.mRefresh.setRefreshing(false);
+                    }
+
+                    activity.mAdapter.notifyDataSetChanged();
+                    activity.footer.setVisibility(View.INVISIBLE);
+                }
+                break;
+                case HomeActionError: {
+                    String errMsg = (String) msg.obj;
+                    activity.showWarningMsg(errMsg);
+                }
+                default:{
+
+                }
+                break;
+            }
+        }
+    }
+
+    /**
+     * Load Data
+     * */
+    private final int LoadDataOptionInitial = 0;
+    private final int LoadDataOptionReload = 1;
+    private final int LoadDataOptionLoadMore = 2;
+
+    private void handleJson(JSONObject json, int option) {
+
+        if (json != null) {
+
+            mFilter = json.optJSONObject("filter");
+
+            JSONArray orders = json.optJSONArray("orders");
+            if (orders != null) {
+                if (option != LoadDataOptionLoadMore) {
+                    mOrders.clear();
+                }
+
+                for (int i = 0; i < orders.length(); i++) {
+                    JSONObject order = orders.optJSONObject(i);
+                    if (order != null) {
+                        HomeOrderModel model = new HomeOrderModel(mContext);
+                        model.status = order.optInt("status", 0);
+                        model.title = order.optString("title");
+                        model.orderNo = order.optString("orderNo");
+                        model.containerNo = order.optString("containerNo");
+                        model.date = order.optString("date");
+                        model.orderID = order.optString("orderID");
+                        model.order_type2 = order.optString("order_type2");
+                        model.setIconURL(order.optString("iconURL"));
+                        model.setBackendFlag(order.optBoolean("backendFlag"));
+                        model.status_no = order.optString("status_no");
+
+                        mOrders.add(model);
+                    }
+                }
+            }
+        }
+
+
+
+        Message msg = new Message();
+        msg.what = HomeMoreHandler.HomeActionLoadData;
+        mHandler.sendMessage(msg);
+    }
+
+    private void loadData(final int option) {
+
+        if (isLoading) {
+            return;
+        }
+        isLoading = true;
+
+        final int offset = option == LoadDataOptionLoadMore ? mOrders.size() : 0;
+
+        showProgressDialog();
+
+        if (option != LoadDataOptionLoadMore) {
+            if (mOrders != null) {
+                mOrders.clear();
+            }
+            if (mAdapter != null) {
+                mAdapter.notifyDataSetChanged();
+            }
+        }
+
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+
+                JSONObject json = com.usai.redant.apexdrivers.network.Network.requestMoreOrder(mContext,mType,offset,limit,mFilterParams);
+                if (json != null) {
+                    try {
+
+                        int restul = json.getInt("result");
+                        if (restul == Network.RESULT_TRUE) {
+
+                            mSelectedModel = null;
+                            handleJson(json,option);
+
+                        } else {
+
+                            // error
+                            Message msg = new Message();
+                            msg.what = HomeMoreHandler.HomeActionError;
+
+                            String errMsg = json.optString("err_msg");
+                            if (errMsg == null || errMsg.length() == 0) {
+                                errMsg = "Sorry,there is something wrong";
+                            }
+                            msg.obj = errMsg;
+
+                            mHandler.sendMessage(msg);
+
+                        }
+
+                    } catch (JSONException e) {
+                        e.printStackTrace();
+                        // error
+                        Message msg = new Message();
+                        msg.what = HomeMoreHandler.HomeActionError;
+
+                        String errMsg = "Sorry,there is something wrong";
+                        msg.obj = errMsg;
+
+                        mHandler.sendMessage(msg);
+                    }
+                } else {
+                    // error;
+                    Message msg = new Message();
+                    msg.what = HomeMoreHandler.HomeActionError;
+
+                    String errMsg = "Sorry,there is something wrong";
+                    msg.obj = errMsg;
+
+                    mHandler.sendMessage(msg);
+                }
+                isLoading = false;
+            }
+
+        }).start();
+
+    }
+
+    private void loadData() {
+        loadData(LoadDataOptionInitial);
+    }
+
+    private void reload() {
+        loadData(LoadDataOptionReload);
+    }
+
+    private void loadMore() {
+        loadData(LoadDataOptionLoadMore);
+    }
+
+    /**
+     * Adapter
+     * */
+    private class HomeMoreListAdapter extends BaseAdapter {
+
+        private class OrderCellHolder {
+
+            WeakReference<HomeCellLayout> weakCell;
+
+            OrderCellHolder(View view) {
+
+                view.setTag(this);
+                weakCell = new WeakReference<>((HomeCellLayout) view);
+            }
+
+            void bindOrderModel(HomeOrderModel model) {
+
+                if (weakCell != null && weakCell.get() != null) {
+                    weakCell.get().bindOrderModel(model);
+                }
+            }
+
+        }
+
+        @Override
+        public boolean areAllItemsEnabled() {
+            return super.areAllItemsEnabled();
+        }
+
+        @Override
+        public boolean isEnabled(int position) {
+            return super.isEnabled(position);
+        }
+
+        @Override
+        public void registerDataSetObserver(DataSetObserver observer) {
+            super.registerDataSetObserver(observer);
+        }
+
+        @Override
+        public void unregisterDataSetObserver(DataSetObserver observer) {
+            super.unregisterDataSetObserver(observer);
+        }
+
+        @Override
+        public int getCount() {
+            return mOrders.size();
+        }
+
+        @Override
+        public Object getItem(int position) {
+            return mOrders.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public boolean hasStableIds() {
+            return true;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            View cell;
+            OrderCellHolder holder;
+            if (convertView == null) {
+
+                cell = LayoutInflater.from(mContext).inflate(R.layout.home_order_cell,null);
+                holder = new OrderCellHolder(cell);
+
+            } else {
+
+                cell = convertView;
+                holder = (OrderCellHolder)convertView.getTag();
+            }
+
+            HomeOrderModel orderModel = mOrders.get(position);
+
+            holder.bindOrderModel(orderModel);
+
+            return cell;
+        }
+
+        @Override
+        public int getItemViewType(int position) {
+            return 0;
+        }
+
+        @Override
+        public int getViewTypeCount() {
+            return 1;
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return super.isEmpty();
+        }
+    }
+}

+ 125 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/home/HomeOrderModel.java

@@ -0,0 +1,125 @@
+package com.usai.redant.apexdrivers.home;
+import android.content.Context;
+import android.graphics.Bitmap;
+
+import com.usai.redant.apexdrivers.ApexDriverApplication;
+import com.usai.redant.apexdrivers.utils.OperationQueue;
+import com.usai.redant.rautils.utils.ImageUtil;
+import com.usai.redant.rautils.utils.RAUtil;
+
+import java.net.URI;
+
+public class HomeOrderModel {
+
+    static final int OrderStatusFinish = 3;
+    static final int OrderStatusProcessing = 1;
+    static final int OrderStatusNew = 2;
+
+    int status;
+    String title;
+    String orderNo;
+    String containerNo;
+    String date;
+    String orderID;
+    boolean selected;
+    String order_type2;
+    String iconURL;
+    boolean backendFlag;
+    String status_no;
+
+    OrderModelDelegate delegate;
+
+
+    private Bitmap mIcon;
+    private Context mCtx;
+    HomeOrderModel(Context context) {
+        mCtx = context;
+    }
+
+    public void setSelection(boolean selection) {
+        selected = selection;
+
+        if (delegate != null) {
+            delegate.refresh();
+        }
+    }
+
+    public void setBackendFlag(boolean flag) {
+        backendFlag = flag;
+
+        if (delegate != null) {
+            delegate.refresh();
+        }
+    }
+
+    public boolean getBackendFlag() {
+        return backendFlag;
+    }
+
+    public void setIconURL(String url) {
+        iconURL = url;
+
+        ApexDriverApplication.sharedApplication().getNetworkQueue().addOperationTask(new OperationQueue.OperationBackgroundCallBack() {
+            @Override
+            public Object operationDoInBackground() {
+
+                try {
+
+//                    String url = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1536134884&di=9ea523019212b373ac04f4411e8aa56b&imgtype=jpg&er=1&src=http%3A%2F%2Fp3.music.126.net%2Fyp-UZgqWp6FV_AN4hPozjQ%3D%3D%2F109951163414711232.jpg";
+//                    String url = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1535609923557&di=ae4e880ba67797a64ccb8336f5fdee3f&imgtype=0&src=http%3A%2F%2Fpic48.nipic.com%2F20140909%2F6608733_105101600000_2.jpg";
+
+                    int size = (int) (RAUtil.dp2px(mCtx,50.0f));
+                    Bitmap bitmap = ImageUtil.loadImageFromURL(mCtx,new URI(iconURL), size, size);
+
+//                    bitmap = Bitmap.createScaledBitmap(bitmap, size, size, true);
+
+
+                    return bitmap;
+
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+
+                return null;
+            }
+        }, new OperationQueue.OperationCompletionCallBack() {
+
+            @Override
+            public void operationCompletion(Object object) {
+
+                if (object != null) {
+                    Bitmap bitmap = (Bitmap)object;
+                    setIcon(bitmap);
+                }
+
+            }
+        },null);
+
+    }
+
+    public Bitmap getIcon() {
+
+        if (mCtx == null) {
+            return null;
+        }
+
+//        Bitmap bitmap = BitmapFactory.decodeResource(mCtx.getResources(), R.drawable.status_delivered);
+//
+//        return bitmap;
+
+        return mIcon;
+    }
+
+    public void setIcon(Bitmap icon) {
+        mIcon = icon;
+
+        if (delegate != null) {
+            delegate.refresh();
+        }
+    }
+
+    public interface OrderModelDelegate {
+        void refresh();
+    }
+
+}

+ 375 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/login/LoginFragment.java

@@ -0,0 +1,375 @@
+package com.usai.redant.apexdrivers.login;
+
+//import android.app.Fragment;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.usai.redant.apexdrivers.ApexDriverApplication;
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.apexdrivers.base.BasicDialog;
+import com.usai.redant.apexdrivers.network.Network;
+
+import org.json.JSONObject;
+
+
+/**
+ * Activity which displays a login screen to the user, offering registration as
+ * well.
+ */
+public class LoginFragment extends Fragment/* implements OnClickListener */
+{
+
+
+	public interface LoginCallBack{
+		public abstract void onLogin();
+//		public abstract void onLogout();
+	}
+	private LoginCallBack mCallBack;
+	private String m_sUser;
+	private String m_sPassword;
+	private EditText m_etName;
+	private EditText m_etPassword;
+	private UserLoginTask mAuthTask = null;
+	private TextView mLoginStatusMessageView;
+	private View mLoginFormView;
+	private View mLoginStatusView;
+
+	private CheckBox m_cbSave;
+
+	// SQLiteDatabase m_db;
+	public void setCallBack(LoginCallBack callBack) {
+		this.mCallBack = callBack;
+	}
+//
+//	@Override
+//	public void onSaveInstanceState(Bundle outState) {
+//		super.onSaveInstanceState(outState);
+//		outState.putSerializable("hashmap", mCallBack);
+//	}
+
+	@Override
+	public View onCreateView(LayoutInflater inflater, ViewGroup container,
+			Bundle savedInstanceState) {
+
+		View view = inflater.inflate(R.layout.fragment_login, null);
+		TextView tv_ver = (TextView) view.findViewById(R.id.tv_ver);
+
+		try {
+			tv_ver.setText(getText(R.string.str_ver) + "2.20." + getActivity().getPackageManager().getPackageInfo(getActivity().getApplicationContext().getPackageName(), 0).versionName);
+		} catch (NameNotFoundException e1) {
+			// TODO Auto-generated catch block
+			e1.printStackTrace();
+		}
+
+		// // TODO Auto-generated catch block
+
+		m_etName = (EditText) view.findViewById(R.id.user);
+		// mUserView.setText(mUser);
+
+		m_etPassword = (EditText) view.findViewById(R.id.password);
+
+		m_cbSave = (CheckBox) view.findViewById(R.id.cb_save);
+
+		String u = ApexDriverApplication.sharedApplication().savedUser();
+		String p = ApexDriverApplication.sharedApplication().savedPassword();
+
+		if (u != null && p != null) {
+			try {
+				m_etName.setText(u);
+				m_etPassword.setText(p);
+				m_cbSave.setChecked(true);
+			} catch (Exception e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+
+		}
+
+		m_etPassword.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+					@Override
+					public boolean onEditorAction(TextView textView, int id,
+							KeyEvent keyEvent) {
+						if (id ==666 || id == EditorInfo.IME_ACTION_DONE) {
+
+							InputMethodManager inputMethodManager = (InputMethodManager) getActivity().getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+
+							inputMethodManager.hideSoftInputFromWindow(m_etPassword.getWindowToken(), 0);
+							attemptLogin();
+							return true;
+						}
+						return false;
+					}
+				});
+
+		mLoginFormView = view.findViewById(R.id.login_form);
+		mLoginStatusView = view.findViewById(R.id.login_status);
+		mLoginStatusMessageView = view.findViewById(R.id.login_status_message);
+
+		view.findViewById(R.id.sign_in_button).setOnClickListener(
+				new View.OnClickListener() {
+					@Override
+					public void onClick(View view) {
+						InputMethodManager inputMethodManager = (InputMethodManager) getActivity().getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+
+						inputMethodManager.hideSoftInputFromWindow(m_etPassword.getWindowToken(), 0);
+						attemptLogin();
+						// showProgress(true);
+					}
+				});
+		
+		view.findViewById(R.id.tv_retrieve_pass).setOnClickListener(
+				new View.OnClickListener() {
+					@Override
+					public void onClick(View view) {
+						Intent intent = new Intent();
+						intent.setClass(getActivity(), RetrievePasswordActivity.class);
+						startActivity(intent);
+					}
+				});
+		return view;
+	}
+
+	@Override
+	public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+		super.onViewCreated(view, savedInstanceState);
+
+		if (ApexDriverApplication.sharedApplication().getBackgroundReportType() == ApexDriverApplication.BackgroundReportTypeNone) {
+
+			new BasicDialog(getContext(), new BasicDialog.BasicDialogSetupCallBack() {
+				@Override
+				public View createContentView(final BasicDialog dialog) {
+
+					dialog.setCancelable(false);
+
+					View rootView = LayoutInflater.from(getContext()).inflate(R.layout.report_background_location_dialog,null);
+
+					Button rejectBtn, askBtn, allowBtn;
+
+					rejectBtn = rootView.findViewById(R.id.report_reject_btn);
+					rejectBtn.setOnClickListener(new View.OnClickListener() {
+						@Override
+						public void onClick(View v) {
+
+							ApexDriverApplication.sharedApplication().setBackgroundReportType(ApexDriverApplication.BackgroundReportTypeReject);
+							dialog.dismiss();
+						}
+					});
+
+					askBtn = rootView.findViewById(R.id.report_always_ask_btn);
+					askBtn.setOnClickListener(new View.OnClickListener() {
+						@Override
+						public void onClick(View v) {
+
+							ApexDriverApplication.sharedApplication().setBackgroundReportType(ApexDriverApplication.BackgroundReportTypeAlways);
+							dialog.dismiss();
+						}
+					});
+
+
+					allowBtn = rootView.findViewById(R.id.report_allow_btn);
+					allowBtn.setOnClickListener(new View.OnClickListener() {
+						@Override
+						public void onClick(View v) {
+
+							ApexDriverApplication.sharedApplication().setBackgroundReportType(ApexDriverApplication.BackgroundReportTypeAllow);
+							dialog.dismiss();
+						}
+					});
+
+					return rootView;
+				}
+
+				@Override
+				public int dialogGravity(final BasicDialog dialog) {
+					return BasicDialog.BasicDialogContentGravityCenter;
+				}
+			}).show();
+
+		}
+	}
+
+	@Override
+	public void onCreate(@Nullable Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+	}
+
+	public void attemptLogin() {
+
+		if (mAuthTask != null) {
+			return;
+		}
+
+		// Reset errors.
+		m_etName.setError(null);
+		m_etPassword.setError(null);
+
+		// Store values at the time of the login attempt.
+		m_sUser = m_etName.getText().toString().toLowerCase();
+		m_sPassword = m_etPassword.getText().toString();
+
+		boolean cancel = false;
+		View focusView = null;
+
+		// Check for a valid password.
+		if (TextUtils.isEmpty(m_sPassword)) {
+			m_etPassword.setError(getString(R.string.error_field_required));
+			focusView = m_etPassword;
+			cancel = true;
+		} /*else if (m_sPassword.length() < 4) {
+			m_etPassword.setError(getString(R.string.error_invalid_password));
+			focusView = m_etPassword;
+			cancel = true;
+		}*/
+
+		// Check for a valid user name.
+		if (TextUtils.isEmpty(m_sUser)) {
+			m_etName.setError(getString(R.string.error_field_required));
+			focusView = m_etName;
+			cancel = true;
+		}
+		// else if (!m_sName.contains("@")) {
+		// m_etName.setError(getString(R.string.error_invalid_email));
+		// focusView = m_etName;
+		// cancel = true;
+		// }
+
+		if (cancel) {
+			// There was an error; don't attempt login and focus the first
+			// form field with an error.
+			focusView.requestFocus();
+		} else {
+			// Show a progress spinner, and kick off a background task to
+			// perform the user login attempt.
+			mLoginStatusMessageView.setText(R.string.login_progress_signing_in);
+			showProgress(true);
+			mAuthTask = new UserLoginTask();
+			mAuthTask.execute((Void) null);
+		}
+	}
+
+	//
+	// /**
+	// * Shows the progress UI and hides the login form.
+	// */
+	// @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
+	private void showProgress(final boolean show) {
+		// On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
+		// for very easy animations. If available, use these APIs to fade-in
+		// the progress spinner.
+		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
+			int shortAnimTime = getResources().getInteger(
+					android.R.integer.config_shortAnimTime);
+
+			mLoginStatusView.setVisibility(View.VISIBLE);
+			mLoginStatusView.animate().setDuration(shortAnimTime)
+					.alpha(show ? 1 : 0)
+					.setListener(new AnimatorListenerAdapter() {
+						@Override
+						public void onAnimationEnd(Animator animation) {
+							mLoginStatusView.setVisibility(show ? View.VISIBLE
+									: View.INVISIBLE);
+						}
+					});
+
+			mLoginFormView.setVisibility(View.VISIBLE);
+			mLoginFormView.animate().setDuration(shortAnimTime)
+					.alpha(show ? 0 : 1)
+					.setListener(new AnimatorListenerAdapter() {
+						@Override
+						public void onAnimationEnd(Animator animation) {
+							mLoginFormView.setVisibility(show ? View.INVISIBLE
+									: View.VISIBLE);
+						}
+					});
+		} else {
+			// The ViewPropertyAnimator APIs are not available, so simply show
+			// and hide the relevant UI components.
+			mLoginStatusView
+					.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
+			mLoginFormView.setVisibility(show ? View.INVISIBLE : View.VISIBLE);
+		}
+	}
+
+	public class UserLoginTask extends AsyncTask<Void, Void, JSONObject> {
+
+		@Override
+		protected JSONObject doInBackground(Void... params) {
+
+			return Network.login(m_sUser, m_sPassword);
+
+		}
+
+		@Override
+		protected void onPostExecute(final JSONObject json) {
+			Log.d("onPostExecute", "entry");
+			mAuthTask = null;
+			showProgress(false);
+
+			if (json != null) {
+
+				int result = json.optInt("result");
+				if (result == Network.RESULT_TRUE) {
+
+					ApexDriverApplication.sharedApplication().login(m_sUser.toLowerCase(),m_sPassword);
+
+					if(mCallBack!=null)
+						mCallBack.onLogin();
+
+				} else {
+
+					String msg = json.optString("err_msg");
+					if (msg == null || msg.length() == 0) {
+						msg = "Sorry,there is something wrong";
+					}
+
+					Toast toast = Toast.makeText(getActivity().getApplicationContext(),
+							msg,
+							Toast.LENGTH_LONG);
+
+					toast.setGravity(Gravity.CENTER, 0, 0);
+					toast.show();
+
+				}
+			} else {
+
+				String msg = "Sorry,there is something wrong";
+				Toast toast = Toast.makeText(getActivity().getApplicationContext(),
+						msg,
+						Toast.LENGTH_LONG);
+
+				toast.setGravity(Gravity.CENTER, 0, 0);
+				toast.show();
+			}
+		}
+
+		@Override
+		protected void onCancelled() {
+			mAuthTask = null;
+			showProgress(false);
+		}
+	}
+}

+ 363 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/login/RetrievePasswordActivity.java

@@ -0,0 +1,363 @@
+package com.usai.redant.apexdrivers.login;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.annotation.TargetApi;
+import android.app.AlertDialog.Builder;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AlertDialog;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import com.usai.redant.apexdrivers.ApexDriverApplication;
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.apexdrivers.base.BasicActivity;
+import com.usai.redant.apexdrivers.network.Network;
+
+import org.json.JSONObject;
+
+/**
+ * Activity which displays a login screen to the user, offering registration as
+ * well.
+ */
+public class RetrievePasswordActivity extends BasicActivity
+{
+
+	/**
+	 * Keep track of the login task to ensure we can cancel it if requested.
+	 */
+	private UserLoginTask	mAuthTask	= null;
+
+	// Values for email and password at the time of the login attempt.
+	private String			m_sEmail;
+	private String			m_sUser;
+
+	// UI references.
+	private EditText		mEmailView;
+	private EditText		m_userView;
+	private View			mLoginFormView;
+	private View			mLoginStatusView;
+	private TextView		mLoginStatusMessageView;
+
+
+
+	@Override
+	public boolean onOptionsItemSelected(MenuItem item)
+	{
+		Intent intent = new Intent();
+		switch (item.getItemId())
+		{
+			case android.R.id.home:
+				finish();
+				break;
+
+			default:
+				break;
+		}
+		return super.onOptionsItemSelected(item);
+	}
+
+	private void setCustomActionBar() {
+		ActionBar.LayoutParams lp =new ActionBar.LayoutParams(ActionBar.LayoutParams.MATCH_PARENT, ActionBar.LayoutParams.MATCH_PARENT, Gravity.CENTER);
+		View mActionBarView = LayoutInflater.from(this).inflate(R.layout.actionbar_customtitle, null);
+
+		TextView titleview = mActionBarView.findViewById(R.id.title);
+		titleview.setText("Retrieve Password");
+		setTitle("Retrieve Password");
+
+		ActionBar actionBar = getSupportActionBar();
+		actionBar.setCustomView(mActionBarView, lp);
+//		actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
+//		actionBar.setDisplayShowCustomEnabled(true);
+		actionBar.setDisplayHomeAsUpEnabled(true);
+
+
+		actionBar.setDisplayShowTitleEnabled(true);
+	}
+	@Override
+	protected void onCreate(Bundle savedInstanceState)
+	{
+		super.onCreate(savedInstanceState);
+
+		setContentView(R.layout.activity_retrieve_password);
+
+		setCustomActionBar();
+		// Set up the login form.
+		m_userView = (EditText) findViewById(R.id.user);
+		// m_sEmail = getIntent().getStringExtra(EXTRA_EMAIL);
+		mEmailView = (EditText) findViewById(R.id.email);
+		// mEmailView.setText(m_sEmail);
+
+		mEmailView
+				.setOnEditorActionListener(new TextView.OnEditorActionListener()
+				{
+					@Override
+					public boolean onEditorAction(TextView textView, int id,
+							KeyEvent keyEvent)
+					{
+						if (id == 666
+								|| id == EditorInfo.IME_ACTION_DONE)
+						{
+
+							InputMethodManager inputMethodManager = (InputMethodManager) getApplicationContext()
+									.getSystemService(
+											Context.INPUT_METHOD_SERVICE);
+
+							// EditText editText =
+							// (EditText)findViewById(R.id.xxxx);
+							inputMethodManager.hideSoftInputFromWindow(
+									mEmailView.getWindowToken(), 0); // ����
+							retrivev();
+							return true;
+						}
+						return false;
+					}
+				});
+
+		mLoginFormView = findViewById(R.id.login_form);
+		mLoginStatusView = findViewById(R.id.login_status);
+		mLoginStatusMessageView = (TextView) findViewById(R.id.login_status_message);
+
+		findViewById(R.id.btn_ok).setOnClickListener(new View.OnClickListener()
+		{
+			@Override
+			public void onClick(View view)
+			{
+				InputMethodManager inputMethodManager = (InputMethodManager) getApplicationContext()
+						.getSystemService(Context.INPUT_METHOD_SERVICE);
+
+				// EditText editText =
+				// (EditText)findViewById(R.id.xxxx);
+				inputMethodManager.hideSoftInputFromWindow(
+						mEmailView.getWindowToken(), 0); // ����
+				retrivev();
+			}
+		});
+		findViewById(R.id.btn_close).setOnClickListener(
+				new View.OnClickListener()
+				{
+					@Override
+					public void onClick(View view)
+					{
+						finish();
+					}
+				});
+	}
+
+	//
+	// @Override
+	// public boolean onCreateOptionsMenu(Menu menu) {
+	// super.onCreateOptionsMenu(menu);
+	// getMenuInflater().inflate(R.menu.retrieve_password, menu);
+	// return true;
+	// }
+
+	/**
+	 * Attempts to sign in or register the account specified by the login form.
+	 * If there are form errors (invalid email, missing fields, etc.), the
+	 * errors are presented and no actual login attempt is made.
+	 */
+	public void retrivev()
+	{
+		if (mAuthTask != null)
+		{
+			return;
+		}
+
+		// Reset errors.
+		mEmailView.setError(null);
+		m_userView.setError(null);
+
+		// Store values at the time of the login attempt.
+		m_sEmail = mEmailView.getText().toString();
+		m_sUser = m_userView.getText().toString();
+
+		boolean cancel = false;
+		View focusView = null;
+
+		// Check for a valid password.
+		if (TextUtils.isEmpty(m_sUser))
+		{
+			m_userView.setError(getString(R.string.error_field_required));
+			focusView = m_userView;
+			cancel = true;
+		}
+
+		// Check for a valid email address.
+		if (TextUtils.isEmpty(m_sEmail))
+		{
+			mEmailView.setError(getString(R.string.error_field_required));
+			focusView = mEmailView;
+			cancel = true;
+		}
+		else if (!m_sEmail.contains("@"))
+		{
+			mEmailView.setError(getString(R.string.error_invalid_email));
+			focusView = mEmailView;
+			cancel = true;
+		}
+
+		if (cancel)
+		{
+			// There was an error; don't attempt login and focus the first
+			// form field with an error.
+			focusView.requestFocus();
+		}
+		else
+		{
+			// Show a progress spinner, and kick off a background task to
+			// perform the user login attempt.
+			mLoginStatusMessageView.setText(R.string.login_progress_signing_in);
+			showProgress(true);
+			mAuthTask = new UserLoginTask();
+			mAuthTask.execute((Void) null);
+		}
+	}
+
+	/**
+	 * Shows the progress UI and hides the login form.
+	 */
+	@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
+	private void showProgress(final boolean show)
+	{
+		// On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
+		// for very easy animations. If available, use these APIs to fade-in
+		// the progress spinner.
+		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2)
+		{
+			int shortAnimTime = getResources().getInteger(
+					android.R.integer.config_shortAnimTime);
+
+			mLoginStatusView.setVisibility(View.VISIBLE);
+			mLoginStatusView.animate().setDuration(shortAnimTime)
+					.alpha(show ? 1 : 0)
+					.setListener(new AnimatorListenerAdapter()
+					{
+						@Override
+						public void onAnimationEnd(Animator animation)
+						{
+							mLoginStatusView.setVisibility(show ? View.VISIBLE
+									: View.GONE);
+						}
+					});
+
+			mLoginFormView.setVisibility(View.VISIBLE);
+			mLoginFormView.animate().setDuration(shortAnimTime)
+					.alpha(show ? 0 : 1)
+					.setListener(new AnimatorListenerAdapter()
+					{
+						@Override
+						public void onAnimationEnd(Animator animation)
+						{
+							mLoginFormView.setVisibility(show ? View.GONE
+									: View.VISIBLE);
+						}
+					});
+		}
+		else
+		{
+			// The ViewPropertyAnimator APIs are not available, so simply show
+			// and hide the relevant UI components.
+			mLoginStatusView.setVisibility(show ? View.VISIBLE : View.GONE);
+			mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
+		}
+	}
+
+	private void showAlert(String title,String msg) {
+
+		new AlertDialog.Builder(this)
+				.setTitle(title)
+				.setMessage(msg)
+				.setPositiveButton("Ok",null)
+				.show();
+
+	}
+
+	/**
+	 * Represents an asynchronous login/registration task used to authenticate
+	 * the user.
+	 */
+	public class UserLoginTask extends AsyncTask<Void, Void, JSONObject>
+	{
+		int	errorcode;
+
+		@Override
+		protected JSONObject doInBackground(Void... params)
+		{
+			return Network.retrieve_pass(ApexDriverApplication.sharedApplication(),m_sUser, m_sEmail);
+
+		}
+
+		@Override
+		protected void onPostExecute(final JSONObject json)
+		{
+			Log.d("onPostExecute", "entry");
+			mAuthTask = null;
+			showProgress(false);
+
+			if (json != null) {
+
+				int restul = json.optInt("result");
+				if (restul == Network.RESULT_TRUE) {
+
+					Builder builder = new Builder(
+							RetrievePasswordActivity.this);
+					builder.setMessage(getString(R.string.str_email_sent));
+
+					builder.setTitle(getString(R.string.str_retrieve_success));
+
+					builder.setPositiveButton(getString(android.R.string.ok), new OnClickListener()
+					{
+
+						@Override
+						public void onClick(DialogInterface dialog, int which)
+						{
+							dialog.dismiss();
+
+							RetrievePasswordActivity.this.finish();
+						}
+					});
+
+					builder.create().show();
+
+
+				} else {
+
+					String msg = json.optString("err_msg");
+					if (msg == null || msg.length() == 0) {
+						msg = "Sorry,there is something wrong";
+					}
+					showAlert("Warning",msg);
+				}
+			} else {
+
+				String msg = "Sorry,there is something wrong";
+				showAlert("Warning",msg);
+			}
+
+		}
+
+		@Override
+		protected void onCancelled()
+		{
+			mAuthTask = null;
+			showProgress(false);
+		}
+	}
+}

+ 504 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/message/MessageActivity.java

@@ -0,0 +1,504 @@
+package com.usai.redant.apexdrivers.message;
+
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.usai.redant.apexdrivers.ApexDriverApplication;
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.apexdrivers.base.BasicActivity;
+import com.usai.redant.apexdrivers.detail.DetailActivity;
+import com.usai.redant.apexdrivers.message.model.MessageModel;
+import com.usai.redant.apexdrivers.network.Network;
+import com.usai.redant.apexdrivers.utils.OperationQueue;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.w3c.dom.Text;
+
+import java.util.ArrayList;
+
+import static android.view.MenuItem.SHOW_AS_ACTION_ALWAYS;
+import static com.usai.redant.rautils.utils.RAUtil.dp2px;
+import static com.usai.redant.rautils.utils.RAUtil.sp2px;
+
+public class MessageActivity extends BasicActivity implements AbsListView.OnScrollListener {
+
+    public static void startMessageActivity(Context context) {
+        if (context == null) {
+            return;
+        }
+
+        Intent intent = new Intent(context,MessageActivity.class);
+
+        context.startActivity(intent);
+    }
+
+    private static final String SavedMessagesKey = "SavedMessages";
+    private static final String SavedUnreadKey = "SavedUnread";
+
+    private Context mCtx = this;
+    private MessageActivity self = this;
+
+    private SwipeRefreshLayout mRefresh;
+    private ListView mListView;
+    private MessageAdapter mAdapter;
+    private TextView footer;
+    private TextView mEmptyView;
+
+    private ArrayList<MessageModel> messages = new ArrayList<>();
+    private int offset = 0;
+    private static final int limit = 20;
+    private boolean mUnread = false;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_message);
+
+        ActionBar actionBar = getSupportActionBar();
+        if (actionBar != null) {
+            actionBar.setHomeButtonEnabled(true);
+            actionBar.setDisplayHomeAsUpEnabled(true);
+        }
+
+        mAdapter = new MessageAdapter();
+
+        mRefresh = findViewById(R.id.message_refresh_view);
+        mRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
+            @Override
+            public void onRefresh() {
+                reload();
+            }
+        });
+
+        mListView = findViewById(R.id.message_list_view);
+        configureListView();
+
+        if (savedInstanceState != null) {
+            String messagesStr = savedInstanceState.getString(SavedMessagesKey);
+            if (!TextUtils.isEmpty(messagesStr)) {
+                try {
+                    JSONArray array = new JSONArray(messagesStr);
+                    handleJsonArray(array);
+
+                } catch (JSONException e) {
+                    e.printStackTrace();
+                }
+            }
+
+            boolean unread = savedInstanceState.getBoolean(SavedUnreadKey);
+            mUnread = unread;
+
+        } //
+
+        if (messages.size() == 0) {
+            loadData();
+        }
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        super.onPrepareOptionsMenu(menu);
+
+        menu.clear();
+
+
+        if (mUnread) {
+
+            menu.add(0,0,0,"All");
+            menu.getItem(0).setShowAsAction(SHOW_AS_ACTION_ALWAYS);
+
+        } else {
+
+            menu.add(0,1,0,"Unread");
+            menu.getItem(0).setShowAsAction(SHOW_AS_ACTION_ALWAYS);
+        }
+
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home: {
+                finish();
+                return true;
+            }
+
+            case 0: {
+                unreadBtnClick(false);
+            }
+            break;
+
+            case 1: {
+                unreadBtnClick(true);
+            }
+            break;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        JSONArray array = prepareMessages2Save();
+        if (array != null) {
+            outState.putString(SavedMessagesKey,array.toString());
+        }
+        outState.putBoolean(SavedUnreadKey,mUnread);
+    }
+
+    private void unreadBtnClick(boolean unread) {
+        mUnread = unread;
+
+        invalidateOptionsMenu();
+
+        reload();
+    }
+
+    /**
+     * Scroll Listener
+     * */
+    private int last_index;
+    private int total_index;
+    private boolean isLoading = false;//表示是否正处于加载状态
+
+    @Override
+    public void onScrollStateChanged(AbsListView view, int scrollState) {
+        if(last_index == total_index && (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE))
+        {
+            // 表示此时需要显示刷新视图界面进行新数据的加载(要等滑动停止)
+            if(!isLoading)
+            {
+                // 设置刷新界面可见
+                footer.setVisibility(View.VISIBLE);
+                loadMore();
+            }
+        }
+    }
+
+    @Override
+    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
+        last_index = firstVisibleItem+visibleItemCount;
+        total_index = totalItemCount;
+    }
+
+    private void configureListView() {
+
+        footer = new TextView(mCtx);
+        footer.setBackgroundColor(Color.WHITE);
+        footer.setGravity(Gravity.CENTER);
+        footer.setText("loading...");
+        footer.setTextSize(sp2px(mCtx,7));
+        footer.setTextColor(Color.BLACK);
+        footer.setVisibility(View.INVISIBLE);
+
+        AbsListView.LayoutParams footerLayoutParams = new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,dp2px(mCtx,40));
+        footer.setLayoutParams(footerLayoutParams);
+
+        mListView.addFooterView(footer);
+        mListView.setOnScrollListener(this);
+
+        mListView.setAdapter(mAdapter);
+
+        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+
+                if (position >= self.messages.size()) {
+                    return;
+                }
+
+                MessageModel model = self.messages.get(position);
+                if (model.isNew) {
+                    model.setIsNew(false);
+                }
+
+                Intent intent = DetailActivity.build(mCtx,model.orderID,model.orderType,model.orderType2,model.statusNo);
+                startActivity(intent);
+            }
+        });
+
+        mEmptyView = findViewById(R.id.message_empty_btn);
+        mEmptyView.setText("There is no data\nPlease click to reload");
+        mEmptyView.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                loadData();
+            }
+        });
+        mListView.setEmptyView(findViewById(R.id.message_empty_view));
+    }
+
+    /**
+     * Data
+     * */
+
+    private ProgressDialog mProgressDialog;
+    private void showProgressDialog() {
+        if (mProgressDialog == null) {
+            mProgressDialog = new ProgressDialog(this);
+            mProgressDialog.setMessage("loading...");
+            mProgressDialog.setCancelable(false);
+            mProgressDialog.show();
+        }
+    }
+
+    private void dismissProgressDialog() {
+        if (mProgressDialog != null && mProgressDialog.isShowing()) {
+            mProgressDialog.dismiss();
+        }
+        mProgressDialog = null;
+    }
+
+    private void showWarningMsg(String msg) {
+        if (msg == null || msg.length() == 0) {
+            return;
+        }
+
+        new AlertDialog.Builder(mCtx).setTitle("Warning")
+                .setMessage(msg)
+                .setPositiveButton("Ok",null)
+                .show();
+    }
+
+    private static final int LoadActionTypeInitial = 0;
+    private static final int LoadActionTypeReload = 1;
+    private static final int LoadActionTypeMore = 2;
+
+
+    private void loadData() {
+        offset = 0;
+        loadData(LoadActionTypeInitial);
+    }
+
+    private void reload() {
+        offset = 0;
+        loadData(LoadActionTypeReload);
+    }
+
+    private void loadMore() {
+        offset = messages.size();
+        loadData(LoadActionTypeMore);
+    }
+
+    private void loadData(final int type) {
+
+        if (isLoading) {
+            return;
+        }
+        isLoading = true;
+
+        showProgressDialog();
+
+        ApexDriverApplication.sharedApplication().getNetworkQueue().addOperationTask(new OperationQueue.OperationBackgroundCallBack() {
+
+            @Override
+            public Object operationDoInBackground() {
+
+                return Network.requestMessage(offset, limit,mUnread);
+            }
+        }, new OperationQueue.OperationCompletionCallBack() {
+
+            @Override
+            public void operationCompletion(Object object) {
+
+                dismissProgressDialog();
+
+                if (mRefresh.isRefreshing()) {
+                    mRefresh.setRefreshing(false);
+                }
+
+                JSONObject json = (JSONObject) object;
+                if (json != null) {
+
+                    int result = json.optInt("result");
+                    if (result == com.usai.redant.rautils.utils.Network.RESULT_TRUE) {
+
+                        if (type != LoadActionTypeMore) {
+                            self.messages.clear();
+                        }
+
+                        JSONArray messages = json.optJSONArray("messages");
+
+                        long notificationId = json.optLong("notificationId");
+                        ApexDriverApplication.sharedApplication().setNotificationID(notificationId);
+
+                        handleJsonArray(messages);
+
+                    } else {
+
+                        String msg = json.optString("err_msg");
+                        if (TextUtils.isEmpty(msg)) {
+                            msg = "Sorry,something is wrong";
+                        }
+                        showWarningMsg(msg);
+                    }
+
+                } else {
+                    showWarningMsg("Sorry,something is wrong");
+                }
+
+                footer.setVisibility(View.INVISIBLE);
+                isLoading = false;
+            }
+        }, new OperationQueue.OperationCancelCallBack() {
+            @Override
+            public void operationCancelled() {
+
+                dismissProgressDialog();
+
+                isLoading = false;
+            }
+        });
+
+    }
+
+    private JSONArray prepareMessages2Save() {
+        if (messages.size() > 0) {
+
+            JSONArray array = new JSONArray();
+            for (int i = 0; i < messages.size(); i++) {
+
+                MessageModel model = messages.get(i);
+                JSONObject json = model.toJson();
+                if (json != null) {
+                    array.put(json);
+                }
+            }
+
+            return array;
+        }
+        return null;
+    }
+
+    private void handleJsonArray(JSONArray messages) {
+        if (messages == null) {
+            return;
+        }
+
+        if (messages != null) {
+
+            for (int i = 0; i < messages.length(); i++) {
+                JSONObject msgItem = messages.optJSONObject(i);
+                if (msgItem != null) {
+
+                    MessageModel model = new MessageModel();
+                    model.setValuesForKeysWithJSON(msgItem);
+
+                    self.messages.add(model);
+                }
+            }
+
+        }
+
+        mAdapter.notifyDataSetChanged();
+    }
+
+    private class MessageAdapter extends BaseAdapter {
+
+        @Override
+        public int getCount() {
+            return self.messages.size();
+        }
+
+        @Override
+        public Object getItem(int position) {
+            return self.messages.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+
+
+            Holder holder;
+            if (convertView == null) {
+
+                convertView = LayoutInflater.from(mCtx).inflate(R.layout.message_item_cell,null);
+                holder = new Holder(convertView);
+
+            } else {
+                holder = (Holder)convertView.getTag();
+            }
+
+            MessageModel model = self.messages.get(position);
+            holder.setModel(model);
+
+            return convertView;
+        }
+
+
+        private class Holder implements MessageModel.MessageModelDelegate{
+
+            TextView titleTv,msgTv,dateTv;
+            ImageView newMark;
+
+            Holder(View v) {
+                v.setTag(this);
+
+                titleTv = v.findViewById(R.id.message_title_tv);
+                msgTv = v.findViewById(R.id.message_msg_tv);
+                dateTv = v.findViewById(R.id.message_date_tv);
+                newMark = v.findViewById(R.id.message_new_mark);
+            }
+
+            public MessageModel model;
+            public void setModel(MessageModel model) {
+                if (this.model != null) {
+                    this.model.delegate = null;
+                }
+                this.model = model;
+                if (this.model != null) {
+                    this.model.delegate = this;
+                }
+
+                refreshUI();
+            }
+
+            @Override
+            public void refreshUI() {
+
+                if (model != null) {
+
+                    titleTv.setText(model.title);
+                    msgTv.setText(model.message);
+                    dateTv.setText(model.date);
+                    newMark.setVisibility(model.isNew ? View.VISIBLE : View.INVISIBLE);
+
+                } else {
+                    titleTv.setText(null);
+                    msgTv.setText(null);
+                    dateTv.setText(null);
+                    newMark.setVisibility(View.INVISIBLE);
+                }
+            }
+        }
+    }
+}

+ 69 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/message/model/MessageModel.java

@@ -0,0 +1,69 @@
+package com.usai.redant.apexdrivers.message.model;
+
+import com.usai.redant.apexdrivers.base.BasicObject;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class MessageModel extends BasicObject {
+
+    public interface MessageModelDelegate {
+        void refreshUI();
+    }
+
+    public String title;
+    public String message;
+    public String date;
+    public boolean isNew;
+
+    public String orderID;
+    public String orderType2;
+    public String statusNo;
+    public int orderType;
+
+    public MessageModelDelegate delegate;
+
+    public void setIsNew(boolean isNew) {
+        this.isNew = isNew;
+
+        if (delegate != null) {
+            delegate.refreshUI();
+        }
+    }
+
+    public JSONObject toJson() {
+
+        try {
+
+            JSONObject json = new JSONObject();
+
+            if (title != null) {
+                json.put("title",title);
+            }
+            if (message != null) {
+                json.put("message",message);
+            }
+            if (date != null) {
+                json.put("date",date);
+            }
+            json.put("isNew",isNew);
+
+            if (orderID != null) {
+                json.put("orderID",orderID);
+            }
+            if (orderType2 != null) {
+                json.put("orderType2",orderType2);
+            }
+            if (statusNo != null) {
+                json.put("statusNo",statusNo);
+            }
+            json.put("orderType",orderType);
+
+            return json;
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+}

+ 412 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/network/Network.java

@@ -0,0 +1,412 @@
+package com.usai.redant.apexdrivers.network;
+
+
+import android.app.Application;
+import android.content.Context;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+
+import com.usai.redant.apexdrivers.ApexDriverApplication;
+import com.usai.redant.rautils.utils.RAUtil;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import static java.lang.Thread.sleep;
+
+public class Network extends com.usai.redant.rautils.utils.Network {
+
+//    private static final String URL_HOST = "http://192.168.0.124:8080/t";
+    private static final String URL_HOST = "https://ra.apexshipping.com/t";
+
+
+    public static final String URL_LOGIN = URL_HOST  + "/mobile/login.mo/";
+    public static final String URL_HOME = URL_HOST + "/mobile/truckPage.mo/";
+    public static final String URL_MORE_ORDER = URL_HOST + "/mobile/morePickupOrders.mo/";
+    public static final String URL_DETAIL = URL_HOST + "/mobile/orderDetails.mo/";
+    public static final String URL_UPDATE = URL_HOST + "/mobile/actionDetails.mo/";
+    public static final String URL_SUBMIT = URL_HOST + "/mobile/updateContainerData.mo/";
+    public static final String URL_UPLOAD = URL_HOST + "/mobile/uploadContainerFiles.mo/";
+    public static final String URL_LOGOUT = URL_HOST + "/mobile/loginOut.mo/";
+    public static final String URL_REPORT_LOCATION = URL_HOST + "/mobile/uploadDriverLocation.mo/";
+    public static final String URL_RETRIVED_PASSWORD = URL_HOST + "/mobile/resetPassword.mo/";
+    public static final String URL_DRIVER_AVAILABLE = URL_HOST + "/mobile/updateDriverAvailable.mo/";
+    public static final String URL_CHANGE_PASSWORD = URL_HOST + "/mobile/changePassword.mo/";
+    public static final String URL_MESSAGE_LIST = URL_HOST + "/mobile/moreDriverMessages.mo/";
+    public static final String URL_UPLOAD_TOKEN = URL_HOST + "/mobile/uploadToken.mo/";
+    public static final String URL_PULL_NOTIFICATION = URL_HOST + "/mobile/pullNotification.mo/";
+
+    public static final int PUSH_TIME_OUT_INTERVAL = 5 * 1000;
+
+    private static void prepareParams(Bundle params) {
+        if (params == null) {
+            params = new Bundle();
+        }
+        String user = ApexDriverApplication.sharedApplication().encryptUser();
+        String password = ApexDriverApplication.sharedApplication().encryptPassword();
+
+        if (user != null && user.length() > 0 && password != null && password.length() != 0) {
+            params.putString("name",user); // "WMoy059hzweXPSC74plhBA=="
+            params.putString("password",password); // "CzADM7u51eNR7ScfQtJw7w=="
+        }
+
+        params.putString("platform","android");
+    }
+
+    private static JSONObject handleJson(String jsonStr) {
+
+        if (jsonStr == null || jsonStr.isEmpty()) {
+            return null;
+        }
+
+        try {
+            JSONObject json = new JSONObject(jsonStr);
+            String msg = json.optString("msg");
+            if (msg != null) {
+                json.put("err_msg",msg);
+            }
+
+            return json;
+        } catch (JSONException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public static boolean NetworkIsAvailable(Application application) {
+        return com.usai.redant.rautils.utils.Network.isNetworkAvailable(application);
+    }
+
+    public static String getJson(String url, Bundle parms) {
+        return getJson(url,parms, com.usai.redant.rautils.utils.Network.SO_TIMEOUT);
+    }
+
+    public static String getJSON(String url, Bundle params, int timeout) {
+        return getJson(url,params,timeout);
+    }
+
+    public static JSONObject retrieve_pass(Application application, String user, String email) {
+
+        Bundle parms = new Bundle();
+        parms.putString("name", user);
+        parms.putString("email", email);
+
+        parms.putString("module_name", "Retrieve Password");
+        parms.putString("action", "handset_search");
+
+
+        String jsonStr = getJson(URL_RETRIVED_PASSWORD, parms);
+
+        return handleJson(jsonStr);
+
+    }
+
+    public static JSONObject login(String user, String password) {
+
+        Bundle params = new Bundle();
+
+        user = ApexDriverApplication.sharedApplication().encryptString(user);
+        password = ApexDriverApplication.sharedApplication().encryptString(password);
+
+        if (user != null) {
+            params.putString("name",user); // "WMoy059hzweXPSC74plhBA=="
+        }
+        if (password != null) {
+            params.putString("password",password); // "CzADM7u51eNR7ScfQtJw7w=="
+        }
+
+        prepareParams(params);
+
+        String jsonStr = getJson(URL_LOGIN,params);
+//        if (jsonStr == null || jsonStr.isEmpty()) {
+//            return RESULT_NET_ERROR;
+//        }
+//
+//        try {
+//            JSONObject json = new JSONObject(jsonStr);
+//            int result = json.getInt("result");
+//
+//            return result;
+//
+//        } catch (JSONException e) {
+//            e.printStackTrace();
+//            return RESULT_ERROR;
+//        }
+
+        return handleJson(jsonStr);
+    }
+
+    public static JSONObject requestOrderList(Context context) {
+
+        Bundle params = new Bundle();
+
+        prepareParams(params);
+        String jsonStr = getJson(URL_HOME,params);
+
+        return handleJson(jsonStr);
+    }
+
+    public static JSONObject requestMoreOrder(Context context, int orderType, int offset, int limit,String filter) {
+
+        Bundle params = new Bundle();
+        params.putInt("type",orderType);
+        params.putInt("offset",offset);
+        params.putInt("limit",limit);
+
+        if (filter != null && filter.length() > 0) {
+            params.putString("filter",filter);
+        }
+
+        prepareParams(params);
+        String jsonStr = getJson(URL_MORE_ORDER,params);
+
+        return handleJson(jsonStr);
+
+    }
+
+    public static JSONObject requestOrderDetail(Context context,String orderID,int status,String type2,String statusNo) {
+
+
+        if (orderID == null) {
+            return null;
+        }
+
+        Bundle params = new Bundle();
+        params.putString("orderID",orderID);
+        if (type2 != null) {
+            params.putString("orderType2",type2);
+        }
+        params.putInt("orderType",status);
+        if (statusNo != null) {
+            params.putString("statusNo",statusNo);
+        }
+
+        prepareParams(params);
+        String jsonStr = getJson(URL_DETAIL,params);
+
+        return handleJson(jsonStr);
+    }
+
+    public static JSONObject requestOrderUpdateInfo(Context context,String orderID, int actionID) {
+
+//        return loadFakeData(context,R.raw.fake_order_edit);
+
+        if (orderID == null) {
+            return null;
+        }
+
+        Bundle params = new Bundle();
+        params.putString("orderID",orderID);
+        params.putInt("action",actionID);
+
+        prepareParams(params);
+        String jsonStr = getJson(URL_UPDATE,params);
+
+        return handleJson(jsonStr);
+    }
+
+    public static JSONObject report(String url, Bundle params) {
+
+
+        if (url == null || url.isEmpty()) {
+            try {
+                return new JSONObject("{\n" +
+                        "  \"result\": 0\n" +
+                        "}");
+            } catch (JSONException e) {
+                e.printStackTrace();
+                return null;
+            }
+        }
+
+        if (params == null) {
+            params = new Bundle();
+        }
+
+        prepareParams(params);
+        String jsonStr = getJson(url,params);
+
+        return handleJson(jsonStr);
+    }
+
+    public static JSONObject submitUpdateParams(Bundle params) {
+
+        if (params == null) {
+            params = new Bundle();
+        }
+
+        prepareParams(params);
+        String jsonStr = getJson(URL_SUBMIT,params);
+
+        return handleJson(jsonStr);
+    }
+
+    public static JSONObject uploadFile(String filePath,Bundle params) {
+
+        prepareParams(params);
+        return com.usai.redant.rautils.utils.Network.uploadFileJSON(filePath,URL_UPLOAD,params,null);
+    }
+
+    public static void logout() {
+
+
+        Bundle params = new Bundle();
+
+        prepareParams(params);
+        getJson(URL_LOGOUT,params);
+    }
+
+    public static void reportLocation(String location, String orderId) {
+
+        Bundle params = new Bundle();
+        if (location != null) {
+            params.putString("location",location);
+        }
+
+        if (orderId != null) {
+            params.putString("orderID",orderId);
+        }
+
+        params.putInt("userOption",0);
+
+        prepareParams(params);
+        getJson(URL_REPORT_LOCATION,params);
+    }
+
+    public static void rejectReportLocation(String reason, String orderId) {
+
+        Bundle params = new Bundle();
+        if (reason != null) {
+            params.putString("reason",reason);
+        }
+
+        if (orderId != null) {
+            params.putString("orderID",orderId);
+        }
+
+        params.putInt("userOption",1);
+
+        prepareParams(params);
+        getJson(URL_REPORT_LOCATION,params);
+    }
+
+    public static JSONObject updateDriverAvailable(boolean available) {
+
+        Bundle params = new Bundle();
+        int a = available ? 1 : 0;
+        params.putInt("available",a);
+
+        prepareParams(params);
+        String jsonStr = getJson(URL_DRIVER_AVAILABLE,params);
+
+        return handleJson(jsonStr);
+    }
+
+    public static JSONObject changePassword(String old,String pass) {
+
+        Bundle params = new Bundle();
+        if (!TextUtils.isEmpty(old)) {
+            params.putString("oldPassword",old);
+        }
+        if (!TextUtils.isEmpty(pass)) {
+            params.putString("newPassword",pass);
+        }
+
+        prepareParams(params);
+        String jsonStr = getJson(URL_CHANGE_PASSWORD,params);
+
+        return handleJson(jsonStr);
+    }
+
+    public static JSONObject requestMessage(int offset,int limit,boolean unread) {
+
+        Bundle params = new Bundle();
+        params.putInt("offset",offset);
+        params.putInt("limit",limit);
+        params.putInt("unread",unread ? 1 : 0);
+
+        prepareParams(params);
+        String jsonStr = getJson(URL_MESSAGE_LIST,params);
+
+        return handleJson(jsonStr);
+    }
+
+    public static JSONObject loadFakeData(Context context, int rawId) {
+
+        try
+        {
+            InputStream in = context.getResources().openRawResource(rawId);
+            BufferedReader reader = new BufferedReader(new InputStreamReader(in, "utf-8"));
+            StringBuffer sb = new StringBuffer();
+
+            String line;
+            while ((line = reader.readLine()) != null)
+            {
+                sb.append(line).append("\n");
+            }
+
+            JSONObject json = new JSONObject(sb.toString());
+            return json;
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    private static JSONObject fakeError() {
+        try {
+            return new JSONObject("{\n" +
+                    "  \"container_photo_0\": \"12346798\",\n" +
+                    "  \"container_photo_1\": \"78945612\",\n" +
+                    "  \"result\": 2\n" +
+                    "}");
+        } catch (JSONException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * @brief 上传推送令牌,在登陆成功的时候
+     * */
+    public static JSONObject uploadToken(String token) {
+
+        Bundle params = new Bundle();
+        if (token != null) {
+            params.putString("token",token);
+        }
+
+        prepareParams(params);
+        String jsonStr = getJson(URL_UPLOAD_TOKEN,params);
+
+        return handleJson(jsonStr);
+
+    }
+
+    public static JSONObject pullNotification() {
+
+        Bundle params = new Bundle();
+        // token
+        params.putString("token", RAUtil.getDeviceId(ApexDriverApplication.sharedApplication().getApplicationContext()));
+
+        // id
+        long id = ApexDriverApplication.sharedApplication().getNotificationID();
+        params.putLong("id",id);
+
+        prepareParams(params);
+
+        String url = "http://192.168.0.130:8080/MyWeb/Test";
+//        String url = URL_UPLOAD_TOKEN;
+        String jsonStr = getJSON(url,params,PUSH_TIME_OUT_INTERVAL);
+
+        return handleJson(jsonStr);
+    }
+
+}

+ 214 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/receiver/ApexDriverAlarmReceiver.java

@@ -0,0 +1,214 @@
+package com.usai.redant.apexdrivers.receiver;
+
+import android.app.AlarmManager;
+import android.app.Application;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.usai.redant.apexdrivers.ApexDriverApplication;
+import com.usai.redant.rautils.receiver.AlarmReceiver;
+import com.usai.redant.rautils.receiver.RABroadcast;
+import com.usai.redant.rautils.utils.Network;
+import com.usai.redant.rautils.utils.RAUtil;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import static com.usai.redant.rautils.utils.Network.RESULT_TRUE;
+
+public class ApexDriverAlarmReceiver extends AlarmReceiver {
+    //负责通过alarm 从后台取notification 列表
+
+
+//    protected static final int	SO_TIMEOUT						= 15 * 1000;
+    @Override
+    protected void InitAlarm(Context context, Intent intent) {
+
+
+        int alarm_timeInterval = aggressive_alarm_timeInterval;
+        if(ApexDriverApplication.sharedApplication().isbackground)
+            alarm_timeInterval = normal_alarm_timeInterval;
+        Log.d("ApexDriverAlarmReceiver", "InitAlarm ");
+
+
+        // 启动完成
+
+        Intent iAlarm = new Intent(RABroadcast.ACTION_REDANT_ALARM);
+
+
+        iAlarm.setClass(context, ApexDriverAlarmReceiver.class);
+
+        PendingIntent sender = PendingIntent.getBroadcast(context, 0,
+                iAlarm, PendingIntent.FLAG_UPDATE_CURRENT);
+
+//        long firstime = SystemClock.elapsedRealtime();
+        AlarmManager am = (AlarmManager) context.getSystemService(
+                Context.ALARM_SERVICE);
+
+
+        if(am==null)
+            return;
+
+        am.cancel(sender);
+
+
+
+        Log.d("ApexDriverAlarmReceiver", "setup up alarm");
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            am.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), sender);
+        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            am.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), sender);
+        } else {
+            am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), alarm_timeInterval, sender);
+        }
+
+    }
+
+    @Override
+    protected void CancelAlarm(Context context, Intent intent) {
+        cancelalarm(context);
+    }
+
+
+    private void cancelalarm(Context context) {
+
+//        // 启动完成
+//        Intent iAlarm = new Intent(context, ApexDriverAlarmReceiver.class);
+//        iAlarm.setAction(RABroadcast.ACTION_PUSHNOTIFICATION_CHECK);
+//        PendingIntent sender = PendingIntent.getBroadcast(context, 0,
+//                iAlarm, PendingIntent.FLAG_UPDATE_CURRENT);
+//
+//        AlarmManager am = (AlarmManager) getSystemService(
+//                Context.ALARM_SERVICE);
+//
+//        am.cancel(sender);
+////        SharedPreferences pref = getSharedPreferences("Apex & Drivers", 0);
+////        SharedPreferences.Editor editor = pref.edit();
+////
+////        editor.putBoolean("enable_notification", false);
+////        editor.commit();
+
+        Intent iAlarm = new Intent(RABroadcast.ACTION_REDANT_ALARM);
+
+
+        iAlarm.setClass(context, ApexDriverAlarmReceiver.class);
+
+        PendingIntent sender = PendingIntent.getBroadcast(context, 0,
+                iAlarm, PendingIntent.FLAG_UPDATE_CURRENT);
+
+//        long firstime = SystemClock.elapsedRealtime();
+        AlarmManager am = (AlarmManager) context.getSystemService(
+                Context.ALARM_SERVICE);
+
+
+        if(am==null)
+            return;
+
+        am.cancel(sender);
+    }
+
+    @Override
+    protected void AlarmProc(Context context, Intent intent) {
+
+        int alarm_timeInterval = aggressive_alarm_timeInterval;
+        if(ApexDriverApplication.sharedApplication().isbackground)
+            alarm_timeInterval = normal_alarm_timeInterval;
+
+        Log.d("ApexDriverAlarmReceiver", "AlarmProc ");
+
+        check_push(context);
+
+
+        Intent iAlarm = new Intent(RABroadcast.ACTION_REDANT_ALARM);
+
+        iAlarm.setClass(context, ApexDriverAlarmReceiver.class);
+        PendingIntent sender = PendingIntent.getBroadcast(context, 0,
+                iAlarm, PendingIntent.FLAG_UPDATE_CURRENT);
+
+
+        AlarmManager am = (AlarmManager) context.getSystemService(
+                Context.ALARM_SERVICE);
+        if(am==null)
+            return;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            am.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + alarm_timeInterval, sender);
+        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            am.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + alarm_timeInterval, sender);
+        }
+    }
+
+//    @Override
+//    public void onReceive(Context context, Intent intent) {
+//        // TODO: This method is called when the BroadcastReceiver is receiving
+//        // an Intent broadcast.
+//        throw new UnsupportedOperationException("Not yet implemented");
+//    }
+
+
+
+    private void check_push(final Context context)
+    {
+
+
+//        String dev_id = RAUtil.getDeviceId(context);
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+
+                if (ApexDriverApplication.sharedApplication().isLogin()) {
+
+                    JSONObject json = com.usai.redant.apexdrivers.network.Network.pullNotification();
+
+                    if (json != null) {
+
+                        int result = json.optInt("result",0);
+                        if (result == RESULT_TRUE) {
+
+                            JSONArray notifications = json.optJSONArray("notifications");
+                            if (notifications != null) {
+
+                                for (int i = 0; i < notifications.length(); i++) {
+                                    JSONObject notification = notifications.optJSONObject(i);
+                                    if (notification != null) {
+                                        ApexDriverApplication.sharedApplication().receiveNotificationOnMainThread(notification);
+                                    }
+                                }
+
+                            }
+
+                        }
+
+                    }
+
+                }
+
+            }
+        }).start();
+
+
+        return ;
+//
+//        new Thread(new Runnable() {
+//            @Override
+//            public void run() {
+//                logout();
+//
+////                runOnUiThread(new Runnable() {
+////                    @Override
+////                    public void run() {
+////                        dismissProgressDialog();
+////                        ApexDriverApplication.sharedApplication().logout();
+////                        showLogin();
+////                    }
+////                });
+//            }
+//        }).start();
+////        return dev_id;
+    }
+
+
+}

+ 72 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/receiver/ApexDriverBootCompleteReceiver.java

@@ -0,0 +1,72 @@
+package com.usai.redant.apexdrivers.receiver;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.util.Log;
+
+import com.usai.redant.apexdrivers.ApexDriversBackgroundService;
+import com.usai.redant.rautils.receiver.BootCompleteBroadcastReceiver;
+import com.usai.redant.rautils.utils.dbgUtil;
+
+public class ApexDriverBootCompleteReceiver extends BootCompleteBroadcastReceiver {
+
+	//负责启动service
+//    @Override
+//    public void onReceive(Context context, Intent intent) {
+//        // TODO: This method is called when the BroadcastReceiver is receiving
+//        // an Intent broadcast.
+//        throw new UnsupportedOperationException("Not yet implemented");
+//    }
+
+    @Override
+    protected void OnBootComplete(Context context, Intent intent) {
+        Log.d("ApexDriver", "OnBootComplete: ");
+
+
+        dbgUtil.fileLog(context,"ApexDriver: OnBootComplete");
+
+
+
+			Intent intentservice = new Intent();
+			intentservice.setClass(context, ApexDriversBackgroundService.class);
+			if (intent.getExtras() != null)
+				intentservice.putExtras(intent.getExtras());
+			intentservice.setAction(intent.getAction());
+//			context.startService(intentservice);
+
+
+		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+			context.startForegroundService(intentservice);
+		} else
+			{
+			context.startService(intentservice);
+		}
+
+    }
+
+	@Override
+	protected void OnLockedBootComplete(Context context, Intent intent) {
+		Log.d("ApexDriver", "OnLockedBootComplete: ");
+
+
+		dbgUtil.fileLog(context,"ApexDriver: OnLockedBootComplete");
+
+
+
+		Intent intentservice = new Intent();
+		intentservice.setClass(context, ApexDriversBackgroundService.class);
+		if (intent.getExtras() != null)
+			intentservice.putExtras(intent.getExtras());
+		intentservice.setAction(intent.getAction());
+		ComponentName cn ;
+		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            cn =context.startForegroundService(intentservice);
+		} else
+        {
+            cn =context.startService(intentservice);
+        }
+		Log.d("ApexDriver", "startservice: "+cn);
+	}
+}

+ 120 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/roundCornerImageView/RoundCornerImageView.java

@@ -0,0 +1,120 @@
+package com.usai.redant.apexdrivers.roundCornerImageView;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+
+public class RoundCornerImageView extends android.support.v7.widget.AppCompatImageView {
+
+    private int roundCorner;
+
+    public RoundCornerImageView(Context context) {
+        this(context,null);
+    }
+
+    public RoundCornerImageView(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs,0);
+    }
+
+    public RoundCornerImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        init();
+    }
+
+    public void setRoundCorner(int roundCorner) {
+        this.roundCorner = roundCorner;
+
+        postInvalidate();
+    }
+
+    public int getRoundCorner() {
+        return roundCorner;
+    }
+
+    Paint mPaint;
+
+    private void init() {
+        mPaint = new Paint();
+        mPaint.setAntiAlias(true);
+        mPaint.setStyle(Paint.Style.FILL);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+
+        // 可操作区域
+        int width = getWidth();
+        int height = getHeight();
+        RectF rect = new RectF(0,0,width,height);
+
+
+        // 原始图片
+        Drawable drawable = getDrawable();
+        Bitmap src = ((BitmapDrawable)drawable).getBitmap();
+
+        // 先缩放原图,适配View大小
+        Bitmap scaleSrc = scaleBitmap(src, width, height);
+
+        // 创建Mask
+        Bitmap mask=Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        Canvas maskCanvas=new Canvas(mask);
+        maskCanvas.drawRoundRect(rect,roundCorner,roundCorner,mPaint);
+
+        /**
+         *
+         * 产生新的layer。新layer相当于一个区域为传递的bounds的“新画布”,它关联一个bitmap(an offscreen bitmap,它是完全透明的),
+         * 之后的绘制操作都在此bitmap上执行。每个layer可以看做一个独立的画布,所有layer形成一个栈,栈底是初始的layer。
+         * 每次在栈顶产生的新layer,任何时候都在栈顶的layer上执行绘图,
+         * 调用restoreToCount()后栈顶layer出栈,其对应的bitmap的内容合并(进行像素的argb混合)到之前layer中。
+         *
+         * */
+//        int sc = canvas.saveLayer(rect,mPaint);
+
+        /**
+         *
+         * 根据saveLayer方法的文档介绍,可以去掉saveLayer()/restoreToCount()的调用,
+         * 只需要在onDraw()中开启硬件加速就可以实现相同的目标了,性能会更好
+         *
+         * */
+        setLayerType(LAYER_TYPE_HARDWARE, mPaint); // 开启硬件加速
+
+        canvas.drawBitmap(mask, 0, 0, mPaint);
+
+        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+
+        canvas.drawBitmap(scaleSrc, 0, 0, mPaint);
+
+        mPaint.setXfermode(null);
+
+//        canvas.restoreToCount(sc);
+
+    }
+
+    private Bitmap scaleBitmap(Bitmap src, int width, int height) {
+
+        if (src == null || width <= 0 || height <= 0) {
+            return src;
+        }
+
+        Rect rct = new Rect(0,0,width,height);
+
+        Bitmap scaleSrc = Bitmap.createBitmap(width, height, src.getConfig());
+
+        Canvas scaleCanvas = new Canvas(scaleSrc);
+
+        scaleCanvas.drawBitmap(src,null,rct,mPaint);
+
+        return scaleSrc;
+    }
+
+}

+ 33 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/scaleimageview/ScaleImageView.java

@@ -0,0 +1,33 @@
+package com.usai.redant.apexdrivers.scaleimageview;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+
+public class ScaleImageView extends android.support.v7.widget.AppCompatImageView {
+
+    public ScaleImageView(Context context) {
+        super(context);
+    }
+
+    public ScaleImageView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public ScaleImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+
+
+        int width = MeasureSpec.getSize(widthMeasureSpec);
+        int height = width * 109 / 253;
+        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height,MeasureSpec.EXACTLY);
+
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+}

+ 290 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/SettingActivity.java

@@ -0,0 +1,290 @@
+package com.usai.redant.apexdrivers.setting;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Handler;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ExpandableListView;
+import android.widget.Toast;
+
+import com.usai.redant.apexdrivers.ApexDriverApplication;
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.apexdrivers.base.BasicActivity;
+import com.usai.redant.apexdrivers.network.Network;
+import com.usai.redant.apexdrivers.setting.changepassword.ChangePasswordDialog;
+import com.usai.redant.apexdrivers.setting.model.ActionModel;
+import com.usai.redant.apexdrivers.setting.model.BaseModel;
+import com.usai.redant.apexdrivers.setting.model.LinkModel;
+import com.usai.redant.apexdrivers.setting.model.OptionModel;
+import com.usai.redant.apexdrivers.setting.model.SectionModel;
+import com.usai.redant.apexdrivers.setting.option.OptionActivity;
+import com.usai.redant.apexdrivers.utils.OperationQueue;
+import com.usai.redant.rautils.utils.FileManager;
+import com.usai.redant.rautils.utils.ImageUtil;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.util.ArrayList;
+
+public class SettingActivity extends BasicActivity implements SettingAdapter.SettingAdapterDelegate {
+
+    public static void startSettingActivity(Context context) {
+        if (context == null) {
+            return;
+        }
+
+        Intent intent = new Intent(context,SettingActivity.class);
+        context.startActivity(intent);
+    }
+
+    private static final int REQUEST_CODE_OPTION = 0;
+
+    private ExpandableListView mListView;
+    private SettingAdapter mAdapter;
+    private ArrayList<SectionModel> mSections;
+    private Context mCtx = this;
+
+    private OptionModel mClickedOptionModel = null;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_setting);
+
+        ActionBar actionBar = getSupportActionBar();
+        if (actionBar != null) {
+            actionBar.setHomeButtonEnabled(true);
+            actionBar.setDisplayHomeAsUpEnabled(true);
+        }
+        mSections = new ArrayList<>();
+        loadData();
+
+        mAdapter = new SettingAdapter(mCtx,this,mSections);
+
+        mListView = findViewById(R.id.setting_list_view);
+        mListView.setAdapter(mAdapter);
+        mListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
+            @Override
+            public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
+                return true;
+            }
+        });
+        mListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
+            @Override
+            public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
+
+                SectionModel sectionModel = mSections.get(groupPosition);
+                BaseModel baseModel = sectionModel.settings.get(childPosition);
+                switch (baseModel.type) {
+                    case BaseModel.SettingTypeOption: {
+
+                        showOption((OptionModel)baseModel);
+
+                    }
+                    break;
+                    case BaseModel.SettingTypeSwitch: {
+
+                    }
+                    break;
+                    case BaseModel.SettingTypeAction: {
+
+                        clickAction((ActionModel)baseModel);
+
+                    }
+                    break;
+                    case BaseModel.SettingTypeAbout: {
+
+                    }
+                    break;
+                    case BaseModel.SettingTypeLink: {
+                        showPolicy((LinkModel)baseModel);
+                    }
+                    break;
+                }
+
+                return false;
+            }
+        });
+        mListView.addFooterView(LayoutInflater.from(mCtx).inflate(R.layout.setting_footer,null));
+
+        /**
+         *
+         *  打开所有Group,否则默认关闭
+         *
+         * */
+        if (mSections.size() > 0) {
+            for (int i = 0; i < mSections.size(); i++) {
+                mListView.expandGroup(i);
+            }
+        }
+        mAdapter.notifyDataSetChanged();
+
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home: {
+                finish();
+                return true;
+            }
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            case REQUEST_CODE_OPTION: {
+                if (resultCode == RESULT_OK && data != null) {
+
+                    int selectedOption = data.getIntExtra(OptionActivity.SelectedKey,-1);
+                    if (mClickedOptionModel != null) {
+                        mClickedOptionModel.setSelectedOption(selectedOption);
+                    }
+
+                }
+                mClickedOptionModel = null;
+            }
+            break;
+        }
+
+    }
+
+    public void loadData() {
+
+        mSections.clear();
+
+        JSONObject json = Network.loadFakeData(mCtx,R.raw.setting);
+        if (json != null) {
+            JSONArray sections = json.optJSONArray("sections");
+            if (sections != null) {
+                for (int i = 0; i < sections.length(); i++) {
+                    JSONObject section = sections.optJSONObject(i);
+                    if (section != null) {
+
+                        SectionModel model = new SectionModel();
+                        JSONArray settings = section.optJSONArray("settings");
+                        if (settings != null) {
+                            model.setSettings(settings);
+                        }
+                        mSections.add(model);
+                    }
+                }
+            }
+        }
+    }
+
+    private void showOption(OptionModel model) {
+        if (model == null) {
+            return;
+        }
+
+        mClickedOptionModel = model;
+        OptionActivity.startOptionActivity(this,model.title, model.optionsJsonString(), model.selectedOption(), REQUEST_CODE_OPTION);
+
+    }
+
+    private void clickAction(ActionModel model) {
+        if (model == null) {
+            return;
+        }
+
+        switch (model.actionType) {
+            case ActionModel.ActionTypeChangePassword : {
+
+                new ChangePasswordDialog(mCtx).show();
+
+            }
+            break;
+            case ActionModel.ActionTypeCleanCache: {
+                cleanDisk(model);
+            }
+            break;
+        }
+
+    }
+
+    private void cleanDisk(final ActionModel model) {
+
+        if (model == null) {
+            return;
+        }
+
+        new AlertDialog.Builder(mCtx)
+                .setTitle("Warning")
+                .setMessage("are you sure to clean cached file")
+                .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+
+
+                    }
+                })
+                .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+
+                        model.setActive(true);
+
+                        ApexDriverApplication.sharedApplication().getNetworkQueue().addOperationTask(new OperationQueue.OperationBackgroundCallBack() {
+                            @Override
+                            public Object operationDoInBackground() {
+
+                                String imageCacheDir = ImageUtil.imageCacheDir(mCtx);
+                                File file = new File(imageCacheDir);
+                                FileManager.deleteFile(file);
+
+                                return null;
+                            }
+                        }, new OperationQueue.OperationCompletionCallBack() {
+                            @Override
+                            public void operationCompletion(Object object) {
+
+                                new Handler().postDelayed(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        model.setActive(false);
+                                        Toast.makeText(mCtx,"Clean Success",Toast.LENGTH_LONG).show();
+                                    }
+                                },1000);
+
+
+
+                            }
+                        },null);
+
+                    }
+                })
+                .setOnCancelListener(new DialogInterface.OnCancelListener() {
+                    @Override
+                    public void onCancel(DialogInterface dialog) {
+                        model.setActive(false);
+                    }
+                })
+                .setCancelable(false)
+                .show();
+
+    }
+
+    private void showPolicy(LinkModel model) {
+
+        Intent intent = new Intent();
+        intent.setAction("android.intent.action.VIEW");
+        Uri content_url = Uri.parse(model.link);
+        intent.setData(content_url);
+        startActivity(intent);
+    }
+}

+ 410 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/SettingAdapter.java

@@ -0,0 +1,410 @@
+package com.usai.redant.apexdrivers.setting;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseExpandableListAdapter;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.apexdrivers.roundCornerImageView.RoundCornerImageView;
+import com.usai.redant.apexdrivers.setting.model.AboutModel;
+import com.usai.redant.apexdrivers.setting.model.ActionModel;
+import com.usai.redant.apexdrivers.setting.model.BaseModel;
+import com.usai.redant.apexdrivers.setting.model.LinkModel;
+import com.usai.redant.apexdrivers.setting.model.OptionModel;
+import com.usai.redant.apexdrivers.setting.model.SectionModel;
+import com.usai.redant.apexdrivers.setting.model.SwitchModel;
+import com.usai.redant.rautils.utils.RAUtil;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+import static com.usai.redant.apexdrivers.setting.model.ActionModel.ActionTypeCleanCache;
+
+public class SettingAdapter extends BaseExpandableListAdapter {
+
+    public interface SettingAdapterDelegate {
+
+    }
+
+
+    private WeakReference<SettingAdapterDelegate> weakDelegate;
+    private ArrayList<SectionModel> sections;
+    private Context mCtx;
+    SettingAdapter(Context context,SettingAdapterDelegate delegate,ArrayList<SectionModel> sections) {
+        mCtx = context;
+        if (delegate != null) {
+            weakDelegate = new WeakReference<>(delegate);
+        }
+        this.sections = sections;
+    }
+
+    @Override
+    public int getGroupCount() {
+        if (sections == null) {
+            return 0;
+        }
+        return sections.size();
+    }
+
+    @Override
+    public int getChildrenCount(int groupPosition) {
+
+        SectionModel sectionModel = sections.get(groupPosition);
+        if (sectionModel.settings == null) {
+            return 0;
+        }
+
+        return sectionModel.settings.size();
+    }
+
+    @Override
+    public Object getGroup(int groupPosition) {
+        SectionModel sectionModel = sections.get(groupPosition);
+        return sectionModel;
+    }
+
+    @Override
+    public Object getChild(int groupPosition, int childPosition) {
+        SectionModel sectionModel = sections.get(groupPosition);
+        return sectionModel.settings.get(childPosition);
+    }
+
+    @Override
+    public long getGroupId(int groupPosition) {
+        return groupPosition;
+    }
+
+    @Override
+    public long getChildId(int groupPosition, int childPosition) {
+        return childPosition;
+    }
+
+    @Override
+    public int getChildTypeCount() {
+        return BaseModel.settingTypeCount();
+    }
+
+    @Override
+    public int getChildType(int groupPosition, int childPosition) {
+        SectionModel sectionModel = sections.get(groupPosition);
+        return sectionModel.settings.get(childPosition).type;
+    }
+
+    @Override
+    public boolean hasStableIds() {
+        return false;
+    }
+
+    @Override
+    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
+
+        SectionHolder holder;
+        if (convertView == null) {
+
+            convertView = LayoutInflater.from(mCtx).inflate(R.layout.setting_section_header,null);
+            holder = new SectionHolder(convertView);
+
+        } else {
+            holder = (SectionHolder)convertView.getTag();
+        }
+
+        return convertView;
+    }
+
+    @Override
+    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
+
+        SectionModel sectionModel = sections.get(groupPosition);
+        BaseModel model = sectionModel.settings.get(childPosition);
+        switch (model.type) {
+            case BaseModel.SettingTypeOption: {
+
+                OptionHolder holder;
+                if (convertView == null) {
+
+                    convertView = LayoutInflater.from(mCtx).inflate(R.layout.setting_option_cell,null);
+                    holder = new OptionHolder(convertView);
+
+                } else {
+                    holder = (OptionHolder)convertView.getTag();
+                }
+                holder.setModel(model);
+            }
+            break;
+            case BaseModel.SettingTypeSwitch: {
+
+                SwitchHolder holder;
+                if (convertView == null) {
+
+                    convertView = LayoutInflater.from(mCtx).inflate(R.layout.setting_switch_cell,null);
+                    holder = new SwitchHolder(convertView);
+
+                } else {
+                    holder = (SwitchHolder)convertView.getTag();
+                }
+                holder.setModel(model);
+            }
+            break;
+            case BaseModel.SettingTypeAction: {
+
+                ActionHolder holder;
+                if (convertView == null) {
+
+                    convertView = LayoutInflater.from(mCtx).inflate(R.layout.setting_action_cell,null);
+                    holder = new ActionHolder(convertView);
+
+                } else {
+                    holder = (ActionHolder)convertView.getTag();
+                }
+                holder.setModel(model);
+            }
+            break;
+            case BaseModel.SettingTypeAbout: {
+
+                AboutHolder holder;
+                if (convertView == null) {
+
+                    convertView = LayoutInflater.from(mCtx).inflate(R.layout.setting_about_cell,null);
+                    holder = new AboutHolder(convertView);
+
+                } else {
+                    holder = (AboutHolder)convertView.getTag();
+                }
+                holder.setModel(model);
+            }
+            break;
+            case BaseModel.SettingTypeLink: {
+
+                LinkHolder holder;
+                if (convertView == null) {
+
+                    convertView = LayoutInflater.from(mCtx).inflate(R.layout.setting_link_cell,null);
+                    holder = new LinkHolder(convertView);
+
+                } else {
+                    holder = (LinkHolder)convertView.getTag();
+                }
+                holder.setModel(model);
+            }
+            break;
+
+        }
+
+        return convertView;
+    }
+
+    @Override
+    public boolean isChildSelectable(int groupPosition, int childPosition) {
+        return true;
+    }
+
+
+    private class SectionHolder {
+
+        SectionHolder(View view) {
+            view.setTag(this);
+        }
+    }
+
+    private class BaseHolder implements BaseModel.ModelDelegate {
+
+        private BaseModel model;
+
+        public void setModel(BaseModel model) {
+            if (this.model != null) {
+                this.model.setDelegate(null);
+            }
+            this.model = model;
+            if (this.model != null) {
+                this.model.setDelegate(this);
+            }
+
+            refreshUI();
+        }
+
+        public BaseModel getModel() {
+            return model;
+        }
+
+        @Override
+        public void refreshUI() {
+
+        }
+    }
+
+    private class OptionHolder extends BaseHolder {
+
+        private TextView titleTv;
+        private TextView valueTv;
+
+        OptionHolder(View view) {
+            view.setTag(this);
+
+            titleTv = view.findViewById(R.id.setting_option_title_tv);
+            valueTv = view.findViewById(R.id.setting_option_value_tv);
+        }
+
+        @Override
+        public void refreshUI() {
+            super.refreshUI();
+
+            OptionModel model = (OptionModel) getModel();
+            if (model != null) {
+                titleTv.setText(model.title);
+                if (model.option != null) {
+                    valueTv.setText(model.option.title);
+                } else {
+                    valueTv.setText(null);
+                }
+            } else {
+                valueTv.setText(null);
+            }
+
+        }
+    }
+
+    private class SwitchHolder extends BaseHolder {
+
+        private TextView titleTv;
+        private Switch aSwitch;
+
+        SwitchHolder(View view) {
+            view.setTag(this);
+
+            titleTv = view.findViewById(R.id.setting_switch_title_tv);
+            aSwitch = view.findViewById(R.id.setting_action_switch_view);
+        }
+
+        @Override
+        public void refreshUI() {
+            super.refreshUI();
+
+            SwitchModel model = (SwitchModel)getModel();
+            if (model != null) {
+
+                titleTv.setText(model.title);
+                aSwitch.setChecked(model.switchValue);
+
+            } else {
+                titleTv.setText(null);
+                aSwitch.setChecked(false);
+            }
+        }
+    }
+
+    private class ActionHolder extends BaseHolder {
+
+        private TextView titleTv,detailTv;
+        private ProgressBar indicator;
+
+        ActionHolder(View view) {
+            view.setTag(this);
+
+            titleTv = view.findViewById(R.id.setting_action_title_tv);
+            detailTv = view.findViewById(R.id.setting_action_detail_tv);
+            indicator = view.findViewById(R.id.setting_action_indicator_view);
+        }
+
+        @Override
+        public void refreshUI() {
+            super.refreshUI();
+
+            ActionModel model = (ActionModel)getModel();
+            if (model != null) {
+
+                titleTv.setText(model.title);
+                if (model.actionType == ActionTypeCleanCache) {
+
+                    detailTv.setText(model.detail);
+
+                    indicator.setVisibility(model.active ? View.VISIBLE : View.INVISIBLE);
+                    detailTv.setVisibility(model.active ? View.INVISIBLE : View.VISIBLE);
+
+                } else {
+
+                    detailTv.setText(null);
+                    indicator.setVisibility(View.INVISIBLE);
+                    detailTv.setVisibility(View.INVISIBLE);
+                }
+
+            } else {
+                titleTv.setText(null);
+                detailTv.setText(null);
+                indicator.setVisibility(View.INVISIBLE);
+            }
+        }
+    }
+
+    private class AboutHolder extends BaseHolder {
+
+        private ImageView redantLogoView;
+        private RoundCornerImageView iconView;
+        private TextView appNameTv,appVerTv,appSupportTv;
+
+        AboutHolder(View view) {
+            view.setTag(this);
+
+            iconView = view.findViewById(R.id.setting_about_icon_view);
+            redantLogoView = view.findViewById(R.id.setting_about_redant_view);
+            appNameTv = view.findViewById(R.id.setting_about_name_tv);
+            appVerTv = view.findViewById(R.id.setting_about_ver_tv);
+            appSupportTv = view.findViewById(R.id.setting_about_surpport_value_tv);
+
+            iconView.setRoundCorner(RAUtil.dp2px(mCtx,10.0f));
+        }
+
+        @Override
+        public void refreshUI() {
+            super.refreshUI();
+
+            AboutModel model = (AboutModel)getModel();
+            if (model != null) {
+
+                iconView.setImageResource(model.appIcon());
+                redantLogoView.setImageResource(model.redantLogo());
+                appNameTv.setText(model.appName(mCtx));
+                appVerTv.setText(model.appVer(mCtx));
+                appSupportTv.setText(model.appSupport());
+
+            } else {
+                iconView.setImageBitmap(null);
+                redantLogoView.setImageBitmap(null);
+                appNameTv.setText(null);
+                appVerTv.setText(null);
+                appSupportTv.setText(null);
+
+            }
+        }
+    }
+
+    private class LinkHolder extends BaseHolder {
+
+        private TextView titleTv;
+        private ImageView detailV;
+
+        LinkHolder(View v) {
+            v.setTag(this);
+
+            titleTv = v.findViewById(R.id.setting_link_title_tv);
+            detailV = v.findViewById(R.id.setting_link_detail_view);
+        }
+
+        @Override
+        public void refreshUI() {
+            super.refreshUI();
+
+            LinkModel model = (LinkModel) getModel();
+            String title = null;
+            if (model != null) {
+                title = model.title;
+            }
+            titleTv.setText(title);
+        }
+    }
+}

+ 213 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/changepassword/ChangePasswordDialog.java

@@ -0,0 +1,213 @@
+package com.usai.redant.apexdrivers.setting.changepassword;
+
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AlertDialog;
+import android.text.TextUtils;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.usai.redant.apexdrivers.ApexDriverApplication;
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.apexdrivers.network.Network;
+import com.usai.redant.apexdrivers.utils.OperationQueue;
+
+import org.json.JSONObject;
+
+public class ChangePasswordDialog extends Dialog {
+
+
+    private Context mCtx;
+    private LinearLayout mRootView;
+    private TextView titleTv;
+    private EditText oldEt,newEt,confirmEt;
+    private Button cancelBtn,changeBtn;
+
+    public ChangePasswordDialog(@NonNull Context context) {
+        this(context,R.style.RedAntDialog);
+    }
+
+    public ChangePasswordDialog(@NonNull Context context, int themeResId) {
+        super(context, themeResId);
+
+        mCtx = context;
+        init();
+    }
+
+    protected ChangePasswordDialog(@NonNull Context context, boolean cancelable, @Nullable OnCancelListener cancelListener) {
+        super(context, cancelable, cancelListener);
+
+        mCtx = context;
+        init();
+    }
+
+    private void init() {
+        mRootView = (LinearLayout)LayoutInflater.from(mCtx).inflate(R.layout.change_password_dialog, null);
+        setContentView(mRootView);
+
+        titleTv = mRootView.findViewById(R.id.change_password_title_tv);
+        oldEt = mRootView.findViewById(R.id.change_password_old_et);
+        newEt = mRootView.findViewById(R.id.change_password_new_et);
+        confirmEt = mRootView.findViewById(R.id.change_password_confirm_et);
+        cancelBtn = mRootView.findViewById(R.id.change_password_cancel_btn);
+        changeBtn = mRootView.findViewById(R.id.change_password_change_btn);
+
+        cancelBtn.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                dismiss();
+            }
+        });
+
+        changeBtn.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+
+                hideKeyboard();
+                changePassword();
+            }
+        });
+    }
+
+    private void changePassword() {
+
+        final String oldPass = oldEt.getText().toString();
+        final String newPass = newEt.getText().toString();
+        String confirmPass = confirmEt.getText().toString();
+
+        if (TextUtils.isEmpty(oldPass) && TextUtils.isEmpty(newPass) && TextUtils.isEmpty(confirmPass)) {
+
+            new AlertDialog.Builder(mCtx)
+                    .setTitle("Warning")
+                    .setMessage("Required fields can not be empty!")
+                    .setPositiveButton("Ok",null)
+                    .show();
+
+            return;
+        }
+
+        if (!newPass.equals(confirmPass)) {
+
+            new AlertDialog.Builder(mCtx)
+                    .setTitle("Warning")
+                    .setMessage("New password not equal confirm password!")
+                    .setPositiveButton("Ok",null)
+                    .show();
+
+            return;
+        }
+
+        showProgressDialog();
+
+        final String encryptOldPass = ApexDriverApplication.sharedApplication().encryptString(oldPass);
+        final String encryptNewPass = ApexDriverApplication.sharedApplication().encryptString(newPass);
+
+        ApexDriverApplication.sharedApplication().getNetworkQueue().addOperationTask(new OperationQueue.OperationBackgroundCallBack() {
+            @Override
+            public Object operationDoInBackground() {
+                return Network.changePassword(encryptOldPass,encryptNewPass);
+            }
+        }, new OperationQueue.OperationCompletionCallBack() {
+            @Override
+            public void operationCompletion(Object object) {
+
+                dismissProgressDialog();
+
+                JSONObject json = (JSONObject)object;
+                if (json != null) {
+
+                    int result = json.optInt("result");
+                    if (result == com.usai.redant.rautils.utils.Network.RESULT_TRUE) {
+
+                        showWarningMsg("change password success");
+                        ApexDriverApplication.sharedApplication().updatePassword(newPass);
+
+                        dismiss();
+
+                    } else {
+                        String msg = json.optString("err_msg");
+                        if (TextUtils.isEmpty(msg)) {
+                            msg = "Sorry,something is wrong";
+                        }
+                        showWarningMsg(msg);
+                    }
+
+                } else {
+                    showWarningMsg("Sorry,something is wrong");
+                }
+
+            }
+        },null);
+
+    }
+
+    private void hideKeyboard() {
+
+        InputMethodManager imm = (InputMethodManager)mCtx.getSystemService(Context.INPUT_METHOD_SERVICE);
+        if(imm.isActive()&&getCurrentFocus()!=null){
+            if (getCurrentFocus().getWindowToken()!=null) {
+                imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
+            }
+        }
+    }
+
+    private ProgressDialog mProgressDialog;
+    private void showProgressDialog() {
+        if (mProgressDialog == null) {
+            mProgressDialog = new ProgressDialog(mCtx);
+            mProgressDialog.setMessage("loading...");
+            mProgressDialog.setCancelable(false);
+            mProgressDialog.show();
+        }
+    }
+
+    private void dismissProgressDialog() {
+        if (mProgressDialog != null && mProgressDialog.isShowing()) {
+            mProgressDialog.dismiss();
+        }
+        mProgressDialog = null;
+    }
+
+    private void showWarningMsg(String msg) {
+        if (msg == null || msg.length() == 0) {
+            return;
+        }
+
+        new AlertDialog.Builder(mCtx).setTitle("Warning")
+                .setMessage(msg)
+                .setPositiveButton("Ok",null)
+                .show();
+    }
+
+    @Override
+    public void show() {
+
+        Window dialogWindow = getWindow();
+        if (dialogWindow != null) {
+
+            dialogWindow.setGravity(Gravity.CENTER);
+            WindowManager.LayoutParams layoutParams = dialogWindow.getAttributes(); // 获取对话框当前的参数值
+            layoutParams.x = 0; // 新位置X坐标
+            layoutParams.y = 0; // 新位置Y坐标
+            layoutParams.width = (int) mCtx.getResources().getDisplayMetrics().widthPixels; // 宽度
+            mRootView.measure(0, 0);
+            layoutParams.height = mRootView.getMeasuredHeight();
+
+            layoutParams.alpha = 9f; // 透明度
+            dialogWindow.setAttributes(layoutParams);
+        }
+
+        super.show();
+    }
+}

+ 52 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/model/AboutModel.java

@@ -0,0 +1,52 @@
+package com.usai.redant.apexdrivers.setting.model;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.rautils.utils.RAUtil;
+
+public class AboutModel extends BaseModel {
+
+    public String appName(Context context) {
+        if (context == null)
+            return null;
+
+        return RAUtil.getApplicationName(context);
+    }
+
+    public String appVer(Context context) {
+        if (context == null)
+            return null;
+
+        try {
+            PackageManager pm = context.getPackageManager();
+            PackageInfo pi = pm.getPackageInfo(context.getApplicationContext().getPackageName(), 0);
+            String ver_name = pi.versionName;
+            int ver_code = pi.versionCode;
+
+            return "Ver: " + ver_name + " " + ver_code;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return null;
+    }
+
+    public String appCopyright() {
+        return "Copyright © 2014 United Software Applications, Inc.";
+    }
+
+    public String appSupport() {
+        return "redantsupport@united-us.net";
+    }
+
+    public int appIcon() {
+        return R.drawable.icon_50;
+    }
+
+    public int redantLogo() {
+        return R.drawable.redant_logo;
+    }
+}

+ 49 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/model/ActionModel.java

@@ -0,0 +1,49 @@
+package com.usai.redant.apexdrivers.setting.model;
+
+import com.usai.redant.apexdrivers.ApexDriverApplication;
+import com.usai.redant.rautils.utils.FileManager;
+import com.usai.redant.rautils.utils.ImageUtil;
+
+import java.io.File;
+
+public class ActionModel extends BaseModel {
+
+    public static final int ActionTypeCleanCache = 0;
+    public static final int ActionTypeChangePassword = 1;
+
+
+    public int actionType;
+    public boolean active;
+    public String detail;
+
+    public void setActive(boolean active) {
+        this.active = active;
+
+        if (actionType == ActionTypeCleanCache) {
+            updateCacheSize();
+        }
+
+        if (getDelegate() != null) {
+            getDelegate().refreshUI();
+        }
+    }
+
+    public void setActionType(int actionType) {
+        this.actionType = actionType;
+
+        if (actionType == ActionTypeCleanCache) {
+            updateCacheSize();
+        }
+    }
+
+    private void updateCacheSize() {
+
+        String imgCacheDir = ImageUtil.imageCacheDir(ApexDriverApplication.sharedApplication().getBaseContext());
+        float size = FileManager.getSizeOfDir(new File(imgCacheDir)) / 1024.0f / 1024.0f;
+
+        String detail = String.format("%.1fM",size);
+
+        this.detail = detail;
+
+    }
+}

+ 32 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/model/BaseModel.java

@@ -0,0 +1,32 @@
+package com.usai.redant.apexdrivers.setting.model;
+
+import com.usai.redant.apexdrivers.base.BasicObject;
+
+public class BaseModel extends BasicObject {
+
+    public static final int SettingTypeOption = 0;
+    public static final int SettingTypeSwitch = 1;
+    public static final int SettingTypeAction = 2;
+    public static final int SettingTypeAbout = 3;
+    public static final int SettingTypeLink = 4;
+
+    public static int settingTypeCount() {
+        return 5;
+    }
+
+    public interface ModelDelegate {
+        void refreshUI();
+    }
+
+    public int type;
+    public String title;
+
+    private ModelDelegate delegate;
+    public void setDelegate(ModelDelegate delegate) {
+        this.delegate = delegate;
+    }
+
+    public ModelDelegate getDelegate() {
+        return delegate;
+    }
+}

+ 6 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/model/LinkModel.java

@@ -0,0 +1,6 @@
+package com.usai.redant.apexdrivers.setting.model;
+
+public class LinkModel extends BaseModel {
+
+    public String link;
+}

+ 105 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/model/OptionModel.java

@@ -0,0 +1,105 @@
+package com.usai.redant.apexdrivers.setting.model;
+
+import android.content.Intent;
+import android.text.TextUtils;
+
+import com.usai.redant.apexdrivers.ApexDriverApplication;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+
+public class OptionModel extends BaseModel {
+
+    public String keyPath;
+    public SettingOption option;
+    public ArrayList<SettingOption> options;
+    private JSONArray optionsJson;
+
+    public void setSelectedOption(int option) {
+
+        if (options != null && options.size() > 0) {
+            for (SettingOption op : options) {
+                if (op.option == option) {
+
+                    setOption(op);
+                    return;
+                }
+            }
+        }
+    }
+
+    public int selectedOption() {
+        if (option == null) {
+            return -1;
+        }
+        return option.option;
+    }
+
+    public void setOption(SettingOption option) {
+        this.option = option;
+
+        if (getDelegate() != null) {
+            getDelegate().refreshUI();
+        }
+
+        if (option != null && !TextUtils.isEmpty(keyPath)) {
+            ApexDriverApplication.sharedApplication().setValueForKeyPath(keyPath,option.option);
+        }
+    }
+
+    public void setKeyPath(String keyPath) {
+        this.keyPath = keyPath;
+        if (!TextUtils.isEmpty(keyPath) && options != null && options.size() > 0) {
+            for (SettingOption option : options) {
+
+
+                Object obj = ApexDriverApplication.sharedApplication().getValueForKeyPath(keyPath);
+
+                if (obj != null && obj.getClass() == Integer.class && option.option == (Integer)obj) {
+                    setOption(option);
+                }
+            }
+        }
+    }
+
+    public void setOptions(JSONArray options) {
+        optionsJson = options;
+
+        if (options != null) {
+
+            ArrayList<SettingOption> tmpArr = new ArrayList<>();
+            for (int i = 0; i < options.length(); i++) {
+                JSONObject json = options.optJSONObject(i);
+                if (json != null) {
+
+                    SettingOption option = new SettingOption();
+                    option.setValuesForKeysWithJSON(json);
+                    tmpArr.add(option);
+
+                    if (!TextUtils.isEmpty(keyPath)) {
+
+                        Object obj = ApexDriverApplication.sharedApplication().getValueForKeyPath(keyPath);
+                        if (obj != null && obj.getClass() == Integer.class && option.option == (Integer)obj) {
+                            setOption(option);
+                        }
+
+                    }
+                }
+            }
+            this.options = tmpArr;
+
+        } else {
+            this.options = null;
+        }
+    }
+
+    public String optionsJsonString() {
+        if (optionsJson == null) {
+            return null;
+        }
+        return optionsJson.toString();
+    }
+
+}

+ 77 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/model/SectionModel.java

@@ -0,0 +1,77 @@
+package com.usai.redant.apexdrivers.setting.model;
+
+import com.usai.redant.apexdrivers.base.BasicObject;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+
+public class SectionModel extends BasicObject {
+
+    public ArrayList<BaseModel> settings;
+
+    public void setSettings(JSONArray settings) {
+        if (settings != null) {
+
+            ArrayList<BaseModel> tmpArr = new ArrayList<>();
+
+            for (int i = 0; i < settings.length(); i++) {
+
+                JSONObject json = settings.optJSONObject(i);
+                if (json != null) {
+
+
+                    int type = json.optInt("type");
+                    BaseModel model = null;
+
+                    switch (type) {
+
+                        case BaseModel.SettingTypeOption: {
+
+                            model = new OptionModel();
+                            JSONArray options = json.optJSONArray("options");
+                            if (options != null) {
+                                ((OptionModel)model).setOptions(options);
+                            }
+                        }
+                        break;
+                        case BaseModel.SettingTypeSwitch: {
+
+                            model = new SwitchModel();
+
+                        }
+                        break;
+                        case BaseModel.SettingTypeAction: {
+
+                            model = new ActionModel();
+
+                        }
+                        break;
+                        case BaseModel.SettingTypeAbout: {
+
+                           model = new AboutModel();
+                        }
+                        break;
+                        case BaseModel.SettingTypeLink: {
+                            model = new LinkModel();
+                        }
+                        break;
+
+                    }
+
+                    if (model != null) {
+                        model.setValuesForKeysWithJSON(json);
+                        tmpArr.add(model);
+                    }
+
+                }
+
+            }
+
+            this.settings = tmpArr;
+        } else {
+            this.settings = null;
+        }
+    }
+}

+ 9 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/model/SettingOption.java

@@ -0,0 +1,9 @@
+package com.usai.redant.apexdrivers.setting.model;
+
+import com.usai.redant.apexdrivers.base.BasicObject;
+
+public class SettingOption extends BasicObject {
+
+    public int option;
+    public String title;
+}

+ 7 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/model/SwitchModel.java

@@ -0,0 +1,7 @@
+package com.usai.redant.apexdrivers.setting.model;
+
+public class SwitchModel extends BaseModel {
+
+    public String keyPath;
+    public boolean switchValue;
+}

+ 311 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/option/OptionActivity.java

@@ -0,0 +1,311 @@
+package com.usai.redant.apexdrivers.setting.option;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.BaseExpandableListAdapter;
+import android.widget.ExpandableListView;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.apexdrivers.base.BasicActivity;
+import com.usai.redant.apexdrivers.setting.model.SettingOption;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+
+public class OptionActivity extends BasicActivity {
+
+    private static final String OptionTitleKey = "OptionTitle";
+    private static final String OptionsKey = "OptionsKey";
+    public static final String SelectedKey = "SelectedKey";
+    private static final String SelectedIndexKey = "SelectedIndexKey";
+
+    public static void startOptionActivity(Activity activity, String optionTitle, String optionsJson, int selectedOption, int requestCode) {
+        if (activity == null) {
+            return;
+        }
+
+        Intent intent = new Intent(activity,OptionActivity.class);
+        if (optionTitle != null) {
+            intent.putExtra(OptionTitleKey,optionTitle);
+        }
+        if (optionsJson != null) {
+            intent.putExtra(OptionsKey,optionsJson);
+        }
+        intent.putExtra(SelectedKey,selectedOption);
+
+        activity.startActivityForResult(intent,requestCode);
+    }
+
+    private Context mCtx = this;
+    private String optionsJson;
+    private int selectedOption;
+    private String title;
+
+    private ArrayList<SettingOption> options = new ArrayList<>();
+    private int selectedIndex = -1;
+
+    private ExpandableListView mListView;
+    private OptionAdapter mAdapter;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_option);
+
+        ActionBar actionBar = getSupportActionBar();
+        if (actionBar != null) {
+            actionBar.setHomeButtonEnabled(true);
+            actionBar.setDisplayHomeAsUpEnabled(true);
+        }
+
+        if (savedInstanceState != null) {
+
+            optionsJson = savedInstanceState.getString(OptionsKey);
+            selectedIndex = savedInstanceState.getInt(SelectedIndexKey);
+            title = savedInstanceState.getString(OptionTitleKey);
+
+        } else {
+            if (getIntent() != null) {
+
+                optionsJson = getIntent().getStringExtra(OptionsKey);
+                selectedOption = getIntent().getIntExtra(SelectedKey,-1);
+                title = getIntent().getStringExtra(OptionTitleKey);
+            }
+        }
+
+
+        if (optionsJson != null) {
+            try {
+
+                JSONArray jsonArr = new JSONArray(optionsJson);
+                for (int i = 0; i < jsonArr.length(); i++) {
+                    JSONObject json = jsonArr.optJSONObject(i);
+                    if (json != null) {
+
+                        SettingOption model = new SettingOption();
+                        model.setValuesForKeysWithJSON(json);
+
+                        if (model.option == selectedOption) {
+                            selectedIndex = i;
+                        }
+
+                        options.add(model);
+                    }
+
+                }
+
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+        }
+
+        mAdapter = new OptionAdapter();
+        mListView = findViewById(R.id.option_list_view);
+        mListView.setAdapter(mAdapter);
+        mListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
+            @Override
+            public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
+
+                if (childPosition != selectedIndex) {
+                    selectedIndex = childPosition;
+                    mAdapter.notifyDataSetChanged();
+                }
+
+                return false;
+            }
+        });
+        mListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
+            @Override
+            public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
+                return true;
+            }
+        });
+        mListView.setGroupIndicator(null);
+
+        int groupCount = mAdapter.getGroupCount();
+        for (int i = 0; i < groupCount; i++) {
+            mListView.expandGroup(i);
+        }
+
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        outState.putInt(SelectedIndexKey,selectedIndex);
+        if (optionsJson != null) {
+            outState.putString(OptionsKey,optionsJson);
+        }
+        if (title != null) {
+            outState.putString(OptionTitleKey,title);
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home: {
+                finish();
+                return true;
+            }
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    public void finish() {
+
+        int selectedOption = -1;
+        if (selectedIndex >= 0) {
+            SettingOption option = options.get(selectedIndex);
+            if (option != null) {
+                selectedOption = option.option;
+            }
+        }
+
+        Intent intent = new Intent();
+        intent.putExtra(SelectedKey,selectedOption);
+        setResult(RESULT_OK,intent);
+
+        super.finish();
+    }
+
+    private class OptionAdapter extends BaseExpandableListAdapter {
+
+        @Override
+        public int getGroupCount() {
+            return 1;
+        }
+
+        @Override
+        public int getChildrenCount(int groupPosition) {
+            return options.size();
+        }
+
+        @Override
+        public Object getGroup(int groupPosition) {
+            return options;
+        }
+
+        @Override
+        public Object getChild(int groupPosition, int childPosition) {
+            return options.get(childPosition);
+        }
+
+        @Override
+        public long getGroupId(int groupPosition) {
+            return groupPosition;
+        }
+
+        @Override
+        public long getChildId(int groupPosition, int childPosition) {
+            return childPosition;
+        }
+
+        @Override
+        public boolean hasStableIds() {
+            return false;
+        }
+
+        @Override
+        public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
+
+            HeaderHolder holder;
+            if (convertView == null) {
+
+                convertView = LayoutInflater.from(mCtx).inflate(R.layout.option_group_header,null);
+                holder = new HeaderHolder(convertView);
+
+            } else {
+
+                holder = (HeaderHolder)convertView.getTag();
+            }
+
+            holder.titleTv.setText(title);
+
+            return convertView;
+        }
+
+        @Override
+        public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
+
+            Holder holder;
+            if (convertView == null) {
+
+                convertView = LayoutInflater.from(mCtx).inflate(R.layout.option_item_cell,null);
+                holder = new Holder(convertView);
+
+            } else {
+
+                holder = (Holder)convertView.getTag();
+            }
+
+            SettingOption option = options.get(childPosition);
+            holder.setModel(option,childPosition);
+
+            return convertView;
+        }
+
+        @Override
+        public boolean isChildSelectable(int groupPosition, int childPosition) {
+            return true;
+        }
+    }
+
+    private class HeaderHolder {
+        TextView titleTv;
+
+        HeaderHolder(View view) {
+            view.setTag(this);
+
+            titleTv = view.findViewById(R.id.option_group_header_title_tv);
+        }
+    }
+
+    private class Holder {
+
+        private TextView titleTv;
+        private ImageView markView;
+
+        Holder(View v) {
+            v.setTag(this);
+
+            titleTv = v.findViewById(R.id.option_title_tv);
+            markView = v.findViewById(R.id.option_mark_view);
+        }
+
+        public void setModel(SettingOption model, int position) {
+            if (model != null) {
+
+                titleTv.setText(model.title);
+                if (position == selectedIndex) {
+                    markView.setVisibility(View.VISIBLE);
+                } else {
+                    markView.setVisibility(View.GONE);
+                }
+
+            } else {
+
+                titleTv.setText(null);
+                markView.setVisibility(View.GONE);
+            }
+        }
+    }
+}

+ 143 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/signature/SignatureActivity.java

@@ -0,0 +1,143 @@
+package com.usai.redant.apexdrivers.signature;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.Menu;
+import android.view.MenuItem;
+
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.apexdrivers.base.BasicActivity;
+import com.usai.redant.rautils.utils.FileManager;
+import com.usai.redant.rautils.utils.ImageUtil;
+import com.usai.redant.rautils.utils.RAUtil;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class SignatureActivity extends BasicActivity {
+
+    public static void startSignature(Activity context,int requestCode) {
+
+        if (context == null) {
+            return;
+        }
+
+        Intent intent = new Intent(context, SignatureActivity.class);
+
+        context.startActivityForResult(intent,requestCode);
+    }
+
+    private Context mCtx = this;
+    private SignatureView mSignatureView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_signature);
+
+        android.support.v7.app.ActionBar actionBar = getSupportActionBar();
+        if(actionBar != null){
+            actionBar.setHomeButtonEnabled(true);
+            actionBar.setDisplayHomeAsUpEnabled(true);
+        }
+
+        mSignatureView = findViewById(R.id.signature_content_view);
+
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.signature_menu,menu);
+
+        MenuItem saveItem = menu.getItem(1);
+        Drawable saveItemIcon = saveItem.getIcon();
+        ImageUtil.renderingDrawable(saveItemIcon,getResources(),R.color.ApexDriverWhite);
+
+        MenuItem clearItem = menu.getItem(0);
+        Drawable clearItemIcon = clearItem.getIcon();
+        ImageUtil.renderingDrawable(clearItemIcon,getResources(),R.color.ApexDriverWhite);
+
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        int i = item.getItemId();
+        if (i == R.id.signature_clear_btn) {
+            clearSignature();
+
+        } else if (i == R.id.signature_save_btn) {
+            saveAndFinish();
+
+        } else if (i == android.R.id.home) {
+            finish();
+
+        }
+        return true;
+    }
+
+    private void saveAndFinish() {
+
+        Intent intent = new Intent();
+        String signaturePath = saveSignature();
+        if (!TextUtils.isEmpty(signaturePath)) {
+            intent.putExtra("signaturePath",signaturePath);
+        }
+
+        setResult(RESULT_OK,intent);
+
+        finish();
+    }
+
+    private String saveSignature() {
+
+        Bitmap signature = mSignatureView.saveSignature();
+        if (signature != null) {
+
+            String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmSS").format(new Date());
+            String signatureName = "JPEG_" + timeStamp + ".jpg";
+
+//            String root = FileManager.SDCardRoot();
+//            String appName = RAUtil.getApplicationName(mCtx);
+//            String signatureDir = root + File.separator + appName + File.separator + "signature";
+
+            String signatureDir = FileManager.internalStorageFileDir(mCtx) + File.separator + "Signature";
+            String signaturePath = signatureDir + File.separator + signatureName;
+
+            File parentFile = new File(signatureDir);
+            if (!parentFile.exists()) {
+                parentFile.mkdirs();
+            }
+
+            File signatureFile = new File(signaturePath);
+            if (signatureFile.exists()) {
+                signatureFile.delete();
+            }
+            try {
+
+                signatureFile.createNewFile();
+
+                ImageUtil.saveJPGToFile(signature,signatureFile);
+
+                return signaturePath;
+
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+        return null;
+    }
+
+    private void clearSignature() {
+        mSignatureView.clear();
+    }
+}

+ 167 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/signature/SignatureView.java

@@ -0,0 +1,167 @@
+package com.usai.redant.apexdrivers.signature;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+public class SignatureView extends View {
+
+    private Paint mPaint;
+    private Path mPath;
+    private float mLineWidth = 10;
+    private float mX,mY;
+    private float minX = Float.MAX_VALUE,minY = Float.MAX_VALUE,maxX = Float.MIN_VALUE,maxY = Float.MIN_VALUE;
+
+    public SignatureView(Context context) {
+        this(context,null);
+    }
+
+    public SignatureView(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs,0);
+    }
+
+    public SignatureView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        init();
+    }
+
+    private void init() {
+
+        mPaint = new Paint();
+        mPaint.setAntiAlias(true);
+        mPaint.setDither(true);
+        mPaint.setStyle(Paint.Style.STROKE);
+        mPaint.setStrokeJoin(Paint.Join.ROUND);
+        mPaint.setStrokeCap(Paint.Cap.ROUND);
+
+        setLineWidth(mLineWidth);
+        setLineColor(Color.BLACK);
+
+        mPath = new Path();
+    }
+
+    public void setLineWidth(float lineWidth) {
+        mLineWidth = lineWidth;
+
+        mPaint.setStrokeWidth(mLineWidth);
+    }
+
+    public void setLineColor(int color) {
+        mPaint.setColor(color);
+    }
+
+    public void clear() {
+
+        mX = mY = minX = minY = maxX = maxY= 0;
+        mPath.reset();
+        invalidate();
+    }
+
+    public Bitmap saveSignature() {
+
+        minX -= mLineWidth;
+        minY -= mLineWidth;
+        maxX += mLineWidth;
+        maxY += mLineWidth;
+
+        float w = maxX - minX;
+        float h = maxY - minY;
+
+//        Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.RGB_565);
+        Bitmap bitmap = Bitmap.createBitmap((int)w, (int)h, Bitmap.Config.RGB_565);
+        mPath.offset(-minX,-minY);
+
+        Canvas canvas = new Canvas(bitmap);
+
+
+//        RectF rect = new RectF(minX,minY,maxX,maxY);
+        canvas.drawColor(Color.WHITE);
+        canvas.drawPath(mPath,mPaint);
+//        canvas.clipRect(rect);
+
+        return bitmap;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+
+        float x = event.getX();
+        float y = event.getY();
+
+        calculateRect(x, y);
+
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_DOWN: {
+                onTouchDown(x, y);
+            }
+            break;
+            case MotionEvent.ACTION_MOVE: {
+                onTouchMove(x, y);
+            }
+            break;
+            case MotionEvent.ACTION_UP: {
+                onTouchUp(x, y);
+            }
+            break;
+        }
+
+        return true;
+    }
+
+    private void calculateRect(float x,float y) {
+
+        minX = Math.min(x,minX);
+
+        minY = Math.min(y,minY);
+
+        maxX = Math.max(x,maxX);
+        maxY = Math.max(y,maxY);
+    }
+
+    private void onTouchDown(float x,float y) {
+
+        mPath.moveTo(x,y);
+
+        mX = x;
+        mY = y;
+
+        invalidate();
+    }
+
+    private void onTouchMove(float x,float y) {
+
+        float dx = Math.abs(x - mX);
+        float dy = Math.abs(y - mY);
+
+        if (dx >= 2 || dy >= 2) {
+
+            mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);// 二次方贝塞尔曲线
+            mX = x;
+            mY = y;
+        }
+
+
+        invalidate();
+    }
+
+    private void onTouchUp(float x,float y) {
+
+        mPath.lineTo(x,y);
+
+        invalidate();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+
+        canvas.drawPath(mPath, mPaint);
+    }
+}

+ 110 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/update/PhotoPreviewActivity.java

@@ -0,0 +1,110 @@
+package com.usai.redant.apexdrivers.update;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.apexdrivers.base.BasicActivity;
+import com.usai.redant.apexdrivers.update.xuanimageview.XuanImageView;
+import com.usai.redant.apexdrivers.update.xuanimageview.XuanImageViewSettings;
+import com.usai.redant.rautils.utils.ImageUtil;
+
+import java.io.File;
+
+public class PhotoPreviewActivity extends BasicActivity {
+
+    public final static String PreviewActionDeleteKey = "delete";
+
+    private final static String PhotoPathKey = "PhotoPath";
+    private final static String CanDeleteKey = "CanDelete";
+    public static Intent build(Context context,String photoPath, boolean canDelete) {
+        if (context == null) {
+            return null;
+        }
+        Intent intent = new Intent(context,PhotoPreviewActivity.class);
+        if (photoPath != null) {
+            intent.putExtra(PhotoPathKey,photoPath);
+        }
+        intent.putExtra(CanDeleteKey,canDelete);
+
+        return intent;
+    }
+
+    private XuanImageView previewImageView;
+    private String photoPath;
+    private boolean delete;
+    private boolean canDelete;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_photo_preview);
+
+        android.support.v7.app.ActionBar actionBar = getSupportActionBar();
+        if(actionBar != null){
+            actionBar.setHomeButtonEnabled(true);
+            actionBar.setDisplayHomeAsUpEnabled(true);
+        }
+
+        previewImageView = findViewById(R.id.preview_image_view);
+        previewImageView.setAutoRotateCategory(XuanImageViewSettings.AUTO_ROTATE_CATEGORY_MAGNETISM);
+
+        photoPath = getIntent().getStringExtra(PhotoPathKey);
+        if (photoPath != null) {
+//            Bitmap bitmap = BitmapFactory.decodeFile(photoPath);
+
+            Bitmap bitmap = ImageUtil.adjustPhotoOrientation(photoPath);
+
+            previewImageView.setImageBitmap(bitmap);
+        }
+        canDelete = getIntent().getBooleanExtra(CanDeleteKey,false);
+
+        delete = false;
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.photo_preview_menu,menu);
+        if (!canDelete) {
+            menu.removeItem(R.id.delete_btn);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == R.id.delete_btn) {
+            if (photoPath != null) {
+                File file = new File(photoPath);
+                if (file.exists()) {
+                    file.delete();
+                    ImageUtil.updateGallery(this,photoPath);
+                }
+                delete = true;
+                finish();
+            }
+
+        }
+        if (item.getItemId() == android.R.id.home) {
+            finish();
+        }
+
+        return true;
+    }
+
+    @Override
+    public void finish() {
+
+        Intent intent = new Intent();
+        intent.putExtra(PreviewActionDeleteKey,delete);
+        setResult(RESULT_OK,intent);
+
+        super.finish();
+    }
+}

+ 1135 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/update/UpdateActivity.java

@@ -0,0 +1,1135 @@
+package com.usai.redant.apexdrivers.update;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.location.Location;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Message;
+import android.provider.MediaStore;
+import android.support.v4.content.PermissionChecker;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ExpandableListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.usai.redant.apexdrivers.ApexDriverApplication;
+import com.usai.redant.apexdrivers.MainActivity;
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.apexdrivers.base.BasicActivity;
+import com.usai.redant.apexdrivers.codescanner.CaptureActivity;
+import com.usai.redant.apexdrivers.home.HomeFragment;
+import com.usai.redant.apexdrivers.network.Network;
+import com.usai.redant.apexdrivers.signature.SignatureActivity;
+import com.usai.redant.apexdrivers.update.model.UpdateBaseModel;
+import com.usai.redant.apexdrivers.update.model.UpdateImageBaseModel;
+import com.usai.redant.apexdrivers.update.model.UpdateInputModel;
+import com.usai.redant.apexdrivers.update.model.UpdateMultInputModel;
+import com.usai.redant.apexdrivers.update.model.UpdatePhotoModel;
+import com.usai.redant.apexdrivers.update.model.UpdateSignatureModel;
+import com.usai.redant.rautils.receiver.RABroadcast;
+import com.usai.redant.rautils.utils.FileManager;
+import com.usai.redant.rautils.utils.ImageUtil;
+import com.usai.redant.rautils.utils.RAUtil;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+
+import static com.usai.redant.rautils.utils.Network.RESULT_TRUE;
+
+public class UpdateActivity extends BasicActivity implements UpdateAdapter.UpdateAdapterDelegate {
+
+    private final static String OrderIDKey = "OrderID";
+    private final static String ActionIDKey = "ActionID";
+    private final static String ActionTitleKey = "ActionTitle";
+    private final static String OrderType2Key = "OrderType2";
+
+    private final static String SaveDataKey = "UpdateSavedJson";
+    private final static String SaveOrderIDKey = "UpdateSavedOrderID";
+    private final static String SaveActionIDKey = "UpdateSavedActionID";
+    private final static String SaveTitleKey = "UpdateSavedTitle";
+    private final static String SaveOrderType2Key = "UpdateSaveOrderType2";
+
+    private final static int REQUEST_SCANNER_CODE = 0;
+    private final static int REQUEST_CAMERA_CODE = 1;
+    private final static int REQUEST_PREVIEW_CODE = 2;
+    private final static int REQUEST_SIGNATURE_CODE = 3;
+
+
+    public static Intent build(Context ctx, String orderID, int actionID,String actionTitle, String orderType2) {
+
+        Intent intent = new Intent(ctx, UpdateActivity.class);
+        if (orderID != null) {
+            intent.putExtra(OrderIDKey,orderID);
+        }
+        if (actionTitle != null) {
+            intent.putExtra(ActionTitleKey,actionTitle);
+        }
+        if (orderType2 != null) {
+            intent.putExtra(OrderType2Key,orderType2);
+        }
+        intent.putExtra(ActionIDKey,actionID);
+
+        return intent;
+    }
+
+    private String mOrderID;
+    private String mTitle;
+    private int mActionID;
+    private String mOrderType2;
+
+    private Context mCtx = this;
+    private UpdateActivity self = this;
+    private UpdateHandler mHandler;
+    private ArrayList<UpdateSectionModel> mSectionArray = new ArrayList<>();
+    private UpdateAdapter mAdapter;
+
+    private ExpandableListView mListView;
+    private SwipeRefreshLayout mRefresh;
+
+    private UpdateInputModel mInputModel;
+    private UpdatePhotoModel mPhotoModel;
+    private UpdateSignatureModel mSignatueModel;
+
+    private File photoFile = null;
+
+    private ProgressDialog mProgressDialog;
+    private AlertDialog mDialog;
+    private ArrayList<File> mPhotoArray = new ArrayList<>();// 用于直接返回后删除Photo文件
+
+    private TextView mEmptyView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_update);
+
+        android.support.v7.app.ActionBar actionBar = getSupportActionBar();
+        if(actionBar != null){
+            actionBar.setHomeButtonEnabled(true);
+            actionBar.setDisplayHomeAsUpEnabled(true);
+        }
+
+        if (getIntent() != null) {
+            mTitle = getIntent().getStringExtra(ActionTitleKey);
+            setTitle(mTitle);
+
+            mOrderID = getIntent().getStringExtra(OrderIDKey);
+            mActionID = getIntent().getIntExtra(ActionIDKey,0);
+            mOrderType2 = getIntent().getStringExtra(OrderType2Key);
+        }
+
+        mHandler = new UpdateHandler(self);
+
+        mListView = findViewById(R.id.update_list_view);
+        mAdapter = new UpdateAdapter(mCtx,mSectionArray,self);
+
+        mListView.setGroupIndicator(null);
+        mListView.setAdapter(mAdapter);
+        mListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
+            @Override
+            public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
+                return true; // 不允许GroupView点击
+            }
+        });
+
+        mRefresh = findViewById(R.id.update_refresh);
+        mRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
+            @Override
+            public void onRefresh() {
+                loadData();
+            }
+        });
+
+        mEmptyView = findViewById(R.id.update_empty_btn);
+        mEmptyView.setText("There is no data\nPlease click to reload");
+        mEmptyView.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                loadData();
+            }
+        });
+        mListView.setEmptyView(findViewById(R.id.update_empty_view));
+
+        if (savedInstanceState != null) {
+
+            mTitle = savedInstanceState.getString(SaveTitleKey);
+            setTitle(mTitle);
+
+            mOrderID = savedInstanceState.getString(SaveOrderIDKey);
+            mActionID = savedInstanceState.getInt(SaveActionIDKey,0);
+            mOrderType2 = savedInstanceState.getString(SaveOrderType2Key);
+
+            String jsonStr = savedInstanceState.getString(SaveDataKey);
+            if (jsonStr != null) {
+                try {
+                    JSONObject json = new JSONObject(jsonStr);
+
+                    handleJSON(json);
+                    changeData();
+
+                } catch (JSONException e) {
+                    e.printStackTrace();
+                }
+            }
+
+        } else {
+            loadData();
+        }
+
+        checkCameraPermission();
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        JSONObject json = prepareSavedJson();
+        if (json != null) {
+            outState.putString(SaveDataKey,json.toString());
+        }
+        if (mOrderID != null) {
+            outState.putString(SaveOrderIDKey, mOrderID);
+        }
+        if (mTitle != null) {
+            outState.putString(SaveTitleKey, mTitle);
+        }
+        if (mOrderType2 != null) {
+            outState.putString(SaveOrderType2Key,mOrderType2);
+        }
+        outState.putInt(SaveActionIDKey, mActionID);
+
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+
+        dismissProgressDialog();
+        if (mDialog != null && mDialog.isShowing()) {
+            mDialog.dismiss();
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        int i = item.getItemId();
+        if (i == android.R.id.home) {
+            clearPhotos();
+            this.finish(); // back buttonreturn true;
+        } else if (i == R.id.update_btn) {
+            update();
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    public void onBackPressed() {
+        clearPhotos();
+        super.onBackPressed();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+
+        getMenuInflater().inflate(R.menu.update_menu,menu);
+
+        return true;
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+
+        switch (requestCode) {
+            case REQUEST_SCANNER_CODE: {
+
+                if (resultCode == Activity.RESULT_OK) {
+                    Bundle bundle = data.getExtras();
+                    String pid = bundle.getString("pid");
+                    if (mInputModel != null) {
+                        mInputModel.setScannerInputValue(pid);
+                    }
+
+                }
+                mInputModel = null;
+            }
+            break;
+            case REQUEST_CAMERA_CODE: {
+
+                if (resultCode == Activity.RESULT_OK) {
+
+                    File scaleFile = compressImageFile(photoFile);
+//                    File scaleFile = photoFile;
+                    if (scaleFile != null) {
+
+                        ImageUtil.updateGallery(mCtx,scaleFile.getAbsolutePath());
+
+                        if (mPhotoModel != null) {
+                            mPhotoModel.setPhotoPath(scaleFile.getAbsolutePath());
+                        }
+
+                        mPhotoArray.add(scaleFile);
+                    }
+
+
+
+                } else {
+                    if (photoFile.exists()) {
+                        photoFile.delete(); // 没有拍照直接返回,需要将文件删除
+                    }
+                }
+                mPhotoModel = null;
+                photoFile = null;
+            }
+            break;
+            case REQUEST_PREVIEW_CODE: {
+                if (resultCode == Activity.RESULT_OK) {
+
+                    boolean delete = data.getBooleanExtra(PhotoPreviewActivity.PreviewActionDeleteKey,false);
+                    if (delete) {
+                        mPhotoModel.setPhotoPath(null);
+                    }
+                }
+                mPhotoModel = null;
+            }
+            break;
+            case REQUEST_SIGNATURE_CODE: {
+                if (resultCode == Activity.RESULT_OK) {
+                    String signaturePath = data.getStringExtra("signaturePath");
+                    if (mSignatueModel != null) {
+                        mSignatueModel.setSignaturePath(signaturePath);
+                    }
+                }
+                mSignatueModel = null;
+            }
+            break;
+        }
+    }
+
+    private void clearPhotos() {
+
+        for(File file : mPhotoArray) {
+            if (file.exists()) {
+                file.delete();
+                ImageUtil.updateGallery(mCtx,file.getAbsolutePath());
+            }
+        }
+
+    }
+
+
+    private File compressImageFile(File imgFile) {
+
+
+        String imageFileName = imgFile.getName();
+
+        File routedFile = ImageUtil.routeBitmap(mCtx,imgFile,null);
+
+        Bitmap source = null;
+        try {
+            FileInputStream stream = new FileInputStream(routedFile);
+            source = BitmapFactory.decodeStream(stream);
+        } catch (FileNotFoundException e) {
+            return routedFile;
+        }
+
+        if (source == null) {
+            return routedFile;
+        }
+
+        int originWidth = source.getWidth();
+        int originHeight = source.getHeight();
+
+        int width = originWidth, height = originHeight;
+
+        if (originHeight > 1024 || originWidth > 1024)
+        {
+            if (originWidth > originHeight)
+            {
+                width = 1024;
+                height = originHeight * 1024 / originWidth;
+
+            }
+            else
+            {
+
+                height = 1024;
+                width = originWidth * 1024 / originHeight;
+
+            }
+        }
+
+
+        Bitmap scaled = Bitmap.createScaledBitmap(source, width, height, true);
+
+        String scalePath = FileManager.internalStorageFileDir(mCtx) + File.separator + "Photo" + File.separator +imageFileName;
+        File scaleFile = new File(scalePath);
+        if (!scaleFile.getParentFile().exists()) {
+            scaleFile.getParentFile().mkdirs();
+        }
+
+        try {
+
+            FileOutputStream outputStream = new FileOutputStream(scaleFile);
+            scaled.compress(Bitmap.CompressFormat.JPEG, 95, outputStream);
+
+            outputStream.flush();
+            outputStream.close();
+
+            String rotedpath = routedFile.getAbsolutePath();
+            routedFile.delete();
+
+            ImageUtil.updateGallery(mCtx,rotedpath);
+
+            return scaleFile;
+
+        } catch (IOException e) {
+            e.printStackTrace();
+            return routedFile;
+        }
+
+    }
+
+    /**
+     * Camera Permission
+     * */
+    private void checkCameraPermission() {
+
+        String[] permissions = {Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE};
+
+        RAUtil.checkPermissions(self,permissions);
+    }
+
+
+    /**
+     * data
+     * */
+
+    private void showProgressDialog() {
+        if (mProgressDialog == null) {
+            mProgressDialog = new ProgressDialog(this);
+            mProgressDialog.setMessage("loading...");
+            mProgressDialog.setCancelable(false);
+            mProgressDialog.show();
+        }
+    }
+
+    private void dismissProgressDialog() {
+        if (mProgressDialog != null && mProgressDialog.isShowing()) {
+            mProgressDialog.dismiss();
+        }
+        mProgressDialog = null;
+    }
+
+    private void showWarningMsg(String msg) {
+        if (msg == null || msg.length() == 0) {
+            return;
+        }
+
+        new android.support.v7.app.AlertDialog.Builder(mCtx).setTitle("Warning")
+                .setMessage(msg)
+                .setPositiveButton("Ok",null)
+                .show();
+    }
+
+    private void loadData() {
+
+        showProgressDialog();
+
+        if (mSectionArray != null) {
+            mSectionArray.clear();
+        }
+        if (mAdapter != null) {
+            mAdapter.notifyDataSetChanged();
+        }
+
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+
+                JSONObject json = com.usai.redant.apexdrivers.network.Network.requestOrderUpdateInfo(mCtx,mOrderID,mActionID);
+
+                Message msg = new Message();
+                msg.what = UpdateHandler.UpdateActionReloadData;
+                msg.obj = json;
+                mHandler.sendMessage(msg);
+            }
+
+        }).start();
+
+    }
+
+    private void handleJSON(JSONObject json) {
+
+        JSONArray sectionArr = json.optJSONArray("sections");
+        if (sectionArr != null) {
+
+            mSectionArray.clear();
+            try {
+                for (int i = 0; i < sectionArr.length(); i++) {
+
+                    JSONObject section = sectionArr.getJSONObject(i);
+                    UpdateSectionModel sectionModel = new UpdateSectionModel();
+                    sectionModel.title = section.optString("title");
+                    sectionModel.setItems(section.optJSONArray("items"));
+
+                    mSectionArray.add(sectionModel);
+                }
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+
+        }
+    }
+
+    private void changeData() {
+
+        /**
+         * 手动调用打开/关闭 Group,否则默认关闭
+         * */
+        if (mSectionArray.size() > 0) {
+            for (int i = 0; i < mSectionArray.size(); i++) {
+                mListView.expandGroup(i);
+            }
+        }
+        mAdapter.notifyDataSetChanged();
+    }
+
+    private String list2String(List<String> list, String seperator) {
+
+        Objects.requireNonNull(seperator);
+
+        if (list == null || list.size() == 0) {
+            return "";
+        }
+        if (list.size() == 1) {
+            return list.get(0);
+        }
+
+        StringBuffer stringBuffer = new StringBuffer();
+        for (int i = 0; i < list.size(); i++) {
+            String e = list.get(i);
+
+            stringBuffer.append(e);
+
+            if (i != list.size() - 1) {
+                stringBuffer.append(seperator);
+            }
+        }
+        return stringBuffer.toString();
+    }
+
+    private void update() {
+
+
+        Bundle params = new Bundle();
+        if (mOrderType2 != null) {
+            params.putString("orderType2",mOrderType2);
+        }
+        if (ApexDriverApplication.sharedApplication().getRequiredLocation()) {
+            Location location = ApexDriverApplication.sharedApplication().getCurrentLocation();
+
+            String locationStr = null;
+            if (location != null) {
+                locationStr = location.getLatitude() + "," + location.getLongitude();
+            } else {
+                locationStr = "-999,-999";
+            }
+            params.putString("location",locationStr);
+        }
+
+        ArrayList<String> emptyArr = new ArrayList<>();
+
+        final ArrayList<UpdateImageBaseModel> photoArr = prepareParams(params,emptyArr);
+
+        if (emptyArr.size() > 0) {
+
+            String msg = "please complete missing field:\n" + list2String(emptyArr,"\n");
+
+            new AlertDialog.Builder(mCtx)
+                    .setTitle("Warning")
+                    .setMessage(msg)
+                    .setPositiveButton("Ok",null)
+                    .show();
+
+            return;
+        }
+
+        showProgressDialog();
+
+        final Bundle finalParams = params;
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+
+                final JSONObject json = com.usai.redant.apexdrivers.network.Network.submitUpdateParams(finalParams);
+
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+
+                        if (json != null) {
+                            final int result = json.optInt("result",0);
+                            if (result == RESULT_TRUE) {
+
+                                boolean requiredLocation = json.optBoolean("requiredLocation");
+                                ApexDriverApplication.sharedApplication().setRequiredLocation(requiredLocation);
+
+                                if (photoArr.size() > 0) {
+                                    syncUploadPhotos(photoArr,json);
+                                } else {
+                                    dismissProgressDialog();
+                                    goHome();
+                                }
+
+                            } else {
+                                dismissProgressDialog();
+                                // error
+                                String errMsg = json.optString("err_msg");
+                                if (errMsg == null || errMsg.length() == 0) {
+                                    errMsg = "Sorry,there is something wrong";
+                                }
+                                showWarningMsg(errMsg);
+                            }
+                        } else {
+                            dismissProgressDialog();
+                            // error
+                            String errMsg = "Sorry,there is something wrong";
+                            showWarningMsg(errMsg);
+                        }
+                    }
+                });
+            }
+        }).start();
+    }
+
+    private void syncUploadPhotos(ArrayList<UpdateImageBaseModel> photos, final JSONObject json) {
+
+        if (photos == null || (photos != null && photos.size() == 0) || json == null) {
+            return;
+        }
+
+        final ArrayList<UpdateImageBaseModel> photoArr = photos;
+        final String serial = json.optString("serial");
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                int retryCount = 0;
+                ArrayList<UpdateImageBaseModel> finishArr = new ArrayList<>();
+                for (int i = 0; i < photoArr.size(); i++) {
+
+                    UpdateImageBaseModel model = photoArr.get(i);
+
+                    if (serial != null && serial.length() > 0) {
+
+                        Bundle fileParams = new Bundle();
+                        fileParams.putString("serial",serial);
+                        if (mOrderType2 != null) {
+                            fileParams.putString("orderType2",mOrderType2);
+                        }
+                        if (model.key != null) {
+                            fileParams.putString("key",model.key);
+                        }
+                        fileParams.putInt("type",model.type);
+
+                        JSONObject jsonObj = com.usai.redant.apexdrivers.network.Network.uploadFile(model.getImagePath(),fileParams);
+                        if (jsonObj != null) {
+                            int result = jsonObj.optInt("result",0);
+                            if (result != RESULT_TRUE) {
+                                i--;
+                                retryCount++;
+                                if (retryCount >= 3) {
+                                    break;
+                                }
+                            } else {
+                                retryCount = 0;
+                                File photoFile = new File(model.getImagePath());
+                                if (photoFile.exists()) {
+                                    photoFile.delete();
+                                    ImageUtil.updateGallery(mCtx,model.getImagePath());
+                                }
+                                finishArr.add(model);
+                            }
+                        } else {
+                            // error
+                            i--;
+                            retryCount++;
+                            if (retryCount >= 3) {
+                                break;
+                            }
+                        }
+                    }
+                }
+
+                for (UpdateImageBaseModel model : finishArr) {
+                    photoArr.remove(model);
+                }
+
+                final ArrayList<UpdateImageBaseModel> uploadFaildArr = photoArr;
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        dismissProgressDialog();
+                        if (uploadFaildArr.size() > 0) {
+
+                            mDialog = new AlertDialog.Builder(mCtx)
+                                    .setTitle("Warning")
+                                    .setMessage("upload the photos failed,would you like to retry or do it background?")
+                                    .setPositiveButton("Background", new DialogInterface.OnClickListener() {
+                                        @Override
+                                        public void onClick(DialogInterface dialog, int which) {
+
+                                            backgroundUpload(uploadFaildArr,json);
+                                            goHome();
+                                        }
+                                    })
+                                    .setNegativeButton("Retry", new DialogInterface.OnClickListener() {
+                                        @Override
+                                        public void onClick(DialogInterface dialog, int which) {
+                                            syncUploadPhotos(uploadFaildArr,json);
+                                        }
+                                    })
+                                    .show();
+
+                        } else {
+
+                            goHome();
+                        }
+
+                    }
+                });
+            }
+        }).start();
+
+    }
+
+    private void goHome() {
+        Intent intent = new Intent(self, MainActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        self.startActivity(intent);
+
+        sendBroadcast(new Intent(HomeFragment.HomeReloadBroadcastAction));
+    }
+
+    private void backgroundUpload(ArrayList<UpdateImageBaseModel> photos, final JSONObject json) {
+
+        if (photos == null || (photos != null && photos.size() == 0) || json == null) {
+            return;
+        }
+
+        ArrayList<Bundle> taskArr = new ArrayList<Bundle>();
+        final ArrayList<UpdateImageBaseModel> photoArr = photos;
+
+        Date currentTime = new Date();
+        SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/YYYY HH:mm");
+        String dateString = formatter.format(currentTime);
+
+        final String serial = json.optString("serial");
+
+        for (int i = 0; i < photoArr.size(); i++) {
+
+            UpdateImageBaseModel model = photoArr.get(i);
+
+            if (serial != null && serial.length() > 0) {
+
+                Bundle task = new Bundle();
+                task.putString("path",model.getImagePath());
+                task.putString("file", FileManager.lastPathComponent(model.getImagePath()));
+                task.putString("url", Network.URL_UPLOAD);
+                task.putString("order",mOrderID);
+                task.putString("action",getTitle().toString());
+                task.putString("time",dateString);
+                task.putString("name",model.title);
+
+
+                Bundle params = new Bundle();
+                params.putString("platform","android");
+                params.putString("serial", serial);
+                params.putString("name",ApexDriverApplication.sharedApplication().encryptUser());
+                params.putString("password",ApexDriverApplication.sharedApplication().encryptPassword());
+                if (mOrderType2 != null) {
+                    params.putString("orderType2",mOrderType2);
+                }
+                if (model.key != null) {
+                    params.putString("key",model.key);
+                }
+                params.putInt("type",model.type);
+
+                task.putBundle("params",params);
+                taskArr.add(task);
+            }
+        }
+
+        if (taskArr.size() > 0) {
+
+            Intent intent = new Intent(RABroadcast.ACTION_UPLOAD_ADD_TASK);
+            intent.putParcelableArrayListExtra("tasks",taskArr);
+
+            sendBroadcast(intent);
+        }
+
+    }
+
+    private ArrayList<UpdateImageBaseModel> prepareParams(Bundle params,ArrayList<String> emptyArr) {
+
+        if (params == null) {
+            return null;
+        }
+
+        ArrayList<UpdateImageBaseModel> photoArr = new ArrayList<>();
+
+        if (mOrderID != null) {
+            params.putString("orderID",mOrderID);
+        }
+        params.putInt("actionID",mActionID);
+
+        String user = ApexDriverApplication.sharedApplication().encryptUser();
+        String password = ApexDriverApplication.sharedApplication().encryptPassword();
+        if (user != null) {
+            params.putString("user",user);
+        }
+        if (password != null) {
+            params.putString("password",password);
+        }
+        params.putString("platform","android");
+//        params.putString("location",lat + "," + lon);
+
+        for (int i = 0; i < mSectionArray.size(); i++) {
+            UpdateSectionModel sectionModel = mSectionArray.get(i);
+            for (int j = 0; j < sectionModel.itemCount(); j++) {
+                UpdateBaseModel baseModel = sectionModel.itemModelForIndex(j);
+                switch (baseModel.type) {
+                    case UpdateBaseModel.UpdateTypeLabel: {
+
+                    }
+                    break;
+                    case UpdateBaseModel.UpdateTypeInput: {
+
+                        UpdateInputModel inputModel = (UpdateInputModel)baseModel;
+
+                        if (emptyArr != null && baseModel.required && (TextUtils.isEmpty(inputModel.value))) {
+                            emptyArr.add(emptyArr.size() + 1 + "." + baseModel.title);
+                            continue;
+                        }
+
+                        if (inputModel.key != null && inputModel.value != null && inputModel.value.length() > 0) {
+                            params.putString(inputModel.key,inputModel.value);
+                        }
+                    }
+                    break;
+                    case UpdateBaseModel.UpdateTypeMultInput: {
+
+                        UpdateMultInputModel multInputModel = (UpdateMultInputModel)baseModel;
+
+                        if (emptyArr != null && baseModel.required && (TextUtils.isEmpty(multInputModel.value))) {
+                            emptyArr.add(emptyArr.size() + 1 + "." + baseModel.title);
+                            continue;
+                        }
+
+                        if (multInputModel.key != null && multInputModel.value != null && multInputModel.value.length() > 0) {
+                            params.putString(multInputModel.key,multInputModel.value);
+                        }
+                    }
+                    break;
+                    case UpdateBaseModel.UpdateTypeSignature:
+                    case UpdateBaseModel.UpdateTypePhoto: {
+
+                        UpdateImageBaseModel photoModel = (UpdateImageBaseModel)baseModel;
+
+                        if (emptyArr != null && baseModel.required && (TextUtils.isEmpty(photoModel.getImagePath()))) {
+                            emptyArr.add(emptyArr.size() + 1 + "." + baseModel.title);
+                            continue;
+                        }
+
+                        if (photoModel.getImagePath() != null && photoModel.getImagePath().length() > 0 && photoModel.key != null) {
+                            params.putString(photoModel.key,photoModel.getImageName());
+
+                            photoArr.add(photoModel);
+                        }
+                    }
+                    break;
+                }
+            }
+        }
+
+        return photoArr;
+    }
+
+    /**
+     * Adapter Delegate
+     * */
+
+    @Override
+    public void scannerButtonDidClick(UpdateInputModel model) {
+
+        if (model == null) {
+            return;
+        }
+
+        if (PermissionChecker.checkSelfPermission(mCtx,Manifest.permission.CAMERA)!= PermissionChecker.PERMISSION_GRANTED) {
+
+            Toast.makeText(mCtx,"please allow app use camera",Toast.LENGTH_LONG).show();
+
+            return;
+        }
+
+        mInputModel = model;
+
+        Intent intent = new Intent();
+        intent.setClass(mCtx, CaptureActivity.class);
+        startActivityForResult(intent, REQUEST_SCANNER_CODE);
+    }
+
+    @Override
+    public void photoButtonDidClick(UpdatePhotoModel model) {
+
+        if (model == null) {
+            return;
+        }
+
+        mPhotoModel = model;
+        if (model.getImagePath() == null || model.getImagePath().length() == 0) {
+
+            startCamera();
+        } else {
+
+            Intent intent = PhotoPreviewActivity.build(self,model.getImagePath(),true);
+            startActivityForResult(intent,REQUEST_PREVIEW_CODE);
+        }
+    }
+
+    @Override
+    public void signatureClick(UpdateSignatureModel model) {
+        if (model == null) {
+            return;
+        }
+        mSignatueModel = model;
+        SignatureActivity.startSignature(this,REQUEST_SIGNATURE_CODE);
+    }
+
+    /**
+     * Camera
+     * */
+    private void startCamera()
+    {
+
+        boolean cameraPermission = PermissionChecker.checkSelfPermission(mCtx,Manifest.permission.CAMERA) == PermissionChecker.PERMISSION_GRANTED;
+        boolean storageReadPermission = PermissionChecker.checkSelfPermission(mCtx,Manifest.permission.READ_EXTERNAL_STORAGE) == PermissionChecker.PERMISSION_GRANTED;
+        boolean storageWritePermission = PermissionChecker.checkSelfPermission(mCtx,Manifest.permission.WRITE_EXTERNAL_STORAGE) == PermissionChecker.PERMISSION_GRANTED;
+
+        if (!cameraPermission || !storageReadPermission || !storageWritePermission) {
+
+            Toast.makeText(mCtx,"please allow app use camera and storage",Toast.LENGTH_LONG).show();
+
+            return;
+        }
+
+
+        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+        // Ensure that there's a camera activity to handle the intent
+
+        if (takePictureIntent.resolveActivity(getPackageManager()) != null)
+        {
+            if (photoFile != null)
+                photoFile = null;
+            try
+            {
+                photoFile = createImageFile();
+            }
+            catch (IOException ex)
+            {
+                Log.d("Update Order", "StartCamera: " + ex);
+            }
+            // Continue only if the File was successfully created
+            if (photoFile != null)
+            {
+                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, getImageContentUri(this,photoFile));
+                startActivityForResult(takePictureIntent, REQUEST_CAMERA_CODE);
+            }
+        }
+    }
+
+    private File createImageFile() throws IOException
+    {
+        // Create an image file name
+        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmSS").format(new Date());
+        String imageFileName = "JPEG_" + timeStamp + "_";
+        String appName = RAUtil.getApplicationName(mCtx);
+
+        File storageDir = new File(Environment.getExternalStorageDirectory().getPath() + "/" + appName + "/photo/temp/");
+
+        File dir1 = new File(Environment.getExternalStorageDirectory().getPath() + "/" + appName);
+        File dir2 = new File(Environment.getExternalStorageDirectory().getPath() + "/" + appName + "/photo");
+        File dir3 = new File(Environment.getExternalStorageDirectory().getPath() + "/" + appName + "/photo/temp/");
+
+
+        if (!dir1.exists())
+        {
+            boolean b = dir1.mkdir();
+        }
+        if (!dir2.exists())
+        {
+            boolean b = dir2.mkdir();
+        }
+        if (!dir3.exists())
+        {
+            boolean b = dir3.mkdir();
+        }
+
+
+        File image = File.createTempFile(imageFileName, /* prefix */
+                ".jpg", /* suffix */
+                storageDir /* directory */
+        );
+
+        return image;
+    }
+
+    private Uri getImageContentUri(Context context, File imageFile) {
+        String filePath = imageFile.getAbsolutePath();
+        Cursor cursor = context.getContentResolver().query(
+                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+                new String[] { MediaStore.Images.Media._ID },
+                MediaStore.Images.Media.DATA + "=? ",
+                new String[] { filePath }, null);
+
+        if (cursor != null && cursor.moveToFirst()) {
+            int id = cursor.getInt(cursor
+                    .getColumnIndex(MediaStore.MediaColumns._ID));
+            Uri baseUri = Uri.parse("content://media/external/images/media");
+            return Uri.withAppendedPath(baseUri, "" + id);
+        } else {
+            if (imageFile.exists()) {
+                ContentValues values = new ContentValues();
+                values.put(MediaStore.Images.Media.DATA, filePath);
+                return context.getContentResolver().insert(
+                        MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
+            } else {
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Handler
+     * */
+    private static class UpdateHandler extends Handler {
+
+        public static final int UpdateActionReloadData = 0;
+
+        private WeakReference<UpdateActivity> mWeakActivity;
+
+        UpdateHandler(UpdateActivity activity) {
+            if (activity != null) {
+                mWeakActivity = new WeakReference<>(activity);
+            }
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            super.handleMessage(msg);
+
+            UpdateActivity activity = mWeakActivity.get();
+            activity.dismissProgressDialog();
+
+            switch (msg.what) {
+                case UpdateActionReloadData: {
+                    if (activity.mRefresh.isRefreshing()) {
+                        activity.mRefresh.setRefreshing(false);
+                    }
+                    JSONObject json = (JSONObject) msg.obj;
+                    if (json != null) {
+
+                        try {
+
+                            int restul = json.getInt("result");
+                            if (restul == RESULT_TRUE) {
+
+                                activity.handleJSON(json);
+
+                            } else {
+                                // error
+                                String errMsg = json.optString("err_msg");
+                                if (errMsg == null || errMsg.length() == 0) {
+                                    errMsg = "Sorry,there is something wrong";
+                                }
+                                activity.showWarningMsg(errMsg);
+                            }
+
+                        } catch (JSONException e) {
+                            e.printStackTrace();
+                            // error
+                            String errMsg = "Sorry,there is something wrong";
+                            activity.showWarningMsg(errMsg);
+                        }
+                    } else {
+                        // error
+                        String errMsg = "Sorry,there is something wrong";
+                        activity.showWarningMsg(errMsg);
+                    }
+
+                    activity.changeData();
+
+                }
+                break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Save
+     * */
+
+    private JSONObject prepareSavedJson() {
+
+        JSONObject json = new JSONObject();
+        JSONArray sections = new JSONArray();
+        try {
+            for (int i = 0; i < mSectionArray.size(); i++) {
+
+                UpdateSectionModel sectionModel = mSectionArray.get(i);
+                JSONObject sectionJson = sectionModel.sectionModel2Json();
+                if (sectionJson != null) {
+                    sections.put(sectionJson);
+                }
+            }
+            json.put("sections",sections);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+        return json;
+    }
+}

+ 552 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/update/UpdateAdapter.java

@@ -0,0 +1,552 @@
+package com.usai.redant.apexdrivers.update;
+
+import android.content.Context;
+import android.content.Intent;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseExpandableListAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.apexdrivers.signature.SignatureActivity;
+import com.usai.redant.apexdrivers.update.model.UpdateBaseModel;
+import com.usai.redant.apexdrivers.update.model.UpdateInputModel;
+import com.usai.redant.apexdrivers.update.model.UpdateLabelModel;
+import com.usai.redant.apexdrivers.update.model.UpdateMultInputModel;
+import com.usai.redant.apexdrivers.update.model.UpdatePhotoModel;
+import com.usai.redant.apexdrivers.update.model.UpdateSignatureModel;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+public class UpdateAdapter extends BaseExpandableListAdapter {
+
+    private Context mCtx;
+    private ArrayList<UpdateSectionModel> mSections;
+    private WeakReference<UpdateAdapterDelegate> mDelegate;
+    UpdateAdapter(Context context, ArrayList<UpdateSectionModel> sections, UpdateAdapterDelegate delegate) {
+        mCtx = context;
+        mSections = sections;
+        mDelegate = new WeakReference<>(delegate);
+    }
+
+    @Override
+    public int getGroupCount() {
+        if (mSections == null) {
+            return 0;
+        }
+        return mSections.size();
+    }
+
+    @Override
+    public int getChildrenCount(int groupPosition) {
+        UpdateSectionModel model = mSections.get(groupPosition);
+        return model.itemCount();
+    }
+
+    @Override
+    public Object getGroup(int groupPosition) {
+        UpdateSectionModel model = mSections.get(groupPosition);
+        return model;
+    }
+
+    @Override
+    public Object getChild(int groupPosition, int childPosition) {
+        UpdateSectionModel sectionModel = mSections.get(groupPosition);
+        UpdateBaseModel model = sectionModel.itemModelForIndex(childPosition);
+        return model;
+    }
+
+    @Override
+    public long getGroupId(int groupPosition) {
+        return groupPosition;
+    }
+
+    @Override
+    public long getChildId(int groupPosition, int childPosition) {
+        return childPosition;
+    }
+
+    @Override
+    public int getChildTypeCount() {
+
+        return UpdateBaseModel.getTypeCount();
+    }
+
+    @Override
+    public int getChildType(int groupPosition, int childPosition) {
+
+        UpdateSectionModel sectionModel = mSections.get(groupPosition);
+        UpdateBaseModel model = sectionModel.itemModelForIndex(childPosition);
+        return model.type;
+    }
+
+    @Override
+    public boolean hasStableIds() {
+        return true;
+    }
+
+    @Override
+    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
+        if (mCtx == null) {
+            return null;
+        }
+
+        SectionHolder holder;
+        if (convertView == null) {
+
+            convertView = LayoutInflater.from(mCtx).inflate(R.layout.update_section_header,null);
+            holder = new SectionHolder(convertView);
+
+        } else {
+            holder = (SectionHolder) convertView.getTag();
+        }
+        UpdateSectionModel sectionModel = mSections.get(groupPosition);
+        holder.bindSecionModel(sectionModel);
+
+
+        return convertView;
+    }
+
+    @Override
+    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
+
+        UpdateSectionModel sectionModel = mSections.get(groupPosition);
+        UpdateBaseModel model = sectionModel.itemModelForIndex(childPosition);
+
+        switch (getChildType(groupPosition,childPosition)) {
+            case UpdateBaseModel.UpdateTypeLabel: {
+
+                LabelHolder holder;
+                if (convertView == null) {
+
+                    convertView = LayoutInflater.from(mCtx).inflate(R.layout.update_label_cell,null);
+                    holder = new LabelHolder(convertView);
+
+                } else {
+
+                    holder = (LabelHolder)convertView.getTag();
+                }
+
+                UpdateLabelModel labelModel = (UpdateLabelModel)model;
+                holder.bindModel(labelModel);
+
+            }
+            break;
+            case UpdateBaseModel.UpdateTypeInput: {
+
+                ScannerInputHolder holder;
+                if (convertView == null) {
+
+                    convertView = LayoutInflater.from(mCtx).inflate(R.layout.update_scanner_input_cell,null);
+                    holder = new ScannerInputHolder(convertView);
+
+                } else {
+
+                    holder = (ScannerInputHolder)convertView.getTag();
+                }
+
+                UpdateInputModel inputModel = (UpdateInputModel)model;
+                holder.bindModel(inputModel);
+
+            }
+            break;
+            case UpdateBaseModel.UpdateTypeMultInput: {
+
+                MultInputHolder holder;
+                if (convertView == null) {
+
+                    convertView = LayoutInflater.from(mCtx).inflate(R.layout.update_multinput_cell,null);
+                    holder = new MultInputHolder(convertView);
+
+                } else {
+
+                    holder = (MultInputHolder)convertView.getTag();
+                }
+
+                UpdateMultInputModel multInputModel = (UpdateMultInputModel)model;
+                holder.bindModel(multInputModel);
+
+            }
+            break;
+            case UpdateBaseModel.UpdateTypePhoto: {
+
+                PhotoHolder holder;
+                if (convertView == null) {
+
+                    convertView = LayoutInflater.from(mCtx).inflate(R.layout.update_photo_cell,null);
+                    holder = new PhotoHolder(convertView);
+
+                } else {
+
+                    holder = (PhotoHolder)convertView.getTag();
+                }
+
+                UpdatePhotoModel photoModel = (UpdatePhotoModel)model;
+                holder.bindModel(photoModel);
+            }
+            break;
+            case UpdateBaseModel.UpdateTypeSignature: {
+
+                SignatureHolder holder;
+                if (convertView == null) {
+
+                    convertView = LayoutInflater.from(mCtx).inflate(R.layout.update_signature_cell,null);
+                    holder = new SignatureHolder(convertView);
+
+                } else {
+                    holder = (SignatureHolder) convertView.getTag();
+                }
+
+                UpdateSignatureModel signatureModel = (UpdateSignatureModel)model;
+                holder.bindModel(signatureModel);
+            }
+        }
+
+        return convertView;
+    }
+
+    @Override
+    public boolean isChildSelectable(int groupPosition, int childPosition) {
+        return false;
+    }
+
+    /**
+     * Holder
+     * */
+    private class SectionHolder {
+
+        TextView titleTv;
+        SectionHolder(View view) {
+
+            titleTv = view.findViewById(R.id.header_title_tv);
+
+            view.setTag(this);
+        }
+
+        public void bindSecionModel(UpdateSectionModel model) {
+            titleTv.setText(model.title);
+        }
+    }
+
+    private class LabelHolder {
+
+        TextView titleTv,valueTv;
+
+        LabelHolder(View view) {
+
+            titleTv = view.findViewById(R.id.update_label_title_tv);
+            valueTv = view.findViewById(R.id.update_label_value_tv);
+            view.setTag(this);
+        }
+
+        public void bindModel(UpdateLabelModel model) {
+            titleTv.setText(model.title);
+            valueTv.setText(model.value);
+        }
+    }
+
+    private class ScannerInputHolder implements UpdateBaseModel.UpdateModelDelegate {
+
+        TextView titleTv,requiredTv;
+        EditText valueEt;
+        ImageButton scannerBtn;
+        WeakReference<UpdateInputModel> weakInput;
+        UpdateTextWatcher mTextWatcher = new UpdateTextWatcher();
+
+        ScannerInputHolder(View view) {
+
+            titleTv = view.findViewById(R.id.update_scanner_input_title_tv);
+            requiredTv = view.findViewById(R.id.update_scanner_input_required_tv);
+            valueEt = view.findViewById(R.id.update_input_tv);
+            scannerBtn = view.findViewById(R.id.update_scanner_btn);
+            scannerBtn.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    if (weakInput != null && weakInput.get() != null && mDelegate != null && mDelegate.get() != null) {
+                        UpdateInputModel model = weakInput.get();
+                        mDelegate.get().scannerButtonDidClick(model);
+                    }
+                }
+            });
+
+            valueEt.addTextChangedListener(mTextWatcher);
+
+            view.setTag(this);
+        }
+
+        public void bindModel(UpdateInputModel model) {
+
+            // 释放旧的
+            if (weakInput != null && weakInput.get() != null) {
+                weakInput.get().setDelegate(null);
+            }
+
+            if (model != null) {
+                weakInput = new WeakReference<>(model);
+                weakInput.get().setDelegate(this);
+            } else {
+                weakInput = null;
+            }
+            mTextWatcher.setInputModel(weakInput.get());
+
+            refresh();
+        }
+
+        @Override
+        public void refresh() {
+
+            if (weakInput != null && weakInput.get() != null) {
+                titleTv.setText(weakInput.get().title);
+                valueEt.setText(weakInput.get().value);
+                if (weakInput.get().scannable) {
+                    scannerBtn.setVisibility(View.VISIBLE);
+                } else {
+                    scannerBtn.setVisibility(View.GONE);
+                }
+                requiredTv.setVisibility(weakInput.get().required ? View.VISIBLE : View.GONE);
+            } else {
+                titleTv.setText(null);
+                valueEt.setText(null);
+                scannerBtn.setVisibility(View.VISIBLE);
+                requiredTv.setVisibility(View.GONE);
+            }
+        }
+    }
+
+    private class MultInputHolder implements UpdateBaseModel.UpdateModelDelegate,View.OnTouchListener {
+
+        TextView titleTv,requiredTv;
+        EditText valueEt;
+
+        WeakReference<UpdateMultInputModel> weakInput;
+        UpdateTextWatcher mTextWatcher = new UpdateTextWatcher();
+
+        MultInputHolder(View view) {
+
+            titleTv = view.findViewById(R.id.update_multiple_input_title_tv);
+            requiredTv = view.findViewById(R.id.update_multiple_input_required_tv);
+            valueEt = view.findViewById(R.id.update_multiple_input_value_tv);
+            valueEt.addTextChangedListener(mTextWatcher);
+            valueEt.setOnTouchListener(this);
+
+            view.setTag(this);
+        }
+
+        public void bindModel(UpdateMultInputModel model) {
+
+            // 释放旧的
+            if (weakInput != null && weakInput.get() != null) {
+                weakInput.get().setDelegate(null);
+            }
+
+            if (model != null) {
+                weakInput = new WeakReference<>(model);
+                weakInput.get().setDelegate(this);
+            } else {
+                weakInput = null;
+            }
+            mTextWatcher.setInputModel(weakInput.get());
+
+            refresh();
+        }
+
+        @Override
+        public void refresh() {
+
+            if (weakInput != null && weakInput.get() != null) {
+                titleTv.setText(weakInput.get().title);
+                valueEt.setText(weakInput.get().value);
+                requiredTv.setVisibility(weakInput.get().required ? View.VISIBLE : View.GONE);
+            } else {
+                titleTv.setText(null);
+                valueEt.setText(null);
+                requiredTv.setVisibility(View.GONE);
+            }
+        }
+
+        @Override
+        public boolean onTouch(View v, MotionEvent event) {
+
+            int i = v.getId();
+            if (i == R.id.update_multiple_input_value_tv) {// 解决scrollView中嵌套EditText导致不能上下滑动的问题
+                v.getParent().requestDisallowInterceptTouchEvent(true);
+                switch (event.getAction() & MotionEvent.ACTION_MASK) {
+                    case MotionEvent.ACTION_UP:
+                        v.getParent().requestDisallowInterceptTouchEvent(false);
+                        break;
+                }
+            }
+
+            return false;
+        }
+    }
+
+    private class PhotoHolder implements UpdateBaseModel.UpdateModelDelegate {
+
+        TextView titleTv,requiredTv;
+        ImageButton photoBtn;
+        WeakReference<UpdatePhotoModel> weakPhoto;
+        PhotoHolder(View view) {
+
+            titleTv = view.findViewById(R.id.update_photo_title_tv);
+            requiredTv = view.findViewById(R.id.update_photo_required_tv);
+            photoBtn = view.findViewById(R.id.update_photo_btn);
+            photoBtn.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    if (weakPhoto != null && weakPhoto.get() != null && mDelegate != null && mDelegate.get() != null) {
+                        UpdatePhotoModel model = weakPhoto.get();
+                        mDelegate.get().photoButtonDidClick(model);
+                    }
+                }
+            });
+            view.setTag(this);
+        }
+
+        public void bindModel(UpdatePhotoModel model) {
+
+            if (weakPhoto != null && weakPhoto.get() != null) {
+                weakPhoto.get().setDelegate(null);
+            }
+
+            if (model != null) {
+                weakPhoto = new WeakReference<>(model);
+                weakPhoto.get().setDelegate(this);
+            } else {
+                weakPhoto = null;
+            }
+
+            refresh();
+        }
+
+        @Override
+        public void refresh() {
+
+            if (weakPhoto != null && weakPhoto.get() != null) {
+                titleTv.setText(weakPhoto.get().title);
+                photoBtn.setImageBitmap(weakPhoto.get().getPhoto());
+                requiredTv.setVisibility(weakPhoto.get().required ? View.VISIBLE : View.GONE);
+            } else {
+                titleTv.setText(null);
+                photoBtn.setImageBitmap(null);
+                requiredTv.setVisibility(View.GONE);
+            }
+
+        }
+    }
+
+    private class SignatureHolder implements UpdateBaseModel.UpdateModelDelegate {
+
+        TextView titleTv,requiredTv;
+        ImageView signatureView;
+        WeakReference<UpdateSignatureModel> weakSignature;
+
+        SignatureHolder(View view) {
+
+            titleTv = view.findViewById(R.id.update_signature_title_tv);
+            requiredTv = view.findViewById(R.id.update_signature_required_tv);
+            signatureView = view.findViewById(R.id.update_signature_image_view);
+            signatureView.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+
+                    if (weakSignature != null && weakSignature.get() != null && mDelegate != null && mDelegate.get() != null) {
+                        UpdateSignatureModel model = weakSignature.get();
+                        mDelegate.get().signatureClick(model);
+                    }
+                }
+            });
+            view.setTag(this);
+        }
+
+        public void bindModel(UpdateSignatureModel model) {
+
+            if (weakSignature != null && weakSignature.get() != null) {
+                weakSignature.get().setDelegate(null);
+            }
+
+            if (model != null) {
+                weakSignature = new WeakReference<>(model);
+                weakSignature.get().setDelegate(this);
+            } else {
+                weakSignature = null;
+            }
+
+            refresh();
+        }
+
+
+        @Override
+        public void refresh() {
+
+            if (weakSignature != null && weakSignature.get() != null) {
+                titleTv.setText(weakSignature.get().title);
+                signatureView.setImageBitmap(weakSignature.get().getImage());
+                requiredTv.setVisibility(weakSignature.get().required ? View.VISIBLE : View.GONE);
+            } else {
+                titleTv.setText(null);
+                signatureView.setImageBitmap(null);
+                requiredTv.setVisibility(View.GONE);
+            }
+
+        }
+    }
+
+    public interface UpdateAdapterDelegate {
+        void scannerButtonDidClick(UpdateInputModel model);
+        void photoButtonDidClick(UpdatePhotoModel model);
+        void signatureClick(UpdateSignatureModel model);
+    }
+
+    public class UpdateTextWatcher implements TextWatcher {
+
+        private WeakReference<UpdateBaseModel> weakModel;
+
+        void setInputModel(UpdateBaseModel model) {
+            if (model != null) {
+                weakModel = new WeakReference<>(model);
+            } else {
+                weakModel = null;
+            }
+        }
+
+        @Override
+        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+        }
+
+        @Override
+        public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+            if (weakModel == null) {
+                return;
+            }
+
+            if (weakModel.get() instanceof UpdateInputModel) {
+
+                UpdateInputModel inputModel = (UpdateInputModel)weakModel.get();
+                inputModel.value = s.toString();
+
+            } else if (weakModel.get() instanceof  UpdateMultInputModel) {
+
+                UpdateMultInputModel multInputModel = (UpdateMultInputModel) weakModel.get();
+                multInputModel.value = s.toString();
+            }
+
+        }
+
+        @Override
+        public void afterTextChanged(Editable s) {
+
+        }
+    }
+}

+ 132 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/update/UpdateSectionModel.java

@@ -0,0 +1,132 @@
+package com.usai.redant.apexdrivers.update;
+
+import com.usai.redant.apexdrivers.update.model.UpdateBaseModel;
+import com.usai.redant.apexdrivers.update.model.UpdateInputModel;
+import com.usai.redant.apexdrivers.update.model.UpdateLabelModel;
+import com.usai.redant.apexdrivers.update.model.UpdateMultInputModel;
+import com.usai.redant.apexdrivers.update.model.UpdatePhotoModel;
+import com.usai.redant.apexdrivers.update.model.UpdateSignatureModel;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+
+public class UpdateSectionModel {
+
+    public String title;
+    public ArrayList<UpdateBaseModel> items = new ArrayList<>();
+
+    public void setItems(JSONArray items) {
+        this.items.clear();
+
+        if (items != null) {
+
+            for (int i = 0; i < items.length(); i++) {
+                JSONObject item = items.optJSONObject(i);
+                if (item != null) {
+                    int type = item.optInt("type");
+                    switch (type) {
+                        case UpdateBaseModel.UpdateTypeLabel: {
+
+                            UpdateLabelModel model = new UpdateLabelModel();
+                            model.type = type;
+                            model.title = item.optString("title");
+                            model.key = item.optString("key");
+                            model.value = item.optString("value");
+                            model.required = item.optBoolean("required");
+                            this.items.add(model);
+                        }
+                        break;
+                        case UpdateBaseModel.UpdateTypeInput: {
+
+                            UpdateInputModel model = new UpdateInputModel();
+                            model.type = type;
+                            model.title = item.optString("title");
+                            model.key = item.optString("key");
+                            model.value = item.optString("value");
+                            model.scannable = item.optBoolean("scannable");
+                            model.required = item.optBoolean("required");
+                            this.items.add(model);
+                        }
+                        break;
+                        case UpdateBaseModel.UpdateTypeMultInput: {
+
+                            UpdateMultInputModel model = new UpdateMultInputModel();
+                            model.type = type;
+                            model.title = item.optString("title");
+                            model.key = item.optString("key");
+                            model.value = item.optString("value");
+                            model.required = item.optBoolean("required");
+                            this.items.add(model);
+                        }
+                        break;
+                        case UpdateBaseModel.UpdateTypePhoto: {
+
+                            UpdatePhotoModel model = new UpdatePhotoModel();
+                            model.type = type;
+                            model.title = item.optString("title");
+                            model.key = item.optString("key");
+                            model.setPhotoPath(item.optString("photoPath")); // 本地使用的,保存、恢复
+                            model.required = item.optBoolean("required");
+                            this.items.add(model);
+                        }
+                        break;
+                        case UpdateBaseModel.UpdateTypeSignature: {
+
+                            UpdateSignatureModel model = new UpdateSignatureModel();
+                            model.type = type;
+                            model.title = item.optString("title");
+                            model.key = item.optString("key");
+                            model.setSignaturePath(item.optString("signaturePath")); // 本地使用的,保存、恢复
+                            model.required = item.optBoolean("required");
+                            this.items.add(model);
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    public int itemCount() {
+        return items.size();
+    }
+
+    public UpdateBaseModel itemModelForIndex(int index) {
+
+        if (index < 0 || index >= items.size()) {
+            return null;
+        }
+
+        return items.get(index);
+    }
+
+    public JSONObject sectionModel2Json() {
+
+        JSONObject json = new JSONObject();
+
+        try {
+
+            if (title != null) {
+                json.put("title",title);
+            }
+            JSONArray items = new JSONArray();
+            for (int i = 0; i < itemCount(); i++) {
+                UpdateBaseModel model = itemModelForIndex(i);
+                JSONObject itemJson = model.model2Json();
+                if (itemJson != null) {
+                    items.put(itemJson);
+                }
+            }
+            json.put("items",items);
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        return json;
+    }
+
+}

+ 58 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/update/model/UpdateBaseModel.java

@@ -0,0 +1,58 @@
+package com.usai.redant.apexdrivers.update.model;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.lang.ref.WeakReference;
+
+public class UpdateBaseModel {
+
+    public interface UpdateModelDelegate {
+        void refresh();
+    }
+
+    public final static int UpdateTypeLabel = 0;
+    public final static int UpdateTypeInput = 1;
+    public final static int UpdateTypeMultInput= 2;
+    public final static int UpdateTypePhoto = 3;
+    public final static int UpdateTypeSignature = 4;
+
+    public static int getTypeCount() {
+        return 5;
+    }
+
+    public int type;
+    public String key;
+    public String title;
+    public boolean required;
+
+    public WeakReference<UpdateModelDelegate> weakDelegate;
+    public void setDelegate(UpdateModelDelegate delegate) {
+        if (delegate == null) {
+            weakDelegate = null;
+        } else {
+            weakDelegate = new WeakReference<>(delegate);
+        }
+    }
+
+    public JSONObject model2Json() {
+
+        try {
+            JSONObject json = new JSONObject();
+            json.put("type",type);
+            if (title != null) {
+                json.put("title",title);
+            }
+            if (key != null) {
+                json.put("key",key);
+            }
+            json.put("required",required);
+
+            return json;
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+}

+ 19 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/update/model/UpdateImageBaseModel.java

@@ -0,0 +1,19 @@
+package com.usai.redant.apexdrivers.update.model;
+
+import android.graphics.Bitmap;
+
+public class UpdateImageBaseModel extends UpdateBaseModel {
+
+    public Bitmap getImage() {
+        return null;
+    }
+
+    public String getImageName() {
+        return null;
+    }
+
+    public String getImagePath() {
+        return null;
+    }
+
+}

+ 37 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/update/model/UpdateInputModel.java

@@ -0,0 +1,37 @@
+package com.usai.redant.apexdrivers.update.model;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class UpdateInputModel extends UpdateBaseModel {
+
+    public boolean scannable;
+    public String value;
+
+    public void setScannerInputValue(String value) {
+        this.value = value;
+        if (weakDelegate != null) {
+            weakDelegate.get().refresh();
+        }
+    }
+
+    @Override
+    public JSONObject model2Json() {
+        JSONObject json =  super.model2Json();
+
+        if (json != null) {
+            try {
+
+                json.put("scannable",scannable);
+                if (value != null) {
+                    json.put("value",value);
+                }
+
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+        }
+
+        return json;
+    }
+}

+ 26 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/update/model/UpdateLabelModel.java

@@ -0,0 +1,26 @@
+package com.usai.redant.apexdrivers.update.model;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class UpdateLabelModel extends UpdateBaseModel {
+
+    public String value;
+
+    @Override
+    public JSONObject model2Json() {
+        JSONObject json = super.model2Json();
+
+        try {
+
+            if (value != null) {
+                json.put("value",value);
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        return json;
+    }
+}

+ 33 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/update/model/UpdateMultInputModel.java

@@ -0,0 +1,33 @@
+package com.usai.redant.apexdrivers.update.model;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class UpdateMultInputModel extends UpdateBaseModel {
+
+    public String value;
+
+    public void setMultInputValue(String value) {
+        this.value = value;
+        if (weakDelegate != null) {
+            weakDelegate.get().refresh();
+        }
+    }
+
+    @Override
+    public JSONObject model2Json() {
+        JSONObject json = super.model2Json();
+
+        try {
+
+            if (value != null) {
+                json.put("value",value);
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        return json;
+    }
+}

+ 86 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/update/model/UpdatePhotoModel.java

@@ -0,0 +1,86 @@
+package com.usai.redant.apexdrivers.update.model;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+
+public class UpdatePhotoModel extends UpdateImageBaseModel {
+
+    private Bitmap photo;
+    private String photoPath;
+
+    @Override
+    public String getImageName() {
+
+        return getPhotoName();
+    }
+
+    @Override
+    public Bitmap getImage() {
+
+        return getPhoto();
+    }
+
+    @Override
+    public String getImagePath() {
+
+        return photoPath;
+    }
+
+    public String getPhotoName() {
+        if (photoPath == null) {
+            return null;
+        }
+        File tmpFile = new File(photoPath);
+        return tmpFile.getName();
+    }
+
+    public void setPhoto(Bitmap photo) {
+        this.photo = photo;
+        if (weakDelegate != null) {
+            weakDelegate.get().refresh();
+        }
+    }
+
+    public Bitmap getPhoto() {
+        if (photo != null) {
+            return photo;
+        }
+        if (photoPath != null && photoPath.length() > 0) {
+            photo = BitmapFactory.decodeFile(photoPath);
+            return photo;
+        }
+        return null;
+    }
+
+    public void setPhotoPath(String photoPath) {
+        this.photoPath = photoPath;
+        if (photoPath == null) {
+            setPhoto(null);
+        } else {
+            setPhoto(BitmapFactory.decodeFile(photoPath)); // 拍照成功后,立即获取Bitmap可能会取到null
+        }
+    }
+
+    @Override
+    public JSONObject model2Json() {
+        JSONObject json = super.model2Json();
+
+        try {
+
+            if (photoPath != null && photoPath.length() > 0) {
+                json.put("photoPath",photoPath);
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        return json;
+    }
+
+}

+ 75 - 0
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/update/model/UpdateSignatureModel.java

@@ -0,0 +1,75 @@
+package com.usai.redant.apexdrivers.update.model;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+
+public class UpdateSignatureModel extends UpdateImageBaseModel {
+
+    private Bitmap signature;
+    private String signaturePath;
+
+    public void setSignature(Bitmap signature) {
+        this.signature = signature;
+
+        if (weakDelegate != null && weakDelegate.get() != null) {
+            weakDelegate.get().refresh();
+        }
+    }
+
+    public Bitmap getSignature() {
+        return signature;
+    }
+
+    @Override
+    public Bitmap getImage() {
+        return getSignature();
+    }
+
+    @Override
+    public String getImageName() {
+        if (signaturePath == null) {
+            return null;
+        }
+        File tmpFile = new File(signaturePath);
+        return tmpFile.getName();
+    }
+
+    @Override
+    public String getImagePath() {
+        return signaturePath;
+    }
+
+    public void setSignaturePath(String signaturePath) {
+        this.signaturePath = signaturePath;
+
+        if (signaturePath == null) {
+            setSignature(null);
+        } else {
+            setSignature(BitmapFactory.decodeFile(signaturePath));
+        }
+    }
+
+    @Override
+    public JSONObject model2Json() {
+        JSONObject json = super.model2Json();
+
+        try {
+
+            if (signaturePath != null && signaturePath.length() > 0) {
+                json.put("signaturePath",signaturePath);
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        return json;
+    }
+
+
+}

Some files were not shown because too many files changed in this diff