Bläddra i källkod

规范library 包名

Ray Zhang 7 år sedan
förälder
incheckning
d3a74628f6
94 ändrade filer med 19005 tillägg och 33 borttagningar
  1. 167 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contacts/Contact.java
  2. 475 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contacts/ContactsManager.java
  3. 281 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/receiver/AlarmReceiver.java
  4. 53 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/receiver/BootCompleteBroadcastReceiver.java
  5. 21 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/receiver/RABroadcast.java
  6. 50 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/receiver/RABroadcastReceiver.java
  7. 793 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/service/RAService.java
  8. 141 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/upload/RAOperationQueue.java
  9. 742 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/upload/RAUploadManager.java
  10. 10 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/upload/UploadCallback.java
  11. 547 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/upload/UploadService.java
  12. 327 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/utils/AESUtil.java
  13. 114 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/utils/CrashHandler.java
  14. 66 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/utils/DBHelper.java
  15. 110 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/utils/DBUtil.java
  16. 277 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/utils/FileManager.java
  17. 275 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/utils/ImageUtil.java
  18. 692 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/utils/Network.java
  19. 386 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/utils/RAUtil.java
  20. 276 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/utils/dbgUtil.java
  21. 14 14
      ApexDrivers/app/src/main/AndroidManifest.xml
  22. 2 2
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/ApexDriverApplication.java
  23. 4 4
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/MainActivity.java
  24. 143 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/badgeview/BadgeView.java
  25. 87 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/codescanner/AmbientLightManager.java
  26. 131 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/codescanner/BeepManager.java
  27. 888 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/codescanner/CaptureActivity.java
  28. 170 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/codescanner/CaptureActivityHandler.java
  29. 102 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/codescanner/DecodeFormatManager.java
  30. 122 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/codescanner/DecodeHandler.java
  31. 236 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/codescanner/DecodeHintManager.java
  32. 105 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/codescanner/DecodeThread.java
  33. 49 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/codescanner/FinishListener.java
  34. 116 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/codescanner/InactivityTimer.java
  35. 261 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/codescanner/Intents.java
  36. 35 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/codescanner/ViewfinderResultPointCallback.java
  37. 189 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/codescanner/ViewfinderView.java
  38. 689 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/detail/DetailActivity.java
  39. 652 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/detail/DetailAdapter.java
  40. 131 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/detail/DetailSectionModel.java
  41. 84 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailActionModel.java
  42. 9 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailActionSelectionModel.java
  43. 45 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailBaseModel.java
  44. 13 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailLocationModel.java
  45. 10 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailMapModel.java
  46. 12 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailMultipleLineModel.java
  47. 85 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailPhotoModel.java
  48. 84 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailSignatureModel.java
  49. 13 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailSingleLineModel.java
  50. 71 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailSubActionModel.java
  51. 479 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/filter/OrderFilterActivity.java
  52. 367 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/filter/OrderFilterAdapter.java
  53. 122 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/home/HomeCellLayout.java
  54. 809 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/home/HomeFragment.java
  55. 190 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/home/HomeHeaderView.java
  56. 660 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/home/HomeMoreActivity.java
  57. 118 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/home/HomeOrderModel.java
  58. 309 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/login/LoginFragment.java
  59. 363 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/login/RetrievePasswordActivity.java
  60. 46 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/message/MessageActivity.java
  61. 303 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/network/Network.java
  62. 122 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/receiver/ApexDriverAlarmReceiver.java
  63. 71 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/receiver/ApexDriverBootCompleteReceiver.java
  64. 45 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/setting/SettingActivity.java
  65. 133 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/signature/SignatureActivity.java
  66. 167 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/signature/SignatureView.java
  67. 97 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/update/PhotoPreviewActivity.java
  68. 1054 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/update/UpdateActivity.java
  69. 553 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/update/UpdateAdapter.java
  70. 132 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/update/UpdateSectionModel.java
  71. 58 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/update/model/UpdateBaseModel.java
  72. 19 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/update/model/UpdateImageBaseModel.java
  73. 37 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/update/model/UpdateInputModel.java
  74. 26 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/update/model/UpdateLabelModel.java
  75. 33 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/update/model/UpdateMultInputModel.java
  76. 86 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/update/model/UpdatePhotoModel.java
  77. 75 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/update/model/UpdateSignatureModel.java
  78. 202 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/update/xuanimageview/RotationGestureDetector.java
  79. 1033 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/update/xuanimageview/XuanImageView.java
  80. 13 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/update/xuanimageview/XuanImageViewSettings.java
  81. 407 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/upload/TaskActivity.java
  82. 429 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/upload/UploadListActivity.java
  83. 79 0
      ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/utils/OperationQueue.java
  84. 1 1
      ApexDrivers/app/src/main/res/layout/activity_detail.xml
  85. 1 1
      ApexDrivers/app/src/main/res/layout/activity_home_more.xml
  86. 1 1
      ApexDrivers/app/src/main/res/layout/activity_message.xml
  87. 1 1
      ApexDrivers/app/src/main/res/layout/activity_order_filter.xml
  88. 1 1
      ApexDrivers/app/src/main/res/layout/activity_photo_preview.xml
  89. 1 1
      ApexDrivers/app/src/main/res/layout/activity_signature.xml
  90. 1 1
      ApexDrivers/app/src/main/res/layout/activity_upload_list.xml
  91. 1 1
      ApexDrivers/app/src/main/res/layout/capture.xml
  92. 2 2
      ApexDrivers/app/src/main/res/layout/home_header_view.xml
  93. 2 2
      ApexDrivers/app/src/main/res/layout/home_order_cell.xml
  94. 1 1
      ApexDrivers/app/src/main/res/layout/section_header.xml

+ 167 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contacts/Contact.java

@@ -0,0 +1,167 @@
+package com.usai.redant.rautils.contacts;
+
+import android.graphics.Bitmap;
+import android.provider.ContactsContract;
+
+import java.io.ByteArrayOutputStream;
+
+public class Contact {
+
+    public static final String NUMBER_TYPE_WORK = String.valueOf(ContactsContract.CommonDataKinds.Phone.TYPE_WORK);
+    public static final String NUMBER_TYPE_FAX_WORK = String.valueOf(ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK);
+    public static final String EMAIL_TYPE_WORK = String.valueOf(ContactsContract.CommonDataKinds.Email.TYPE_WORK);
+    public static final String WEB_TYPE_HOMEPAGE = String.valueOf(ContactsContract.CommonDataKinds.Website.TYPE_HOMEPAGE);
+    public static final String POSTAL_TYPE_WORK = String.valueOf(ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK);
+
+    private String id;
+    private String name;
+    private String number;
+    private String numberType;
+    private String fax;
+    private String faxType;
+    private String email;
+    private String emailType;
+    private String homePage;
+    private String homePageType;
+    private String address;
+    private String addressType;
+    private Bitmap photo;
+
+
+    public Contact(){
+    }
+
+    public Contact(Contact contact){
+        copyContact(contact);
+    }
+
+    public void copyContact(Contact contact) {
+        this.name = contact.getName();
+        this.number = contact.getNumber();
+        this.numberType = contact.getNumberType();
+        this.email = contact.getEmail();
+        this.emailType = contact.getEmailType();
+        this.fax = contact.getFax();
+        this.faxType = contact.getFaxType();
+        this.homePage = contact.getHomePage();
+        this.homePageType = contact.getHomePageType();
+        this.address = contact.getAddress();
+        this.addressType = contact.getAddressType();
+        this.photo = contact.getPhoto();
+    }
+
+    public String getEmail() {
+        return email;
+    }
+    public String getEmailType() {
+        if (emailType == null) {
+            return EMAIL_TYPE_WORK;
+        }
+        return emailType;
+    }
+    public String getId() {
+        return id;
+    }
+    public String getName() {
+        return name;
+    }
+    public String getNumber() {
+        return number;
+    }
+    public String getNumberType() {
+        if (numberType == null) {
+            return NUMBER_TYPE_WORK;
+        }
+        return numberType;
+    }
+    public String getFax() {
+        return fax;
+    }
+    public String getFaxType() {
+        if (faxType == null) {
+            return NUMBER_TYPE_FAX_WORK;
+        }
+        return faxType;
+    }
+    public String getAddress() {
+        return address;
+    }
+
+    public String getAddressType() {
+        if (addressType == null) {
+            return POSTAL_TYPE_WORK;
+        }
+        return addressType;
+    }
+
+    public String getHomePage() {
+        if (homePage == null) {
+            return WEB_TYPE_HOMEPAGE;
+        }
+        return homePage;
+    }
+
+    public String getHomePageType() {
+        return homePageType;
+    }
+
+    public Bitmap getPhoto() {
+        return photo;
+    }
+
+    public byte[] getPhotoData() {
+        if (photo == null) {
+            return null;
+        }
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        photo.compress(Bitmap.CompressFormat.PNG, 100, baos);
+        byte[] data = baos.toByteArray();
+        return data;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+    public void setEmailType(String emailType) {
+        this.emailType = emailType;
+    }
+    public void setId(String id) {
+        this.id = id;
+    }
+    public void setName(String name) {
+        this.name = name;
+    }
+    public void setNumber(String number) {
+        this.number = number;
+    }
+    public void setNumberType(String numberType) {
+        this.numberType = numberType;
+    }
+
+    public void setFax(String fax) {
+        this.fax = fax;
+    }
+    public void setFaxType(String faxType) {
+        this.faxType = faxType;
+    }
+
+    public void setHomePage(String homePage) {
+        this.homePage = homePage;
+    }
+
+    public void setHomePageType(String homePageType) {
+        this.homePageType = homePageType;
+    }
+
+    public void setAddress(String address) {
+        this.address = address;
+    }
+
+    public void setAddressType(String addressType) {
+        this.addressType = addressType;
+    }
+
+    public void setPhoto(Bitmap photo) {
+        this.photo = photo;
+    }
+}

+ 475 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contacts/ContactsManager.java

@@ -0,0 +1,475 @@
+package com.usai.redant.rautils.contacts;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+public class ContactsManager {
+
+    private ContentResolver contentResolver;
+    private static final String TAG = "ContactsManager";
+
+
+    private static final String COLUMN_CONTACT_ID =
+            ContactsContract.Data.CONTACT_ID;
+    private static final String COLUMN_RAW_CONTACT_ID =
+            ContactsContract.Data.RAW_CONTACT_ID;
+    private static final String COLUMN_MIMETYPE =
+            ContactsContract.Data.MIMETYPE;
+    private static final String COLUMN_NAME =
+            ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME;
+    private static final String COLUMN_NUMBER =
+            ContactsContract.CommonDataKinds.Phone.NUMBER;
+    private static final String COLUMN_NUMBER_TYPE =
+            ContactsContract.CommonDataKinds.Phone.TYPE;
+    private static final String COLUMN_EMAIL =
+            ContactsContract.CommonDataKinds.Email.DATA;
+    private static final String COLUMN_EMAIL_TYPE =
+            ContactsContract.CommonDataKinds.Email.TYPE;
+
+    private static final String COLUMN_WEB =
+            ContactsContract.CommonDataKinds.Website.DATA;
+    private static final String COLUMN_WEB_TYPE =
+            ContactsContract.CommonDataKinds.Website.TYPE;
+
+    private static final String COLUMN_POSTAL =
+            ContactsContract.CommonDataKinds.StructuredPostal.DATA;
+    private static final String COLUMN_POSTAL_TYPE =
+            ContactsContract.CommonDataKinds.StructuredPostal.TYPE;
+
+    private static final String COLUMN_PHOTO =
+            ContactsContract.CommonDataKinds.Photo.PHOTO;
+
+
+    private static final String MIMETYPE_STRING_NAME =
+            ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE;
+    private static final String MIMETYPE_STRING_PHONE =
+            ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE;
+    private static final String MIMETYPE_STRING_EMAIL =
+            ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE;
+    private static final String MIMETYPE_STRING_WEB =
+            ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE;
+    private static final String MIMETYPE_STRING_ADDRESS =
+            ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE;
+    private static final String MIMETYPE_STRING_PHOTO =
+            ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE;
+
+    public ContactsManager(ContentResolver contentResolver) {
+        this.contentResolver = contentResolver;
+    }
+
+
+    public Contact searchContact(String name) {
+        Log.w(TAG, "**search start**");
+        Contact contact = new Contact();
+        contact.setName(name);
+        Log.d(TAG, "search name: " + contact.getName());
+        String id = getContactID(contact.getName());
+        contact.setId(id);
+
+        if(id.equals("0")) {
+            Log.d(TAG, contact.getName() + " not exist. exit.");
+        } else {
+            Log.d(TAG, "find id: " + id);
+            //Fetch Phone Number
+            Cursor cursor = contentResolver.query(
+                    ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
+                    new String[]{COLUMN_NUMBER, COLUMN_NUMBER_TYPE},
+                    COLUMN_CONTACT_ID + "='" + id + "'", null, null);
+            while(cursor.moveToNext()) {
+                contact.setNumber(cursor.getString(cursor.getColumnIndex(COLUMN_NUMBER)));
+                contact.setNumberType(cursor.getString(cursor.getColumnIndex(COLUMN_NUMBER_TYPE)));
+                Log.d(TAG, "find number: " + contact.getNumber());
+                Log.d(TAG, "find numberType: " + contact.getNumberType());
+            }
+            //cursor.close();
+
+            //Fetch email
+            cursor = contentResolver.query(
+                    ContactsContract.CommonDataKinds.Email.CONTENT_URI,
+                    new String[]{COLUMN_EMAIL, COLUMN_EMAIL_TYPE},
+                    COLUMN_CONTACT_ID + "='" + id + "'", null, null);
+            while(cursor.moveToNext()) {
+                contact.setEmail(cursor.getString(cursor.getColumnIndex(COLUMN_EMAIL)));
+                contact.setEmailType(cursor.getString(cursor.getColumnIndex(COLUMN_EMAIL_TYPE)));
+                Log.d(TAG, "find email: " + contact.getEmail());
+                Log.d(TAG, "find emailType: " + contact.getEmailType());
+            }
+            cursor.close();
+        }
+        Log.w(TAG, "**search end**");
+        return contact;
+    }
+
+
+    public String getContactID(String name) {
+        String id = "0";
+
+        String searchName = name;
+        Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_FILTER_URI, searchName);
+
+        //  Uri uri2 = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, phoneNumber); 根据电话号码查找联系人
+
+        String[] projection = new String[]{ContactsContract.Contacts._ID};
+        Cursor cursor = contentResolver.query(uri, projection, null, null, null);
+
+        if (cursor.moveToFirst()) {
+            id = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts._ID));
+        }
+        return id;
+    }
+//    public String getContactID(String name) {
+//        String id = "0";
+//
+//
+////        ContactsContract.Contacts.DISPLAY_NAME_ALTERNATIVE
+//
+//        Cursor cursor = contentResolver.query(
+//                ContactsContract.Contacts.CONTENT_URI,
+//                new String[]{ContactsContract.Contacts._ID},
+//                ContactsContract.Contacts.DISPLAY_NAME_ALTERNATIVE +
+//                        "='" + name + "'", null, null);
+//        if(cursor.moveToNext()) {
+//            id = cursor.getString(cursor.getColumnIndex(
+//                    ContactsContract.Contacts._ID));
+//        }
+//        return id;
+//    }
+
+    public boolean contactExist(String name) {
+        if (name == null) {
+            return false;
+        }
+        String id = getContactID(name);
+        if(!id.equals("0")) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+
+    public void addContact(Contact contact,boolean checkExist) {
+        Log.w(TAG, "**add start**");
+
+        if (checkExist) {
+            String id = getContactID(contact.getName());
+            if(!id.equals("0")) {
+                Log.d(TAG, "contact already exist. exit.");
+            } else if(contact.getName().trim().equals("")){
+                Log.d(TAG, "contact name is empty. exit.");
+            } else {
+                addContact(contact);
+            }
+        } else {
+            addContact(contact);
+        }
+
+        Log.w(TAG, "**add end**");
+
+    }
+
+    private void addContact(Contact contact) {
+
+        ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
+
+        ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
+                .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null)
+                .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null)
+                .build());
+        ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+                .withValueBackReference(COLUMN_RAW_CONTACT_ID, 0)
+                .withValue(COLUMN_MIMETYPE, MIMETYPE_STRING_NAME)
+                .withValue(COLUMN_NAME, contact.getName())
+                .build());
+        Log.d(TAG, "add name: " + contact.getName());
+
+        // photo
+        ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+                .withValueBackReference(COLUMN_RAW_CONTACT_ID, 0)
+                .withValue(COLUMN_MIMETYPE, MIMETYPE_STRING_PHOTO)
+                .withValue(COLUMN_PHOTO, contact.getPhotoData())
+                .build());
+
+        // web
+        ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+                .withValueBackReference(COLUMN_RAW_CONTACT_ID, 0)
+                .withValue(COLUMN_MIMETYPE, MIMETYPE_STRING_WEB)
+                .withValue(COLUMN_WEB,contact.getHomePage())
+                .withValue(COLUMN_WEB_TYPE, contact.getHomePage())
+                .build());
+
+        // postal
+        ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+                .withValueBackReference(COLUMN_RAW_CONTACT_ID, 0)
+                .withValue(COLUMN_MIMETYPE, MIMETYPE_STRING_ADDRESS)
+                .withValue(COLUMN_POSTAL,contact.getAddress())
+                .withValue(COLUMN_POSTAL_TYPE, contact.getAddressType())
+                .build());
+
+        if(!contact.getNumber().trim().equals("")) {
+            ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+                    .withValueBackReference(COLUMN_RAW_CONTACT_ID, 0)
+                    .withValue(COLUMN_MIMETYPE, MIMETYPE_STRING_PHONE)
+                    .withValue(COLUMN_NUMBER, contact.getNumber())
+                    .withValue(COLUMN_NUMBER_TYPE, contact.getNumberType())
+                    .build());
+            Log.d(TAG, "add number: " + contact.getNumber());
+        }
+
+        if(!contact.getFax().trim().equals("")) {
+            ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+                    .withValueBackReference(COLUMN_RAW_CONTACT_ID, 0)
+                    .withValue(COLUMN_MIMETYPE, MIMETYPE_STRING_PHONE)
+                    .withValue(COLUMN_NUMBER, contact.getFax())
+                    .withValue(COLUMN_NUMBER_TYPE, contact.getFaxType())
+                    .build());
+            Log.d(TAG, "add fax: " + contact.getFax());
+        }
+
+        if(!contact.getEmail().trim().equals("")) {
+            ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+                    .withValueBackReference(COLUMN_RAW_CONTACT_ID, 0)
+                    .withValue(COLUMN_MIMETYPE, MIMETYPE_STRING_EMAIL)
+                    .withValue(COLUMN_EMAIL, contact.getEmail())
+                    .withValue(COLUMN_EMAIL_TYPE, contact.getEmailType())
+                    .build());
+            Log.d(TAG, "add email: " + contact.getEmail());
+        }
+
+        try {
+            contentResolver.applyBatch(ContactsContract.AUTHORITY, ops);
+            Log.d(TAG, "add contact success.");
+        } catch (Exception e) {
+            Log.d(TAG, "add contact failed.");
+            Log.e(TAG, e.getMessage());
+        }
+    }
+
+
+    public void deleteContact(Contact contact) {
+        Log.w(TAG, "**delete start**");
+        ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
+
+        String id = getContactID(contact.getName());
+        //delete contact
+        ops.add(ContentProviderOperation.newDelete(ContactsContract.RawContacts.CONTENT_URI)
+                .withSelection(ContactsContract.RawContacts.CONTACT_ID+"="+id, null)
+                .build());
+        //delete contact information such as phone number,email
+        ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
+                .withSelection(COLUMN_CONTACT_ID + "=" + id, null)
+                .build());
+        Log.d(TAG, "delete contact: " + contact.getName());
+
+        try {
+            contentResolver.applyBatch(ContactsContract.AUTHORITY, ops);
+            Log.d(TAG, "delete contact success");
+        } catch (Exception e) {
+            Log.d(TAG, "delete contact failed");
+            Log.e(TAG, e.getMessage());
+        }
+        Log.w(TAG, "**delete end**");
+    }
+
+
+    public void updateContact(Contact contactOld, Contact contactNew) {
+        Log.w(TAG, "**update start**");
+        String id = getContactID(contactOld.getName());
+        if(id.equals("0")) {
+            Log.d(TAG, contactOld.getName()+" not exist.");
+        }/* else if(contactNew.getName().trim().equals("")){
+            Log.d(TAG, "contact name is empty. exit.");
+        } else if(!getContactID(contactNew.getName()).equals("0")){
+            Log.d(TAG, "new contact name already exist. exit.");
+        }*/ else {
+
+            ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
+
+            //update name
+//            ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
+//                    .withSelection(COLUMN_CONTACT_ID + "=? AND " + COLUMN_MIMETYPE + "=?",
+//                            new String[]{id, MIMETYPE_STRING_NAME})
+//                    .withValue(COLUMN_NAME, contactNew.getName())
+//                    .build());
+            Log.d(TAG, "update name: " + contactNew.getName());
+
+            // photo
+            ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
+                    .withSelection(COLUMN_CONTACT_ID + "=? AND " + COLUMN_MIMETYPE + "=?",
+                            new String[]{id, MIMETYPE_STRING_PHOTO})
+                    .withValue(COLUMN_PHOTO, contactNew.getPhotoData())
+                    .build());
+
+            // web
+            ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
+                    .withSelection(COLUMN_CONTACT_ID + "=? AND " + COLUMN_MIMETYPE + "=?",
+                            new String[]{id, MIMETYPE_STRING_WEB})
+                    .withValue(COLUMN_WEB,contactNew.getHomePage())
+                    .withValue(COLUMN_WEB_TYPE, contactNew.getHomePage())
+                    .build());
+
+            // postal
+            ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
+                    .withSelection(COLUMN_CONTACT_ID + "=? AND " + COLUMN_MIMETYPE + "=?",
+                            new String[]{id, MIMETYPE_STRING_ADDRESS})
+                    .withValue(COLUMN_POSTAL,contactNew.getAddress())
+                    .withValue(COLUMN_POSTAL_TYPE, contactNew.getAddressType())
+                    .build());
+
+            //update number
+            if(!contactNew.getNumber().trim().equals("")) {
+                ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
+                        .withSelection(COLUMN_CONTACT_ID + "=? AND " + COLUMN_MIMETYPE + "=? AND " + COLUMN_NUMBER_TYPE + "=?",
+                                new String[]{id, MIMETYPE_STRING_PHONE,contactNew.getNumberType()})
+                        .withValue(COLUMN_NUMBER, contactNew.getNumber())
+                        .withValue(COLUMN_NUMBER_TYPE, contactNew.getNumberType())
+                        .build());
+                Log.d(TAG, "update number: " + contactNew.getNumber());
+            }
+
+            // fax
+            if(!contactNew.getNumber().trim().equals("")) {
+                ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
+                        .withSelection(COLUMN_CONTACT_ID + "=? AND " + COLUMN_MIMETYPE + "=? AND " + COLUMN_NUMBER_TYPE + "=?",
+                                new String[]{id, MIMETYPE_STRING_PHONE,contactNew.getFaxType()})
+                        .withValue(COLUMN_NUMBER, contactNew.getFax())
+                        .withValue(COLUMN_NUMBER_TYPE, contactNew.getFaxType())
+                        .build());
+                Log.d(TAG, "update fax: " + contactNew.getNumber());
+            }
+
+
+            //update email if mail
+            if(!contactNew.getEmail().trim().equals("")) {
+                ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
+                        .withSelection(COLUMN_CONTACT_ID + "=? AND " + COLUMN_MIMETYPE + "=?",
+                                new String[]{id, MIMETYPE_STRING_EMAIL})
+                        .withValue(COLUMN_EMAIL, contactNew.getEmail())
+                        .withValue(COLUMN_EMAIL_TYPE, contactNew.getEmailType())
+                        .build());
+                Log.d(TAG, "update email: " + contactNew.getEmail());
+            }
+
+            try {
+                contentResolver.applyBatch(ContactsContract.AUTHORITY, ops);
+                Log.d(TAG, "update success");
+            } catch (Exception e) {
+                Log.d(TAG, "update failed");
+                Log.e(TAG, e.getMessage());
+            }
+        }
+        Log.w(TAG, "**update end**");
+    }
+
+
+    public Uri getContactUri(String contactId, Context context)  {
+
+        if (context == null || contactId == null) {
+            return null;
+        }
+
+        //获取联系人信息的Uri
+        Uri uri = ContactsContract.Contacts.CONTENT_URI;
+        //获取ContentResolver
+        ContentResolver contentResolver = context.getContentResolver();
+        //查询数据,返回Cursor
+        Cursor cursor = contentResolver.query(uri, null, null, null, null);
+
+
+        while (cursor.moveToNext()) {
+
+            //获取联系人的ID
+            String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
+            //获取联系人的姓名
+            String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
+
+            String lookupKey = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
+
+            if (id != null && id.equals(contactId)) {
+
+                return ContactsContract.Contacts.getLookupUri(Integer.valueOf(contactId), lookupKey);
+
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @brief 显示联系人编辑界面
+     * */
+    public void editContact(String contactId,Context context) {
+
+        if (context == null || contactId == null) {
+            return ;
+        }
+
+        Uri uri = getContactUri(contactId,context);
+        if (uri != null) {
+
+            Intent editIntent = new Intent(Intent.ACTION_EDIT);
+            editIntent.setDataAndType(uri,ContactsContract.Contacts.CONTENT_ITEM_TYPE);
+//            editIntent.putExtra("finishActivityOnSaveCompleted", true);
+            context.startActivity(editIntent);
+
+        } else {
+
+        }
+    }
+
+    /**
+     * @brief 新建联系人显示编辑界面
+     * */
+    public void insertContact(Contact contact,Context context) {
+
+        if (context == null) {
+            return;
+        }
+
+        Intent intent = new Intent(ContactsContract.Intents.Insert.ACTION);
+        intent.setType(ContactsContract.RawContacts.CONTENT_TYPE);
+
+        intent.putExtra(ContactsContract.Intents.Insert.NAME, contact.getName());
+
+        intent.putExtra(ContactsContract.Intents.Insert.PHONE, contact.getNumber());
+        intent.putExtra(ContactsContract.Intents.Insert.PHONE_TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_WORK);
+
+        intent.putExtra(ContactsContract.Intents.Insert.SECONDARY_PHONE, contact.getFax());
+        intent.putExtra(ContactsContract.Intents.Insert.SECONDARY_PHONE_TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK);
+
+        intent.putExtra(ContactsContract.Intents.Insert.EMAIL, contact.getEmail());
+        intent.putExtra(ContactsContract.Intents.Insert.EMAIL_TYPE, ContactsContract.CommonDataKinds.Email.TYPE_WORK);
+
+        intent.putExtra(ContactsContract.Intents.Insert.POSTAL, contact.getAddress());
+        intent.putExtra(ContactsContract.Intents.Insert.POSTAL_TYPE, ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK);
+
+        // Insert中没有的类型,通过Data写入
+        ArrayList<ContentValues> data = new ArrayList<ContentValues>();
+
+        ContentValues row1 = new ContentValues();
+        row1.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE);
+        row1.put(ContactsContract.CommonDataKinds.Website.DATA, contact.getHomePage());
+        data.add(row1);
+
+        ContentValues row2 = new ContentValues();
+        row2.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE);
+        row2.put(ContactsContract.CommonDataKinds.Photo.PHOTO, contact.getPhotoData());
+        data.add(row2);
+
+//		Intent intent = new Intent(Intent.ACTION_INSERT, ContactsContract.Contacts.CONTENT_URI);
+
+        intent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, data);
+
+        context.startActivity(intent);
+
+    }
+}
+

+ 281 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/receiver/AlarmReceiver.java

@@ -0,0 +1,281 @@
+package com.usai.redant.rautils.receiver;
+
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public abstract class AlarmReceiver extends BootCompleteBroadcastReceiver {
+    protected  int alarm_timeInterval = 30 * 1000;
+
+//    protected static final int	SO_TIMEOUT						= 15 * 1000;
+
+    protected abstract void InitAlarm(Context context, Intent intent);
+    protected abstract void AlarmProc(Context context, Intent intent);
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+
+        super.onReceive(context,intent);
+
+        String action = intent
+                .getAction();
+
+        Log.d("AlarmReceiver", "onReceive: " + action);
+
+        if (RABroadcast.ACTION_REDANT_INIT_ALARM.equals(intent.getAction())) {
+            InitAlarm(context,intent);
+        }
+        else
+        if (RABroadcast.ACTION_REDANT_ALARM.equals(intent.getAction())) {
+
+
+            AlarmProc(context,intent);
+
+        }
+//        else
+//        if (Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(intent.getAction())) {
+//
+//            InitAlarm(context,intent);
+//        }
+
+
+/*
+
+?????
+
+        Intent iAlarm = new Intent(context, AlarmReceiver.class);
+//        iAlarm.putExtra("caller", caller);
+        iAlarm.setAction("ACTION_PUSHNOTIFICATION_CHECK");
+        PendingIntent sender = PendingIntent.getBroadcast(context, 0,
+                iAlarm, PendingIntent.FLAG_UPDATE_CURRENT);
+
+
+        AlarmManager am = (AlarmManager) context.getSystemService(
+                Context.ALARM_SERVICE);
+//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+//            am.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + pushcheck_timeInterval, sender);
+//        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+//            am.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + pushcheck_timeInterval, sender);
+//        }
+
+        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
+    protected void OnBootComplete(Context context, Intent intent) {
+
+        Log.d("AlarmReceiver", "OnBootComplete: SEND" + RABroadcast.ACTION_REDANT_INIT_ALARM);
+        Intent bintent = new Intent(RABroadcast.ACTION_REDANT_INIT_ALARM);
+//        bintent.putExtra("msg", msg.toString());
+
+        bintent.setClass(context, this.getClass());
+        context.sendBroadcast(bintent);
+
+    }
+
+    @Override
+    protected void OnLockedBootComplete(Context context, Intent intent) {
+
+        Log.d("AlarmReceiver", "OnLockedBootComplete: SEND" + RABroadcast.ACTION_REDANT_INIT_ALARM);
+
+        Log.d("AlarmReceiver", "OnLockedBootComplete: class" + this.getClass());
+        Intent bintent = new Intent(RABroadcast.ACTION_REDANT_INIT_ALARM);
+//        bintent.putExtra("msg", msg.toString());
+
+
+        bintent.setClass(context, this.getClass());
+        context.sendBroadcast(bintent);
+    }
+
+/*
+
+    private String getDeviceId(Context context) {
+        TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
+            // TODO: Consider calling
+            //    ActivityCompat#requestPermissions
+            // here to request the missing permissions, and then overriding
+            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
+            //                                          int[] grantResults)
+            // to handle the case where the user grants the permission. See the documentation
+            // for ActivityCompat#requestPermissions for more details.
+            return "";
+        }
+        String deviceId = telephonyManager.getDeviceId().toString();
+        return deviceId;
+    }
+
+    private void check_push(Context context)
+    {
+
+
+        String dev_id = getDeviceId(context);
+        Log.d("AlarmReceiver", "check_push: "+dev_id);
+
+
+
+        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;
+    }
+
+
+
+
+    private  String getJson(String url, Bundle parms)
+    {
+        String TAG = "net_dbg@GetJson";
+        Log.d(TAG, "entry");
+        String ret=null;
+
+        HttpURLConnection connection = null;
+
+        try {
+
+
+            URL _url;
+            _url = new URL(url);
+            connection = (HttpURLConnection) _url.openConnection();
+            connection.setReadTimeout(SO_TIMEOUT);
+            connection.setConnectTimeout(SO_TIMEOUT);
+            // 设置请求方式
+            connection.setRequestMethod("POST");
+            // 设置编码格式
+            connection.setRequestProperty("Charset", "UTF-8");
+            // 传递自定义参数
+//			connection.setRequestProperty("MyProperty", "this is me!");
+
+            Set<String> keys = parms.keySet();
+            Log.d(TAG, "================parms============");
+            for (String key : keys)
+            {
+                if (key.contains("_file"))
+                {
+
+                }
+                else
+                {
+                    Log.d(TAG, "key=" + key + "    val=" + parms.get(key).toString());
+                }
+
+            }
+            Log.d(TAG, "================parms============");
+
+            Log.d(TAG,"URL: "+url);
+            // 设置容许输出
+            connection.setDoInput(true);
+            connection.setDoOutput(true);
+
+
+            OutputStream os = connection.getOutputStream();
+            BufferedWriter writer = new BufferedWriter(
+                    new OutputStreamWriter(os, "UTF-8"));
+            writer.write(createPostParameters(parms));
+
+            writer.flush();
+            writer.close();
+            os.close();
+
+            // 获取返回数据
+            if(connection.getResponseCode() == 200){
+                InputStream is = connection.getInputStream();
+
+                BufferedReader br = new BufferedReader(
+                        new InputStreamReader(is, "utf-8"), 8);
+                StringBuilder sb = new StringBuilder();
+                String line = null;
+                while ((line = br.readLine()) != null)
+                {
+                    sb.append(line + "\n");
+                }
+
+                Log.d(TAG, "Response: content begin");
+                Log.d(TAG, sb.toString());
+                Log.d(TAG, "Response: content end");
+
+                if (sb.length() <= 0)
+                {
+
+                    ret= null;
+                }
+                ret= sb.toString();
+
+            }
+
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+
+            Log.e(TAG, e.toString());
+
+        } catch (ProtocolException e) {
+            e.printStackTrace();
+
+            Log.e(TAG, e.toString());
+
+        } catch (MalformedURLException e) {
+            e.printStackTrace();
+
+            Log.e(TAG, e.toString());
+
+        } catch (IOException e) {
+            e.printStackTrace();
+
+            Log.e(TAG, e.toString());
+
+        } finally {
+
+            if(connection!=null){
+                connection.disconnect();
+            }
+        }
+        return ret;
+    }
+    private  String createPostParameters(Bundle parms) throws UnsupportedEncodingException {
+        StringBuilder result = new StringBuilder();
+        boolean first = true;
+        for(String key : parms.keySet()){
+            if (first)
+                first = false;
+            else
+                result.append("&");
+
+            result.append(URLEncoder.encode(key, "UTF-8"));
+            result.append("=");
+            result.append(URLEncoder.encode(parms.get(key).toString(), "UTF-8"));
+        }
+
+        return result.toString();
+    }
+    private static final String URL_HOST = "http://192.168.0.124:8080";
+    public static final String URL_LOGOUT = URL_HOST + "/j/mobile/loginOut.mo/";
+    public  void logout() {
+
+
+        Bundle params = new Bundle();
+        params.putString("platform","android");
+
+        getJson(URL_LOGOUT,params);
+    }
+
+    */
+}

+ 53 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/receiver/BootCompleteBroadcastReceiver.java

@@ -0,0 +1,53 @@
+package com.usai.redant.rautils.receiver;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import com.usai.redant.rautils.utils.dbgUtil;
+
+public abstract class BootCompleteBroadcastReceiver extends BroadcastReceiver {
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+
+        dbgUtil.fileLog(context,"RAUtilsLibrary: onReceive" + intent.getAction());
+
+//        dbgUtil.fileLog(context,"RAUtilsLibrary: BootCompleteBroadcastReceiver onReceive");
+
+        if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
+
+            Log.d("BootComplete", "onReceive: ACTION_BOOT_COMPLETED");
+            OnBootComplete(context,intent);
+
+//            dbgUtil.fileLog(context,"RAUtilsLibrary: BootCompleteBroadcastReceiver ACTION_BOOT_COMPLETED redeived");
+//            // 启动完成
+//            Intent iAlarm = new Intent(context, Alarmreceiver.class);
+//            iAlarm.setAction("com.usai.apex.push");
+//            PendingIntent sender = PendingIntent.getBroadcast(context, 0,
+//            		iAlarm, 0);
+//            long firstime = SystemClock.elapsedRealtime();
+//            AlarmManager am = (AlarmManager) context
+//                    .getSystemService(Context.ALARM_SERVICE);
+//
+//            // 10秒一个周期,不停的发送广播
+//            am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, firstime,
+//                    30 * 1000, sender);
+//			Intent intentservice = new Intent();
+//			intentservice.setClass(context, ApexPushService.class);
+//			if (intent.getExtras() != null)
+//				intentservice.putExtras(intent.getExtras());
+//			intentservice.setAction(intent.getAction());
+//			context.startService(intentservice);
+//            ApexTrackingApplication.startalarm("boot completed");
+        }
+        else if (Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(intent.getAction())) {
+            Log.d("BootComplete", "onReceive: ACTION_LOCKED_BOOT_COMPLETED");
+            OnLockedBootComplete(context,intent);
+        }
+    }
+
+    protected abstract void OnBootComplete(Context context, Intent intent);
+    protected abstract void OnLockedBootComplete(Context context, Intent intent);
+}

+ 21 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/receiver/RABroadcast.java

@@ -0,0 +1,21 @@
+package com.usai.redant.rautils.receiver;
+
+public class RABroadcast {
+    public static String EVENT_SILENCECOMMAND="REDANT.BROADCAST.EVENT_SILENCECOMMAND";
+    public static String EVENT_PUSHNOTIFICATION="REDANT.BROADCAST.EVENT_PUSHNOTIFICATION";
+    public static String EVENT_RETURN_LOCATION="REDANT.BROADCAST.EVENT_RETURN_LOCATION";
+
+    public static String ACTION_UPLOAD_ADD_TASK="REDANT.BROADCAST.ACTION_UPLOAD_ADD_TASK";
+    public static String ACTION_LOCATION_ENABLE_TRACING="REDANT.BROADCAST.ACTION_LOCATION_ENABLE_TRACING";
+    public static String ACTION_LOCATION_DISABLE_TRACING="REDANT.BROADCAST.ACTION_LOCATION_DISABLE_TRACING";
+    public static String ACTION_LOCATION_REQUEST_LOCATION="REDANT.BROADCAST.ACTION_LOCATION_REQUEST_LOCATION";
+    public static String ACTION_PUSHNOTIFICATION_CHECK="REDANT.BROADCAST.ACTION_PUSHNOTIFICATION_CHECK";
+
+
+    public static String ACTION_REDANT_ALARM="REDANT.BROADCAST.ACTION_REDANT_ALARM";
+    public static String ACTION_REDANT_INIT_ALARM="REDANT.BROADCAST.ACTION_REDANT_INIT_ALARM";
+
+    public static String ACTION_PUSHNOTIFICATION_ENABLE_CHECK="REDANT.BROADCAST.ACTION_PUSHNOTIFICATION_ENABLE_CHECK";
+    public static String ACTION_PUSHNOTIFICATION_DISABLE_CHECK="REDANT.BROADCAST.ACTION_PUSHNOTIFICATION_DISABLE_CHECK";
+
+}

+ 50 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/receiver/RABroadcastReceiver.java

@@ -0,0 +1,50 @@
+package com.usai.redant.rautils.receiver;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import com.usai.redant.rautils.utils.dbgUtil;
+
+public class RABroadcastReceiver extends BroadcastReceiver {
+
+//    PushNotificationCallback notificationCallback=null;
+//    private String receiverID = null;
+
+
+
+//    public RABroadcastReceiver(String receiverID)
+//    {
+//        if(receiverID==null)
+//            throw new IllegalArgumentException("receiverID can't be null");
+//        this.receiverID = receiverID;
+//    }
+//    public void initPushNotificationCallback(PushNotificationCallback notificationCallback)
+//    {
+//        this.notificationCallback = notificationCallback;
+//    }
+
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        dbgUtil.fileLog(context,"RABroadcastReceiver: onReceive" + intent.getAction());
+
+//        if(!(intent.getStringExtra("receiverID").equals(receiverID)|| TextUtils.isEmpty(intent.getStringExtra("receiverID"))))
+//            return;
+//        if (RABroadcast.EVENT_PUSHNOTIFICATION.equals(intent.getAction())) {
+//            if(notificationCallback!=null)
+//                notificationCallback.onPushNotification(intent);
+//        }
+//        else if (RABroadcast.EVENT_SILENCECOMMAND.equals(intent.getAction())) {
+//            if(notificationCallback!=null)
+//                notificationCallback.onSilentCommand(intent);
+//        }
+    }
+//
+//    public interface PushNotificationCallback {
+//        public abstract void onPushNotification(Intent intent);
+//        public abstract void onSilentCommand(Intent intent);
+//    }
+
+
+}

+ 793 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/service/RAService.java

@@ -0,0 +1,793 @@
+package com.usai.redant.rautils.service;
+
+import android.Manifest;
+import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.AsyncTask;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.app.NotificationCompat;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.usai.redant.rautils.R;
+import com.usai.redant.rautils.receiver.RABroadcast;
+import com.usai.redant.rautils.receiver.RABroadcastReceiver;
+import com.usai.redant.rautils.upload.RAUploadManager;
+import com.usai.redant.rautils.utils.Network;
+import com.usai.redant.rautils.utils.dbgUtil;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+
+//@Deprecated
+public abstract class RAService extends Service {
+
+    private static final String TAG = "RAService";
+
+    public static final int DEFAULT_LOCATION_TIMEINTERVAL = 60 * 1000;
+    public static final int DEFAULT_DISTANCE = 60 * 50;
+    public static final int DEFAULT_PUSHNOTIFICATION_TIMEINTERVAL = 5 * 1000;
+
+    public static final int FLAG_SERVICE_NONE = 0;
+    public static final int FLAG_SERVICE_LOCATION = 1 << 1;
+    public static final int FLAG_SERVICE_NOTIFICATION = 1 << 2;
+    public static final int FLAG_SERVICE_UPLOAD = 1 << 3;
+//    public static final int FLAG_LOCATION_SERVICE = 1<<1;
+
+    // service setup
+    protected int service_flag = FLAG_SERVICE_NONE;
+    private IntentFilter msgFilter = new IntentFilter();
+
+    protected abstract void Setup();
+
+    // sub function upload
+    public interface ServiceUpload {
+
+    }
+
+    public void initServiceUpload(ServiceUpload uploadCallback) {
+        service_flag = service_flag | FLAG_SERVICE_UPLOAD;
+//        this.upload = locationCallback;
+    }
+
+    private RAUploadManager uploadManager = null;
+
+    public RAUploadManager getUploadManager() {
+        return uploadManager;
+    }
+
+    // sub function location
+    LocationListener locationListener = null;
+    LocationManager locationManager = null;
+    int locationTracing_timeInterval = DEFAULT_LOCATION_TIMEINTERVAL;
+    int locationTracing_distance = DEFAULT_DISTANCE;
+    //    protected abstract void onLocationChanged(Location location);
+    private ServiceLocation locationCallback = null;
+
+    public interface ServiceLocation {
+
+
+        public abstract Location RequestCachedLocation();
+//        public abstract void RequestLocation_ByBroadcast(String receiverID);
+
+        public abstract void onLocationChanged(Location location);
+//        public abstract void onLogin();
+//		public abstract void onLogout();
+    }
+
+    public void initServiceLocation(ServiceLocation locationCallback) {
+        service_flag = service_flag | FLAG_SERVICE_LOCATION;
+        this.locationCallback = locationCallback;
+    }
+
+
+    // sub function Notification
+    protected String url_checknotification = null;
+    int pushcheck_timeInterval = DEFAULT_PUSHNOTIFICATION_TIMEINTERVAL;
+    protected static final int NOTIFICATION_TYPE_PUSHNOTIFICATION = 0;
+    protected static final int NOTIFICATION_TYPE_SILENCECOMMAND = 1;
+    protected boolean enable_pushNotification = false;
+
+    //    protected abstract Notification prepareNotification(JSONObject msg);
+//    protected abstract void handleSilenceMessage(JSONObject msg);
+    public interface ServicePushNotification {
+        public abstract Notification prepareNotification(JSONObject msg);
+
+        public abstract void handleSilenceMessage(JSONObject msg);
+    }
+
+    public void initServiceNotification(ServicePushNotification pushNotificationCallback, String url_checknotification) {
+        service_flag = service_flag | FLAG_SERVICE_NOTIFICATION;
+        this.url_checknotification = url_checknotification;
+        this.pushNotificationCallback = pushNotificationCallback;
+    }
+
+    private ServicePushNotification pushNotificationCallback = null;
+    private checkPushTask m_task = null;
+
+    class checkPushTask extends AsyncTask<Void, Void, Boolean> {
+        int errorcode;
+        String content = null;
+        Context mcontext;
+
+        public checkPushTask(Context context) {
+            mcontext = context;
+        }
+
+        @Override
+        protected Boolean doInBackground(Void... params) {
+            Log.d("SearchTask", "doInBackground");
+
+            if (!Network.isNetworkAvailable(getApplication()))
+
+            {
+                errorcode = Network.RESULT_NET_NOTAVAILABLE;
+                return false;
+            }
+            String jstr = "";
+            jstr = Network.check_push(mcontext, new Bundle(), url_checknotification);
+            // if (module_name.equals("Announcements"))
+            // jstr = Network.get_announcements(lastid, limit);
+            // else
+            // jstr = Network.get_marketnews(lastid, limit);
+            if (jstr == null || jstr.length() <= 0) {
+                // Log.d(TAG, "json is wrong");
+
+                errorcode = Network.RESULT_NET_ERROR;
+                return false;
+            }
+
+            content = jstr;
+            return true;
+        }
+
+        @Override
+        protected void onPostExecute(Boolean success) {
+            String tag = "onPostExec";
+            Log.i(tag, "entry");
+            m_task = null;
+
+
+            if (success) {
+                JSONObject jsobj;
+                //
+                // array = new JSONArray(json);
+                try {
+                    jsobj = new JSONObject(content);
+
+                    int count = jsobj.getInt("count");
+
+                    for (int i = 0; i < count; i++) {
+                        JSONObject msg = jsobj.getJSONObject("msg_" + i);
+                        int type = msg.getInt("type");
+                        if (type == NOTIFICATION_TYPE_SILENCECOMMAND) {
+
+                            pushNotificationCallback.handleSilenceMessage(msg);
+                            Intent intent = new Intent(RABroadcast.EVENT_SILENCECOMMAND);
+                            intent.putExtra("msg", msg.toString());
+                            sendBroadcast(intent);
+
+                        } else {
+                            pushNotification(msg);
+
+                            Intent intent = new Intent(RABroadcast.EVENT_PUSHNOTIFICATION);
+                            intent.putExtra("msg", msg.toString());
+                            sendBroadcast(intent);
+
+
+                        }
+                    }
+
+
+                } catch (JSONException e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                }
+
+            }
+
+            super.onPostExecute(success);
+        }
+
+        @Override
+        protected void onCancelled() {
+            m_task = null;
+            // showProgress(false);
+        }
+    }
+
+
+    public void pushNotification(JSONObject msg) {
+
+        if (enable_pushNotification) {
+
+            boolean bsound = true;
+//                    mcontext.getSharedPreferences(
+//                    "setting", 0).getBoolean(
+//                    "notifications_new_message_sound", true);
+            boolean bvibrate = true;
+//            mcontext.getSharedPreferences(
+//                    "setting", 0).getBoolean(
+//                    "notifications_new_message_vibrate", true);
+            NotificationManager nManager = (NotificationManager)
+                    getSystemService(Context.NOTIFICATION_SERVICE);
+
+/*
+       此部分在虚函数 prepareNotificaiton 中实现
+            Intent intent = new Intent(mcontext,
+                    FunctionSelectActivity.class);
+            intent.putExtra("launcher", "notification");
+
+            PendingIntent pintent = PendingIntent.getActivity(
+                    this, 0, intent,
+                    PendingIntent.FLAG_UPDATE_CURRENT);
+
+
+            Notification.Builder builder = new Notification.Builder(mcontext);//新建Notification.Builder对象
+            String ntitle = "";
+            int number = ApexTrackingApplication.ncount++;
+            if (number > 1)
+                ntitle=number
+                        + " "
+                        + ApexTrackingApplication
+                        .get_instance()
+                        .getString(
+                                R.string.str_mnotification_title);
+            else
+                ntitle = ApexTrackingApplication
+                        .get_instance()
+                        .getString(
+                                R.string.str_notification_title);
+
+            builder.setContentTitle(ntitle);//设置标题
+            builder.setContentText(ApexTrackingApplication
+                    .get_instance()
+                    .getString(
+                            R.string.str_notification_text)
+                    + date);//设置内容
+            builder.setSmallIcon(R.drawable.ic_launcher);//设置图片
+            builder.setContentIntent(pintent);//执行intent
+
+
+            */
+            Notification notification = pushNotificationCallback.prepareNotification(msg);//builder.getNotification();//将builder对象转换为普通的notifications
+            notification.defaults = Notification.DEFAULT_ALL;
+
+//            if (bsound && bvibrate)
+//                notification.defaults = Notification.DEFAULT_ALL;
+//            else if (bsound)
+//                notification.defaults = Notification.DEFAULT_SOUND
+//                        | Notification.DEFAULT_LIGHTS;
+//            else if (bvibrate)
+//                notification.defaults = Notification.DEFAULT_VIBRATE
+//                        | Notification.DEFAULT_LIGHTS;
+            notification.flags = Notification.FLAG_AUTO_CANCEL;
+//            notification.number = number; //number 是不是不需要了?
+
+            nManager.notify(R.layout.support_simple_spinner_dropdown_item, notification);
+        }
+    }
+
+    public void checkpush(Context context) {
+
+        if(true)
+        return;
+        if (m_task != null) {
+            return;
+        }
+        // mStatusMessageView.setText(R.string.str_Loading);
+        // showProgress(true);
+        m_task = new checkPushTask(context);
+
+        // TextView text_page = (TextView) view_page_footer
+        // .findViewById(R.id.text_page);
+        // text_page.setText("Loading...");
+        // text_page.setEnabled(false);
+        m_task.execute();
+
+    }
+
+    public RAService() {
+    }
+
+
+    @Override
+    public IBinder onBind(Intent intent) {
+//         TODO: Return the communication channel to the service.
+//        throw new UnsupportedOperationException("Not yet implemented");
+        Log.d("", "onBind: bind service");
+        return new MyBinder();
+    }
+
+
+    public class MyBinder extends Binder {
+        /**
+         * 获取当前Service的实例
+         *
+         * @return
+         */
+        public RAService getService() {
+            return RAService.this;
+        }
+    }
+
+
+    @Override
+    public void onCreate() {
+
+        Log.d("_SERVICE", "create upload Service!!!");
+
+
+//        // 初始化异常捕获器
+//        CrashHandler.getSharedInstance().init(getApplicationContext());
+//        CrashHandler.getSharedInstance().preserver = new CrashHandler.InfoPreserver() {
+//            @Override
+//            public void saveUserInformation() {
+//                if (uploadManager != null) {
+//                    uploadManager.saveTasks();
+//                }
+//            }
+//
+//            @Override
+//            public void handleCrashInfo(String deviceInfo, String exception) {
+//                dbgUtil.fileLog(getApplicationContext(),"============================ app crash ============================");
+//                dbgUtil.fileLog(getApplicationContext(),"DEVICE");
+//                dbgUtil.fileLog(getApplicationContext(),deviceInfo);
+//                dbgUtil.fileLog(getApplicationContext(),"EXCEPTION");
+//                dbgUtil.fileLog(getApplicationContext(),exception);
+//                dbgUtil.fileLog(getApplicationContext(),"=============================== end ===============================");
+//                Log.d("_SERVICE", "Device Info: " + deviceInfo);
+//                Log.d("_SERVICE", "Crash Info: " + exception);
+//            }
+//        };
+
+
+        Setup();
+
+        msgFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+
+        if ((service_flag & FLAG_SERVICE_UPLOAD) == FLAG_SERVICE_UPLOAD) {
+            uploadManager = new RAUploadManager(getApplicationContext());
+//            msgFilter.addAction("REDANT.POP.RESET_LOCATION");
+//            msgFilter.addAction("REDANT.POP.QUERY_UPLOAD_STATE");
+            msgFilter.addAction("REDANT.BROADCAST.ACTION_UPLOAD_ADD_TASK");
+//            msgFilter.addAction("REDANT.BROADCAST.UPLOAD_MODIFY_QUEUE");
+//            msgFilter.addAction("REDANT.BROADCAST.UPLOAD_RETRY_UPLOAD");
+        }
+        if ((service_flag & FLAG_SERVICE_LOCATION) == FLAG_SERVICE_LOCATION) {
+
+
+            msgFilter.addAction(RABroadcast.ACTION_LOCATION_ENABLE_TRACING);
+            msgFilter.addAction(RABroadcast.ACTION_LOCATION_DISABLE_TRACING);
+            msgFilter.addAction(RABroadcast.ACTION_LOCATION_REQUEST_LOCATION);
+//            msgFilter.addAction("REDANT.BROADCAST.RESET_LOCATION");
+            locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+
+            locationListener = new LocationListener() {
+
+                // Provider的状态在可用、暂时不可用和无服务三个状态直接切换时触发此函数
+                @Override
+                public void onStatusChanged(String provider, int status,
+                                            Bundle extras) {
+
+                }
+
+                // Provider被enable时触发此函数,比如GPS被打开
+                @Override
+                public void onProviderEnabled(String provider) {
+
+                }
+
+                // Provider被disable时触发此函数,比如GPS被关闭
+                @Override
+                public void onProviderDisabled(String provider) {
+
+                }
+
+                // 当坐标改变时触发此函数,如果Provider传进相同的坐标,它就不会被触发
+                @Override
+                public void onLocationChanged(Location location) {
+                    locationCallback.onLocationChanged(location);
+
+                }
+            };
+
+
+            enable_locationTracing(locationTracing_timeInterval, locationTracing_distance);
+
+
+//          msgFilter.addAction("REDANT.POP.GPS_ON");
+//		    msgFilter.addAction("REDANT.POP.GPS_OFF");
+//		    msgFilter.addAction("REDANT.POP.REQUEST_LOCATION");
+        }
+        if ((service_flag & FLAG_SERVICE_NOTIFICATION) == FLAG_SERVICE_NOTIFICATION) {
+
+            if (TextUtils.isEmpty(url_checknotification)) {
+                Log.e(TAG, "onCreate: setup FLAG_SERVICE_NOTIFICATION without url_checknotification");
+            }
+
+            msgFilter.addAction(RABroadcast.ACTION_PUSHNOTIFICATION_CHECK);
+
+        }
+
+        registerReceiver(uploadReceiver, msgFilter);
+
+        super.onCreate();
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            String CHANNEL_ID = "apex & driver";
+            String CHANNEL_NAME = "Background Service";
+
+            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
+                    CHANNEL_NAME, NotificationManager.IMPORTANCE_NONE);
+            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
+
+            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
+                    .setCategory(Notification.CATEGORY_SERVICE).setSmallIcon(R.drawable.ic_launcher_foreground).setPriority(1000).build();
+
+            startForeground(101, notification);
+        }
+
+//        SharedPreferences pref = getSharedPreferences("Apex & Drivers", 0);
+//        boolean notification = pref.getBoolean("enable_notification", false);
+//        notification = true;
+//        if (notification == true)
+            startalarm();
+
+
+    }
+
+
+    @Override
+    public void onDestroy() {
+        Log.e("_SERVICE", "onDestroy: ");
+        if (uploadManager != null)
+            uploadManager.saveTasks();
+
+        unregisterReceiver(uploadReceiver);
+        super.onDestroy();
+    }
+
+    private BroadcastReceiver uploadReceiver = new RABroadcastReceiver() {
+
+        public void onReceive(Context context, Intent intent) {
+            String action = intent
+                    .getAction();
+
+            Log.d(TAG, "onReceive: " + action);
+            dbgUtil.fileLog(context,TAG+"   RABroadcastReceiver: onReceive  " + action);
+            // 如果捕捉到的action是ACTION_BATTERY_CHANGED
+
+            if (RABroadcast.ACTION_UPLOAD_ADD_TASK.equals(action)) {
+
+                ArrayList<Bundle> tasks = intent.getParcelableArrayListExtra("tasks");
+
+
+                uploadManager.addTasks(tasks);
+            }
+//            else if ("REDANT.RAImage.SAVE_TASK".equals(action)) {
+//                uploadManager.stopAllTasks();
+//            }
+            else if (intent
+                    .getAction()
+                    .equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+
+                ConnectivityManager connManager = (ConnectivityManager) context
+                        .getSystemService(Context.CONNECTIVITY_SERVICE);
+
+                NetworkInfo networkInfo = connManager.getActiveNetworkInfo();
+                if (networkInfo == null) {
+                    dbgUtil.Logd(
+                            "Current Network info",
+                            "can not get Active NetworkInfo!");
+                    return;
+                }
+                NetworkInfo.State netState = networkInfo
+                        .getState();
+                if (netState != NetworkInfo.State.CONNECTED) {
+                    dbgUtil.Logd(
+                            "Current Network info",
+                            "not Connected!State="
+                                    + netState);
+                    return;
+                } else {
+
+                    int iconntype = -1;
+                    iconntype = networkInfo
+                            .getType();
+                    SharedPreferences pref = getApplication()
+                            .getSharedPreferences(
+                                    "UploadManager",
+                                    0);
+
+
+                    boolean wifi_only = pref.getBoolean("wifi_only", false);
+
+                    if (wifi_only == true && iconntype != ConnectivityManager.TYPE_WIFI && iconntype != 9/* earthnet */) {
+                        uploadManager.stopAllTasks();
+//															 dbgUtil.Log(Log.DEBUG,
+//															 "Current Network info",
+//															 "not allowed!Connection type="
+//															 +
+//															 networkInfo.getTypeName());
+                        return;
+                    }
+                }
+
+
+            } else if (RABroadcast.ACTION_LOCATION_ENABLE_TRACING.equals(action)) {
+                enable_locationTracing(locationTracing_timeInterval, locationTracing_distance);
+            } else if (RABroadcast.ACTION_LOCATION_DISABLE_TRACING
+                    .equals(action)) {
+
+                disable_locationTracing();
+            } else if (RABroadcast.ACTION_LOCATION_REQUEST_LOCATION
+                    .equals(action)) {
+
+
+                String receiverID = intent.getStringExtra("receiverID");
+//                locationManager.requestSingleUpdate();
+                request_location(receiverID);
+//                locationCallback.onLocationChanged();
+            } else if (RABroadcast.ACTION_PUSHNOTIFICATION_CHECK.equals(action)) {
+
+                // 重复定时任务
+
+//                Intent iAlarm = new Intent(RAService.this, RABroadcastReceiver.class);
+//        iAlarm.putExtra("caller", caller);
+                Intent iAlarm = new Intent(RABroadcast.ACTION_PUSHNOTIFICATION_CHECK);
+//                iAlarm.setAction(RABroadcast.ACTION_PUSHNOTIFICATION_CHECK);
+                PendingIntent sender = PendingIntent.getBroadcast(RAService.this, 0,
+                        iAlarm, PendingIntent.FLAG_UPDATE_CURRENT);
+
+
+
+                AlarmManager am = (AlarmManager) RAService.this.getSystemService(
+                        Context.ALARM_SERVICE);
+
+
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                    am.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + pushcheck_timeInterval, sender);
+                } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+                    am.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + pushcheck_timeInterval, sender);
+                }
+
+//                Log.d(TAG, "onReceive: " + RABroadcast.ACTION_PUSHNOTIFICATION_CHECK);
+                checkpush(context);
+            } else if (RABroadcast.ACTION_PUSHNOTIFICATION_ENABLE_CHECK
+                    .equals(action)) {
+
+                String tag = "onReceive@Alarmreceiver";
+//                Log.i( tag, "receive alarm broadcast caller =="
+//                        + intent.getStringExtra("caller"));
+
+                startalarm();
+
+//                checkpush(context);
+
+//                if (SystemClock.elapsedRealtime()
+//                        - ApexTrackingApplication.getLastCheckMessageTime() > 120000)
+//                {
+//                    Log.i(tag, "check messsage");
+//                    ApexTrackingApplication.UpdateLastCheckMessageTime();
+//                    checkpush(context);
+//
+//                }
+            } else if (RABroadcast.ACTION_PUSHNOTIFICATION_DISABLE_CHECK
+                    .equals(action)) {
+                if (m_task != null)
+                    m_task.cancel(true);
+//                NotificationManager nManager = (NotificationManager) context
+//                        .getSystemService(Context.NOTIFICATION_SERVICE);
+//                nManager.cancel(R.layout.activity_apex);
+                cancelalarm();
+                // ApexTrackingApplication.put_password("");
+                // ApexTrackingApplication.put_sessionid("");
+                // ApexTrackingApplication.put_user("");
+//                ApexTrackingApplication.logout();
+            }
+        }
+    };
+
+    private void startalarm()
+
+    {
+        Log.d(TAG, "startalarm: ");
+
+
+        // 启动完成
+//        Intent iAlarm = new Intent(this, RABroadcastReceiver.class);
+//        iAlarm.putExtra("caller", caller);
+        Intent iAlarm = new Intent(RABroadcast.ACTION_PUSHNOTIFICATION_CHECK);
+//        iAlarm.setAction(RABroadcast.ACTION_PUSHNOTIFICATION_CHECK);
+        PendingIntent sender = PendingIntent.getBroadcast(this, 0,
+                iAlarm, PendingIntent.FLAG_UPDATE_CURRENT);
+
+        long firstime = SystemClock.elapsedRealtime();
+//		UpdateLastAlermTime();
+        AlarmManager am = (AlarmManager) this.getSystemService(
+                Context.ALARM_SERVICE);
+
+//        // 30秒一个周期,不停的发送广播
+//        am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, firstime,
+//                pushcheck_timeInterval, sender);
+
+
+
+
+        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(), pushcheck_timeInterval, sender);
+        }
+
+//        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(), pushcheck_timeInterval, sender);
+//        }
+    }
+
+    private void cancelalarm() {
+
+        // 启动完成
+        Intent iAlarm = new Intent(this, RABroadcastReceiver.class);
+        iAlarm.setAction(RABroadcast.ACTION_PUSHNOTIFICATION_CHECK);
+        PendingIntent sender = PendingIntent.getBroadcast(this, 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();
+
+
+    }
+
+//    void registerReceiver() {
+//
+//    }
+
+
+    void disable_locationTracing() {
+        locationManager.removeUpdates(locationListener);
+    }
+
+
+    void enable_locationTracing(int timeInterval, int distance) {
+
+        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
+            // TODO: Consider calling
+            //    ActivityCompat#requestPermissions
+            // here to request the missing permissions, and then overriding
+            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
+            //                                          int[] grantResults)
+            // to handle the case where the user grants the permission. See the documentation
+            // for ActivityCompat#requestPermissions for more details.
+            return;
+        }
+
+
+        locationManager.requestLocationUpdates(
+                "fused", timeInterval, distance,
+                locationListener);
+//        if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
+//
+//
+//
+//
+//        } else if (locationManager
+//                .isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
+//
+//
+//            locationManager.requestLocationUpdates(
+//                    LocationManager.NETWORK_PROVIDER, timeInterval, distance,
+//                    locationListener);
+//
+//        }
+    }
+
+    protected Location request_cachedlocation() {
+        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
+
+            return null;
+        }
+
+
+        //使用GPS获取上一次的地址,这样获取到的信息需要多次,才能够显示出来,所以后面有动态的判断
+        Location location = locationManager.getLastKnownLocation("fused");
+        return location;
+
+    }
+
+    protected void request_location(final String receiverID) {
+        if (receiverID == null)
+            throw new IllegalArgumentException("receiverID can't be null");
+
+//        Criteria criteria = new Criteria();
+//        criteria.setAccuracy(Criteria.ACCURACY_COARSE);//低精度,如果设置为高精度,依然获取不了location。
+//        criteria.setAltitudeRequired(false);//不要求海拔
+//        criteria.setBearingRequired(false);//不要求方位
+//        criteria.setCostAllowed(true);//允许有花费
+//        criteria.setPowerRequirement(Criteria.POWER_LOW);//低功耗
+//
+//        //从可用的位置提供器中,匹配以上标准的最佳提供器
+//        String locationProvider = locationManager.getBestProvider(criteria, true);
+
+
+        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
+            // TODO: Consider calling
+            //    ActivityCompat#requestPermissions
+            // here to request the missing permissions, and then overriding
+            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
+            //                                          int[] grantResults)
+            // to handle the case where the user grants the permission. See the documentation
+            // for ActivityCompat#requestPermissions for more details.
+            return;
+        }
+
+        LocationListener singleListener = new LocationListener() {
+            @Override
+            public void onLocationChanged(Location location) {
+
+                Intent Bintent = new Intent(RABroadcast.EVENT_RETURN_LOCATION);
+                Bintent.putExtra("location", location);
+                Bintent.putExtra("receiverID", receiverID);
+                sendBroadcast(Bintent);
+
+
+            }
+
+            @Override
+            public void onStatusChanged(String s, int i, Bundle bundle) {
+
+            }
+
+            @Override
+            public void onProviderEnabled(String s) {
+
+            }
+
+            @Override
+            public void onProviderDisabled(String s) {
+
+            }
+        };
+
+
+        locationManager.requestSingleUpdate("fused", singleListener, null);
+
+
+    }
+}

+ 141 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/upload/RAOperationQueue.java

@@ -0,0 +1,141 @@
+package com.usai.redant.rautils.upload;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * Created by ray on 28/06/2017.
+ */
+
+public class RAOperationQueue {
+
+    public interface OperationCallback {
+        /**
+            每个具体任务执行的回调
+         */
+        public Boolean operate(Bundle taskinfo);
+        public void operateFinish(Bundle taskinfo);
+    }
+
+//    private Application mApp;
+//    public void setApplication(Application application) {
+//        mApp = application;
+//    }
+
+
+    public RAOperationQueue(Context applicationContext)
+    {
+        if(applicationContext==null)
+            throw new IllegalArgumentException("applicationContext can't be null");
+        this.applicationContext = applicationContext;
+    }
+    Context applicationContext;
+    OperationCallback execCallback;
+    public void setCallback(OperationCallback callback)
+    {
+        execCallback=callback;
+    }
+    //并发队列
+    private ConcurrentLinkedQueue<Bundle> queue = new ConcurrentLinkedQueue<Bundle>();
+    // 最大并发数
+    public int MaxThread = 3;
+    //线程池
+    private ExecutorService es = Executors.newCachedThreadPool();
+
+
+//    public RAOperation runThread;
+    public void addOperation(final Bundle taskinfo)
+    {
+
+
+        queue.offer(taskinfo);
+        while (((ThreadPoolExecutor)es).getActiveCount()< MaxThread) {
+
+
+            es.submit(new Runnable() {
+                boolean wrong = false;
+                @Override
+                public void run() {
+
+                    while (!queue.isEmpty()) {
+                        Bundle task=queue.poll();
+
+                        System.out.println("running  tid="+Thread.currentThread().getId());
+                        task.putLong("tid",Thread.currentThread().getId());
+//                        int min=1000;
+//                        int max=5000;
+//                        int sleep=new Random().nextInt(max)%(max-min+1) + min;
+//                        try{
+//                            Thread.currentThread().sleep(sleep);
+//                        }catch(InterruptedException ie){
+//                            ie.printStackTrace();
+//                        }
+                        boolean retry=execCallback.operate(task);
+                        if(retry)
+                            queue.offer(task);
+                        else
+                        {
+                            task.remove("tid");
+                            execCallback.operateFinish(task);
+
+                            RAUploadManager.TaskStatus status= RAUploadManager.TaskStatus.values()[task.getInt("status",0)];
+
+                            if (status== RAUploadManager.TaskStatus.TaskStatusError)
+                                wrong=true;
+
+                        }
+                        System.out.println("     finish  tid="+Thread.currentThread().getId());
+                    }
+
+                    synchronized (this)
+                    {
+                        if(((ThreadPoolExecutor) es).getActiveCount()==1)
+                        {
+
+
+                            Intent intent = new Intent("REDANT.RAImage.UPLOAD_COMPLETE");
+
+
+
+
+                            if(wrong)
+                            {
+                                intent.putExtra("result","error");
+                            }
+                            else
+                            {
+                                intent.putExtra("result","finish");
+                            }
+
+                            applicationContext.sendBroadcast(intent);
+
+                            //getApplicationContext()
+                            Log.e("Finish_msg", "run: -----------task finish" );
+
+//                                                        Toast toast = Toast.makeText(getApplicationContext(),
+//                                    "Piid must be a number",
+//                                    Toast.LENGTH_LONG);
+//                            toast.setGravity(Gravity.CENTER, 0, 0);
+//                            toast.show();;
+
+                        }
+                    }
+
+                }
+            });
+
+        }
+    }
+    public void stopQueue()
+    {
+        queue.clear();
+        es.shutdown();
+    }
+}

+ 742 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/upload/RAUploadManager.java

@@ -0,0 +1,742 @@
+package com.usai.redant.rautils.upload;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.util.Base64;
+import android.util.Log;
+
+import com.usai.redant.rautils.utils.ImageUtil;
+import com.usai.redant.rautils.utils.Network;
+import com.usai.redant.rautils.utils.RAUtil;
+import com.usai.redant.rautils.utils.dbgUtil;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by ray on 13/06/2017.
+ */
+
+public class RAUploadManager {
+
+    public int maxThread = 3;
+    public int activeThread;
+    public int maxRetry = 5;
+    public int retryWaiting = 300;
+
+
+    public Boolean wifiOnly = false;
+    //    public Boolean autoStart=true;
+//    public Boolean removeFinish=true;
+//    public Boolean removeError=false;
+    public QueueStatus queue_status;
+
+
+    public static final int RESULT_BARCODE_ERROR = -50;
+
+    public TaskStatus newtaskStatus = TaskStatus.TaskStatusWait;
+    private ArrayList<Bundle> arr_queue;
+
+    public ArrayList<Bundle> get_arr_queue() {
+        return arr_queue;
+    }
+
+    //    private List<Bundle> operation_queue;
+//    private ExecutorService es = Executors.newFixedThreadPool(3);//
+    // .newFixedThreadPool();
+//    private CountDownLatch latch = new CountDownLatch(maxThread);
+    private RAOperationQueue operation_queue;
+
+    public enum QueueStatus {
+        QueueStatusDefault,
+        QueueStatusAdd,
+        QueueStatusError,
+        QueueStatusFinishWithError,
+        QueueStatusFinish
+    }
+
+    public enum TaskStatus {
+        TaskStatusStop,
+        TaskStatusStart,
+        TaskStatusError,
+        TaskStatusWait,
+        TaskStatusFinish,
+        //TaskStatusCancel,
+    }
+
+//    public RAUploadManager(Context applicationContext)
+//    {
+//        if(applicationContext==null)
+//            throw new IllegalArgumentException("applicationContext can't be null");
+//        this.applicationContext = applicationContext;
+//    }
+    public UIUpdateListener uiUpdateListener;
+
+//    private Application mApp;
+    private Context applicationContext;
+//    public void setApplication(Application application) {
+//        mApp = application;
+//    }
+
+    public interface UIUpdateListener {
+        //        void onProgress(long index, double percentage);
+        void updateCell(long index, Bundle taskinfo);
+//        void updateList();
+
+        void updateList(ArrayList<Bundle> newlist);
+//        void addTasks(ArrayList<Bundle> tasks);
+//        void RemoveTasks(ArrayList<Bundle> tasks);
+
+//        void onFinish(int code, String res/*, HashMap<String, List<String>> headers*/);
+    }
+
+
+//    class Poll implements Runnable {
+//        public void run() {
+//            // while (queue.size()>0) {
+//            while (!operation_queue.isEmpty()) {
+//                System.out.println(operation_queue.poll());
+//            }
+//            latch.countDown();
+//        }
+//    }
+
+    public RAUploadManager(Context appCtx) {
+
+        if(appCtx==null)
+            throw new IllegalArgumentException("applicationContext can't be null");
+        this.applicationContext = appCtx;
+
+        queue_status = QueueStatus.QueueStatusDefault;
+
+
+        SharedPreferences UMSetting = applicationContext.getSharedPreferences("UploadManager", 0);
+
+        if (UMSetting != null) {
+//            autoStart = UMSetting.getBoolean("auto_upload",false);
+//            removeFinish = UMSetting.getBoolean("auto_rm_finish",false);
+//            removeError = UMSetting.getBoolean("auto_rm_error",false);
+            maxRetry = UMSetting.getInt("retry_count", 5);
+            newtaskStatus = TaskStatus.values()[UMSetting.getInt("newtask_status", TaskStatus.TaskStatusWait.ordinal())];
+
+            maxThread = UMSetting.getInt("max_thread", 3);
+
+            wifiOnly = UMSetting.getBoolean("wifi_only", false);
+
+        }
+        operation_queue = new RAOperationQueue(applicationContext);
+//        operation_queue.setApplication(mApp);
+        operation_queue.setCallback(new RAOperationQueue.OperationCallback() {
+            @Override
+            public Boolean operate(final Bundle taskinfo) {
+
+
+//
+//                ConnectivityManager connManager = (ConnectivityManager) context
+//                        .getSystemService(Context.CONNECTIVITY_SERVICE);
+//
+//                NetworkInfo networkInfo = connManager
+//                        .getActiveNetworkInfo();
+//                if (networkInfo == null)
+//                {
+//                    dbgUtil.Logd(
+//                            "Current Network info",
+//                            "can not get Active NetworkInfo!");
+//                    false;
+//                }
+//                NetworkInfo.State netState = networkInfo
+//                        .getState();
+//                if (netState != NetworkInfo.State.CONNECTED)
+//                {
+//                    dbgUtil.Logd(
+//                            "Current Network info",
+//                            "not Connected!State="
+//                                    + netState);
+//                    return;
+//                }
+//                int iconntype = -1;
+//                iconntype = networkInfo
+//                        .getType();
+//                SharedPreferences pref = RedAntApplication
+//                        .getInstance()
+//                        .getSharedPreferences(
+//                                "UploadManager",
+//                                0);
+//
+////															String aa = pref
+////																	.getString(
+////																			"aa",
+////																			null);
+////															String ea = pref
+////																	.getString(
+////																			"ea",
+////																			null);
+////															if (iconntype == ConnectivityManager.TYPE_WIFI)
+////															{
+////																if (!TextUtils
+////																		.isEmpty(aa))
+////																	RedAntApplication.active_address = aa;
+////															}
+////															else
+////															{
+////																if (!TextUtils
+////																		.isEmpty(ea))
+////																	RedAntApplication.active_address = ea;
+////															}
+//                boolean
+//                        wifi_only
+//                        =pref.getBoolean("wifi_only",
+//                        true);
+//
+//                if
+//                        (wifi_only
+//                        == true
+//                        && iconntype !=
+//                        ConnectivityManager.TYPE_WIFI
+//                        && iconntype !=
+//                        9/* earthnet */)
+//
+
+                boolean ret = false;
+                String path = taskinfo.getString("path");
+                taskinfo.putInt("status", TaskStatus.TaskStatusStart.ordinal());
+                String url = taskinfo.getString("url");
+
+                int waiting = taskinfo.getInt("retry_waiting", 0);
+
+                int newwaiting = waiting;
+                if (newwaiting / 60.0 < 20)
+                    newwaiting += retryWaiting;
+                if (newwaiting > 1200)
+                    newwaiting = 1200;
+                taskinfo.putInt("retry_waiting", newwaiting);
+
+                //                try{
+                try {
+                    Thread.currentThread().sleep(waiting * 1000);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+//                }catch(InterruptedException ie){
+//                    ie.printStackTrace();
+//                }
+                JSONObject result = Network.uploadFileJSON(path, url, taskinfo.getBundle("params"), new Network.FileUploadListener() {
+                    @Override
+                    public double percent_step() {
+                        //进度刷新步进 3%
+                        return 3.0;
+                    }
+
+                    @Override
+                    public boolean interupt() {
+//                        if(taskinfo.getBoolean("iscancel",false))
+//                            return false;
+                        return taskinfo.getBoolean("iscancel", false);
+                    }
+
+                    @Override
+                    public void onProgress(long pro, double percentage) {
+
+
+                        synchronized (this) {
+                            Log.d("_synchronized", "UploadImage onProgress: " + percentage);
+                            long timeStart = System.currentTimeMillis();
+                            taskinfo.putDouble("progress", percentage);
+                            if (uiUpdateListener != null) {
+                                int index = arr_queue.indexOf(taskinfo);
+//                            uiUpdateListener.onProgress(index,percentage);
+
+                                uiUpdateListener.updateCell(index, taskinfo);
+                            }
+                            Log.d("_synchronized", "UploadImage onProgress: end" + (System.currentTimeMillis() - timeStart));
+                        }
+
+
+                    }
+
+                    @Override
+                    public void onFinish(int code, String res) {
+                        Log.d("", "onFinish: ");
+
+                    }
+                });
+
+                int r = 0;
+                try {
+                    r = result.getInt("result");
+
+
+                    if (r == 2 || r == RESULT_BARCODE_ERROR) {
+                        taskinfo.putInt("status", TaskStatus.TaskStatusFinish.ordinal());
+                        if (r == 2)
+                            taskinfo.putString("msg", "upload successful");
+                        else {
+                            String rmsg = result.getString("msg");
+                            taskinfo.putString("msg", "warning: " + rmsg);
+                        }
+                    } else {
+                        if (taskinfo.getInt("retry", 0) >= maxRetry) {
+                            taskinfo.putInt("status", TaskStatus.TaskStatusError.ordinal());
+                            taskinfo.putDouble("progress", 0.0);
+                            String rmsg = result.getString("msg");
+                            taskinfo.putString("msg", rmsg);
+                        } else {
+                            taskinfo.putInt("retry", taskinfo.getInt("retry", 0) + 1);
+                            taskinfo.putInt("status", TaskStatus.TaskStatusWait.ordinal());
+                            taskinfo.putDouble("progress", 0.0);
+                            taskinfo.putString("msg", "connection lost, retry...");
+                            ret = true;
+
+                        }
+                    }
+
+
+                } catch (JSONException e) {
+//                    String rmsg=result.getString("msg");
+                    taskinfo.putInt("status", TaskStatus.TaskStatusError.ordinal());
+                    taskinfo.putDouble("progress", 0.0);
+                    taskinfo.putString("msg", "can not upload, please contact administrator.");
+                    e.printStackTrace();
+                } finally {
+                    synchronized (this) {
+                        Log.d("_synchronized", "UploadImage finally ");
+
+                        long timeStart = System.currentTimeMillis();
+                        if (uiUpdateListener != null) {
+                            int index = arr_queue.indexOf(taskinfo);
+
+                            uiUpdateListener.updateCell(index, taskinfo);
+                        }
+                        Log.d("_synchronized", "UploadImage finally finish" + (System.currentTimeMillis() - timeStart));
+
+                    }
+                }
+
+                return ret;
+//                int a=0;
+//                System.out.println(a+"running  tid="+Thread.currentThread().getId());
+//                int min=1000;
+//                int max=5000;
+//                int sleep=new Random().nextInt(max)%(max-min+1) + min;
+//                try{
+//                    Thread.currentThread().sleep(sleep);
+//                }catch(InterruptedException ie){
+//                    ie.printStackTrace();
+//                }
+//                System.out.println(a+"   "+sleep+"     finish  tid="+Thread.currentThread().getId());
+
+            }
+
+            @Override
+            public void operateFinish(Bundle taskinfo) {
+
+                TaskStatus status = TaskStatus.values()[taskinfo.getInt("status", 0)];
+
+                //新增逻辑,对于成功上传的图片,从书记删除
+                //taskinfo.getString("msg").indexOf("warning:")<0 没有匹配错误
+                String path = filePath(taskinfo);
+                File file = new File(path);
+                //只有finish的task 可以删除图片
+
+                if (file.exists() && taskinfo.getInt("status") == TaskStatus.TaskStatusFinish.ordinal() && taskinfo.getString("msg").indexOf("warning:") < 0) {
+                    String filepath = file.getAbsolutePath();
+                    file.delete();
+                    ImageUtil.updateGallery(applicationContext,filepath);
+                }
+                ///////////////////////////////////////////////
+
+                boolean removefromlist = false;
+//                if (status==TaskStatus.TaskStatusFinish&&removeFinish)
+//                    removefromlist=true;
+//                else if (status==TaskStatus.TaskStatusError&&removeError)
+//                    removefromlist=true;
+
+                synchronized (this) {
+
+                    Log.d("_synchronized", "OperationCallback operateFinish: ");
+                    long timeStart = System.currentTimeMillis();
+                    if (removefromlist) {
+
+                        removeTask(taskinfo);
+//                            arr_queue.remove(taskinfo);
+//                        if(uiUpdateListener!=null)
+//                            uiUpdateListener.updateList();
+
+
+                    } else {
+                        saveTasks();
+                    }
+
+                    Log.d("_synchronized", "OperationCallback operateFinish: end" + (System.currentTimeMillis() - timeStart));
+
+
+//                        saveTasks();
+
+
+                }
+
+
+            }
+        });
+
+        //            operation_queue = new List<Bundle>() {};
+
+        loadTask();
+
+    }
+
+    private void loadTask() {
+        stopAllTasks();
+        arr_queue = new ArrayList<Bundle>();
+
+
+        Log.d("_RAIMAGE", "saveTasks: RAUploadManager");
+
+        SharedPreferences pref = applicationContext.getSharedPreferences("RA Image", 0);
+        try {
+            String tasksBase64 = pref.getString("task_list", "");
+
+
+//            ArrayList<String> wrap_arr = new ArrayList<String>();
+//            for( Bundle b:arr_queue)
+//            {
+//                String sjson=RAUtil.Bundle2Json(b).toString();
+//                Log.d("_RAIMAGE", "saveTasks: "+sjson);
+//                wrap_arr.add(sjson);
+//            }
+//
+//            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+//            ObjectOutputStream oos = new ObjectOutputStream(baos);
+//            oos.writeObject(wrap_arr);
+//
+//            String tasksBase64 = android.util.Base64.encodeToString(baos.toByteArray(), android.util.Base64.DEFAULT);//new String(Base64.encodeBase64(baos.toByteArray()));
+//
+//
+//            editor.putString("task_list", tasksBase64);
+//
+//
+//            editor.commit();
+
+            if (tasksBase64.length() > 0) {
+                byte[] decode = Base64.decode(tasksBase64, Base64.DEFAULT);
+                ByteArrayInputStream bais = new ByteArrayInputStream(decode);
+
+                ObjectInputStream ois = null;
+
+                ois = new ObjectInputStream(bais);
+
+                ArrayList<String> arr_load = (ArrayList<String>) ois.readObject();
+                for (String s : arr_load) {
+                    Log.d("_RAIMAGE", "loadTask: " + s);
+                    JSONObject jsobj = new JSONObject(s);
+                    Bundle b = RAUtil.Json2Bundle(jsobj);
+                    arr_queue.add(b);
+//                                String sjson=RAUtil.Bundle2Json(b).toString();
+
+//                wrap_arr.add(sjson);
+                }
+            }
+
+
+        } catch (IOException e) {
+            e.printStackTrace();
+        } catch (ClassNotFoundException e) {
+            e.printStackTrace();
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+        for (Bundle b : arr_queue) {
+
+            TaskStatus status = TaskStatus.values()[b.getInt("status", 0)];
+
+            if (status != TaskStatus.TaskStatusFinish && status != TaskStatus.TaskStatusError)
+                startTask(b);
+        }
+//            int a = 0;
+        // 此处缺少读取持久话保存任务队列的实现。
+    }
+
+    public void addTask(Bundle task) {
+
+        synchronized (this) {
+            Log.d("_synchronized", "addTask: ");
+            long timeStart = System.currentTimeMillis();
+            arr_queue.add(task);
+            saveTasks();
+            Log.d("_synchronized", "addTask: end" + (System.currentTimeMillis() - timeStart));
+        }
+        if (newtaskStatus == TaskStatus.TaskStatusWait) {
+            startTask(task);
+        }
+    }
+
+    ;
+
+    public void addTasks(ArrayList<Bundle> tasks) {
+
+        synchronized (this) {
+            Log.d("_synchronized", "addTasks: ");
+            long timeStart = System.currentTimeMillis();
+            arr_queue.addAll(tasks);
+            saveTasks();
+
+            Log.d("_synchronized", "addTasks: end" + (System.currentTimeMillis() - timeStart));
+
+        }
+        if (newtaskStatus == TaskStatus.TaskStatusWait) {
+            for (Bundle task : tasks) {
+                startTask(task);
+            }
+
+        }
+
+    }
+
+    ;
+
+    public void startTask(Bundle task) {
+
+        task.putBoolean("iscancel", false);
+
+
+        boolean canstart = true;
+
+        ConnectivityManager connManager = (ConnectivityManager) applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+
+        NetworkInfo networkInfo = connManager
+                .getActiveNetworkInfo();
+        if (networkInfo == null) {
+            dbgUtil.Logd(
+                    "Current Network info",
+                    "can not get Active NetworkInfo!");
+            canstart = false;
+        } else {
+            NetworkInfo.State netState = networkInfo
+                    .getState();
+            if (netState != NetworkInfo.State.CONNECTED) {
+                dbgUtil.Logd(
+                        "Current Network info",
+                        "not Connected!State="
+                                + netState);
+                canstart = false;
+            }
+            int iconntype = -1;
+            iconntype = networkInfo
+                    .getType();
+
+            if (wifiOnly == true
+                    && iconntype !=
+                    ConnectivityManager.TYPE_WIFI
+                    && iconntype !=
+                    9/* earthnet */) {
+                canstart = false;
+            }
+        }
+
+        if (canstart) {
+
+            task.putInt("retry_waiting", 0);
+            task.putInt("status", TaskStatus.TaskStatusWait.ordinal());
+            operation_queue.addOperation(task);
+        } else {
+            task.putInt("status", TaskStatus.TaskStatusStop.ordinal());
+            if (uiUpdateListener != null) {
+                int index = arr_queue.indexOf(task);
+//                            uiUpdateListener.onProgress(index,percentage);
+
+                uiUpdateListener.updateCell(index, task);
+            }
+
+        }
+
+
+    }
+
+    ;
+
+    public void clearTask() {
+        ArrayList<Bundle> remove = new ArrayList<Bundle>();
+        for (Bundle b : arr_queue) {
+//            if(TaskStatus.values()[b.getInt("status",0)]==TaskStatus.TaskStatusStart||TaskStatus.values()[b.getInt("status",0)]==TaskStatus.TaskStatusWait)
+//                continue;
+
+            if (TaskStatus.values()[b.getInt("status", 0)] == TaskStatus.TaskStatusFinish || TaskStatus.values()[b.getInt("status", 0)] == TaskStatus.TaskStatusError)
+                remove.add(b);
+//            removeTask(b);
+        }
+        removeTasks(remove);
+
+
+    }
+
+    public void removeTask(Bundle task) {
+
+
+        synchronized (this) {
+            Log.d("_synchronized", "removeTask");
+            long timeStart = System.currentTimeMillis();
+            arr_queue.remove(task);
+            saveTasks();
+
+
+            String path = filePath(task);
+            File file = new File(path);
+            //只有finish的task 可以删除图片
+            if (file.exists() && task.getInt("status") == TaskStatus.TaskStatusFinish.ordinal()) {
+                String filepath = file.getAbsolutePath();
+                file.delete();
+                ImageUtil.updateGallery(applicationContext,filepath);
+            }
+
+            if (uiUpdateListener != null)
+                uiUpdateListener.updateList(arr_queue);
+
+            Log.d("_synchronized", "removeTask: end" + (System.currentTimeMillis() - timeStart));
+        }
+
+
+    }
+
+    ;
+
+    public void removeTasks(List<Bundle> tasks) {
+
+        synchronized (this) {
+            Log.d("_synchronized", "removeTasks: ");
+            long timeStart = System.currentTimeMillis();
+            arr_queue.removeAll(tasks);
+            saveTasks();
+            for (Bundle task : tasks) {
+
+                String path = filePath(task);
+                File file = new File(path);
+                if (file.exists() && task.getInt("status") == TaskStatus.TaskStatusFinish.ordinal()) {
+                    String filepath = file.getAbsolutePath();
+                    file.delete();
+                    ImageUtil.updateGallery(applicationContext,filepath);
+                }
+            }
+            if (uiUpdateListener != null)
+                uiUpdateListener.updateList(arr_queue);
+
+
+            Log.d("_synchronized", "removeTasks: end" + (System.currentTimeMillis() - timeStart));
+        }
+
+    }
+
+    ;
+
+    public void stopAllTasks() {
+        if (arr_queue == null)
+            return;
+        for (Bundle task : arr_queue) {
+            stopTask(task);
+        }
+    }
+
+    ;
+
+    public void stopTask(Bundle task) {
+        if (task.getInt("status") != TaskStatus.TaskStatusStart.ordinal() && task.getInt("status") != TaskStatus.TaskStatusWait.ordinal())
+            return;
+
+        task.putBoolean("iscancel", true);
+//        long tid=task.getLong("tid",-1);
+//        if(tid>0)
+//        {
+//
+//
+//        }
+//        task.tet("tid",Thread.currentThread().getId());
+
+//        RAUploadOperation
+
+
+    }
+
+    public void saveTasks() {
+
+        Log.d("_RAIMAGE", "saveTasks: RAUploadManager");
+
+        SharedPreferences pref = applicationContext.getSharedPreferences("RA Image", 0);
+        SharedPreferences.Editor editor = pref.edit();
+
+        try {
+
+            ArrayList<String> wrap_arr = new ArrayList<String>();
+            for (Bundle b : arr_queue) {
+                String sjson = RAUtil.Bundle2Json(b).toString();
+                Log.d("_RAIMAGE", "saveTasks: " + sjson);
+                wrap_arr.add(sjson);
+            }
+
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            ObjectOutputStream oos = new ObjectOutputStream(baos);
+            oos.writeObject(wrap_arr);
+
+            String tasksBase64 = Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT);//new String(Base64.encodeBase64(baos.toByteArray()));
+
+
+            editor.putString("task_list", tasksBase64);
+
+
+            editor.commit();
+
+//            byte[] decode=Base64.decode(personBase64,Base64.DEFAULT);
+//            ByteArrayInputStream bais= new ByteArrayInputStream(decode);
+//
+//            ObjectInputStream ois = new ObjectInputStream(bais);
+//            ArrayList<String> arr_load=(ArrayList<String>)ois.readObject();
+//            int a = 0;
+
+        } catch (Exception e) {
+            e.printStackTrace();
+
+        }
+
+
+//        if (m_swSave.isChecked()&&!TextUtils.isEmpty(encryptPass)&&!TextUtils.isEmpty(encryptUser))
+//        {
+//            editor.putString("user", encryptUser);
+//            editor.putString("password", encryptPass);
+//            editor.putBoolean("kepppass", true);
+//        }
+//        else
+//        {
+//            editor.putString("user", null);
+//            editor.putString("password", null);
+//            editor.putBoolean("kepppass", false);
+//        }
+//
+//        editor.puts
+//
+//        editor.commit();
+
+    }
+
+    ;
+
+
+    private String filePath(Bundle task) {
+//        String path = Environment.getExternalStorageDirectory().getPath();
+//        path+="/"+task.getString("path");
+//        path+="/"+task.getString("file");
+
+        return task.getString("path", "");
+    }
+
+
+}

+ 10 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/upload/UploadCallback.java

@@ -0,0 +1,10 @@
+package com.usai.redant.rautils.upload;
+
+public interface UploadCallback {
+
+
+//    private boolean queue_changed = false;
+
+//    public RAUploadManager uploadManager = new RAUploadManager();
+
+}

+ 547 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/upload/UploadService.java

@@ -0,0 +1,547 @@
+package com.usai.redant.rautils.upload;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.usai.redant.rautils.utils.CrashHandler;
+import com.usai.redant.rautils.utils.dbgUtil;
+
+import java.util.ArrayList;
+
+@Deprecated
+public class UploadService extends Service
+{
+
+//	private UploadThread	uploadThread;
+
+    private boolean queue_changed = false;
+//	LocationManager			locationManager;
+//	LocationListener		locationListener;
+
+    public RAUploadManager uploadManager = new RAUploadManager(getApplicationContext());
+
+    // private boolean location_changed = false;
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        // TODO Auto-generated method stub
+        Log.d("", "onBind: bind service");
+        return new MyBinder();
+    }
+
+
+    public class MyBinder extends Binder {
+        /**
+         * 获取当前Service的实例
+         *
+         * @return
+         */
+        public UploadService getService() {
+            return UploadService.this;
+        }
+    }
+
+    @Override
+    public void onCreate() {
+
+        Log.d("_SERVICE", "create upload Service!!!");
+//		if (uploadThread == null)
+//		{
+//			uploadThread = new UploadThread();
+//			Log.d("", "create upload thread!!!");
+//			// upleadThread.start();
+//
+//		}
+
+
+        // 初始化异常捕获器
+        CrashHandler.getSharedInstance().init(getApplicationContext());
+        CrashHandler.getSharedInstance().preserver = new CrashHandler.InfoPreserver() {
+            @Override
+            public void saveUserInformation() {
+                if (uploadManager != null) {
+                    uploadManager.saveTasks();
+                }
+            }
+
+            @Override
+            public void handleCrashInfo(String deviceInfo, String exception) {
+                dbgUtil.fileLog(getApplicationContext(),"============================ app crash ============================");
+                dbgUtil.fileLog(getApplicationContext(),"DEVICE");
+                dbgUtil.fileLog(getApplicationContext(),deviceInfo);
+                dbgUtil.fileLog(getApplicationContext(),"EXCEPTION");
+                dbgUtil.fileLog(getApplicationContext(),exception);
+                dbgUtil.fileLog(getApplicationContext(),"=============================== end ===============================");
+                Log.d("_SERVICE", "Device Info: " + deviceInfo);
+                Log.d("_SERVICE", "Crash Info: " + exception);
+            }
+        };
+
+        IntentFilter msgFilter = new IntentFilter();
+
+        msgFilter.addAction("REDANT.POP.MODIFY_QUEUE");
+//		msgFilter.addAction("REDANT.POP.GPS_ON");
+//		msgFilter.addAction("REDANT.POP.GPS_OFF");
+//		msgFilter.addAction("REDANT.POP.REQUEST_LOCATION");
+        msgFilter.addAction("REDANT.POP.RESET_LOCATION");
+        msgFilter.addAction("REDANT.POP.QUERY_UPLOAD_STATE");
+        msgFilter.addAction("REDANT.RAImage.ADD_TASK");
+        msgFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        msgFilter.addAction("REDANT.POP.RETRY_UPLOAD");
+
+        registerReceiver(uploadReceiver, msgFilter);
+        // registerReceiver(uploadReceiver,
+        // new IntentFilter("modify upload queue"));
+
+//		locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+
+//		locationListener = new LocationListener()
+//		{
+//
+//			// Provider的状态在可用、暂时不可用和无服务三个状态直接切换时触发此函数
+//			@Override
+//			public void onStatusChanged(String provider, int status,
+//					Bundle extras)
+//			{
+//
+//			}
+//
+//			// Provider被enable时触发此函数,比如GPS被打开
+//			@Override
+//			public void onProviderEnabled(String provider)
+//			{
+//
+//			}
+//
+//			// Provider被disable时触发此函数,比如GPS被关闭
+//			@Override
+//			public void onProviderDisabled(String provider)
+//			{
+//
+//			}
+//
+//			// 当坐标改变时触发此函数,如果Provider传进相同的坐标,它就不会被触发
+//			@Override
+//			public void onLocationChanged(Location location)
+//			{
+//				// if (location != null)
+//				// {
+//				// location_changed = true;
+//				// Log.d("Map",
+//				// "Location changed : Lat: " + location.getLatitude()
+//				// + " Lng: " + location.getLongitude());
+//				// }
+//			}
+//		};
+
+        super.onCreate();
+
+    }
+
+    @Override
+    public void onDestroy() {
+        Log.e("_SERVICE", "onDestroy: ");
+        uploadManager.saveTasks();
+    }
+//	void gps_off()
+//	{
+//		locationManager.removeUpdates(locationListener);
+//	}
+//
+//	void gps_on()
+//	{
+//		if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER))
+//		{
+//
+////            RedAntApplication.getInstance()
+//
+////            if(!RAUtil.checkPermission(this.getApplicationContext(),"android.permission.ACCESS_FINE_LOCATION"))
+////                return;
+//
+//
+//
+//			locationManager.requestLocationUpdates(
+//					LocationManager.GPS_PROVIDER, 60 * 1000, 50,
+//					locationListener);
+//
+//		}
+//		else if (locationManager
+//				.isProviderEnabled(LocationManager.NETWORK_PROVIDER))
+//		{
+//
+//			locationManager.requestLocationUpdates(
+//					LocationManager.NETWORK_PROVIDER, 30 * 1000, 50,
+//					locationListener);
+//
+//		}
+//	}
+
+//	void update_location()
+//	{
+//
+//		Location location = null;
+//		if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER))
+//		{
+//
+//			location = locationManager
+//					.getLastKnownLocation(LocationManager.GPS_PROVIDER);
+//
+//		}
+//		else if (locationManager
+//				.isProviderEnabled(LocationManager.NETWORK_PROVIDER))
+//		{
+//
+//			location = locationManager
+//					.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
+//
+//		}
+//
+//		SharedPreferences pref = RedAntApplication.getInstance()
+//				.getSharedPreferences("RA Image", 0);
+//
+//		SharedPreferences.Editor editor = pref.edit();
+//		if (location != null)
+//		{
+//
+//			editor.putFloat("Lat", (float) location.getLatitude());
+//			editor.putFloat("Lon", (float) location.getLongitude());
+//
+//		}
+//		editor.commit();
+//
+//	}
+//
+//	void check_location()
+//	{
+//
+//		Location location = null;
+//		if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER))
+//		{
+//
+//			location = locationManager
+//					.getLastKnownLocation(LocationManager.GPS_PROVIDER);
+//
+//		}
+//		else if (locationManager
+//				.isProviderEnabled(LocationManager.NETWORK_PROVIDER))
+//		{
+//
+//			location = locationManager
+//					.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
+//
+//		}
+//
+//		SharedPreferences pref = RedAntApplication.getInstance()
+//				.getSharedPreferences("RA Image", 0);
+//		double lat = pref.getFloat("Lat", 9999);
+//		double lon = pref.getFloat("Lon", 9999);
+//
+//		// SharedPreferences.Editor editor = pref.edit();
+//		if (location != null)
+//		{
+//			float[] result = new float[1];
+//			if (lat != 9999 && lon != 9999)
+//			{
+//				Location.distanceBetween(lat, lon, location.getLatitude(),
+//						location.getLongitude(), result);
+//				if (result[0] > 1000)
+//				{
+//					sendBroadcast(new Intent("REDANT.POP.STATION_CHANGE"));
+//					return;
+//				}
+//				else
+//				{
+//					sendBroadcast(new Intent("REDANT.POP.STATION_NOT_CHANGE"));
+//					return;
+//				}
+//			}
+//			sendBroadcast(new Intent("REDANT.POP.STATION_NOT_CHANGE"));
+//			return;
+//			// new Location();
+//			//
+//			// Location.distanceBetween(lat, lon, endLatitude, endLongitude,
+//			// results)
+//			//
+//			// editor.putFloat("Lat", (float) location.getLatitude());
+//			// editor.putFloat("Lon", (float) location.getLongitude());
+//
+//		}
+//		else
+//		{
+//			sendBroadcast(new Intent("REDANT.POP.STATION_NOT_CHANGE"));
+//			return;
+//		}
+//		// editor.commit();
+//
+//		// Log.d("location:", "lat:" + latitude + "lon:" + longitude);
+//	}
+
+    // void loadlist()
+    // {
+    //
+    // }
+
+//	public class UploadThread extends Thread
+//	{
+//		@Override
+//		public void run()
+//		{
+//
+//			Log.d("", "upload thread running");
+//
+//			super.run();
+//			doupload();
+//			Log.d("", "upload thread stop");
+//
+//		}
+//
+//		private void doupload()
+//		{
+//			boolean error = false;
+//			boolean didupload = false;
+//			// int verifyresult = Network.VerifyUser(RedAntApplication.user,
+//			// RedAntApplication.password);
+//			// if (verifyresult == Network.RESULT_TRUE)
+//			// {
+//			SQLiteDatabase dbr = dbUtil.OpenDB(UploadService.this, null, false);
+//			Cursor cursor = dbr.query("pics", new String[] { "_id", "pid",
+//					"local_path", "picker", "server" }, "err_code!="
+//					+ Network.AP_UPLOAD_SUCCESS+" and err_code!="+ Network.RESULT_LOCALFILE_ERROR+" and try_count<3 ", null, null, null, "_id", null);
+//			while (cursor.moveToNext())
+//			{
+//				didupload = true;
+//				if (queue_changed)
+//				{
+//					queue_changed = false;
+//					dbUtil.CloseCursor(cursor);
+//					cursor = dbr.query("pics", new String[] { "_id", "pid",
+//							"local_path", "picker", "server" }, "err_code!="
+//							+ Network.AP_UPLOAD_SUCCESS+" and err_code!="+ Network.RESULT_LOCALFILE_ERROR +" and try_count<3 ", null, null, null,
+//							"_id", null);
+//					if (!cursor.moveToNext())
+//						break;
+//
+//				}
+//				int _id = cursor.getInt(0);
+//				String pid = cursor.getString(1);
+//				String path = cursor.getString(2);
+//				String picker = cursor.getString(3);
+//				String server = cursor.getString(4);
+//
+//				String encryptUser = "";
+//
+//				try
+//				{
+//					encryptUser = AES.encrypt("apexu", picker);
+//
+//				}
+//				catch (Exception e1)
+//				{
+//					// TODO Auto-generated catch block
+//					e1.printStackTrace();
+//				}
+//
+//				Cursor cursor_user = dbr.query("users",
+//						new String[] { "pass" }, "name='" + encryptUser + "'",
+//						null, null, null, "_id", null);
+//				String pass = "";
+//				if (cursor_user.moveToNext())
+//					pass = cursor_user.getString(0);
+//				try
+//				{
+//					pass = AES.decrypt("apexp", pass);
+//				}
+//				catch (Exception e)
+//				{
+//					// TODO Auto-generated catch block
+//					e.printStackTrace();
+//				}
+//
+//				int uploadresult = Network.UploadImage(path, picker, pass, pid,
+//						server);
+//				if (uploadresult == Network.RESULT_TRUE)
+//				{
+//
+//					RedAntApplication.writeLock.lock();
+//
+//					// write sql process
+//					SQLiteDatabase dbw = dbUtil.OpenDB(UploadService.this,
+//							null, true);
+//					// String sql = "update pics set err_code ="
+//					// + Network.AP_UPLOAD_SUCCESS + " where _id="
+//					// + _id;
+//					String sql = "delete from pics  where _id=" + _id;
+//					dbw.execSQL(sql);
+//
+//					String timeStamp = new SimpleDateFormat("yyyy_MM_dd")
+//							.format(new Date());
+//
+//					File storageDir = new File(Environment
+//							.getExternalStorageDirectory().getPath()
+//							+ "/redant/pop/done/" + timeStamp);
+//
+//					if (!storageDir.exists())
+//						storageDir.mkdirs();
+//
+//					File pic = new File(path);
+//					// pic.delete();
+//
+//					dbUtil.CloseDB(dbw);
+//					RedAntApplication.writeLock.unlock();
+//					dbgUtil.fileLog("upload success move file "
+//							+ pic.getName()
+//							+ " to "
+//							+ Environment.getExternalStorageDirectory()
+//									.getPath() + "/redant/pop/done/"
+//							+ timeStamp + File.separator);
+//					pic.renameTo(new File(Environment
+//							.getExternalStorageDirectory().getPath()
+//							+ "/redant/pop/done/"
+//							+ timeStamp
+//							+ File.separator
+//							+ pic.getName()));
+//
+//
+//				}
+//				else
+//				{
+//					error = true;
+//					RedAntApplication.writeLock.lock();
+//
+//					{
+//						// write sql process
+//						SQLiteDatabase dbw = dbUtil.OpenDB(UploadService.this,
+//								null, true);
+//						String sql = "update pics set err_code ="
+//								+ uploadresult + ", try_count = try_count+1 where _id=" + _id;
+//
+//						dbw.execSQL(sql);
+//						dbUtil.CloseDB(dbw);
+//					}
+//					RedAntApplication.writeLock.unlock();
+//					dbgUtil.fileLog("upload failed pid:" + pid + " file:"
+//							+ path + " server:" + server + " err_code:"
+//							+ uploadresult);
+//
+//				}
+//
+//				sendBroadcast(new Intent("REDANT.POP.UPDATE_QUEUE_VIEW"));
+//			}
+//
+//			dbUtil.CloseCursor(cursor);
+//			dbUtil.CloseDB(dbr);
+//
+//			// }
+//			// else
+//			// {
+//			// Log.d("upload thread", "user check failed!");
+//			//
+//			// sendBroadcast(new Intent("REDANT.POP.USER_CHECK_FAILED"));
+//			// }
+//
+//			if (queue_changed)
+//			{
+//				queue_changed = false;
+//				doupload();
+//			}
+//			if (didupload)
+//			{
+//				if (error)
+//				{
+//					sendBroadcast(new Intent(
+//							"REDANT.POP.FINISH_UPLOAD_QUEUE_WITH_ERROR"));
+//					RedAntApplication.startalarm();
+//				}
+//				else
+//				{
+//					sendBroadcast(new Intent("REDANT.POP.FINISH_UPLOAD_QUEUE"));
+//					RedAntApplication.cancelalarm();
+//				}
+//			}
+//		}
+//	}
+
+    private BroadcastReceiver uploadReceiver = new BroadcastReceiver() {
+
+        public void onReceive(Context context, Intent intent) {
+            String action = intent
+                    .getAction();
+            // 如果捕捉到的action是ACTION_BATTERY_CHANGED
+
+            if ("REDANT.RAImage.ADD_TASK".equals(action)) {
+
+                ArrayList<Bundle> tasks = intent.getParcelableArrayListExtra("tasks");
+
+
+                uploadManager.addTasks(tasks);
+            } else if ("REDANT.RAImage.SAVE_TASK".equals(action)) {
+                uploadManager.stopAllTasks();
+            } else if (intent
+                    .getAction()
+                    .equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+
+                ConnectivityManager connManager = (ConnectivityManager) context
+                        .getSystemService(Context.CONNECTIVITY_SERVICE);
+
+                NetworkInfo networkInfo = connManager.getActiveNetworkInfo();
+                if (networkInfo == null) {
+                    dbgUtil.Logd(
+                            "Current Network info",
+                            "can not get Active NetworkInfo!");
+                    return;
+                }
+                NetworkInfo.State netState = networkInfo
+                        .getState();
+                if (netState != NetworkInfo.State.CONNECTED) {
+                    dbgUtil.Logd(
+                            "Current Network info",
+                            "not Connected!State="
+                                    + netState);
+                    return;
+                } else {
+                    int iconntype = -1;
+                    iconntype = networkInfo
+                            .getType();
+                    SharedPreferences pref = getApplication()
+                            .getSharedPreferences(
+                                    "UploadManager",
+                                    0);
+
+
+                    boolean
+                            wifi_only
+                            = pref.getBoolean("wifi_only",
+                            false);
+
+                    if
+                            (wifi_only
+                            == true
+                            && iconntype !=
+                            ConnectivityManager.TYPE_WIFI
+                            && iconntype !=
+                            9/* earthnet */) {
+                        uploadManager.stopAllTasks();
+//															 dbgUtil.Log(Log.DEBUG,
+//															 "Current Network info",
+//															 "not allowed!Connection type="
+//															 +
+//															 networkInfo.getTypeName());
+                        return;
+                    }
+                }
+
+
+            }
+        }
+    };
+}

+ 327 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/utils/AESUtil.java

@@ -0,0 +1,327 @@
+package com.usai.redant.rautils.utils;
+
+import android.util.Base64;
+import android.util.Log;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Security;
+import java.util.Arrays;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import static android.content.ContentValues.TAG;
+
+public class AESUtil {
+
+    // 算法名称
+    final static String KEY_ALGORITHM = "AES";
+    // 加解密算法/模式/填充方式
+    final static String algorithmStr = "AES/CBC/PKCS7Padding";
+    //
+//    private Key key;
+//    private Cipher cipher;
+//    boolean isInited = false;
+
+    static byte[] iv = new byte[16];//{ 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, 0x30, 0x36, 0x30, 0x37, 0x30, 0x38 };
+//    private static void init(byte[] keyBytes) {
+//
+//        // 如果密钥不足16位,那么就补足.  这个if 中的内容很重要
+//        int base = 16;
+//        if (keyBytes.length % base != 0) {
+//            int groups = keyBytes.length / base + (keyBytes.length % base != 0 ? 1 : 0);
+//            byte[] temp = new byte[groups * base];
+//            Arrays.fill(temp, (byte) 0);
+//            System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length);
+//            keyBytes = temp;
+//        }
+//        // 初始化
+//        Security.addProvider(new BouncyCastleProvider());
+//        // 转化成JAVA的密钥格式
+//        key = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
+//        try {
+//            // 初始化cipher
+//            cipher = Cipher.getInstance(algorithmStr, "BC");
+//        } catch (NoSuchAlgorithmException e) {
+//            // TODO Auto-generated catch block
+//            e.printStackTrace();
+//        } catch (NoSuchPaddingException e) {
+//            // TODO Auto-generated catch block
+//            e.printStackTrace();
+//        } catch (NoSuchProviderException e) {
+//            // TODO Auto-generated catch block
+//            e.printStackTrace();
+//        }
+//    }
+
+    private static String toHex(byte[] buf)
+    {
+        if (buf == null)
+            return "";
+        StringBuffer result = new StringBuffer(2 * buf.length);
+        for (int i = 0; i < buf.length; i++)
+        {
+            appendHex(result, buf[i]);
+        }
+        return result.toString();
+    }
+    private static void appendHex(StringBuffer sb, byte b)
+    {
+        sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
+    }
+    private final static String HEX = "0123456789ABCDEF";
+
+    public static String encrypt128(String keystring,String content)
+    {
+
+        try {
+
+            Key key= null;
+            key = createKey128(keystring);
+            byte[] result=encrypt(content.getBytes(),key);
+            Log.d(TAG, "encrypt: "+toHex(result));
+            return Base64.encodeToString(result,Base64.DEFAULT);
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+        }
+        return null;
+
+    }
+
+    public static String decrypt128(String keystring,String content)
+    {
+
+
+
+        try {
+
+            byte[] buffer =Base64.decode(content, Base64.DEFAULT);
+
+
+            Key key= null;
+            key = createKey128(keystring);
+
+            byte[] result = decrypt(buffer,key);
+
+            return new String(result);
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+    public static String encrypt256(String keystring,String content)
+    {
+
+        try {
+
+            Key key= null;
+            key = createKey256(keystring);
+            byte[] result=encrypt(content.getBytes(),key);
+            Log.d(TAG, "encrypt: "+toHex(result));
+            return Base64.encodeToString(result,Base64.DEFAULT);
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+        }
+        return null;
+
+    }
+
+    public static String decrypt256(String keystring,String content)
+    {
+
+
+
+        try {
+
+            byte[] buffer =Base64.decode(content, Base64.DEFAULT);
+
+
+            Key key= null;
+            key = createKey256(keystring);
+
+            byte[] result = decrypt(buffer,key);
+
+            return new String(result);
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+    /**
+     * 加密方法
+     *
+     * @param content
+     *            要加密的字符串
+     * @param key
+     *            加密密钥
+     * @return
+     */
+    private static byte[] encrypt(byte[] content,  Key key) {
+        byte[] encryptedText = null;
+//        init(keyBytes);
+
+
+        Cipher cipher;
+
+
+
+        // 初始化
+        Security.addProvider(new BouncyCastleProvider());
+        try {
+            // 初始化cipher
+            cipher = Cipher.getInstance(algorithmStr, "BC");
+
+
+            cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
+            encryptedText = cipher.doFinal(content);
+
+        } catch (NoSuchAlgorithmException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        } catch (NoSuchPaddingException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        } catch (NoSuchProviderException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        } catch (BadPaddingException e) {
+            e.printStackTrace();
+        } catch (InvalidKeyException e) {
+            e.printStackTrace();
+        } catch (IllegalBlockSizeException e) {
+            e.printStackTrace();
+        } catch (InvalidAlgorithmParameterException e) {
+            e.printStackTrace();
+        }
+
+
+//        System.out.println("IV:" + new String(iv));
+//        try {
+//
+//        } catch (Exception e) {
+//            // TODO Auto-generated catch block
+//            e.printStackTrace();
+//        }
+        return encryptedText;
+    }
+    /**
+     * 解密方法
+     *
+     * @param encryptedData
+     *            要解密的字符串
+     * @param key
+     *            解密密钥
+     * @return
+     */
+    private static byte[] decrypt(byte[] encryptedData, Key key) {
+        byte[] encryptedText = null;
+//        init(keyBytes);
+
+//        Key key;
+        Cipher cipher;
+        // 如果密钥不足16位,那么就补足.  这个if 中的内容很重要
+        int base = 16;
+//        if (keyBytes.length % base != 0) {
+//            int groups = keyBytes.length / base + (keyBytes.length % base != 0 ? 1 : 0);
+//            byte[] temp = new byte[groups * base];
+//            Arrays.fill(temp, (byte) 0);
+//            System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length);
+//            keyBytes = temp;
+//        }
+        // 初始化
+        Security.addProvider(new BouncyCastleProvider());
+        // 转化成JAVA的密钥格式
+//        key = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
+        try {
+            // 初始化cipher
+            cipher = Cipher.getInstance(algorithmStr, "BC");
+            cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
+            encryptedText = cipher.doFinal(encryptedData);
+        } catch (NoSuchAlgorithmException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        } catch (NoSuchPaddingException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        } catch (NoSuchProviderException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        } catch (BadPaddingException e) {
+            e.printStackTrace();
+        } catch (InvalidKeyException e) {
+            e.printStackTrace();
+        } catch (IllegalBlockSizeException e) {
+            e.printStackTrace();
+        } catch (InvalidAlgorithmParameterException e) {
+            e.printStackTrace();
+        }
+
+//        System.out.println("IV:" + new String(iv));
+//        try {
+//
+//        } catch (Exception e) {
+//            // TODO Auto-generated catch block
+//            e.printStackTrace();
+//        }
+
+        return encryptedText;
+    }
+
+
+    private static Key createKey128(String keystring) throws UnsupportedEncodingException, NoSuchAlgorithmException {
+
+
+        byte[] keyBytes=keystring.getBytes();
+        Key key;
+        // 如果密钥不足16位,那么就补足.  这个if 中的内容很重要
+        int base = 16;
+        if (keyBytes.length % base != 0) {
+            int groups = keyBytes.length / base + (keyBytes.length % base != 0 ? 1 : 0);
+            byte[] temp = new byte[groups * base];
+            Arrays.fill(temp, (byte) 0);
+            System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length);
+            keyBytes = temp;
+        }
+
+        // 转化成JAVA的密钥格式
+        key = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
+        return key;
+    }
+
+
+    private static Key createKey256(String key) throws UnsupportedEncodingException, NoSuchAlgorithmException {
+        MessageDigest digest = MessageDigest.getInstance("SHA-256");
+        byte[] keyBytes = digest.digest(key.getBytes("UTF-8"));
+
+        int base = 16;
+        if (keyBytes.length % base != 0) {
+            Log.d(TAG, "createKey256: "+"key is too short, expanded");
+            int groups = keyBytes.length / base + (keyBytes.length % base != 0 ? 1 : 0);
+            byte[] temp = new byte[groups * base];
+            Arrays.fill(temp, (byte) 0);
+            System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length);
+            keyBytes = temp;
+        }
+        return new SecretKeySpec(keyBytes, KEY_ALGORITHM);
+    }
+}

+ 114 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/utils/CrashHandler.java

@@ -0,0 +1,114 @@
+package com.usai.redant.rautils.utils;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+
+import java.io.IOException;
+import java.io.LineNumberReader;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+
+
+public class CrashHandler implements Thread.UncaughtExceptionHandler{
+
+    public interface InfoPreserver {
+        public void saveUserInformation();
+        public void handleCrashInfo(String deviceInfo, String exception);
+    }
+    // 当前应用上下文
+    private Context ctx;
+
+    // 系统默认UncaughtException处理类
+    private Thread.UncaughtExceptionHandler mDefaultHandler;
+
+    private static CrashHandler sharedInstance;
+    public InfoPreserver preserver;
+
+    private CrashHandler() {
+
+    }
+
+    public static synchronized CrashHandler getSharedInstance() {
+        if (sharedInstance == null) {
+            sharedInstance = new CrashHandler();
+        }
+        return sharedInstance;
+    }
+
+    public void init(Context ctx) {
+        this.ctx = ctx;
+        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
+        Thread.setDefaultUncaughtExceptionHandler(this);
+    }
+
+
+    @Override
+    public void uncaughtException(Thread t, Throwable e) {
+        // 如果不能处理异常,并且系统处理器不为空,则交给系统处理
+        if (!handleException(e) && mDefaultHandler != null) {
+            mDefaultHandler.uncaughtException(t,e);
+        } else {
+            android.os.Process.killProcess(android.os.Process.myPid());
+            System.exit(1);
+        }
+    }
+
+    private boolean handleException(Throwable e) {
+
+        if (e == null) {
+            return false;
+        }
+
+        if (preserver != null) {
+            preserver.saveUserInformation();
+            preserver.handleCrashInfo(getDeviceInfo(),getThrowableStackString(e));
+        }
+
+
+        return true;
+    }
+
+    private String getDeviceInfo() {
+        try {
+            PackageManager pm = ctx.getPackageManager();
+            PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
+            String appVer = "App Version: " + pi.versionName + "_" + pi.versionCode + "\n";
+            String osVer = "OS Version: " + Build.VERSION.RELEASE + "_" + Build.VERSION.SDK_INT + "\n";
+            String manufacture = "Manufacturer: " + Build.MANUFACTURER + "\n";
+            String model = "Model: " + Build.MODEL;
+            return appVer + osVer + manufacture + model;
+        } catch (PackageManager.NameNotFoundException e) {
+            e.printStackTrace();
+            return null;
+        }
+
+    }
+
+    private String getThrowableStackString(Throwable throwable) {
+        if (throwable == null) {
+            return null;
+        }
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        throwable.printStackTrace(pw);
+        pw.flush();
+        LineNumberReader reader = new LineNumberReader(new StringReader(sw.toString()));
+        StringBuffer lines = new StringBuffer();
+        try {
+            String line = reader.readLine();
+            while (line != null) {
+                lines.append(line + "\n");
+                line = reader.readLine();
+            }
+        } catch (IOException ex) {
+            lines.append(ex.toString() + "\n");
+        }
+
+        return lines.toString();
+    }
+
+
+}

+ 66 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/utils/DBHelper.java

@@ -0,0 +1,66 @@
+package com.usai.redant.rautils.utils;
+
+import android.content.Context;
+import android.database.DatabaseErrorHandler;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+import java.util.ArrayList;
+
+public class DBHelper extends SQLiteOpenHelper {
+
+    public interface DBHelperListener {
+
+        ArrayList<String> prepareInitialForDBCreate();
+        ArrayList<String> prepareUpgrade(int oldVersion, int newVersion);
+
+    }
+
+    private DBHelperListener mListener;
+
+    public DBHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
+        super(context, name, factory, version);
+    }
+
+    public DBHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version, DatabaseErrorHandler errorHandler) {
+        super(context, name, factory, version, errorHandler);
+    }
+
+    public void setListener(DBHelperListener listener) {
+        mListener = listener;
+    }
+
+    @Override
+    public void onCreate(SQLiteDatabase db) {
+
+        if (mListener != null) {
+            ArrayList<String> tableArr = mListener.prepareInitialForDBCreate();
+            for (int i = 0; i < tableArr.size(); i++) {
+                String sql = tableArr.get(i);
+                db.execSQL(sql);
+            }
+        }
+
+    }
+
+    @Override
+    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+
+        if (mListener != null) {
+            ArrayList<String> tableArr = mListener.prepareUpgrade(oldVersion, newVersion);
+            for (int i = 0; i < tableArr.size(); i++) {
+                String sql = tableArr.get(i);
+                db.execSQL(sql);
+            }
+        }
+
+    }
+
+    public SQLiteDatabase openDataBase(boolean writable) {
+        if (writable) {
+            return getWritableDatabase();
+        } else  {
+            return getReadableDatabase();
+        }
+    }
+}

+ 110 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/utils/DBUtil.java

@@ -0,0 +1,110 @@
+package com.usai.redant.rautils.utils;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
+import java.util.ArrayList;
+
+public class DBUtil implements DBHelper.DBHelperListener{
+
+    private static DBUtil dbUtil;
+    private SQLiteDatabase db;
+
+    public interface DBInitialBlock {
+        ArrayList<String> prepareDatabaseInitial();
+    }
+
+    public interface DBUpgradeBlock {
+        ArrayList<String> prepareDatabaseUpgrade();
+    }
+
+    private DBInitialBlock mInitialBlock;
+    @Override
+    public ArrayList<String> prepareInitialForDBCreate() {
+        if (mInitialBlock != null) {
+            return mInitialBlock.prepareDatabaseInitial();
+        }
+        return null;
+    }
+
+    private DBUpgradeBlock mUpgradeBlock;
+    @Override
+    public ArrayList<String> prepareUpgrade(int oldVersion, int newVersion) {
+        if (mUpgradeBlock != null) {
+            return mUpgradeBlock.prepareDatabaseUpgrade();
+        }
+        return null;
+    }
+
+
+    public static DBUtil sharedDBUtil() {
+        synchronized (DBUtil.class) {
+            if (dbUtil == null) {
+                dbUtil = new DBUtil();
+            }
+        }
+        return dbUtil;
+    }
+
+    public void openDatabase(Context ctx, String path, int version, boolean wirtable,DBInitialBlock initialBlk, DBUpgradeBlock upgradeBlk) {
+
+        DBHelper openHandler = new DBHelper(ctx,path,null,version);
+
+        mInitialBlock = initialBlk;
+        mUpgradeBlock = upgradeBlk;
+
+        openHandler.setListener(this);
+
+        db = openHandler.openDataBase(wirtable);
+    }
+
+    public void insert(String table,ContentValues values) {
+        db.insert(table,null,values);
+    }
+
+    public void execute(ArrayList<String> sqlArr) {
+        try {
+            db.beginTransaction(); // 开启事务
+            for (int i = 0; i < sqlArr.size(); i++) {
+                String sql = sqlArr.get(i);
+                db.execSQL(sql);
+            }
+            db.setTransactionSuccessful(); // 设置事务完成
+        } finally {
+            db.endTransaction(); // 结束事务
+        }
+    }
+
+    public void execSQL(String sql) {
+
+        db.execSQL(sql);
+    }
+
+    public Cursor query(String sql,String[] args) {
+        Cursor cur = db.rawQuery(sql,args);
+        cur.moveToFirst();
+        return cur;
+    }
+
+    public void closeDatabase() {
+        if (db == null) {
+            return;
+        }
+        if (db.isOpen() == false)
+            return;
+        db.close();
+    }
+
+    public void closeCursor(Cursor cursor) {
+
+        if (cursor == null)
+            return;
+        if (cursor.isClosed())
+            return;
+        cursor.close();
+    }
+
+
+}

+ 277 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/utils/FileManager.java

@@ -0,0 +1,277 @@
+package com.usai.redant.rautils.utils;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.support.v4.content.FileProvider;
+import android.util.Log;
+import android.webkit.MimeTypeMap;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.security.MessageDigest;
+
+public class FileManager {
+
+    private static final char HEX_DIGITS[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+    public static String toHexString(byte[] b) {
+        StringBuilder sb = new StringBuilder(b.length * 2);
+        for (int i = 0; i < b.length; i++) {
+            sb.append(HEX_DIGITS[(b[i] & 0xf0) >>> 4]);
+            sb.append(HEX_DIGITS[b[i] & 0x0f]);
+        }
+        return sb.toString();
+    }
+
+    public static String md5sum(String filename) {
+        InputStream fis;
+        byte[] buffer = new byte[1024];
+        int numRead = 0;
+        MessageDigest md5;
+        try{
+            fis = new FileInputStream(filename);
+            md5 = MessageDigest.getInstance("MD5");
+            while((numRead=fis.read(buffer)) > 0) {
+                md5.update(buffer,0,numRead);
+            }
+            fis.close();
+            return toHexString(md5.digest());
+        } catch (Exception e) {
+            System.out.println("error");
+            return null;
+        }
+    }
+
+
+    public static String SDCardRoot() {
+        return Environment.getExternalStorageDirectory()+ File.separator;
+    }
+
+    public static File createSDDir(String dir)throws IOException {
+        File dirFile=new File(SDCardRoot()+dir);
+        dirFile.mkdir();//mkdir()只能创建一层文件目录,mkdirs()可以创建多层文件目录
+        return dirFile;
+    }
+
+    //在SD卡上创建文件
+    public static File createFileInSDCard(String fileName,String dir) throws IOException {
+        File file=new File(SDCardRoot()+dir+File.separator+fileName);
+        file.createNewFile();
+        return file;
+    }
+
+    public static boolean isFileExist(String fileName,String dir){
+        File file=new File(SDCardRoot()+dir+File.separator+fileName);
+        return file.exists();
+    }
+
+    public static File createFileInDir(String fileName,String dir) throws IOException {
+        File file=new File(dir+File.separator+fileName);
+        file.createNewFile();
+        return file;
+    }
+
+    //将一个InoutStream里面的数据写入到SD卡中
+    public static File write2SDFromInput(String fileName,String dir,InputStream input){
+        File file=null;
+        OutputStream output=null;
+        try {
+//            //创建目录
+//            createSDDir(dir);
+            //创建文件
+            file=createFileInDir(fileName,dir);
+            //写数据流
+            output=new FileOutputStream(file);
+            byte buffer[]=new byte[4*1024];//每次存4K
+            int temp;
+            //写入数据
+            while((temp=input.read(buffer))!=-1){
+                output.write(buffer,0,temp);
+            }
+            output.flush();
+        } catch (Exception e) {
+            System.out.println("写数据异常:"+e);
+        }
+        finally{
+            try {
+                output.close();
+            } catch (Exception e2) {
+                System.out.println(e2);
+            }
+        }
+        return file;
+    }
+
+    public static String getMimeType(String filePath) {
+        String ext = MimeTypeMap.getFileExtensionFromUrl(filePath);
+        String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext);
+        if (mime == null || mime.isEmpty()) {
+            mime = "text/plain";
+        }
+        return mime;
+    }
+
+    /**
+     * 复制单个文件
+     * @param oldPath String 原文件路径 如:c:/fqf.txt
+     * @param newPath String 复制后路径 如:f:/fqf.txt
+     * @return boolean
+     */
+    public static boolean copyFile(String oldPath, String newPath) {
+        try {
+            int bytesum = 0;
+            int byteread = 0;
+            File oldfile = new File(oldPath);
+            if (oldfile.exists()) { //文件存在时
+                InputStream inStream = new FileInputStream(oldPath); //读入原文件
+                FileOutputStream fs = new FileOutputStream(newPath);
+                byte[] buffer = new byte[4*1024];
+                int length;
+                while ( (byteread = inStream.read(buffer)) != -1) {
+                    bytesum += byteread; //字节数 文件大小
+                    System.out.println(bytesum);
+                    fs.write(buffer, 0, byteread);
+                }
+                inStream.close();
+            }
+        }
+        catch (Exception e) {
+            System.out.println("复制单个文件操作出错");
+            e.printStackTrace();
+            return false;
+
+        }
+        return true;
+
+    }
+
+    public static void openFile(Context context, File file) {
+
+        if (context == null || file == null || !file.exists()) {
+            return;
+        }
+
+        Uri uri = null;
+        String type = getMimeType(file.getPath());
+
+        // type "application/pdf"
+        Intent intent = new Intent(Intent.ACTION_VIEW);
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+
+            // "com.usai.apex.fileprovider"即是在Manifest文件中配置的authorities
+            uri = FileProvider.getUriForFile(context, "com.usai.apex.fileprovider", file);
+            // 给目标应用一个临时授权
+            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        } else {
+            uri = Uri.fromFile(file);
+        }
+
+        intent.setDataAndType(uri, type);
+
+        if (context.getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
+            // someone knows how to handle this mime type with this scheme, don't download.
+            try {
+                context.startActivity(intent);
+                return;
+            } catch (Exception ex) {
+                Log.d("Open File", "activity not found for " + type + " over " + uri, ex);
+            }
+
+        } else {
+            Log.d("Open File", "openFileAtPath: " + "No App " + uri);
+            try {
+                shareFile(context,uri,type);
+            } catch (Exception e) {
+                Log.e("Show Dialog Error", "openFileAtPath: ", e);
+            }
+        }
+
+    }
+
+    public static void shareFile(Context context,Uri uri, String type) {
+
+        Intent shareIntent = new Intent();
+        shareIntent.setAction(Intent.ACTION_SEND);
+        shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
+        shareIntent.setType(type);
+        context.startActivity(Intent.createChooser(shareIntent, "Share"));
+    }
+
+    public static void writeJson(String json, String filePath) {
+
+        Writer writer = null;
+        try {
+
+            OutputStream out = new FileOutputStream(new File(filePath));
+            writer = new OutputStreamWriter(out);
+            writer.write(json);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (writer != null) {
+                try {
+                    writer.close();
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+    }
+
+    public static String readJson(String filePath) {
+        BufferedReader reader = null;
+        StringBuilder jsonString = null;
+        try {
+            InputStream in = new FileInputStream(new File(filePath));
+            reader = new BufferedReader(new InputStreamReader(in));
+            jsonString = new StringBuilder();
+            String line = null;
+            while ((line = reader.readLine()) != null) {
+                jsonString.append(line);
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        if (jsonString != null) {
+            return jsonString.toString();
+        } else {
+            return null;
+        }
+    }
+
+    public static String lastPathComponent(String path){
+
+        int start=path.lastIndexOf("/");
+        int end=path.lastIndexOf(".");
+        if(start!=-1 && end!=-1){
+            return path.substring(start+1,end);
+        }else{
+            return null;
+        }
+
+    }
+
+}

+ 275 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/utils/ImageUtil.java

@@ -0,0 +1,275 @@
+package com.usai.redant.rautils.utils;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Matrix;
+import android.media.ExifInterface;
+import android.media.MediaScannerConnection;
+import android.net.Uri;
+import android.os.Build;
+import android.support.v4.content.FileProvider;
+import android.util.Log;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URI;
+
+public class ImageUtil {
+
+    public static File routeBitmap(Context context, File sourceFile, String file_name)
+    {
+        /**
+         * 获取图片的旋转角度,有些系统把拍照的图片旋转了,有的没有旋转
+         */
+        int degree = readPictureDegree(sourceFile.getAbsolutePath());
+
+        BitmapFactory.Options opts=new BitmapFactory.Options();//获取缩略图显示到屏幕上
+        opts.inSampleSize=1;
+        Bitmap cbitmap=BitmapFactory.decodeFile(sourceFile.getAbsolutePath(),opts);
+
+        /**
+         * 把图片旋转为正的方向
+         */
+        Bitmap newbitmap = rotaingImageView(degree, cbitmap);
+
+        //sourceFile.get
+        String routedPath = "";
+        if(file_name==null) {
+            String path = sourceFile.getPath();
+
+            routedPath=path.replace(".jpg", "_routed.jpg");
+        }
+        else
+        {
+            String path = sourceFile.getParent();
+            routedPath = path+"/"+file_name;
+        }
+        File routedFile = new File(routedPath);
+        try {
+
+            FileOutputStream outputStream = new FileOutputStream(routedFile);
+            newbitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
+
+            outputStream.flush();
+            outputStream.close();
+
+            String filepath = sourceFile.getAbsolutePath();
+            sourceFile.delete();
+            updateGallery(context,filepath);
+
+            return routedFile;
+
+        } catch (IOException e) {
+            e.printStackTrace();
+            return null;
+        }
+
+    }
+    /**
+     * 读取图片属性:旋转的角度
+     * @param path 图片绝对路径
+     * @return degree旋转的角度
+     */
+    public static int readPictureDegree(String path) {
+        int degree  = 0;
+        try {
+            ExifInterface exifInterface = new ExifInterface(path);
+            int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
+            switch (orientation) {
+                case ExifInterface.ORIENTATION_ROTATE_90:
+                    degree = 90;
+                    break;
+                case ExifInterface.ORIENTATION_ROTATE_180:
+                    degree = 180;
+                    break;
+                case ExifInterface.ORIENTATION_ROTATE_270:
+                    degree = 270;
+                    break;
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return degree;
+    }
+    /*
+     * 旋转图片
+     * @param angle
+     * @param bitmap
+     * @return Bitmap
+     */
+    public static Bitmap rotaingImageView(int angle , Bitmap bitmap) {
+        //旋转图片 动作
+        Matrix matrix = new Matrix();;
+        matrix.postRotate(angle);
+        System.out.println("angle2=" + angle);
+        // 创建新的图片
+        Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
+                bitmap.getWidth(), bitmap.getHeight(), matrix, true);
+        return resizedBitmap;
+    }
+
+    /**
+     * @param context ApplicationContext
+     * */
+    public static void updateGallery(Context context, String filename)//filename是我们的文件全名,包括后缀哦
+    {
+        if (context == null) {
+            return;
+        }
+
+//        Context context = getApplication().getApplicationContext();
+        MediaScannerConnection.scanFile(context, new String[] { filename }, null, new MediaScannerConnection.OnScanCompletedListener() {
+                    public void onScanCompleted(String path, Uri uri) {
+                        Log.i("ExternalStorage", "Scanned " + path + ":");
+                        Log.i("ExternalStorage", "-> uri=" + uri);
+                    }
+                });
+    }
+
+    public static Bitmap loadImageFromURL(Context context, URI uri) {
+        if (uri == null) {
+            return null;
+        }
+
+        String scheme = uri.getScheme();
+        if (scheme == null) {
+            File file = new File(uri);
+            return loadImageFromFile(context,file);
+        }
+
+        String md5 = RAUtil.stringToMD5(uri.toString());
+
+        Bitmap bitmap = null;
+        if (context == null) {
+            bitmap = Network.getImageFromLink(uri.toString());
+            return bitmap;
+        } else {
+
+            String appDir = appCacheDir(context);
+
+            String imgDir = appDir + File.separator + "ra_image";
+
+//            File dir = new File(imgDir);
+//            if (!dir.exists()) {
+//                dir.mkdirs();
+//            }
+
+
+            File imgFile = new File(imgDir,md5);
+            File parentFile = new File(imgDir);
+
+            if (!parentFile.exists()) {
+                parentFile.mkdirs();
+            }
+
+            if (imgFile.exists()) {
+
+                bitmap = loadImageFromFile(context,imgFile);
+            }
+
+            if (bitmap == null) {
+
+                bitmap = Network.getImageFromLink(uri.toString());
+                if (bitmap != null) {
+                    try {
+                        if (!imgFile.exists()) {
+                            imgFile.createNewFile();
+                        }
+                        savePhotoToFile(bitmap,imgFile);
+
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                }
+
+            }
+
+            return bitmap;
+        }
+    }
+
+    public static String appCacheDir(Context context) {
+        String root = FileManager.SDCardRoot();
+        String appName = RAUtil.getApplicationName(context);
+        return root + appName;
+    }
+
+    public static Bitmap loadImageFromFile(Context context, File file) {
+        if (file == null) {
+            return null;
+        }
+        if (!file.exists() || file.isDirectory()) {
+            return null;
+        }
+        final BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inJustDecodeBounds = true;
+        BitmapFactory.decodeFile(file.getAbsolutePath(), options);
+
+        // Decode bitmap with inSampleSize set
+        options.inJustDecodeBounds = false;
+        //避免出现内存溢出的情况,进行相应的属性设置。
+        options.inPreferredConfig = Bitmap.Config.RGB_565;
+        options.inDither = true;
+
+//        //置入一个不设防的VmPolicy,否则7.0文件读取会失败
+//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+//            StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
+//            StrictMode.setVmPolicy(builder.build());
+//        }
+
+        try {
+
+            Uri uri = Uri.fromFile(file);
+
+            // 7.0 以后文件读取
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+                uri = FileProvider.getUriForFile(context, "com.usai.apex.driver.fileprovider", file);
+            }
+
+            return BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uri));
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+
+//        return BitmapFactory.decodeFile(file.getAbsolutePath(), options);
+    }
+
+    public static void savePhotoToFile(Bitmap bitmap, File photoFile) {
+        if (photoFile == null || bitmap == null) {
+            return;
+        }
+        try {
+
+            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(photoFile));
+            bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos); // 使用JPG保存,若背景透明的PNG原图,读取出来后背景是黑色的
+            bos.flush();
+            bos.close();
+
+        } catch (IOException e) {
+            e.printStackTrace();
+
+        }
+    }
+
+    public static void saveJPGToFile(Bitmap bitmap, File photoFile) {
+        if (photoFile == null || bitmap == null) {
+            return;
+        }
+        try {
+
+            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(photoFile));
+            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
+            bos.flush();
+            bos.close();
+
+        } catch (IOException e) {
+            e.printStackTrace();
+
+        }
+    }
+
+}

+ 692 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/utils/Network.java

@@ -0,0 +1,692 @@
+package com.usai.redant.rautils.utils;
+
+import android.app.Application;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.os.Environment;
+import android.util.Log;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.ProtocolException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+public class Network {
+
+    private static final int	REQUEST_TIMEOUT					= 15 * 1000;			// request
+
+    private static final int	SO_TIMEOUT						= 15 * 1000;			// so
+
+
+    private static final int	UPLOAD_TIMEOUT						= 30 * 1000;
+    public static int			AP_USER_AUTH					= 1;
+    public static int			AP_USER_NOT_AUTH				= 2;
+    public static int			AP_USER_NOT_EXIST				= 3;
+    public static int			AP_UPLOAD_SUCCESS				= 4;
+    public static int			AP_UPLOAD_FAIL					= 5;
+
+
+    public static final int		RESULT_FALSE					= 0;
+    public static final int		RESULT_TRUE						= 2;
+    public static final int		RESULT_NET_ERROR				= -3;
+    public static final int		RESULT_NET_NOTAVAILABLE			= -4;
+    public static final int		RESULT_ERROR					= -5;
+    public static final int		RESULT_LOCALFILE_ERROR			= -7;
+    public static final int		RESULT_USERAUTH_ERROR			= -9;
+    public static final int		RESULT_UPDATE_USERAUTH_ERROR	= -11;
+    public static final int		RESULT_SESSION_EXPIRED			= -13;
+    public static final int		RESULT_VER_LOW					= -15;
+
+    public static String getJson(String url, Bundle parms)
+    {
+        String TAG = "net_dbg@GetJson";
+        Log.d(TAG, "entry");
+        String ret=null;
+
+        HttpURLConnection connection = null;
+
+        try {
+
+
+            URL _url;
+            _url = new URL(url);
+            connection = (HttpURLConnection) _url.openConnection();
+            connection.setReadTimeout(SO_TIMEOUT);
+            connection.setConnectTimeout(SO_TIMEOUT);
+            // 设置请求方式
+            connection.setRequestMethod("POST");
+            // 设置编码格式
+            connection.setRequestProperty("Charset", "UTF-8");
+            // 传递自定义参数
+//			connection.setRequestProperty("MyProperty", "this is me!");
+
+            Set<String> keys = parms.keySet();
+            Log.d(TAG, "================parms============");
+            for (String key : keys)
+            {
+                if (key.contains("_file"))
+                {
+
+                }
+                else
+                {
+                    Log.d(TAG, "key=" + key + "    val=" + parms.get(key).toString());
+                }
+
+            }
+            Log.d(TAG, "================parms============");
+
+            Log.d(TAG,"URL: "+url);
+            // 设置容许输出
+            connection.setDoInput(true);
+            connection.setDoOutput(true);
+
+
+            OutputStream os = connection.getOutputStream();
+            BufferedWriter writer = new BufferedWriter(
+                    new OutputStreamWriter(os, "UTF-8"));
+            writer.write(createPostParameters(parms));
+
+            writer.flush();
+            writer.close();
+            os.close();
+
+            // 获取返回数据
+            if(connection.getResponseCode() == 200){
+                InputStream is = connection.getInputStream();
+
+                BufferedReader br = new BufferedReader(
+                        new InputStreamReader(is, "utf-8"), 8);
+                StringBuilder sb = new StringBuilder();
+                String line = null;
+                while ((line = br.readLine()) != null)
+                {
+                    sb.append(line + "\n");
+                }
+
+                Log.d(TAG, "Response: content begin");
+                Log.d(TAG, sb.toString());
+                Log.d(TAG, "Response: content end");
+
+                if (sb.length() <= 0)
+                {
+
+                    ret= null;
+                }
+                ret= sb.toString();
+
+            }
+
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+
+            Log.e(TAG, e.toString());
+
+        } catch (ProtocolException e) {
+            e.printStackTrace();
+
+            Log.e(TAG, e.toString());
+
+        } catch (MalformedURLException e) {
+            e.printStackTrace();
+
+            Log.e(TAG, e.toString());
+
+        } catch (IOException e) {
+            e.printStackTrace();
+
+            Log.e(TAG, e.toString());
+
+        } finally {
+
+            if(connection!=null){
+                connection.disconnect();
+            }
+        }
+        return ret;
+    }
+
+    private static String createPostParameters(Bundle parms) throws UnsupportedEncodingException {
+        StringBuilder result = new StringBuilder();
+        boolean first = true;
+        for(String key : parms.keySet()){
+            if (first)
+                first = false;
+            else
+                result.append("&");
+
+            result.append(URLEncoder.encode(key, "UTF-8"));
+            result.append("=");
+            result.append(URLEncoder.encode(parms.get(key).toString(), "UTF-8"));
+        }
+
+        return result.toString();
+    }
+
+    public static boolean isNetworkAvailable(Application application)// Context context)
+    {
+        String TAG = "net_dbg@CheckNetwork";
+        ConnectivityManager connManager = (ConnectivityManager) application.getSystemService(Context.CONNECTIVITY_SERVICE);
+
+        NetworkInfo networkInfo = connManager.getActiveNetworkInfo();
+        if (networkInfo == null)
+        {
+            Log.d(TAG, "can not get Active NetworkInfo!");
+            return false;
+        }
+        NetworkInfo.State netState = networkInfo.getState();
+        if (netState != NetworkInfo.State.CONNECTED)
+        {
+            Log.d(TAG, "not Connected!State=" + netState);
+            return false;
+        }
+
+        boolean bavailable = networkInfo.isAvailable();
+        String strtype = networkInfo.getTypeName();
+
+        Log.d(TAG, " type = " + strtype + " abailable = " + bavailable + " state " + netState);
+
+        return bavailable;
+    }
+
+    public interface FileUploadListener {
+
+        double percent_step();
+
+        boolean interupt();
+
+        void onProgress(long pro, double percentage);
+
+        void onFinish(int code, String res);
+    }
+
+    public static JSONObject uploadFileJSON(String path ,String host,Bundle params,FileUploadListener listener) {
+
+        String jstr = uploadFile(path,host, params,listener);
+
+        String TAG = "net_dbg@UploadFile";
+        if (jstr == null || jstr.length() <= 0)
+        {
+            Log.d(TAG, "json is null");
+            try {
+                return new JSONObject("{result:" + Network.RESULT_NET_ERROR+ "}");
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+
+        }
+        Log.d(TAG, "json string:"+jstr);
+        JSONObject jsobj;
+        try
+        {
+            jsobj = new JSONObject(jstr);
+            if (jsobj.length() > 0)
+            {
+                return jsobj;
+            }
+            try {
+                return new JSONObject("{result:" + Network.RESULT_NET_ERROR+ "}");
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+
+        }
+        catch (JSONException e)
+        {
+            // TODO Auto-generated catch block
+            Log.d(TAG, "json format wrong:"+jstr);
+            e.printStackTrace();
+
+            return null;
+        }
+        return null;
+    }
+
+    public static String uploadFile(String path ,String host,Bundle params,FileUploadListener listener){
+
+
+        File file = new File(path);
+        if(!file.exists())
+        {
+            String result="{\"result\":0,\"msg\":\"file does not exist\"}";
+            if (listener != null) {
+                listener.onFinish(200,result);
+            }
+
+            return result;
+        }
+        String TAG = "net_dbg@UploadFile";
+        String CHARSET = "utf-8"; //设置编码
+        String PREFIX = "--";
+        String LINE_END = "\r\n";
+        String BOUNDARY = UUID.randomUUID().toString(); //边界标识 随机生成 String PREFIX = "--" , LINE_END = "\r\n";
+        String CONTENT_TYPE = "multipart/form-data"; //内容类型
+
+        HttpURLConnection conn = null;
+        try {
+            URL url = new URL(host);
+            conn = (HttpURLConnection) url.openConnection();
+            conn.setReadTimeout(UPLOAD_TIMEOUT);
+            conn.setConnectTimeout(UPLOAD_TIMEOUT);
+            conn.setRequestMethod("POST"); //请求方式
+            conn.setRequestProperty("Charset", "utf-8");//设置编码
+            conn.setRequestProperty("connection", "keep-alive");
+            conn.setRequestProperty("Content-Type", CONTENT_TYPE + ";boundary=" + BOUNDARY);
+            conn.setDoInput(true); //允许输入流
+            conn.setDoOutput(true); //允许输出流
+            conn.setUseCaches(false); //不允许使用缓存
+            if(file!=null) {
+                /** * 当文件不为空,把文件包装并且上传 */
+                OutputStream outputSteam=conn.getOutputStream();
+                DataOutputStream dos = new DataOutputStream(outputSteam);
+                StringBuffer sb = new StringBuffer();
+                sb.append(LINE_END);
+                if(params!=null){//根据格式,开始拼接文本参数
+
+
+                    Set<String> keys = params.keySet();
+                    Log.d(TAG, "================parms============");
+                    for (String key : keys)
+                    {
+
+                        sb.append(PREFIX).append(BOUNDARY).append(LINE_END);//分界符
+                        sb.append("Content-Disposition: form-data; name=\"" + key + "\"" + LINE_END);
+                        sb.append("Content-Type: text/plain; charset=" + CHARSET + LINE_END);
+                        sb.append("Content-Transfer-Encoding: 8bit" + LINE_END);
+                        sb.append(LINE_END);
+                        sb.append(params.get(key).toString());
+                        sb.append(LINE_END);//换行!
+
+//					connection.setRequestProperty(key, parms.get(key).toString());
+                        Log.d(TAG, "key=" + key + "    val="
+                                + params.get(key).toString());
+
+//					reqEntity.addPart(key, new StringBody());
+
+                    }
+
+                    Log.d(TAG, "================parms============");
+
+                }
+                sb.append(PREFIX);//开始拼接文件参数
+                sb.append(BOUNDARY); sb.append(LINE_END);
+                /**
+                 * 这里重点注意:
+                 * name里面的值为服务器端需要key 只有这个key 才可以得到对应的文件
+                 * filename是文件的名字,包含后缀名的 比如:abc.png
+                 */
+                sb.append("Content-Disposition: form-data; name=\"upfile\"; filename=\""+file.getName()+"\""+LINE_END);
+                sb.append("Content-Type: application/octet-stream; charset="+CHARSET+LINE_END);
+                sb.append(LINE_END);
+                //写入文件数据
+                dos.write(sb.toString().getBytes());
+                InputStream is = new FileInputStream(file);
+                byte[] bytes = new byte[1024];
+                long totalbytes = file.length();
+                long curbytes = 0;
+                Log.i("cky","total="+totalbytes);
+                int len = 0;
+                double oldprogress=0.0;
+                while((len=is.read(bytes))!=-1){
+
+                    // 上传进度/ 上传过程中取消
+                    if (listener != null) {
+
+                        if(listener.interupt())
+                        {
+                            is.close();
+                            if(conn!=null)
+                                conn.disconnect();
+                            String result="{\"result\":"+0+",\"msg\":\"user cancel\"}";
+                            listener.onFinish(200,result);
+
+                            return result;
+                        }
+
+                    }
+
+
+                    curbytes += len;
+                    dos.write(bytes, 0, len);
+                    double newprogress =1.0d*curbytes/totalbytes;
+
+
+                    if (listener != null) {
+                        if(newprogress-oldprogress>=listener.percent_step()/100.0||newprogress>=1.0)
+                        {
+                            listener.onProgress(curbytes,newprogress);
+                            oldprogress = newprogress;
+                        }
+                        else
+                        {
+//						Log.d(TAG, "UploadFile: skiped "+newprogress);
+                        }
+                    }
+
+                }
+                is.close();
+                dos.write(LINE_END.getBytes());//一定还有换行
+                byte[] end_data = (PREFIX+BOUNDARY+PREFIX+LINE_END).getBytes();
+                dos.write(end_data);
+                dos.flush();
+                /**
+                 * 获取响应码 200=成功
+                 * 当响应成功,获取响应的流
+                 */
+                int code = conn.getResponseCode();
+                sb.setLength(0);
+                BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+                String line;
+                while((line=br.readLine())!=null){
+
+                    // 获取返回
+                    if (listener != null) {
+
+                        if(listener.interupt())
+                        {
+
+                            if(conn!=null)
+                                conn.disconnect();
+//						br.close();
+                            String result="{\"result\":"+0+",\"msg\":\"user cancel\"}";
+                            listener.onFinish(200,result);
+                            return result;
+                        }
+                    }
+
+                    sb.append(line);
+                }
+                if(conn!=null)
+                    conn.disconnect();
+
+                if (listener != null) {
+                    listener.onFinish(code,sb.toString()/*,conn.getHeaderFields()*/);
+                }
+                return sb.toString();
+            }
+        } catch (MalformedURLException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        finally {
+            if(conn!=null){
+                conn.disconnect();
+            }
+        }
+
+        Log.e(TAG, "UploadFile: !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" );
+        return null;
+    }
+
+    public static Bitmap getImageFromLink(String link) {
+        HttpURLConnection connection = null;
+        Bitmap img = null;
+        try {
+
+            URL url = new URL(link);
+            connection = (HttpURLConnection)url.openConnection();
+            connection.setRequestMethod("GET");
+            img = BitmapFactory.decodeStream(connection.getInputStream());
+
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            if (connection != null) {
+                connection.disconnect();
+            }
+        }
+        return img;
+    }
+
+    //读取响应头
+    public static JSONObject getResponseHeader(HttpURLConnection conn) {
+        Map<String, List<String>> responseHeaderMap = conn.getHeaderFields();
+        int size = responseHeaderMap.size();
+        try {
+            JSONObject responseHeader = new JSONObject();
+            for(int i = 0; i < size; i++){
+                String responseHeaderKey = conn.getHeaderFieldKey(i);
+                String responseHeaderValue = conn.getHeaderField(i);
+                if (responseHeaderKey != null && !responseHeaderKey.isEmpty() && responseHeaderValue != null) {
+                    responseHeader.put(responseHeaderKey,responseHeaderValue);
+                }
+            }
+            return responseHeader;
+        } catch (JSONException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public static String getResponsesuggestedFilename(JSONObject response) {
+
+        if (response == null) {
+            return null;
+        }
+
+        try {
+            String key = "Content-Disposition";
+            String content_disposition = response.getString(key);
+            if (content_disposition != null) {
+                String[] components = content_disposition.split(";");
+                if (components[1] != null) {
+                    String fileName = components[1].split("=")[1];
+                    fileName = fileName.replace("\"","");
+                    return fileName;
+                }
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+
+        return null;
+    }
+
+    public static File downloadFile(Bundle params, String download_url,String dir) {
+
+        if (params == null) {
+            params = new Bundle();
+        }
+
+        HttpURLConnection connection = null;
+
+        File returnFile = null;
+        try {
+
+            String fileName = UUID.randomUUID().toString();
+            if (dir == null) {
+                dir = Environment.getExternalStorageDirectory() + File.separator + "download";
+            }
+
+            if (FileManager.isFileExist(fileName,dir)) {
+                returnFile = new File(dir + File.separator + fileName);
+            } else {
+                // 创建一个URL对象
+                URL url=new URL(download_url);
+                // 创建一个HTTP链接
+                connection=(HttpURLConnection)url.openConnection();
+                connection.setRequestMethod("POST");
+
+                // 拼参数
+                if (params != null && params.keySet().size() > 0) {
+                    OutputStream os = connection.getOutputStream();
+                    BufferedWriter writer = new BufferedWriter(
+                            new OutputStreamWriter(os, "UTF-8"));
+                    writer.write(createPostParameters(params));
+
+                    writer.flush();
+                    writer.close();
+                    os.close();
+                }
+
+                // 获取响应
+                JSONObject responseHeader = getResponseHeader(connection);
+                String suggestedFileName = getResponsesuggestedFilename(responseHeader);
+                Log.d("Download", "download_query: " + suggestedFileName);
+                if (suggestedFileName != null && !suggestedFileName.isEmpty()) {
+                    fileName = suggestedFileName;
+                }
+
+                // 使用IO流获取数据
+                InputStream inputStream=connection.getInputStream();
+                // 写文件
+                File downloadFile = FileManager.write2SDFromInput(fileName,dir,inputStream);
+
+                if (downloadFile == null) {
+                    returnFile = null;
+                } else {
+                    returnFile = downloadFile;
+                }
+            }
+
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            if (connection != null) {
+                connection.disconnect();
+            }
+        }
+
+
+
+        return returnFile;
+    }
+    public static String check_push(Context c,Bundle params,String url)
+    {
+
+        if(true)
+            return null;
+
+        return null;
+//        String TAG = "net_dbg@check_push";
+//        if(params==null)
+//            params = new Bundle();
+//        params.putString("action", "handset_search");
+//        params.putString("module_name", "Pull Message");
+//        params.putString("uuid", RAUtil.getDUID(c.getApplicationContext()));
+////		String sessionid = ApexTrackingApplication.get_sessionid();
+//        // if (TextUtils.isEmpty(sessionid))
+//        // {
+//        //
+//        // Log.d(TAG, "no sessionid return!");
+//        // return null;
+//        //
+//        // }
+//        String user = ApexTrackingApplication.get_user();
+//        if (TextUtils.isEmpty(user))
+//        {
+//
+//            Log.d(TAG, "no user name return!");
+//            return null;
+//
+//        }
+//        // parms.putString("sessionid", sessionid);
+//        parms.putString("user", user);
+//        SQLiteDatabase db = dbUtil.OpenDB(
+//                ApexTrackingApplication.get_instance(), null, false);
+//        Cursor cursor = db.query("push_message", new String[] { "MAX(e_id)" },
+//                "user = '" + ApexTrackingApplication.get_user() + "'", null,
+//                null, null, null, null);
+//        if (cursor.moveToNext())
+//        {
+//            String maxid = cursor.getString(0);
+//            if (!TextUtils.isEmpty(maxid))
+//                parms.putString("id", maxid);
+//        }
+//        dbUtil.CloseCursor(cursor);
+//        dbUtil.CloseDB(db);
+//
+//        String jstr = getJson(Network.URL_PUSH, parms);
+//        if (jstr == null || jstr.length() <= 0)
+//        {
+//            Log.d(TAG, "json is wrong");
+//            return null;
+//        }
+//        JSONObject jsobj;
+//        //
+//        // array = new JSONArray(json);
+//        try
+//        {
+//            jsobj = new JSONObject(jstr);
+//            if (jsobj.length() > 0)
+//            {
+//                int iresult = jsobj.getInt("result");
+//                if (iresult == Network.AP_SESSION_EXPIRED)
+//                {
+//                    Log.e(TAG, "AP_SESSION_EXPIRED");
+//                    String pass;
+//
+//                    pass = ApexTrackingApplication.get_pass();
+//                    Network.get_Auth(user, pass);
+//                    jstr = getJson(Network.URL_PUSH, parms);
+//                    jsobj = new JSONObject(jstr);
+//                    iresult = jsobj.getInt("result");
+//                }
+//                if (iresult != Network.AP_MESSAGE_NEW)
+//                {
+//                    // session expired
+//                    Log.d(TAG,
+//                            "DOSE NOT GET NEW MESSAGE CODE="
+//                                    + jsobj.getInt("result"));
+//                    return null;
+//                    // if (get_Auth(name, password) == RESULT_TRUE)
+//                    // {
+//                    // return get_records(name, password, parms);
+//                    //
+//                    // }
+//                    // else
+//                    // {
+//                    //
+//                    // Log.d(TAG,
+//                    // "USER NOT AUTHORIZED CODE="
+//                    // + jsobj.getInt("result"));
+//                    // return null;
+//                    // }
+//                }
+//                else
+//                {
+//                    return jstr;
+//                }
+//            }
+//            else
+//            {
+//                Log.d(TAG, "json is wrong");
+//                return null;
+//            }
+//        }
+//        catch (JSONException e)
+//        {
+//            // TODO Auto-generated catch block
+//            e.printStackTrace();
+//        }
+//        return null;
+
+        // return RESULT_ERROR;
+    }
+}

+ 386 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/utils/RAUtil.java

@@ -0,0 +1,386 @@
+package com.usai.redant.rautils.utils;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.Application;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.PermissionChecker;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Array;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class RAUtil {
+    public static void LibTest1() {
+        dbgUtil.Logd("RAUtilsLibrary", "LibTest1 aar update successful");
+    }
+
+    public static void LibTest() {
+        dbgUtil.Logd("RAUtilsLibrary", "LibTest aar update successful");
+    }
+
+    public static String getApplicationName(Context context) {
+
+        PackageManager packageManager = null;
+        ApplicationInfo applicationInfo = null;
+        try {
+            packageManager = context.getPackageManager();
+            applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            applicationInfo = null;
+        }
+        String applicationName = (String) packageManager.getApplicationLabel(applicationInfo);
+        return applicationName;
+    }
+
+    public static boolean isNumeric(String str) {
+        for (int i = 0; i < str.length(); i++) {
+            System.out.println(str.charAt(i));
+            if (!Character.isDigit(str.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public static JSONObject Bundle2Json(Bundle bundle) {
+        JSONObject json = new JSONObject();
+        Set<String> keys = bundle.keySet();
+        for (String key : keys) {
+            try {
+                Log.d("", "Bundle2Json: key:" + key + "  val: " + wrap(bundle.get(key)));
+                json.put(key, wrap(bundle.get(key))); //see edit below
+//                json.put(key, JSONObject.wrap(bundle.get(key)));
+            } catch (JSONException e) {
+                //Handle exception here
+            }
+        }
+        return json;
+    }
+
+    private static Object wrap(Object o) {
+        if (o == null) {
+            return JSONObject.NULL;
+        }
+        if (o instanceof JSONArray || o instanceof JSONObject) {
+            return o;
+        }
+        if (o.equals(JSONObject.NULL)) {
+            return o;
+        }
+        try {
+            if (o instanceof Bundle) {
+                return Bundle2Json((Bundle) o);
+
+            }
+            if (o instanceof Collection) {
+                return new JSONArray((Collection) o);
+            } else if (o.getClass().isArray()) {
+                return toJSONArray(o);
+            }
+            if (o instanceof Map) {
+                return new JSONObject((Map) o);
+            }
+            if (o instanceof Boolean ||
+                    o instanceof Byte ||
+                    o instanceof Character ||
+                    o instanceof Double ||
+                    o instanceof Float ||
+                    o instanceof Integer ||
+                    o instanceof Long ||
+                    o instanceof Short ||
+                    o instanceof String) {
+                return o;
+            }
+            if (o.getClass().getPackage().getName().startsWith("java.")) {
+                return o.toString();
+            }
+        } catch (Exception ignored) {
+        }
+        return null;
+    }
+
+    private static JSONArray toJSONArray(Object array) throws JSONException {
+        JSONArray result = new JSONArray();
+        if (!array.getClass().isArray()) {
+            throw new JSONException("Not a primitive array: " + array.getClass());
+        }
+        final int length = Array.getLength(array);
+        for (int i = 0; i < length; ++i) {
+            result.put(wrap(Array.get(array, i)));
+        }
+        return result;
+    }
+
+    public static Bundle Json2Bundle(JSONObject s) {
+        Bundle bundle = new Bundle();
+
+        for (Iterator<String> it = s.keys(); it.hasNext(); ) {
+            String key = it.next();
+            try {
+                Object o = s.get(key);
+
+
+                if (o == null) {
+                    continue;
+                } else if (o instanceof JSONObject) {
+                    bundle.putBundle(key, Json2Bundle((JSONObject) o));
+                } else if (o instanceof JSONArray) {
+                    throw new JSONException("Json2Bundle does not support Json array: " + o.getClass());// bundle.putarr
+                } else if (o.equals(JSONObject.NULL)) {
+                    continue;
+                } else if (o instanceof Boolean) {
+                    bundle.putBoolean(key, ((Boolean) o).booleanValue());
+                } else if (o instanceof Byte) {
+                    bundle.putByte(key, ((Byte) o).byteValue());
+                } else if (o instanceof Character) {
+                    bundle.putChar(key, ((Character) o).charValue());
+                } else if (o instanceof Double) {
+                    bundle.putDouble(key, ((Double) o).doubleValue());
+                } else if (o instanceof Float) {
+                    bundle.putFloat(key, ((Float) o).floatValue());
+                } else if (o instanceof Integer) {
+                    bundle.putInt(key, ((Integer) o).intValue());
+                } else if (o instanceof Long) {
+                    bundle.putLong(key, ((Long) o).longValue());
+                } else if (o instanceof Short) {
+                    bundle.putShort(key, ((Short) o).shortValue());
+                } else if (o instanceof String) {
+                    bundle.putString(key, o.toString());
+                }
+
+//                ArrayList<Parcelable> a = new ArrayList<>();
+//                a.add("");
+//                a.add(1);
+//                bundle.putParcelableArrayList("arr", a);
+
+
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+        }
+        return bundle;
+    }
+
+
+    public static final int MY_PERMISSIONS_REQUEST = 1;
+
+    public static Boolean checkPermissions(Activity activity, String[] permissions) {
+        if (Build.VERSION.SDK_INT >= 23) {
+            return checkPermissions_23(activity, permissions);
+        } else {
+            return false;
+        }
+    }
+
+    public static Boolean checkPermissions_23(Activity activity, String[] permissions) {
+        Log.d("_RAIMAGE", "checkPermissions23:==================================== ");
+
+        ArrayList<String> request_list = new ArrayList<String>();
+        for (String permission : permissions) {
+            boolean bshow = ActivityCompat.shouldShowRequestPermissionRationale(activity, permission);
+
+            boolean pc = PermissionChecker.checkSelfPermission(activity, permission) == PermissionChecker.PERMISSION_GRANTED;
+
+            if (!pc)
+                request_list.add(permission);
+        }
+        Log.d("_RAIMAGE", "checkPermissions23:==================================== ");
+
+
+        if (request_list.isEmpty())
+            return false;
+        ActivityCompat.requestPermissions(activity, request_list.toArray(new String[0]), MY_PERMISSIONS_REQUEST);
+
+
+        return true;
+
+    }
+
+    public static Boolean checkPermissions_23(Context applicationContext, String[] permissions) {
+        if (applicationContext == null) {
+            return false;
+        }
+
+
+        ArrayList<String> request_list = new ArrayList<String>();
+        Context context = applicationContext;
+        for (String permission : permissions) {
+
+            boolean pc = PermissionChecker.checkSelfPermission(context, permission) == PermissionChecker.PERMISSION_GRANTED;
+            if (!pc) {
+                Log.d("RAUtilsLibrary", "checkPermissions23:==================================== ");
+                Log.d("RAUtilsLibrary", "checkPermissions23: permission" + permission + " denied");
+                Log.d("RAUtilsLibrary", "checkPermissions23:==================================== ");
+                return false;
+            }
+        }
+//        Log.d("RAUtilsLibrary", "checkPermissions23:==================================== ");
+
+
+        return true;
+
+    }
+
+    public static Boolean checkPermissions_24(Activity activity, String[] permissions) {
+
+        if (permissions.length == 0)
+            return true;
+        ActivityCompat.requestPermissions(activity, permissions, MY_PERMISSIONS_REQUEST);
+
+
+        return true;
+    }
+
+    public static int iconName2Rid(Context context, String name) {
+
+        if (context == null) {
+            return 0;
+        }
+
+        if (context == null || TextUtils.isEmpty(name)) {
+            return 0;
+        }
+
+        int iconid = context.getResources().getIdentifier(name, "drawable", context.getPackageName());
+
+        return iconid;
+    }
+
+    private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
+
+    /**
+
+     * This value will not collide with ID values generated at build time by aapt for R.id.
+     *
+     * @return a generated ID value
+     */
+    public static int generateViewId() {
+        for (; ; ) {
+            final int result = sNextGeneratedId.get();
+            // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
+            int newValue = result + 1;
+            if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
+            if (sNextGeneratedId.compareAndSet(result, newValue)) {
+                return result;
+            }
+        }
+    }
+
+    public static String getDUID(Application application) {
+
+        if (application == null) {
+            return null;
+        }
+
+        final TelephonyManager tm = (TelephonyManager) application.getBaseContext().getSystemService(Context.TELEPHONY_SERVICE);
+        final String tmDevice, tmSerial, androidId;
+        String uniqueId;
+        try {
+            if (ActivityCompat.checkSelfPermission(application, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
+                // TODO: Consider calling
+                //    ActivityCompat#requestPermissions
+                // here to request the missing permissions, and then overriding
+                //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
+                //                                          int[] grantResults)
+                // to handle the case where the user grants the permission. See the documentation
+                // for ActivityCompat#requestPermissions for more details.
+                return null;
+            }
+            tmDevice = "" + tm.getDeviceId();
+            tmSerial = "" + tm.getSimSerialNumber();
+            androidId = "" + android.provider.Settings.Secure.getString(application.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
+            UUID deviceUuid = new UUID(androidId.hashCode(), ((long) tmDevice.hashCode() << 32) | tmSerial.hashCode());
+            uniqueId = deviceUuid.toString();
+            Log.d("debug", "uuid=" + uniqueId);
+
+        } catch (Exception e) {
+            uniqueId = null;
+        }
+        return uniqueId;
+    }
+
+    public static int px2dp(Context context, float pxValue) {
+        float scale = context.getResources().getDisplayMetrics().density;
+        return (int) (pxValue / scale + 0.5f);
+    }
+
+    public static int dp2px(Context context, float dpValue) {
+        float scale = context.getResources().getDisplayMetrics().density;
+        return (int) (dpValue * scale + 0.5f);
+    }
+
+    public static int px2sp(Context context, float pxValue) {
+        float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
+        return (int) (pxValue / fontScale + 0.5f);
+    }
+
+    public static int sp2px(Context context, float spValue) {
+        float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
+        return (int) (spValue * fontScale + 0.5f);
+    }
+
+
+    public static String getDeviceId(Context context) {
+        TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
+            // TODO: Consider calling
+            //    ActivityCompat#requestPermissions
+            // here to request the missing permissions, and then overriding
+            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
+            //                                          int[] grantResults)
+            // to handle the case where the user grants the permission. See the documentation
+            // for ActivityCompat#requestPermissions for more details.
+            return "";
+        }
+        String deviceId = telephonyManager.getDeviceId().toString();
+        return deviceId;
+    }
+    public static String getAndroidId(Context context) {
+        return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
+    }
+    public static String stringToMD5(String string) {
+        byte[] hash;
+
+        try {
+            hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+            return null;
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+            return null;
+        }
+
+        StringBuilder hex = new StringBuilder(hash.length * 2);
+        for (byte b : hash) {
+            if ((b & 0xFF) < 0x10)
+                hex.append("0");
+            hex.append(Integer.toHexString(b & 0xFF));
+        }
+
+        return hex.toString();
+    }
+
+}

+ 276 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/utils/dbgUtil.java

@@ -0,0 +1,276 @@
+package com.usai.redant.rautils.utils;
+
+import android.Manifest;
+import android.content.Context;
+import android.database.Cursor;
+import android.os.Environment;
+import android.util.Log;
+
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+//import java.util.Calendar;
+
+public class dbgUtil
+{
+
+
+	private static ReadWriteLock loglock = new ReentrantReadWriteLock(true);
+
+	// static boolean ENABLE_DEBUG = true;
+	// static boolean ENABLE_SQL_DEBUG = true;
+	// final int DEBUG_LEVEL_NONE = 8;
+	// static int DEBUG_LEVEL = Log.VERBOSE;
+
+	// private static boolean m_dbgFlag = true;
+	/*
+	 * public dbgUtil(Context context) {
+	 * 
+	 * m_dbgFlag = Boolean.parseBoolean(context.getString(R.string.flag_debug));
+	 * 
+	 * }
+	 * 
+	 * public dbgUtil(Context context) { m_dbgFlag =
+	 * Boolean.parseBoolean(context.getString(R.string.flag_debug)); return
+	 * this; }
+	 */
+
+	public static void fileLog(Context appContext, String content)
+	{
+
+
+		String[] permissions = {
+				Manifest.permission.WRITE_EXTERNAL_STORAGE,
+				Manifest.permission.READ_EXTERNAL_STORAGE,
+		};
+
+
+
+		boolean result= RAUtil.checkPermissions_23(appContext, permissions);
+
+		if(!result) {
+			Log.d("Apex Drivers", "fileLog: no external storage permission");
+			return;
+		}
+		File dir = new File(Environment.getExternalStorageDirectory().getPath()
+				+ "/redant/pop");
+		if (!dir.exists())
+			dir.mkdirs();
+
+		Calendar c = Calendar.getInstance();
+//		c.add(Calendar.DAY_OF_WEEK, -1);
+
+		String newtime = new SimpleDateFormat("yyyy-MM-dd").format(c
+				.getTimeInMillis());
+
+		File[] f = dir.listFiles();
+		if(f==null) {
+			Log.d("Apex Drivers", "fileLog: list file failed");
+			Log.d("Apex Drivers", Environment.getExternalStorageDirectory().getPath());
+			return;
+		}
+		for (int i = 0; i < f.length; i++)
+		{
+			if(!f[i].isFile())
+				continue;
+			String n = f[i].getName();
+			if ((n != null) && (n.length() > 0))
+			{
+				int dot = n.lastIndexOf('.');
+				if ((dot > -1) && (dot < (n.length())))
+				{
+					n = n.substring(0, dot);
+				}
+				if(newtime.compareTo(n)>0)
+					f[i].delete();
+				
+
+
+			}
+		}
+
+		// dir.listfiles
+
+		String file = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
+		String timeStamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
+				.format(new Date());
+
+		FileOutputStream outputStream = null;
+		// try
+		// {
+
+		// dir.createNewFile()
+		File logfile = new File(Environment.getExternalStorageDirectory()
+				.getPath() + "/redant/pop/" + file + ".log");
+
+		// int mode = Application.MODE_APPEND;
+		if (!logfile.exists())
+		{
+			// mode = Application.MODE_PRIVATE;
+			try
+			{
+				logfile.createNewFile();
+			}
+			catch (IOException e)
+			{
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+		}
+		// outputStream
+		// =RedAntApplication.getInstance().openFileOutput("pop.log", mode);
+
+		String logtextString = timeStamp + "   " + content;
+
+		FileWriter fw = null;
+		BufferedWriter bw = null;
+		String datetime = "";
+
+
+		try
+		{
+			Log.d("Apex Drivers", "fileLog: begin write "+ content);
+			loglock.writeLock().lock();
+
+			fw = new FileWriter(Environment.getExternalStorageDirectory()
+					.getPath() + "/redant/pop/" + file + ".log", true);//
+			// 创建FileWriter对象,用来写入字符流
+			bw = new BufferedWriter(fw); // 将缓冲对文件的输出
+			// String myreadline = datetime + "[]" + str;
+
+			bw.write(logtextString + "\n"); // 写入文件
+			bw.newLine();
+			bw.flush(); // 刷新该流的缓冲
+			bw.close();
+			fw.close();
+			Log.d("Apex Drivers", "fileLog: end write");
+
+		}
+		catch (IOException e)
+		{
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+			try
+			{
+				bw.close();
+				fw.close();
+			}
+			catch (IOException e1)
+			{
+				// TODO Auto-generated catch block
+			}
+		}
+		finally {
+
+			loglock.writeLock().unlock();
+		}
+
+	}
+
+	public static void Logd(String tag, String msg)
+	{
+		String pos = getTracePos();
+		Log.d(tag, msg + pos);
+	}
+
+	// public static void Log(int type, String tag, String msg)
+	// {
+	// // if (ENABLE_DEBUG == false)
+	// // return;
+	// // if (type < DEBUG_LEVEL)
+	// // return;
+	// String pos= getTracePos();
+	// switch (type)
+	// {
+	// case Log.ASSERT:
+	// Log.wtf(tag, msg+pos);
+	// break;
+	// case Log.DEBUG:
+	// Log.d(tag, msg+pos);
+	// break;
+	// case Log.ERROR:
+	// Log.e(tag, msg+pos);
+	// break;
+	// case Log.INFO:
+	// Log.i(tag, msg+pos);
+	// break;
+	// case Log.VERBOSE:
+	// Log.v(tag, msg+pos);
+	// break;
+	// case Log.WARN:
+	// Log.w(tag, msg+pos);
+	// break;
+	// default:
+	// Log.wtf("dbgUtil", "LOG TYPE ERROR!"+pos);
+	// break;
+	// }
+	//
+	// }
+
+	private static String getTracePos()
+	{
+		// StackTraceElement[] elements =
+		// Thread.currentThread().getStackTrace();
+		StackTraceElement element = Thread.currentThread().getStackTrace()[4];
+
+		return " at " + element.getClassName() + "." + element.getMethodName()
+				+ "(" + element.getFileName() + ":" + element.getLineNumber()
+				+ ")";
+	}
+
+	public static void dbgCursorInfo(Cursor cursor, Context context/*
+																	 * , String
+																	 * TracePos
+																	 */)
+	{
+		// if (ENABLE_SQL_DEBUG == false)
+		// return;
+
+		// ----------------------debug cursor-----------------------------------
+		Log.d("sql_dbg@dbgCursorInfo",
+				getTracePos() + "Cursor size:	" + cursor.getCount()
+						+ ";	columns count:	" + cursor.getColumnCount() + ";");
+		// dbgUtil.Log(Log.DEBUG,"sql_dbg", getTracePos() + "Cursor size:	" +
+		// cursor.getCount()
+		// + ";	columns count:	" + cursor.getColumnCount() + ";");
+		for (int i = 0; i < cursor.getColumnCount(); i++)
+		{
+			Log.d("sql_dbg@dbgCursorInfo", "column idx:	" + i
+					+ ";		column name:	" + cursor.getColumnName(i) + ";");
+			// dbgUtil.Log(Log.DEBUG,"sql_dbg",
+			// "column idx:	" + i + ";		column name:	"
+			// + cursor.getColumnName(i) + ";");
+		}
+		// ----------------------debug cursor-----------------------------------
+
+	}
+	// public static void dbgCursorMoreInfo(Cursor cursor /*, String TracePos*/)
+	// {
+	// if(Boolean.parseBoolean(context.getString(R.string.flag_debug))==false)
+	// return;
+	//
+	//
+	// //----------------------debug cursor-----------------------------------
+	// dbgUtil.Log(Log.DEBUG,"sql_dbg",Log.getStackTraceString(new Throwable())
+	// +"Cursor size:	"+cursor.getCount()
+	// +";	columns count:	"+cursor.getColumnCount()
+	// +";");
+	// for (int i =0;i<cursor.getColumnCount();i++)
+	// {
+	// dbgUtil.Log(Log.DEBUG,"sql_dbg","column idx:	"+i
+	// +";		column name:	"+cursor.getColumnName(i)
+	// +";");
+	// }
+	// //----------------------debug cursor-----------------------------------
+	//
+	// }
+}

+ 14 - 14
ApexDrivers/app/src/main/AndroidManifest.xml

@@ -72,7 +72,7 @@
         </service>
 
         <receiver
-            android:name=".Receiver.ApexDriverBootCompleteReceiver"
+            android:name=".receiver.ApexDriverBootCompleteReceiver"
             android:directBootAware="true"
             android:enabled="true"
             android:exported="false"
@@ -84,16 +84,16 @@
         </receiver>
 
         <activity
-            android:name=".Detail.DetailActivity"
+            android:name=".detail.DetailActivity"
             android:screenOrientation="portrait"></activity>
         <activity
-            android:name=".Update.UpdateActivity"
+            android:name=".update.UpdateActivity"
             android:screenOrientation="portrait"></activity>
         <activity
             android:name=".camera.PreferencesActivity"
             android:screenOrientation="portrait"></activity>
         <activity
-            android:name=".CodeScanner.CaptureActivity"
+            android:name=".codescanner.CaptureActivity"
             android:clearTaskOnLaunch="true"
             android:label="Scan"
             android:screenOrientation="sensorLandscape"
@@ -101,25 +101,25 @@
             android:theme="@style/CaptureTheme"
             android:windowSoftInputMode="stateAlwaysHidden"></activity>
         <activity
-            android:name=".Update.PhotoPreviewActivity"
+            android:name=".update.PhotoPreviewActivity"
             android:screenOrientation="portrait"></activity>
         <activity
-            android:name=".Login.RetrievePasswordActivity"
+            android:name=".login.RetrievePasswordActivity"
             android:screenOrientation="portrait"></activity>
         <activity
-            android:name=".Home.HomeMoreActivity"
+            android:name=".home.HomeMoreActivity"
             android:screenOrientation="portrait"></activity>
         <activity
-            android:name=".Upload.UploadListActivity"
+            android:name=".upload.UploadListActivity"
             android:screenOrientation="portrait"></activity>
         <activity
-            android:name=".Upload.TaskActivity"
+            android:name=".upload.TaskActivity"
             android:screenOrientation="portrait"></activity>
-        <activity android:name=".Filter.OrderFilterActivity"></activity>
-        <activity android:name=".Signature.SignatureActivity"></activity>
+        <activity android:name=".filter.OrderFilterActivity"></activity>
+        <activity android:name=".signature.SignatureActivity"></activity>
 
         <receiver
-            android:name=".Receiver.ApexDriverAlarmReceiver"
+            android:name=".receiver.ApexDriverAlarmReceiver"
             android:enabled="true"
             android:exported="true"
             android:directBootAware="true"
@@ -133,9 +133,9 @@
                 <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
             </intent-filter>
         </receiver>
-        <activity android:name=".Message.MessageActivity">
+        <activity android:name=".message.MessageActivity">
         </activity>
-        <activity android:name=".Setting.SettingActivity">
+        <activity android:name=".setting.SettingActivity">
         </activity>
     </application>
 

+ 2 - 2
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/ApexDriverApplication.java

@@ -11,8 +11,8 @@ import android.os.Build;
 import android.os.IBinder;
 import android.util.Log;
 
-import com.usai.redant.apexdrivers.Receiver.ApexDriverAlarmReceiver;
-import com.usai.redant.apexdrivers.Utils.OperationQueue;
+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;
 

+ 4 - 4
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/MainActivity.java

@@ -22,10 +22,10 @@ import android.view.Menu;
 import android.view.MenuItem;
 import android.widget.RelativeLayout;
 
-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.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.RAUtil;
 import com.usai.redant.rautils.utils.dbgUtil;
 

+ 143 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/badgeview/BadgeView.java

@@ -0,0 +1,143 @@
+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;
+
+    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, Color.RED);
+        }
+
+    }
+
+    @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);
+    }
+
+}

+ 87 - 0
ApexDrivers/app/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/app/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;
+  }
+
+}

+ 888 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/codescanner/CaptureActivity.java

@@ -0,0 +1,888 @@
+/*
+ * 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 barcode
+	 *            A bitmap of the captured image.
+	 * @param scaleFactor
+	 *            amount by which thumbnail was scaled
+	 * @param rawResult
+	 *            The decoded results which contains the points to draw.
+	 */
+	// 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();
+	}
+}

+ 170 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/codescanner/CaptureActivityHandler.java

@@ -0,0 +1,170 @@
+/*
+ * 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) {
+    switch (message.what) {
+      case R.id.restart_preview:
+        Log.d(TAG, "Got restart preview message");
+        restartPreviewAndDecode();
+        break;
+      case 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);
+        break;
+      case 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);
+        break;
+      case R.id.return_scan_result:
+        Log.d(TAG, "Got return scan result message");
+        activity.setResult(Activity.RESULT_OK, (Intent) message.obj);
+        activity.finish();
+        break;
+      case 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);
+        }
+        break;
+    }
+  }
+
+  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/app/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;
+  }
+
+}

+ 122 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/codescanner/DecodeHandler.java

@@ -0,0 +1,122 @@
+/*
+ * 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;
+    }
+    switch (message.what) {
+      case R.id.decode:
+        decode((byte[]) message.obj, message.arg1, message.arg2);
+        break;
+      case R.id.quit:
+        running = false;
+        Looper.myLooper().quit();
+        break;
+    }
+  }
+
+  /**
+   * 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/app/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/app/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/app/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/app/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/app/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
+     * {@link android.content.Intent#putExtra(String, String)} with one of {@link Contents.Type}.
+     */
+    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/app/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/app/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();
+      }
+    }
+  }
+
+}

+ 689 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/detail/DetailActivity.java

@@ -0,0 +1,689 @@
+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.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.detail.model.DetailActionSelectionModel;
+import com.usai.redant.apexdrivers.detail.model.DetailLocationModel;
+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.rautils.utils.Network.RESULT_TRUE;
+
+public class DetailActivity extends AppCompatActivity 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 SaveDataKey = "DetailSaveJSON";
+    private final static  String SaveOrderIDKey = "DetailSaveOrderID";
+    private final static  String SaveOrderStatusKey = "DetailSaveOrderStatus";
+    private final static  String SaveOrderType2Key = "DetailSaveOrderType2";
+
+    public static Intent build(Context context, String orderID,int status,String type2) {
+
+        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);
+        }
+        intent.putExtra(IntentOrderStatusKey,status);
+        return intent;
+    }
+
+    private String mOrderID;
+    private String mOrderType2;
+    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);
+        }
+
+        mOrderID = getIntent().getStringExtra(IntentOrderIDKey);
+        mOrderType2 = getIntent().getStringExtra(IntentOrderType2Key);
+        mOrderStatus = getIntent().getIntExtra(IntentOrderStatusKey,0);
+
+        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);
+
+        } 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);
+
+        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 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);
+
+                Message msg = new Message();
+                msg.what = DetailHandler.DetailActionReloadData;
+                msg.obj = json;
+                mHandler.sendMessage(msg);
+            }
+
+        }).start();
+    }
+
+    private void report(final String url, final Bundle params) {
+
+        showProgressDialog();
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+
+                JSONObject json = Network.report(url,params);
+
+                Message msg = new Message();
+                msg.what = DetailHandler.DetailActionRemote;
+                msg.obj = json;
+                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) {
+
+        Uri gmmIntentUri = Uri.parse("google.navigation:q=" + locationModel.location + "&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);
+                }
+
+                report(actionModel.url,actionModel.params);
+            }
+            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) {
+
+                            activity.goHome();
+                        } 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);
+            }
+
+        }
+    }
+
+}

+ 652 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/detail/DetailAdapter.java

@@ -0,0 +1,652 @@
+package com.usai.redant.apexdrivers.detail;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.support.v4.content.LocalBroadcastManager;
+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.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.maps.OnMapReadyCallback;
+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.R;
+import com.usai.redant.rautils.utils.RAUtil;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+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);
+        }
+    }
+
+    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);
+        }
+    }
+
+    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,70));
+                    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.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;
+        Button 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);
+        }
+    }
+
+    public final static String MapLifCircleAction = "com.apex.driver.map_life_circle.action";
+
+    private class MapHolder implements OnMapReadyCallback,DetailBaseModel.OrderDetailModelDelegate {
+        TextView titleTv;
+        TextView valueTv;
+        com.google.android.gms.maps.MapView mapView;
+        GoogleMap googleMap;
+        WeakReference<DetailMapModel> weakMap;
+        MapBroadcastReceiver receiver;
+
+        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);
+
+            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;
+            }
+
+        }
+
+        @Override
+        public void onMapReady(GoogleMap googleMap) {
+            this.googleMap = googleMap;
+
+            refreshUI();
+        }
+
+        @Override
+        public void refreshUI() {
+            if (weakMap != null) {
+                DetailMapModel model = weakMap.get();
+                if (model != null) {
+
+                }
+            }
+        }
+    }
+
+    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);
+
+            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);
+
+            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);
+    }
+}

+ 131 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/detail/DetailSectionModel.java

@@ -0,0 +1,131 @@
+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);
+                    switch (type) {
+                        case DetailBaseModel.OrderDetailValueType.OderDetailValueTypeSingleLine: {
+                            DetailSingleLineModel model = new DetailSingleLineModel(mCtx);
+
+                            model.title = value.optString("title");
+                            model.value = value.optString("value");
+                            model.type = type;
+
+                            this.values.add(model);
+                        }
+                        break;
+                        case DetailBaseModel.OrderDetailValueType.OderDetailValueTypeMultipleLine: {
+                            DetailMultipleLineModel model = new DetailMultipleLineModel(mCtx);
+
+                            model.value = value.optString("value");
+                            model.type = type;
+
+                            this.values.add(model);
+                        }
+                        break;
+                        case DetailBaseModel.OrderDetailValueType.OderDetailValueTypeAction: {
+
+                            DetailActionModel model = new DetailActionModel(mCtx);
+
+                            model.setActions(value.optJSONArray("actions"));
+                            model.type = type;
+
+                            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.type = type;
+
+                            this.values.add(model);
+                        }
+                        break;
+                        case DetailBaseModel.OrderDetailValueType.OderDetailValueTypeMap: {
+
+                            DetailMapModel model = new DetailMapModel(mCtx);
+                            model.type = type;
+
+                            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;
+
+                            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;
+
+                            this.values.add(model);
+                        }
+                        break;
+                        default:
+                            break;
+                    }
+                }
+            }
+        }
+    }
+
+}

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

@@ -0,0 +1,84 @@
+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");
+
+                    subActionModel.actionType = type;
+                    subActionModel.actionSubType = subType;
+
+                    if (type == DetailSubActionModel.DetailActionType.DetailActionTypeLocal && subType == DetailSubActionModel.DetailActionSubType.DetailActionSubTypeEnum) {
+
+                        JSONArray enums = action.optJSONArray("enums");
+                        subActionModel.setEnums(enums);
+                    }
+
+                    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;
+                        }
+                    }
+
+                    String title = action.optString("actionTitle");
+                    subActionModel.actionTitle = title;
+
+                    this.actions.add(subActionModel);
+                }
+            }
+        }
+
+
+
+    }
+
+}

+ 9 - 0
ApexDrivers/app/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;
+
+}

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

@@ -0,0 +1,45 @@
+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;
+
+    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;
+    }
+}

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

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

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

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

+ 12 - 0
ApexDrivers/app/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);
+    }
+}

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

@@ -0,0 +1,85 @@
+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 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 {
+
+                    return ImageUtil.loadImageFromURL(mCtx,new URI(photoURL));
+
+                } 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);
+            }
+        });
+    }
+
+}

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

@@ -0,0 +1,84 @@
+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));
+
+                } 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);
+            }
+        });
+    }
+}

+ 13 - 0
ApexDrivers/app/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);
+    }
+}

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

@@ -0,0 +1,71 @@
+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 int actionType;
+    public int actionSubType;
+    public String actionTitle;
+
+    /**
+     * actionType == RADetailActionTypeLocal && actionSubType == RADetailActionSubTypeEnum
+     * */
+    public ArrayList<DetailActionSelectionModel> enums = new ArrayList();
+    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;
+
+    /**
+    * actionType == RADetailActionTypeLocal && actionSubType == DetailActionSubTypeUpdate
+    * */
+    public int actionID;
+
+    /**
+     * alert
+     * */
+    public boolean alert;
+    public String alertTitle;
+    public String alertMsg;
+}

+ 479 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/filter/OrderFilterActivity.java

@@ -0,0 +1,479 @@
+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 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 AppCompatActivity {
+
+    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
+     * */
+
+}

+ 367 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/filter/OrderFilterAdapter.java

@@ -0,0 +1,367 @@
+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 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) {
+
+                    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) {
+                    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/app/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();
+    }
+}

+ 809 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/home/HomeFragment.java

@@ -0,0 +1,809 @@
+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.badgeview.BadgeView;
+import com.usai.redant.apexdrivers.detail.DetailActivity;
+import com.usai.redant.apexdrivers.MainActivity;
+import com.usai.redant.apexdrivers.message.MessageActivity;
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.apexdrivers.Setting.SettingActivity;
+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;
+
+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);
+                mCtx.startActivity(intent);
+
+                return false;
+            }
+        });
+
+        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() {
+        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");
+
+                        if (fragment.mHeaderView != null) {
+                            fragment.mHeaderView.setAvailable(driver_available);
+                            fragment.mHeaderView.setNewCount(new_count);
+                            fragment.mHeaderView.setProcessingCount(processing_count);
+                            fragment.mHeaderView.setFinishCount(finish_count);
+                        }
+                    }
+                }
+                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);
+                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"));
+
+                    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();
+            }
+
+        }
+    }
+}

+ 190 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/home/HomeHeaderView.java

@@ -0,0 +1,190 @@
+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;
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        init();
+    }
+
+    private TextView newCountTv,processingCountTv,finishCountTv,availableTv;
+    private ImageView signoutBtn,settingBtn,availableBtn,messageBtn;
+
+    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);
+
+        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();
+                }
+            }
+        });
+
+    }
+
+    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.available);
+            availableTv.setText("Available");
+
+        } else {
+
+            availableBtn.setImageResource(R.drawable.unavailable);
+            availableTv.setText("Unavailable");
+        }
+    }
+
+    public boolean isAvailable() {
+        return available;
+    }
+
+    public void setExistNewMessage(boolean existNewMessage) {
+        this.existNewMessage = existNewMessage;
+
+        if (existNewMessage) {
+            messageBtn.setImageResource(R.drawable.new_message);
+        } else {
+            messageBtn.setImageResource(R.drawable.message);
+        }
+    }
+
+    public boolean isExistNewMessage() {
+        return existNewMessage;
+    }
+
+    public void setDelegate(HomeHeaderDelegate delegate) {
+        this.delegate = delegate;
+    }
+
+    public HomeHeaderDelegate getDelegate() {
+        return delegate;
+    }
+}

+ 660 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/home/HomeMoreActivity.java

@@ -0,0 +1,660 @@
+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.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.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+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.detail.DetailActivity;
+import com.usai.redant.apexdrivers.filter.OrderFilterActivity;
+import com.usai.redant.apexdrivers.R;
+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 AppCompatActivity 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) {
+        switch (item.getItemId()) {
+            case android.R.id.home: {
+                finish();
+                return true;
+            }
+            case 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) {
+
+
+                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);
+                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"));
+
+                        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();
+        }
+    }
+}

+ 118 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/home/HomeOrderModel.java

@@ -0,0 +1,118 @@
+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 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;
+
+    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";
+                    Bitmap bitmap = ImageUtil.loadImageFromURL(mCtx,new URI(iconURL));
+
+                    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();
+    }
+
+}

+ 309 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/login/LoginFragment.java

@@ -0,0 +1,309 @@
+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.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.CheckBox;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+
+import com.usai.redant.apexdrivers.ApexDriverApplication;
+import com.usai.redant.apexdrivers.network.Network;
+import com.usai.redant.apexdrivers.R;
+
+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." + ApexDriverApplication.sharedApplication().getPackageManager().getPackageInfo("com.usai.redant.apexdrivers", 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 == R.id.login || 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;
+	}
+
+
+	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/app/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.support.v7.app.AppCompatActivity;
+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.network.Network;
+import com.usai.redant.apexdrivers.R;
+
+import org.json.JSONObject;
+
+/**
+ * Activity which displays a login screen to the user, offering registration as
+ * well.
+ */
+public class RetrievePasswordActivity extends AppCompatActivity
+{
+
+	/**
+	 * 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 == R.id.btn_ok
+								|| 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);
+		}
+	}
+}

+ 46 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/message/MessageActivity.java

@@ -0,0 +1,46 @@
+package com.usai.redant.apexdrivers.message;
+
+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.MenuItem;
+
+import com.usai.redant.apexdrivers.R;
+
+public class MessageActivity extends AppCompatActivity {
+
+    public static void startMessageActivity(Context context) {
+        if (context == null) {
+            return;
+        }
+
+        Intent intent = new Intent(context,MessageActivity.class);
+
+        context.startActivity(intent);
+    }
+
+    @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);
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home: {
+                finish();
+                return true;
+            }
+        }
+        return super.onOptionsItemSelected(item);
+    }
+}

+ 303 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/network/Network.java

@@ -0,0 +1,303 @@
+package com.usai.redant.apexdrivers.network;
+
+
+import android.app.Application;
+import android.content.Context;
+import android.os.Bundle;
+
+
+import com.usai.redant.apexdrivers.ApexDriverApplication;
+
+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";
+    public static final String URL_LOGIN = URL_HOST  + "/j/mobile/login.mo/";
+    public static final String URL_HOME = URL_HOST + "/j/mobile/truckPage.mo/";
+    public static final String URL_MORE_ORDER = URL_HOST + "/j/mobile/morePickupOrders.mo/";
+    public static final String URL_DETAIL = URL_HOST + "/j/mobile/orderDetails.mo/";
+    public static final String URL_UPDATE = URL_HOST + "/j/mobile/actionDetails.mo/";
+    public static final String URL_SUBMIT = URL_HOST + "/j/mobile/updateContainerData.mo/";
+    public static final String URL_UPLOAD = URL_HOST + "/j/mobile/uploadContainerFiles.mo/";
+    public static final String URL_LOGOUT = URL_HOST + "/j/mobile/loginOut.mo/";
+    public static final String URL_REPORT_LOCATION = URL_HOST + "/j/mobile/uploadDriverLocation.mo/";
+    public static final String URL_RETRIVED_PASSWORD = URL_HOST + "/j/mobile/resetPassword.mo/";
+    public static final String URL_DRIVER_AVAILABLE = URL_HOST + "/j/mobile/updateDriverAvailable.mo/";
+
+    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 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) {
+
+
+        if (orderID == null) {
+            return null;
+        }
+
+        Bundle params = new Bundle();
+        params.putString("orderID",orderID);
+        if (type2 != null) {
+            params.putString("orderType2",type2);
+        }
+        params.putInt("orderType",status);
+
+        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) {
+
+        if (location == null) {
+            return;
+        }
+
+        Bundle params = new Bundle();
+        params.putString("location",location);
+
+        prepareParams(params);
+        getJson(URL_REPORT_LOCATION,params);
+    }
+
+    public static JSONObject updateDriverAvailable(boolean available) {
+
+        Bundle params = new Bundle();
+        params.putBoolean("available",available);
+
+        prepareParams(params);
+        String jsonStr = getJson(URL_DRIVER_AVAILABLE,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;
+        }
+    }
+
+}

+ 122 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/receiver/ApexDriverAlarmReceiver.java

@@ -0,0 +1,122 @@
+package com.usai.redant.apexdrivers.receiver;
+
+import android.app.AlarmManager;
+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.rautils.receiver.AlarmReceiver;
+import com.usai.redant.rautils.receiver.RABroadcast;
+import com.usai.redant.rautils.utils.RAUtil;
+
+public class ApexDriverAlarmReceiver extends AlarmReceiver {
+    protected  int alarm_timeInterval = 30 * 1000;
+
+    protected static final int	SO_TIMEOUT						= 15 * 1000;
+    @Override
+    protected void InitAlarm(Context context, Intent intent) {
+
+        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 AlarmProc(Context context, Intent intent) {
+
+        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(Context context)
+    {
+
+
+        String dev_id = RAUtil.getDeviceId(context);
+        Log.d("ApexDriverAlarmReceiver", "check_push: "+dev_id);
+
+
+        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;
+    }
+
+
+}

+ 71 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/receiver/ApexDriverBootCompleteReceiver.java

@@ -0,0 +1,71 @@
+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 {
+
+//    @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);
+	}
+}

+ 45 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/setting/SettingActivity.java

@@ -0,0 +1,45 @@
+package com.usai.redant.apexdrivers.Setting;
+
+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.MenuItem;
+
+import com.usai.redant.apexdrivers.R;
+
+public class SettingActivity extends AppCompatActivity {
+
+    public static void startSettingActivity(Context context) {
+        if (context == null) {
+            return;
+        }
+
+        Intent intent = new Intent(context,SettingActivity.class);
+        context.startActivity(intent);
+    }
+
+    @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);
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home: {
+                finish();
+                return true;
+            }
+        }
+        return super.onOptionsItemSelected(item);
+    }
+}

+ 133 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/signature/SignatureActivity.java

@@ -0,0 +1,133 @@
+package com.usai.redant.apexdrivers.signature;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+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.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 AppCompatActivity {
+
+    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);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.signature_clear_btn: {
+                clearSignature();
+            }
+            break;
+            case R.id.signature_save_btn: {
+                saveAndFinish();
+            }
+            break;
+            case android.R.id.home: {
+                finish();
+            }
+            break;
+        }
+        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 + "_";
+
+            String root = FileManager.SDCardRoot();
+            String appName = RAUtil.getApplicationName(mCtx);
+            String signatureDir = root + File.separator + appName + 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/app/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);
+    }
+}

+ 97 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/update/PhotoPreviewActivity.java

@@ -0,0 +1,97 @@
+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.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 AppCompatActivity {
+
+    public final static String PreviewActionDeleteKey = "delete";
+
+    private final static String PhotoPathKey = "PhotoPath";
+    public static Intent build(Context context,String photoPath) {
+        if (context == null) {
+            return null;
+        }
+        Intent intent = new Intent(context,PhotoPreviewActivity.class);
+        if (photoPath != null) {
+            intent.putExtra(PhotoPathKey,photoPath);
+        }
+        return intent;
+    }
+
+    private XuanImageView previewImageView;
+    private String photoPath;
+    private boolean delete;
+
+    @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);
+            previewImageView.setImageBitmap(bitmap);
+        }
+        delete = false;
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.photo_preview_menu,menu);
+        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();
+    }
+}

+ 1054 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/update/UpdateActivity.java

@@ -0,0 +1,1054 @@
+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.location.Location;
+import android.net.Uri;
+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.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+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.codescanner.CaptureActivity;
+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.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.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 AppCompatActivity 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) {
+        switch (item.getItemId()) {
+            case android.R.id.home: {
+                clearPhotos();
+                this.finish(); // back button
+                return true;
+            }
+            case 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);
+//                    if (scaleFile != null) {
+//                        photoFile = scaleFile;
+//                    }
+
+                    ImageUtil.updateGallery(mCtx,photoFile.getAbsolutePath());
+                    if (mPhotoModel != null) {
+                        mPhotoModel.setPhotoPath(photoFile.getAbsolutePath());
+                    }
+
+                    mPhotoArray.add(photoFile);
+
+                } 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());
+            }
+        }
+
+    }
+
+    /**
+     * 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();
+            if (location != null) {
+                String locationStr = location.getLatitude() + "," + location.getLongitude();
+                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());
+            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;
+    }
+}

+ 553 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/update/UpdateAdapter.java

@@ -0,0 +1,553 @@
+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) {
+
+            switch (v.getId()) {
+                case 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/app/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/app/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/app/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/app/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/app/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/app/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/app/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/app/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;
+    }
+
+
+}

+ 202 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/update/xuanimageview/RotationGestureDetector.java

@@ -0,0 +1,202 @@
+package com.usai.redant.apexdrivers.update.xuanimageview;
+
+import android.util.Log;
+import android.view.MotionEvent;
+
+/**
+ * Created by xuanyihuang on 9/4/16.
+ */
+
+public class RotationGestureDetector {
+    private int mXuanImageViewWith;
+    private static final int INVALID_POINTER_ID = -1;
+    private float sX, sY, fX, fY;
+    private float nfX, nfY, nsX, nsY;
+    private int ptrID1, ptrID2;
+    private int ptrID1_Index, ptrID2_Index, ptr_Index;
+    private float mAngle;
+    private float mPreviousAngle;
+    private float mAngleAtPresent;
+    private float mPreviousAngleAtPresent;
+    private float mPivotX;
+    private float mPivotY;
+    private boolean mIsRotated;
+    private int mPointerCount;
+    private float mBasicRotationTrigger;
+    private float mRotationTrigger;
+
+    private OnRotationGestureListener mListener;
+
+    public float getAngle() {
+        return mAngle;
+    }
+
+    public float getPreviousAngle(){
+        return  mPreviousAngle;
+    }
+
+    public float getPivotX(){
+        return mPivotX;
+    }
+
+    public float getPivotY(){
+        return mPivotY;
+    }
+
+    public void setAngle(float angle){
+        mAngle = angle;
+    }
+
+    public void setPreviousAngle(float angle){
+        mPreviousAngle = angle;
+    }
+
+    public boolean IsRotated(){
+        return  mIsRotated;
+    }
+
+    public RotationGestureDetector(OnRotationGestureListener listener, int XuanImageViewWidth){
+        mXuanImageViewWith = XuanImageViewWidth;
+        mListener = listener;
+        ptrID1 = INVALID_POINTER_ID;
+        ptrID2 = INVALID_POINTER_ID;
+        ptrID1_Index = INVALID_POINTER_ID;
+        ptrID2_Index = INVALID_POINTER_ID;
+        ptr_Index = INVALID_POINTER_ID;
+        mAngle = 0;
+        mPreviousAngle = 0;
+        mPivotX = 0;
+        mPivotY = 0;
+        mIsRotated = false;
+        mPointerCount = 0;
+        mBasicRotationTrigger = 10; //basic rotation trigger : 10 degrees
+        mRotationTrigger = mBasicRotationTrigger;
+    }
+
+    public boolean onTouchEvent(MotionEvent event){
+        Log.d("RotationGestureDetector", event + "");
+        switch (event.getAction() & MotionEvent.ACTION_MASK) {
+            case MotionEvent.ACTION_DOWN:
+                //ptrID1 : first finger pressing down
+                ptrID1 = event.getPointerId(event.getActionIndex());
+                break;
+            case MotionEvent.ACTION_POINTER_DOWN:
+                //ptrID2: second finger pressing down
+                mPointerCount = event.getPointerCount();
+                if(mPointerCount == 2) {
+                    ptrID2 = event.getPointerId(event.getActionIndex());
+                    ptrID1_Index = event.findPointerIndex(ptrID1);
+                    ptrID2_Index = event.findPointerIndex(ptrID2);
+                    try {
+                        sX = event.getX(ptrID1_Index);
+                        sY = event.getY(ptrID1_Index);
+                        fX = event.getX(ptrID2_Index);
+                        fY = event.getY(ptrID2_Index);
+                        determineRotationTrigger();
+                    }catch(Exception e){
+                        //pointer index out of range exception
+                        return true;
+                    }
+                }
+                break;
+            case MotionEvent.ACTION_MOVE:
+                Log.d("canStillRotate" , "" + canStillRotate());
+                if(canStillRotate()){
+                    ptrID1_Index = event.findPointerIndex(ptrID1);
+                    ptrID2_Index = event.findPointerIndex(ptrID2);
+                    try {
+                        nsX = event.getX(ptrID1_Index);
+                        nsY = event.getY(ptrID1_Index);
+                        nfX = event.getX(ptrID2_Index);
+                        nfY = event.getY(ptrID2_Index);
+                    } catch (Exception e){
+                        //pointer index out of range exception
+                        return true;
+                    }
+                    mPivotX = (nsX + nfX) / 2.0f;
+                    mPivotY = (nsY + nfY) / 2.0f;
+
+                    mAngleAtPresent = angleBetweenLines(fX, fY, sX, sY, nfX, nfY, nsX, nsY);
+
+                    if (mListener != null) {
+                        if(mIsRotated){
+                            mPreviousAngle = mAngle;
+                            mAngle = mPreviousAngle + (mAngleAtPresent - mPreviousAngleAtPresent);
+                            mPreviousAngleAtPresent = mAngleAtPresent;
+                            mListener.OnRotate(this);
+                        }
+                        else if(Math.abs(mAngleAtPresent) >= mRotationTrigger){
+                            sX = nsX;
+                            sY = nsY;
+                            fX = nfX;
+                            fY = nfY;
+                            mPreviousAngleAtPresent = 0;
+                            mIsRotated = true;
+                        }
+                    }
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+                invalidateTouchPointers();
+                break;
+            case MotionEvent.ACTION_POINTER_UP:
+                ptr_Index = event.getActionIndex();
+                if(ptrID1 == event.getPointerId(ptr_Index) || ptrID2 == event.getPointerId(ptr_Index)){
+                    invalidateTouchPointers();
+                    if(mListener != null){
+                        if(mIsRotated) {
+                            mListener.StopRotate(this);
+                            mIsRotated = false;
+                        }
+                    }
+                }
+                break;
+            case MotionEvent.ACTION_CANCEL:
+                invalidateTouchPointers();
+                break;
+        }
+
+        return true;
+    }
+
+    private boolean canStillRotate() {
+        return (ptrID1 != INVALID_POINTER_ID) && (ptrID2 != INVALID_POINTER_ID) ;
+    }
+
+    private void invalidateTouchPointers(){
+        ptrID1 = INVALID_POINTER_ID;
+        ptrID2 = INVALID_POINTER_ID;
+    }
+
+    private float angleBetweenLines (float fX, float fY, float sX, float sY, float nfX, float nfY, float nsX, float nsY)
+    {
+        float angle1 = (float) Math.atan2( (fY - sY), (fX - sX) );
+        float angle2 = (float) Math.atan2( (nfY - nsY), (nfX - nsX) );
+
+        //angle range: [-180 degrees, +180 degrees]
+        float angle = ((float) Math.toDegrees(angle2 - angle1)) % 360;
+        if (angle < -180.f) angle += 360.0f;
+        if (angle > 180.f) angle -= 360.0f;
+
+        Log.d("AngleBetweenLines :","" + angle);
+        return angle;
+    }
+
+    private double lineDistance(float fX, float fY, float sX, float sY){
+        return  Math.sqrt((fX - sX) * (fX - sX) + (fY - sY) * (fY - sY));
+    }
+
+    private void determineRotationTrigger(){
+        // need further optimization
+        if(lineDistance(fX,fY,sX,sY) <= (mXuanImageViewWith / 3)){
+            mRotationTrigger = mBasicRotationTrigger * 2;
+        }
+        else
+            mRotationTrigger = mBasicRotationTrigger;
+    }
+
+    public interface OnRotationGestureListener {
+        boolean OnRotate(RotationGestureDetector rotationGestureDetector);
+        boolean StopRotate(RotationGestureDetector rotationGestureDetector);
+    }
+}

+ 1033 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/update/xuanimageview/XuanImageView.java

@@ -0,0 +1,1033 @@
+package com.usai.redant.apexdrivers.update.xuanimageview;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+
+import com.usai.redant.apexdrivers.R;
+
+/**
+ * Created by xuanyihuang on 8/30/16.
+ */
+
+public class XuanImageView extends android.support.v7.widget.AppCompatImageView{
+    private int XuanImageViewWidth;
+    private int XuanImageViewHeight;
+    private int XuanImageViewCenterX;
+    private int XuanImageViewCenterY;
+    private int ImageCenterX;
+    private int ImageCenterY;
+    private int mOrientation;   //1 for portrait, 2 for landscape
+    private int mAutoRotateCategory;
+    private float mInitScale;   //for landscape
+    private float mInitPortraitScale;
+    private float mTempInitPortraitScale;
+    private float mMaxScale;
+    private float mPortraitMaxScale;
+    private float mTempPortraitMaxScale;
+    private boolean mRotationToggle;
+    private float mMaxScaleMultiple;
+    private float mDoubleTabScaleMultiple;
+    private float mDoubleTabScale;
+    private float mPortraitDoubleTabScale;
+    private float mTempPortraitDoubleTabScale;
+    private Matrix mScaleMatrix;
+    private ScaleGestureDetector mScaleGestureDetector;
+    private GestureDetector mGestureDetector;
+    private float mLastScaleFocusX;
+    private float mLastScaleFocusY;
+    private boolean isAutoScale;
+    private float mSpringBackGradientScaleUpLevel;
+    private float mSpringBackGradientScaleDownLevel;
+    private float mDoubleTapGradientScaleUpLevel;
+    private float mDoubleTapGradientScaleDownLevel;
+    private int mLastPointerCount;
+    private float mLastX;
+    private float mLastY;
+    private float mAngle;
+    private float mPreviousAngle;
+    private RotationGestureDetector mRotateGestureDetector;
+    private boolean isAutoRotated;
+    private double allowableFloatError;
+    private double allowablePortraitFloatError;
+    private float currentScaleLevel;
+    private float currentAbsScaleLevel;
+    private float autoRotationTrigger;
+    private int autoRotationRunnableDelay;
+    private int autoRotationRunnableTimes;
+    private int doubleTabScaleRunnableDelay;
+    private int springBackRunnableDelay;
+    private boolean hasDrawable;
+    private boolean knowViewSize;
+
+    public XuanImageView(Context context) {
+        this(context, null);
+    }
+
+    public XuanImageView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public XuanImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        initialize(context, attrs);
+    }
+
+    private void initialize(Context context, AttributeSet attrs) {
+        setScaleType(ScaleType.MATRIX);
+        mScaleMatrix = new Matrix();
+
+        mScaleGestureDetector = new ScaleGestureDetector(context, constructOnScaleGestureListener());
+        mGestureDetector = new GestureDetector(context, constructOnGestureListener());
+
+        initCustomAttrs(context, attrs);
+
+        mOrientation = XuanImageViewSettings.ORIENTATION_LANDSCAPE;   //1 for portrait, 2 for landscape
+        isAutoScale = false;
+        mLastPointerCount = 0;
+        mAngle = 0;
+        mPreviousAngle = 0;
+        currentScaleLevel = 1;
+    }
+
+    private ScaleGestureDetector.OnScaleGestureListener constructOnScaleGestureListener() {
+        return new ScaleGestureDetector.OnScaleGestureListener() {
+            @Override
+            public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
+                float scaleFactor = scaleGestureDetector.getScaleFactor();
+
+                currentScaleLevel = getCurrentScaleLevel();
+                currentAbsScaleLevel = Math.abs(currentScaleLevel);
+                Log.d("CurrentAbsScaleLevel", "" + currentAbsScaleLevel);
+
+                boolean isRotating = false;
+                boolean justScale = false;
+
+                if(mRotateGestureDetector.IsRotated()){
+                    // is rotating
+                    isRotating = true;
+                }
+                else{
+                    // not rotating, just scaling
+                    if(mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_RESTORATION){
+                        if((currentScaleLevel <= mMaxScale && scaleFactor > 1.0f) || (currentScaleLevel >= mInitScale && scaleFactor < 1.0f))
+                            justScale = true;
+
+                    }
+                    else if(mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_MAGNETISM){
+                        if (mOrientation == XuanImageViewSettings.ORIENTATION_LANDSCAPE) {
+                            if ((currentAbsScaleLevel <= mMaxScale && scaleFactor > 1.0f) || (currentAbsScaleLevel >= mInitScale && scaleFactor < 1.0f))
+                                justScale = true;
+                        } else if (mOrientation == XuanImageViewSettings.ORIENTATION_PORTRAIT) {
+                            if ((currentAbsScaleLevel <= mTempPortraitMaxScale && scaleFactor > 1.0f) || (currentAbsScaleLevel >= mTempInitPortraitScale && scaleFactor < 1.0f))
+                                justScale = true;
+                        }
+                    }
+                }
+
+
+                if(isRotating) {
+                    mScaleMatrix.postScale(scaleFactor, scaleFactor, mRotateGestureDetector.getPivotX(), mRotateGestureDetector.getPivotY());
+                }
+                else if(justScale)
+                {
+                    mScaleMatrix.postScale(scaleFactor, scaleFactor, scaleGestureDetector.getFocusX(), scaleGestureDetector.getFocusY());
+                    checkBorderAndCenterWhenScale();
+                }
+
+
+                setImageMatrix(mScaleMatrix);
+                mLastScaleFocusX = scaleGestureDetector.getFocusX();
+                mLastScaleFocusY = scaleGestureDetector.getFocusY();
+
+                return true;
+            }
+
+            @Override
+            public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
+                Log.d("onScaleBegin-->", "");
+                return true;
+            }
+
+            @Override
+            public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
+                Log.d("onScaleEnd-->", "");
+            }
+        };
+    }
+
+    private GestureDetector.OnGestureListener constructOnGestureListener() {
+        return new GestureDetector.SimpleOnGestureListener() {
+            @Override
+            public boolean onDoubleTap(MotionEvent e) {
+                currentScaleLevel = getCurrentScaleLevel();
+                currentAbsScaleLevel = Math.abs(currentScaleLevel);
+                float x = e.getX();
+                float y = e.getY();
+
+                if (isAutoScale)
+                    return true;
+
+
+                if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_RESTORATION) {
+                    if (currentScaleLevel < mDoubleTabScale) {
+                        postDelayed(new AutoScaleRunnable(mDoubleTabScale, x, y, mDoubleTapGradientScaleUpLevel, mDoubleTapGradientScaleDownLevel), doubleTabScaleRunnableDelay);
+                        isAutoScale = true;
+                    } else {
+                        postDelayed(new AutoScaleRunnable(mInitScale, x, y, mDoubleTapGradientScaleUpLevel, mDoubleTapGradientScaleDownLevel), doubleTabScaleRunnableDelay);
+                        isAutoScale = true;
+                    }
+                } else if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_MAGNETISM) {
+                    if (mOrientation == XuanImageViewSettings.ORIENTATION_LANDSCAPE) {
+                        if (currentAbsScaleLevel < mDoubleTabScale) {
+                            postDelayed(new AutoScaleRunnable(mDoubleTabScale * (currentScaleLevel / currentAbsScaleLevel), x, y, mDoubleTapGradientScaleUpLevel, mDoubleTapGradientScaleDownLevel), doubleTabScaleRunnableDelay);
+                            isAutoScale = true;
+                        } else {
+                            postDelayed(new AutoScaleRunnable(mInitScale * (currentScaleLevel / currentAbsScaleLevel), x, y, mDoubleTapGradientScaleUpLevel, mDoubleTapGradientScaleDownLevel), doubleTabScaleRunnableDelay);
+                            isAutoScale = true;
+                        }
+                    } else if (mOrientation == XuanImageViewSettings.ORIENTATION_PORTRAIT) {
+                        if (currentAbsScaleLevel < mTempPortraitDoubleTabScale) {
+                            postDelayed(new AutoScaleRunnable(mTempPortraitDoubleTabScale * (currentScaleLevel / currentAbsScaleLevel), x, y, mDoubleTapGradientScaleUpLevel, mDoubleTapGradientScaleDownLevel), doubleTabScaleRunnableDelay);
+                            isAutoScale = true;
+                        } else {
+                            postDelayed(new AutoScaleRunnable(mTempInitPortraitScale * (currentScaleLevel / currentAbsScaleLevel), x, y, mDoubleTapGradientScaleUpLevel, mDoubleTapGradientScaleDownLevel), doubleTabScaleRunnableDelay);
+                            isAutoScale = true;
+                        }
+                    }
+                }
+
+                return true;
+            }
+        };
+    }
+
+
+    private RotationGestureDetector.OnRotationGestureListener constructOnRotationGestureListener(){
+        return new RotationGestureDetector.OnRotationGestureListener() {
+            @Override
+            public boolean OnRotate(RotationGestureDetector rotationGestureDetector) {
+                mAngle = rotationGestureDetector.getAngle();
+                mPreviousAngle = rotationGestureDetector.getPreviousAngle();
+                mScaleMatrix.postRotate(mAngle - mPreviousAngle, rotationGestureDetector.getPivotX(), rotationGestureDetector.getPivotY());
+
+                setImageMatrix(mScaleMatrix);
+
+                return true;
+            }
+
+            @Override
+            public boolean StopRotate(RotationGestureDetector rotationGestureDetector) {
+                if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_RESTORATION)
+                    AutoRotateRestoration(rotationGestureDetector);
+                else if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_MAGNETISM)
+                    AutoRotateMagnetism(rotationGestureDetector);
+
+                return true;
+            }
+        };
+    }
+
+    private void initCustomAttrs(Context context, AttributeSet attrs){
+
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.xuanimageview);
+        mRotationToggle = a.getBoolean(R.styleable.xuanimageview_RotationToggle, true);
+        mAutoRotateCategory = a.getInteger(R.styleable.xuanimageview_AutoRotateCategory, XuanImageViewSettings.AUTO_ROTATE_CATEGORY_RESTORATION);
+        mMaxScaleMultiple = a.getFloat(R.styleable.xuanimageview_MaxScaleMultiple, 4);
+        mDoubleTabScaleMultiple = a.getFloat(R.styleable.xuanimageview_DoubleTabScaleMultiple, 2);
+        mSpringBackGradientScaleUpLevel = a.getFloat(R.styleable.xuanimageview_SpringBackGradientScaleUpLevel, 1.01f);
+        mSpringBackGradientScaleDownLevel = a.getFloat(R.styleable.xuanimageview_SpringBackGradientScaleDownLevel, 0.99f);
+        mDoubleTapGradientScaleUpLevel = a.getFloat(R.styleable.xuanimageview_DoubleTapGradientScaleUpLevel, 1.05f);
+        mDoubleTapGradientScaleDownLevel = a.getFloat(R.styleable.xuanimageview_DoubleTapGradientScaleDownLevel, 0.95f);
+        autoRotationTrigger = a.getFloat(R.styleable.xuanimageview_AutoRotationTrigger, 60);
+        springBackRunnableDelay = a.getInteger(R.styleable.xuanimageview_SpringBackRunnableDelay, 10);
+        doubleTabScaleRunnableDelay = a.getInteger(R.styleable.xuanimageview_DoubleTapScaleRunnableDelay, 10);
+        autoRotationRunnableDelay = a.getInteger(R.styleable.xuanimageview_AutoRotationRunnableDelay, 5);
+        autoRotationRunnableTimes = a.getInteger(R.styleable.xuanimageview_AutoRotationRunnableTimes, 10);
+        try {
+            allowableFloatError = Double.parseDouble(a.getString(R.styleable.xuanimageview_AllowableFloatError));
+        }catch (Exception e){
+            allowableFloatError = 1E-6;   // for normal display aspect ratio
+//        allowableFloatError = 3E-3;   // for Galaxy S8
+        }
+        try {
+            allowablePortraitFloatError = Double.parseDouble(a.getString(R.styleable.xuanimageview_AllowablePortraitFloatError));
+        }catch (Exception e){
+            allowablePortraitFloatError = 1E-12;  // for normal display aspect ratio
+//        allowablePortraitFloatError = 5E-8;   //for Galaxy S8
+        }
+        a.recycle();
+    }
+
+    @Override
+    public void setImageDrawable(Drawable drawable) {
+        super.setImageDrawable(drawable);
+
+        if (drawable == null) {
+            hasDrawable = false;
+            return;
+        }
+
+        if (!drawableHasSize(drawable))
+            return;
+
+        hasDrawable = true;
+        initDrawableMatrix();
+    }
+
+    @Override
+    public void setImageResource(int resId) {
+        Drawable drawable = null;
+        try {
+            drawable = getResources().getDrawable(resId);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        setImageDrawable(drawable);
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+
+        knowViewSize = true;
+        initDrawableMatrix();
+    }
+
+    private boolean drawableHasSize(Drawable drawable) {
+        if ((drawable.getIntrinsicHeight() <= 0 || drawable.getIntrinsicWidth() <= 0)
+                && (drawable.getMinimumHeight() <= 0 || drawable.getMinimumWidth() <= 0)
+                && (drawable.getBounds().height() <= 0 || drawable.getBounds().width() <= 0))
+            return false;
+        else
+            return true;
+    }
+
+    private void initDrawableMatrix() {
+        if (!hasDrawable)
+            return;
+        if (!knowViewSize)
+            return;
+
+        if (mScaleMatrix == null)
+            mScaleMatrix = new Matrix();
+        else
+            mScaleMatrix.reset();
+
+        // get width and height of XuanImageView
+        XuanImageViewWidth = getWidth();
+        XuanImageViewHeight = getHeight();
+
+        //get the center point of XuanImageView
+        XuanImageViewCenterX = XuanImageViewWidth / 2;
+        XuanImageViewCenterY = XuanImageViewHeight / 2;
+
+        // instantiate mRotationGestureDetector after dimension of XuanImageView is got.
+        mRotateGestureDetector = new RotationGestureDetector(constructOnRotationGestureListener(), XuanImageViewWidth);
+
+        //get width and height of the image
+        Drawable imageDrawable = getDrawable();
+        if (imageDrawable == null)
+            return;
+        int imageWidth = imageDrawable.getIntrinsicWidth();
+        int imageHeight = imageDrawable.getIntrinsicHeight();
+
+        //image is scaled to fit the size of XuanImageView at the very beginning.
+        float scale = Math.min(XuanImageViewWidth * 1.0f / imageWidth, XuanImageViewHeight * 1.0f / imageHeight);
+        float portraitscale = Math.min(XuanImageViewWidth * 1.0f / imageHeight, XuanImageViewHeight * 1.0f / imageWidth);
+
+        mInitScale = scale;
+        mInitPortraitScale = portraitscale;
+        mMaxScale = mMaxScaleMultiple * scale;
+        mPortraitMaxScale = mMaxScaleMultiple * portraitscale;
+        mDoubleTabScale = mDoubleTabScaleMultiple * scale;
+        mPortraitDoubleTabScale = mDoubleTabScaleMultiple * portraitscale;
+
+        //center of image overlaps with that of XuanImageView
+        int deltaX = XuanImageViewWidth / 2 - imageWidth / 2;
+        int deltaY = XuanImageViewHeight / 2 - imageHeight / 2;
+
+        mScaleMatrix.postTranslate(deltaX, deltaY);
+        mScaleMatrix.postScale(scale, scale, XuanImageViewWidth / 2, XuanImageViewHeight / 2);
+        setImageMatrix(mScaleMatrix);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent motionEvent) {
+        if (mRotateGestureDetector == null)
+            return true;
+
+        boolean parentDisallowInterceptTouchEventFlag = true;
+
+        // for DoubleTap gesture
+        mGestureDetector.onTouchEvent(motionEvent);
+
+        // for Scale gesture
+        mScaleGestureDetector.onTouchEvent(motionEvent);
+
+
+        currentScaleLevel = getCurrentScaleLevel();
+        currentAbsScaleLevel = Math.abs(currentScaleLevel);
+        if (mRotationToggle) {
+            if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_RESTORATION) {
+                if (mRotateGestureDetector.IsRotated() || Math.abs(currentScaleLevel - mInitScale) < allowableFloatError
+                        || ((motionEvent.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP)
+                        || ((motionEvent.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP)) {
+                    if (!mRotateGestureDetector.IsRotated())
+                        parentDisallowInterceptTouchEventFlag = false;
+
+                    // for Rotation gesture
+                    mRotateGestureDetector.onTouchEvent(motionEvent);
+                }
+            } else if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_MAGNETISM) {
+                if (mOrientation == XuanImageViewSettings.ORIENTATION_LANDSCAPE) {
+                    if (mRotateGestureDetector.IsRotated() || Math.abs(currentAbsScaleLevel - mInitScale) < allowableFloatError
+                            || ((motionEvent.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP)
+                            || ((motionEvent.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP)) {
+                        if (!mRotateGestureDetector.IsRotated())
+                            parentDisallowInterceptTouchEventFlag = false;
+
+                        // for Rotation gesture
+                        mRotateGestureDetector.onTouchEvent(motionEvent);
+
+                    }
+                } else if (mOrientation == XuanImageViewSettings.ORIENTATION_PORTRAIT) {
+                    if (mRotateGestureDetector.IsRotated() || Math.abs(currentAbsScaleLevel - mTempInitPortraitScale) < allowablePortraitFloatError
+                            || ((motionEvent.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP)
+                            || ((motionEvent.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP)) {
+                        if (!mRotateGestureDetector.IsRotated())
+                            parentDisallowInterceptTouchEventFlag = false;
+
+                        // for Rotation gesture
+                        mRotateGestureDetector.onTouchEvent(motionEvent);
+                    }
+                }
+            }
+        }
+
+        //pointerCount won't be 0
+        int pointerCount = motionEvent.getPointerCount();
+        float pivotX = 0;
+        float pivotY = 0;
+        for (int i = 0; i < pointerCount; i++) {
+            pivotX += motionEvent.getX(i);
+            pivotY += motionEvent.getY(i);
+        }
+        pivotX /= pointerCount;   //get integer result of the division
+        pivotY /= pointerCount;
+
+        // when image is being dragged, generally, pointCount == mLastCount holds, so old state is not saved.
+        if (pointerCount != mLastPointerCount) {
+            mLastX = pivotX;
+            mLastY = pivotY;
+            mLastPointerCount = pointerCount;
+        }
+
+        RectF rectF = getMatrixRectF();
+
+        switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
+            case MotionEvent.ACTION_DOWN:
+                if (getParent() != null)
+                    getParent().requestDisallowInterceptTouchEvent(parentDisallowInterceptTouchEventFlag);
+                break;
+            case MotionEvent.ACTION_MOVE:
+                if (getParent() != null)
+                    getParent().requestDisallowInterceptTouchEvent(parentDisallowInterceptTouchEventFlag);
+                float deltaX = pivotX - mLastX;
+                float deltaY = pivotY - mLastY;
+
+                if (getDrawable() != null) {
+                    if (!mRotateGestureDetector.IsRotated()) {
+                        if (rectF.width() <= XuanImageViewWidth)
+                            deltaX = 0;
+                        if (rectF.height() <= XuanImageViewHeight)
+                            deltaY = 0;
+                    }
+                    mScaleMatrix.postTranslate(deltaX, deltaY);
+
+                    if (!mRotateGestureDetector.IsRotated())
+                        checkBorderAndCenterWhenTranslate();
+                    setImageMatrix(mScaleMatrix);
+                }
+                mLastX = pivotX;
+                mLastY = pivotY;
+                break;
+            case MotionEvent.ACTION_POINTER_UP:
+                //one finger loosening (multi-touch)
+                if (pointerCount - 1 < 2) {
+                    if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_RESTORATION) {
+                        //spring back to mInitScale or mMaxScale
+                        if ((currentScaleLevel < mInitScale) && !isAutoRotated) {
+                            postDelayed(new AutoScaleRunnable(mInitScale, mLastScaleFocusX, mLastScaleFocusY, mSpringBackGradientScaleUpLevel, mSpringBackGradientScaleDownLevel), springBackRunnableDelay);
+                            isAutoScale = true;
+                        } else if ((currentScaleLevel > mMaxScale) && !isAutoRotated) {
+                            postDelayed(new AutoScaleRunnable(mMaxScale, mLastScaleFocusX, mLastScaleFocusY, mSpringBackGradientScaleUpLevel, mSpringBackGradientScaleDownLevel), springBackRunnableDelay);
+                            isAutoScale = true;
+                        }
+                    } else if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_MAGNETISM) {
+                        if (mOrientation == XuanImageViewSettings.ORIENTATION_LANDSCAPE) {
+                            if (currentAbsScaleLevel < mInitScale && !isAutoRotated) {
+                                postDelayed(new AutoScaleRunnable(mInitScale * (currentScaleLevel / currentAbsScaleLevel), mLastScaleFocusX, mLastScaleFocusY, mSpringBackGradientScaleUpLevel, mSpringBackGradientScaleDownLevel), springBackRunnableDelay);
+                                isAutoScale = true;
+                            } else if ((currentAbsScaleLevel > mMaxScale) && !isAutoRotated) {
+                                postDelayed(new AutoScaleRunnable(mMaxScale * (currentScaleLevel / currentAbsScaleLevel), mLastScaleFocusX, mLastScaleFocusY, mSpringBackGradientScaleUpLevel, mSpringBackGradientScaleDownLevel), springBackRunnableDelay);
+                                isAutoScale = true;
+                            }
+                        } else if (mOrientation == XuanImageViewSettings.ORIENTATION_PORTRAIT) {
+                            if (currentAbsScaleLevel < mTempInitPortraitScale && !isAutoRotated) {
+                                postDelayed(new AutoScaleRunnable(mTempInitPortraitScale * (currentScaleLevel / currentAbsScaleLevel), mLastScaleFocusX, mLastScaleFocusY, mSpringBackGradientScaleUpLevel, mSpringBackGradientScaleDownLevel), springBackRunnableDelay);
+                                isAutoScale = true;
+                            } else if ((currentAbsScaleLevel > mTempPortraitMaxScale) && !isAutoRotated) {
+                                postDelayed(new AutoScaleRunnable(mTempPortraitMaxScale * (currentScaleLevel / currentAbsScaleLevel), mLastScaleFocusX, mLastScaleFocusY, mSpringBackGradientScaleUpLevel, mSpringBackGradientScaleDownLevel), springBackRunnableDelay);
+                                isAutoScale = true;
+                            }
+                        }
+                    }
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+                mLastPointerCount = 0;
+                break;
+            case MotionEvent.ACTION_CANCEL:
+                mLastPointerCount = 0;
+        }
+
+
+        return true;
+    }
+
+    private void checkBorderAndCenterWhenScale() {
+
+        RectF rectF = getMatrixRectF();
+        float deltaX = 0;
+        float deltaY = 0;
+
+        /**
+         * If width or height of image is bigger than that of XuanImageView,
+         * should prevent image's edge being far away from XuanImageView's edge.
+         */
+        if (rectF.width() >= XuanImageViewWidth) {
+            if (rectF.left > 0)
+                deltaX = -rectF.left;
+            if (rectF.right < XuanImageViewWidth)
+                deltaX = XuanImageViewWidth - rectF.right;
+
+        }
+        if (rectF.height() >= XuanImageViewHeight) {
+            if (rectF.top > 0)
+                deltaY = -rectF.top;
+            if (rectF.bottom < XuanImageViewHeight)
+                deltaY = XuanImageViewHeight - rectF.bottom;
+        }
+
+        /**
+         * If width or height of image is smaller than that of XuanImageView,
+         * make sure the image, in width dimension or height dimension, is centered.
+         */
+        if (rectF.width() < XuanImageViewWidth) {
+            deltaX = XuanImageViewWidth / 2.0f - rectF.left - rectF.width() / 2.0f;
+        }
+        if (rectF.height() < XuanImageViewHeight) {
+            deltaY = XuanImageViewHeight / 2.0f - rectF.top - rectF.height() / 2.0f;
+
+        }
+
+        mScaleMatrix.postTranslate(deltaX, deltaY);
+    }
+
+    private void checkBorderAndCenterWhenTranslate() {
+        /**
+         *  No need to check Center here because it has been handled before checkBorderAndCenterWhenTranslate() is invoked.
+         *  See "case MotionEvent.ACTION_MOVE: " in  onTouch(View view, MotionEvent motionEvent).
+         */
+        RectF rectF = getMatrixRectF();
+
+        float deltaX = 0;
+        float deltaY = 0;
+
+        /**
+         * If width or height of image is bigger than that of XuanImageView,
+         * should prevent image's edge being far away from XuanImageView's edge.
+         */
+        if (rectF.width() >= XuanImageViewWidth) {
+            if (rectF.left > 0)
+                deltaX = -rectF.left;
+            if (rectF.right < XuanImageViewWidth)
+                deltaX = XuanImageViewWidth - rectF.right;
+
+        }
+        if (rectF.height() >= XuanImageViewHeight) {
+            if (rectF.top > 0)
+                deltaY = -rectF.top;
+            if (rectF.bottom < XuanImageViewHeight)
+                deltaY = XuanImageViewHeight - rectF.bottom;
+        }
+
+        mScaleMatrix.postTranslate(deltaX, deltaY);
+    }
+
+
+    private float getCurrentScaleLevel() {
+        float matrixArray[] = new float[9];
+        mScaleMatrix.getValues(matrixArray);
+
+        return matrixArray[Matrix.MSCALE_X];
+    }
+
+    private RectF getMatrixRectF() {
+        Matrix matrix = mScaleMatrix;
+        RectF rectF = new RectF();
+        Drawable image = getDrawable();
+        if (image != null) {
+            rectF.set(0, 0, image.getIntrinsicWidth(), image.getIntrinsicHeight());
+            matrix.mapRect(rectF);
+        }
+
+        return rectF;
+    }
+
+    private boolean calculateImageCenterCoordinates() {
+        RectF rectF = getMatrixRectF();
+        ImageCenterX = (int) ((rectF.left + rectF.right) / 2);
+        ImageCenterY = (int) ((rectF.top + rectF.bottom) / 2);
+
+        return true;
+    }
+
+    public void AutoRotateMagnetism(RotationGestureDetector rotationGestureDetector) {
+        mAngle = rotationGestureDetector.getAngle();//mAngle's range: [-180 degress, 180 degress]
+        float autoRotateAngle;
+        int quotient;
+        float remainder;
+
+        quotient = ((int) mAngle) / 90;
+        remainder = mAngle % 90;
+
+        if (remainder >= autoRotationTrigger) {
+            autoRotateAngle = 90 - remainder;
+            if ((quotient + 1) % 2 == 0)
+                mOrientation = XuanImageViewSettings.ORIENTATION_LANDSCAPE;
+            else
+                mOrientation = XuanImageViewSettings.ORIENTATION_PORTRAIT;
+        } else if (remainder <= -autoRotationTrigger) {
+            autoRotateAngle = -90 - remainder;
+            if ((quotient - 1) % 2 == 0)
+                mOrientation = XuanImageViewSettings.ORIENTATION_LANDSCAPE;
+            else
+                mOrientation = XuanImageViewSettings.ORIENTATION_PORTRAIT;
+        } else {
+            autoRotateAngle = -remainder;
+            if (quotient % 2 == 0)
+                mOrientation = XuanImageViewSettings.ORIENTATION_LANDSCAPE;
+            else
+                mOrientation = XuanImageViewSettings.ORIENTATION_PORTRAIT;
+        }
+
+        postDelayed(new AutoRotateRunnable(autoRotateAngle, getCurrentScaleLevel() / (float) Math.cos(Math.toRadians(mAngle)), autoRotationRunnableTimes), autoRotationRunnableDelay);
+        isAutoRotated = true;
+
+        rotationGestureDetector.setAngle(mAngle + autoRotateAngle);
+        rotationGestureDetector.setPreviousAngle(mAngle + autoRotateAngle);
+    }
+
+    public void AutoRotateRestoration(RotationGestureDetector rotationGestureDetector) {
+        mAngle = rotationGestureDetector.getAngle();//mAngle's range: [-180 degress, 180 degress]
+        float autoRotateAngle;
+
+        if (mAngle >= autoRotationTrigger)
+            autoRotateAngle = 360 - mAngle;
+        else if (mAngle <= -autoRotationTrigger)
+            autoRotateAngle = -360 - mAngle;
+        else
+            autoRotateAngle = -mAngle;
+
+        postDelayed(new AutoRotateRunnable(autoRotateAngle, getCurrentScaleLevel() / (float) Math.cos(Math.toRadians(mAngle)), autoRotationRunnableTimes), autoRotationRunnableDelay);
+        isAutoRotated = true;
+
+        rotationGestureDetector.setAngle(0.0f);
+        rotationGestureDetector.setPreviousAngle(0.0f);
+    }
+
+    private class AutoScaleRunnable implements Runnable {
+        float targetScale;
+        float targetAbsScale;
+        float FocusX;
+        float FocusY;
+        float scaleFactor;
+
+        AutoScaleRunnable(float targetScale, float FocusX, float FocusY, float GradientScaleUp, float GradientScaleDown) {
+            this.targetScale = targetScale;
+            this.targetAbsScale = Math.abs(targetScale);
+            this.FocusX = FocusX;
+            this.FocusY = FocusY;
+            currentScaleLevel = getCurrentScaleLevel();
+            currentAbsScaleLevel = Math.abs(currentScaleLevel);
+            if (currentAbsScaleLevel < targetAbsScale)
+                scaleFactor = GradientScaleUp;
+            else if (currentAbsScaleLevel > targetAbsScale)
+                scaleFactor = GradientScaleDown;
+            else if (currentAbsScaleLevel == targetAbsScale)
+                scaleFactor = 1.0f;
+        }
+
+        @Override
+        public void run() {
+            mScaleMatrix.postScale(scaleFactor, scaleFactor, FocusX, FocusY);
+            checkBorderAndCenterWhenScale();
+            setImageMatrix(mScaleMatrix);
+
+            currentScaleLevel = getCurrentScaleLevel();
+            currentAbsScaleLevel = Math.abs(currentScaleLevel);
+            if ((scaleFactor < 1.0f && currentAbsScaleLevel > targetAbsScale)
+                    || (scaleFactor > 1.0f && currentAbsScaleLevel < targetAbsScale)) {
+                postDelayed(this, springBackRunnableDelay);
+            } else {
+                scaleFactor = targetScale / currentScaleLevel;
+
+                mScaleMatrix.postScale(scaleFactor, scaleFactor, FocusX, FocusY);
+                checkBorderAndCenterWhenScale();
+                setImageMatrix(mScaleMatrix);
+
+                isAutoScale = false;
+            }
+
+        }
+    }
+
+
+    private class AutoRotateRunnable implements Runnable {
+        float targetRotateAngle;
+        long TotalRotateTimes;
+        float AnglePerTime;
+        float AccumulativeRotateTimes;
+        float AccumulativeRotateAngles;
+        float initScaleLevel;
+        double ScalePerTime;
+
+        AutoRotateRunnable(float targetRotateAngle, float initScaleLevel, long TotalRotateTimes) {
+            this.targetRotateAngle = targetRotateAngle;
+            this.TotalRotateTimes = TotalRotateTimes;
+            AnglePerTime = targetRotateAngle / this.TotalRotateTimes;
+            AccumulativeRotateTimes = 0;
+            AccumulativeRotateAngles = 0.0f;
+            this.initScaleLevel = initScaleLevel;
+            if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_RESTORATION) {
+                ScalePerTime = Math.pow(mInitScale / initScaleLevel, 1.0 / TotalRotateTimes);
+            } else if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_MAGNETISM) {
+                if (mOrientation == XuanImageViewSettings.ORIENTATION_LANDSCAPE)
+                    ScalePerTime = Math.pow(mInitScale / initScaleLevel, 1.0 / TotalRotateTimes);
+                else if (mOrientation == XuanImageViewSettings.ORIENTATION_PORTRAIT)
+                    ScalePerTime = Math.pow(mInitPortraitScale / initScaleLevel, 1.0 / TotalRotateTimes);
+            }
+
+        }
+
+        @Override
+        public void run() {
+            calculateImageCenterCoordinates();
+
+            mScaleMatrix.postRotate(AnglePerTime, ImageCenterX, ImageCenterY);
+            mScaleMatrix.postScale((float) ScalePerTime, (float) ScalePerTime, ImageCenterX, ImageCenterY);
+            mScaleMatrix.postTranslate((XuanImageViewCenterX - ImageCenterX) / (TotalRotateTimes - AccumulativeRotateTimes), (XuanImageViewCenterY - ImageCenterY) / (TotalRotateTimes - AccumulativeRotateTimes));
+            setImageMatrix(mScaleMatrix);
+            AccumulativeRotateTimes++;
+            AccumulativeRotateAngles += AnglePerTime;
+
+
+            if (AccumulativeRotateTimes < TotalRotateTimes) {
+                postDelayed(this, autoRotationRunnableDelay);
+            } else {
+                currentScaleLevel = getCurrentScaleLevel();
+                currentAbsScaleLevel = Math.abs(currentScaleLevel);
+                calculateImageCenterCoordinates();
+                mScaleMatrix.postRotate(targetRotateAngle - AccumulativeRotateAngles, ImageCenterX, ImageCenterY);
+                if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_RESTORATION) {
+                    mScaleMatrix.postScale(Math.abs(mInitScale / currentScaleLevel), Math.abs(mInitScale / currentScaleLevel), ImageCenterX, ImageCenterY);
+                } else if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_MAGNETISM) {
+                    if (mOrientation == XuanImageViewSettings.ORIENTATION_LANDSCAPE) {
+                        mScaleMatrix.postScale(Math.abs(mInitScale / currentScaleLevel), Math.abs(mInitScale / currentScaleLevel), ImageCenterX, ImageCenterY);
+                    } else if (mOrientation == XuanImageViewSettings.ORIENTATION_PORTRAIT) {
+//                        mScaleMatrix.postScale(Math.abs(mInitPortraitScale / getCurrentScaleLevel()), Math.abs(mInitPortraitScale / getCurrentScaleLevel()), ImageCenterX, ImageCenterY);
+                        mTempInitPortraitScale = Math.abs(currentScaleLevel);
+                        mTempPortraitMaxScale = mTempInitPortraitScale * mMaxScaleMultiple;
+                        mTempPortraitDoubleTabScale = mTempInitPortraitScale * mDoubleTabScaleMultiple;
+                    }
+                }
+                mScaleMatrix.postTranslate(XuanImageViewCenterX - ImageCenterX, XuanImageViewCenterY - ImageCenterY);
+
+                checkBorderAndCenterWhenScale();
+
+                setImageMatrix(mScaleMatrix);
+                isAutoRotated = false;
+            }
+
+
+        }
+    }
+
+    /**
+     * Set a boolean value to determine whether rotation function is turned on.
+     *
+     * @param toggle determine whether rotation function is turned on
+     */
+    public void setRotationToggle(boolean toggle) {
+        mRotationToggle = toggle;
+    }
+
+    /**
+     * @return current RotationToggle
+     */
+    public boolean getRotationToggle() {
+        return mRotationToggle;
+    }
+
+    /**
+     * Set AutoRotateCategory, there are two alternative values of it : XuanImageViewSettings.AUTO_ROTATE_CATEGORY_RESTORATION, XuanImageViewSettings.AUTO_ROTATE_CATEGORY_MAGNETISM.
+     *
+     * @param category
+     */
+    public void setAutoRotateCategory(int category) {
+        if (category == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_RESTORATION || category == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_MAGNETISM)
+            mAutoRotateCategory = category;
+        else
+            mAutoRotateCategory = XuanImageViewSettings.AUTO_ROTATE_CATEGORY_RESTORATION;
+    }
+
+    /**
+     * @return current AutoRotateCategory
+     */
+    public int getAutoRotateCategory() {
+        return mAutoRotateCategory;
+    }
+
+    /**
+     * An image is scaled to an InitScale to fit the size of XuanImageView at the very beginning.
+     * MaxScale = MaxScaleMultiple * InitScale holds.
+     *
+     * @param maxScaleMultiple
+     */
+    public void setMaxScaleMultiple(float maxScaleMultiple) {
+        mMaxScaleMultiple = maxScaleMultiple;
+    }
+
+    /**
+     * @return current MaxScaleMultiple
+     */
+    public float getMaxScaleMultiple() {
+        return mMaxScaleMultiple;
+    }
+
+    /**
+     * When image's current scale level is smaller than DoubleTabScale, the image will scale up to DoubleTapScale if an double-tap gesture is detected.
+     * DoubleTapScale = DoubleTabScaleMultiple * InitScale holds.
+     *
+     * @param doubleTabScaleMultiple
+     */
+    public void setDoubleTabScaleMultiple(float doubleTabScaleMultiple) {
+        mDoubleTabScaleMultiple = doubleTabScaleMultiple;
+    }
+
+    /**
+     * @return current DoubleTabScaleMultiple
+     */
+    public float getDoubleTabScaleMultiple() {
+        return mDoubleTabScaleMultiple;
+    }
+
+    /**
+     * If current scale level is smaller than InitScale and image is not in rotation state,
+     * the image will scale up to InitScale with SpringBackGradientScaleUpLevel step by step.
+     * Default springBackGradientScaleUpLevel is  1.01f.
+     *
+     * @param springBackGradientScaleUpLevel
+     */
+    public void setSpringBackGradientScaleUpLevel(float springBackGradientScaleUpLevel) {
+        mSpringBackGradientScaleUpLevel = springBackGradientScaleUpLevel;
+    }
+
+    /**
+     * @return current SpringBackGradientScaleUpLevel
+     */
+    public float getSpringBackGradientScaleUpLevel() {
+        return mSpringBackGradientScaleUpLevel;
+    }
+
+    /**
+     * If current scale level is bigger than MaxScale and image is not in rotation state,
+     * the image will scale down to MaxScale with SpringBackGradientScaleDownLevel step by step.
+     * Default springBackGradientScaleDownLevel is 0.99f.
+     *
+     * @param springBackGradientScaleDownLevel
+     */
+    public void setSpringBackGradientScaleDownLevel(float springBackGradientScaleDownLevel) {
+        mSpringBackGradientScaleDownLevel = springBackGradientScaleDownLevel;
+    }
+
+    /**
+     * @return current SpringBackGradientScaleDownLevel
+     */
+    public float getSpringBackGradientScaleDownLevel() {
+        return mSpringBackGradientScaleDownLevel;
+    }
+
+    /**
+     * When image's current scale level is smaller than DoubleTabScale,
+     * the image will scale up to DoubleTapScale with DoubleTapGradientScaleUpLevel step by step if a double-tap gesture is detected.
+     * Default doubleTalGradientScaleUpLevel is 1.05f.
+     *
+     * @param doubleTapGradientScaleUpLevel
+     */
+    public void setDoubleTapGradientScaleUpLevel(float doubleTapGradientScaleUpLevel) {
+        mDoubleTapGradientScaleUpLevel = doubleTapGradientScaleUpLevel;
+    }
+
+    /**
+     * @return current DoubleTapGradientScaleUpLevel
+     */
+    public float getDoubleTapGradientScaleUpLevel() {
+        return mDoubleTapGradientScaleUpLevel;
+    }
+
+    /**
+     * When image's current scale level is bigger than DoubleTabScale,
+     * the image will scale down to InitScale with DoubleTapGradientScaleDownLevel step by step if a double-tap gesture is detected.
+     * Default doubleTabGradientScaleDownLevel is 0.95f.
+     *
+     * @param doubleTapGradientScaleDownLevel
+     */
+    public void setDoubleTabGradientScaleDownLevel(float doubleTapGradientScaleDownLevel) {
+        mDoubleTapGradientScaleDownLevel = doubleTapGradientScaleDownLevel;
+    }
+
+    /**
+     * @return current DoubleTapGradientScaleDownLevel
+     */
+    public float getDoubleTapGradientScaleDownLevel() {
+        return mDoubleTapGradientScaleDownLevel;
+    }
+
+    /**
+     * When image's current rotation angle is bigger than AutoRotationTrigger, the image will rotate in the same direction and scale back to it's initial state if rotation gesture is released.
+     * When image's current rotation angle is smaller than AutoRotationTrigger, the image will rotate in the opposite direction and scale back to it's initial state if rotation gesture is released.
+     * Default AutoRotationTrigger is 60 (degrees).
+     *
+     * @param autoRotationTrigger
+     */
+    public void setAutoRotationTrigger(float autoRotationTrigger) {
+        this.autoRotationTrigger = autoRotationTrigger;
+    }
+
+    /**
+     * @return current AutoRotationTrigger
+     */
+    public float getAutoRotationTrigger() {
+        return autoRotationTrigger;
+    }
+
+    /**
+     * Default SpringBackRunnableDelay is 10 (milliseconds).
+     *
+     * @param delay
+     */
+    public void setSpringBackRunnableDelay(int delay) {
+        springBackRunnableDelay = delay;
+    }
+
+    /**
+     * @return current SpringBackRunnableDelay
+     */
+    public int getSpringBackRunnableDelay() {
+        return springBackRunnableDelay;
+    }
+
+    /**
+     * Default DoubleTapRunnableDelay is 10 (milliseconds).
+     *
+     * @param delay
+     */
+    public void setDoubleTapScaleRunnableDelay(int delay) {
+        doubleTabScaleRunnableDelay = delay;
+    }
+
+    /**
+     * @return current DoubleTabScaleRunnableDelay
+     */
+    public int getDoubleTabScaleRunnableDelay() {
+        return doubleTabScaleRunnableDelay;
+    }
+
+    /**
+     * Default AutoRotationRunnableDelay is 5 (milliseconds).
+     *
+     * @param delay
+     */
+    public void setAutoRotationRunnableDelay(int delay) {
+        autoRotationRunnableDelay = delay;
+    }
+
+    /**
+     * @return current AutoRotationRunnableDelay
+     */
+    public int getAutoRotationRunnalbleDelay() {
+        return autoRotationRunnableDelay;
+    }
+
+    /**
+     * Default AutoRotationRunnableTimes is 10 (times).
+     *
+     * @param times
+     */
+    public void setAutoRotationRunnableTimes(int times) {
+        autoRotationRunnableTimes = times;
+    }
+
+    /**
+     * @return current AutoRotationRunnableTimes
+     */
+    public int getAutoRotationRunnableTimes() {
+        return autoRotationRunnableTimes;
+    }
+
+
+    /**
+     * Notice the Image can only start to be rotated when it's in initial state. But the image may be scaled up or down a little bit by ScaleGestureDetector
+     * in advance when you try to rotate it, hence, currentScaleLevel is not precisely equal to initScaleLevel. Here, an AllowableFloatError is existed to handle
+     * this situation. When Math.abs(currentScaleLevel - initScaleLevel) is smaller than allowableFloatError, RotateGestureDetector.onTouchEvent() can be invoked.
+     * Default allowableFloatError is 1E-6, it should be compatible with most of devices. For devices whose display resolution and aspect ratio is not normal, allowableFloatError may
+     * need to be tuned. eg., for Galaxy S8, 3E-3 works well. Of course, 3E-3 also works for most of devices because 1E-6 is smaller than 3E-3.
+     * @param allowableFloatError
+     */
+    public void setAllowableFloatError(double allowableFloatError){
+        this.allowableFloatError = allowableFloatError;
+    }
+
+    public double getAllowableFloatError(){
+        return allowableFloatError;
+    }
+
+    /**
+     * In  AUTO_ROTATE_CATEGORY_MAGNETISM mode, the image may be showed under a fixed rotation angle like 90 degrees, 270 degrees,
+     * 450 degrees, ect., then allowablePortraitFloatError should handle the situation when currentPortraitScaleLevel is not precisely
+     * equal to initPortraitScaleLevel.
+     * Default allowablePortraitFloatError is 1E-12, it should be compatible with most of devices. For devices whose display resolution and aspect ratio is not normal, allowablePortraitFloatError may
+     * need to be tuned. eg., for Galaxy S8, 5E-8 works well. Of course, 5E-8 also works for most of devices because 1E-12 is smaller than 5E-8.
+     * @see XuanImageView#setAllowableFloatError(double)
+     *
+     * @param allowablePortraitFloatError
+     */
+    public void setAllowablePortraitFloatError(double allowablePortraitFloatError){
+        this.allowablePortraitFloatError = allowablePortraitFloatError;
+    }
+
+    public double getAllowablePortraitFloatError(){
+        return allowablePortraitFloatError;
+    }
+
+}

+ 13 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/update/xuanimageview/XuanImageViewSettings.java

@@ -0,0 +1,13 @@
+package com.usai.redant.apexdrivers.update.xuanimageview;
+
+/**
+ * Created by xuanyihuang on 20/12/2016.
+ */
+
+public class XuanImageViewSettings {
+    public static int ORIENTATION_PORTRAIT = 1;
+    public static int ORIENTATION_LANDSCAPE = 2;
+
+    public static int AUTO_ROTATE_CATEGORY_RESTORATION = 1;
+    public static int AUTO_ROTATE_CATEGORY_MAGNETISM = 2;
+}

+ 407 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/upload/TaskActivity.java

@@ -0,0 +1,407 @@
+package com.usai.redant.apexdrivers.upload;
+
+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.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageButton;
+import android.widget.ListView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.usai.redant.apexdrivers.ApexDriverApplication;
+import com.usai.redant.apexdrivers.ApexDriversBackgroundService;
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.rautils.upload.RAUploadManager;
+import com.usai.redant.rautils.utils.dbgUtil;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+
+public class TaskActivity extends AppCompatActivity {
+
+    ArrayList<Bundle> task_arr= new ArrayList<>();
+    ListView tasklist;
+
+
+    private RAUploadManager uploadManager;
+
+
+    private ApexDriversBackgroundService.MyBinder binder;
+    private ApexDriversBackgroundService uploadServiceservice;
+    private ServiceConnection serviceConnection;
+
+    boolean serviceConnectionFlag;
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_task);
+
+
+//设置返回键
+        ActionBar mActionBar = getSupportActionBar();
+        mActionBar.setHomeButtonEnabled(true);
+        mActionBar.setDisplayHomeAsUpEnabled(true);
+        mActionBar.setTitle("Apex Drivers");
+
+
+
+
+
+        tasklist = (ListView)findViewById(R.id.task_list);
+        tasklist.setAdapter(new TaskAdapter(this,R.layout.upload_cell));
+
+        serviceConnection = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+
+
+                Log.d("_LIST","-------connected service  tid="+Thread.currentThread().getId());
+                serviceConnectionFlag = true;
+
+                binder = (ApexDriversBackgroundService.MyBinder)service;
+                uploadServiceservice = (ApexDriversBackgroundService)binder.getService();
+
+                uploadManager = uploadServiceservice.getUploadManager();
+                if (uploadManager == null) {
+                    return;
+                }
+
+
+                task_arr = (ArrayList<Bundle>) uploadManager.get_arr_queue().clone(); //uploadManager.arr_queue;
+
+                Collections.reverse(task_arr);
+                final TaskAdapter adapter = (TaskAdapter)tasklist.getAdapter();
+
+                adapter.notifyDataSetChanged();
+
+
+                uploadManager.uiUpdateListener = new RAUploadManager.UIUpdateListener() {
+                    @Override
+                    public void updateCell(long index, Bundle taskinfo) {
+                        if(true) {
+
+                            runOnUiThread(new Runnable() {
+                                @Override
+                                public void run() {
+
+                                    adapter.notifyDataSetChanged();
+                                }
+                            });
+
+                            return;
+                        }
+
+                    }
+
+                    @Override
+                    public void updateList(final ArrayList<Bundle> newlist) {
+                        if(true) {
+
+                            runOnUiThread(new Runnable() {
+                                @Override
+                                public void run() {
+                                    task_arr = (ArrayList<Bundle>) newlist.clone();
+                                    Collections.reverse(task_arr);
+                                    adapter.notifyDataSetChanged();
+                                }
+                            });
+
+                            return;
+                        }
+
+                    }
+                };
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                serviceConnectionFlag = false;
+                uploadManager.uiUpdateListener = null;
+            }
+        };
+
+        bindService();
+
+    }
+
+    @Override
+    protected void onDestroy() {
+
+        unbindService(serviceConnection);
+
+        super.onDestroy();
+    }
+
+
+    private void bindService() {
+        Intent intent = new Intent(TaskActivity.this, ApexDriversBackgroundService.class);
+
+        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
+    }
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.upload_list_menu,menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home: {
+                finish();
+            }
+            break;
+            case R.id.clear_upload_list_btn: {
+                clearUploadList();
+            }
+            break;
+        }
+        return true;
+    }
+
+    private void clearUploadList() {
+        if (task_arr.size() == 0) {
+            new AlertDialog.Builder(this)
+                    .setTitle("Warning")
+                    .setMessage("Upload list is empty.")
+                    .setPositiveButton("OK",null)
+                    .show();
+            return;
+        }
+
+        new AlertDialog.Builder(this)
+                .setTitle("Clear upload list")
+                .setMessage("Are you sure remove all error/finish task?")
+                .setPositiveButton("YES", new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        // clear
+                        if (serviceConnectionFlag == true && uploadServiceservice != null) {
+                            uploadManager.clearTask();
+//                            adapter.notifyDataSetChanged();
+                        }
+
+                    }
+                })
+                .setNegativeButton("NO", new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+
+                    }
+                })
+                .show();
+
+    }
+    class TaskAdapter extends ArrayAdapter
+    {
+
+        public TaskAdapter(@NonNull Context context, @LayoutRes int resource/*, @NonNull ArrayList<Bundle> objects*/) {
+            super(context, resource/*, objects*/);
+        }
+
+        @Override
+        public int getCount() {
+            return task_arr.size();
+        }
+
+        @Override
+        public View getView(final int position, @Nullable View convertView, @NonNull ViewGroup parent) {
+
+            Log.d("_LIST","running  tid="+Thread.currentThread().getId());
+            long timeStart = System.currentTimeMillis();
+
+            final Bundle item = task_arr.get(position);
+//            Bundle taskinfo = item.getBundle("params");
+//            String barcode = taskinfo.getString("barcode");
+
+            RAUploadManager.TaskStatus istatus = RAUploadManager.TaskStatus.values()[item.getInt("status",0)];
+            String status="";
+            switch(istatus)
+            {
+                case TaskStatusStart:
+                    status="uploading";
+                    break;
+                case TaskStatusStop:
+                    status="stop";
+                    break;
+                case TaskStatusError:
+                    status="warning";
+                    break;
+                case TaskStatusWait:
+                    status="waiting";
+                    break;
+                case TaskStatusFinish:
+                    status="finish";
+                    break;
+
+                default:
+                    status="warning";
+                    break;
+            }
+            double percent = item.getDouble("progress",(double)item.getInt("progress",0));
+            String err = item.getString("msg");
+            String name = item.getString("order","");
+            String action = item.getString("action","");
+            String time = item.getString("time");
+            String photoName = item.getString("name");
+
+            View cell;
+            ViewHolder holder;
+            if (convertView != null) {
+
+                cell = convertView;
+                holder = (ViewHolder)cell.getTag();
+                Log.d("_LIST", "getView: " + holder);
+
+            } else {
+
+                cell = View.inflate(getApplicationContext(), R.layout.upload_cell, null);
+                holder = new ViewHolder();
+                holder.name_tv = (TextView)cell.findViewById(R.id.upload_cell_title_tv);
+                holder.photoName_tv = cell.findViewById(R.id.upload_cell_name_tv);
+                holder.action_tv = cell.findViewById(R.id.upload_cell_action_tv);
+                holder.time_tv = cell.findViewById(R.id.upload_cell_time_tv);
+                holder.progressBar = (ProgressBar)cell.findViewById(R.id.upload_progressBar);
+                holder.state_tv = (TextView)cell.findViewById(R.id.upload_state_tv);
+                holder.progress_tv = (TextView)cell.findViewById(R.id.upload_progress_tv);
+                holder.err_tv = (TextView)cell.findViewById(R.id.upload_err_tv);
+                holder.btn_reload=(ImageButton) cell.findViewById(R.id.btn_reload);
+                cell.setTag(holder);
+            }
+                holder.btn_reload.setImageResource(R.drawable.ic_action_reload);
+                if(istatus!= RAUploadManager.TaskStatus.TaskStatusError&& istatus!= RAUploadManager.TaskStatus.TaskStatusStop)
+                {
+                    holder.btn_reload.setVisibility(View.GONE);
+                }
+                else
+                {
+                    holder.btn_reload.setVisibility(View.VISIBLE);
+                    Log.d("_LIST", "new listener: position" + position);
+                    holder.btn_reload.setOnClickListener(new View.OnClickListener() {
+                        @Override
+                        public void onClick(View v) {
+
+
+                            Bundle task = task_arr.get(position);
+
+                            Log.d("_LIST", "retry: " + task.getString("file","file is null") +"position" + position);
+
+
+
+//                            boolean canstart = true;
+
+                            ConnectivityManager connManager = (ConnectivityManager) ApexDriverApplication.sharedApplication().getSystemService(Context.CONNECTIVITY_SERVICE);
+
+                            NetworkInfo networkInfo = connManager.getActiveNetworkInfo();
+                            if (networkInfo == null)
+                            {
+                                dbgUtil.Logd(
+                                        "Current Network info",
+                                        "can not get Active NetworkInfo!");
+//                                canstart = false;
+                                Toast toast = Toast.makeText(getApplicationContext(),
+                                        "No available network. Please try upload manually later.",
+                                        Toast.LENGTH_LONG);
+                                toast.setGravity(Gravity.CENTER, 0, 0);
+                                toast.show();
+                                return;
+                            }
+                            else
+                            {
+                                NetworkInfo.State netState = networkInfo
+                                        .getState();
+                                if (netState != NetworkInfo.State.CONNECTED)
+                                {
+                                    dbgUtil.Logd(
+                                            "Current Network info",
+                                            "not Connected!State="
+                                                    + netState);
+//                                    canstart = false;
+
+                                    Toast toast = Toast.makeText(getApplicationContext(),
+                                            "App network connection. Please try upload manually later.",
+                                            Toast.LENGTH_LONG);
+                                    toast.setGravity(Gravity.CENTER, 0, 0);
+                                    toast.show();
+                                    return;
+                                }
+                                else
+                                {
+                                    int iconntype = -1;
+                                    iconntype = networkInfo
+                                            .getType();
+
+                                    SharedPreferences pref = ApexDriverApplication.sharedApplication().getSharedPreferences("UploadManager", 0);
+                                    boolean wifi_only = pref.getBoolean("wifi_only", false);
+                                    if(wifi_only == true && iconntype != ConnectivityManager.TYPE_WIFI && iconntype != 9)
+                                    {
+
+                                        Toast toast = Toast.makeText(getApplicationContext(),
+                                                "App is set to upload via WIFI only. Please change your setting or try upload manually later.",
+                                                Toast.LENGTH_LONG);
+                                        toast.setGravity(Gravity.CENTER, 0, 0);
+                                        toast.show();
+                                        return;
+                                    }
+                                }
+
+                            }
+
+                            v.setVisibility(View.GONE);
+                            uploadManager.startTask(task);
+
+                        }
+                    });
+                }
+
+
+
+            holder.name_tv.setText(name);
+            holder.action_tv.setText(action);
+            holder.time_tv.setText(time);
+            holder.progressBar.setProgress((int) (100 * percent));
+            holder.state_tv.setText(status);
+            holder.progress_tv.setText(String.format("%.2f%%",100*percent));
+            holder.err_tv.setText(err);
+            holder.photoName_tv.setText(photoName);
+
+
+            Log.d("_LIST", "getView(): POSITION   "+position+" file   " + item.getString("file","no file"));
+            return cell;
+        }
+
+        private class ViewHolder {
+            public TextView name_tv;
+            public TextView photoName_tv;
+            public TextView action_tv;
+            public TextView time_tv;
+            public ProgressBar progressBar;
+            public TextView state_tv;
+            public TextView progress_tv;
+            public TextView err_tv;
+            public ImageButton btn_reload;
+        }
+    }
+}

+ 429 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/upload/UploadListActivity.java

@@ -0,0 +1,429 @@
+package com.usai.redant.apexdrivers.upload;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageButton;
+import android.widget.ListView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.usai.redant.apexdrivers.ApexDriversBackgroundService;
+import com.usai.redant.apexdrivers.R;
+import com.usai.redant.rautils.upload.RAUploadManager;
+
+
+import java.util.ArrayList;
+
+
+public class UploadListActivity extends AppCompatActivity {
+
+
+    private ArrayList<Bundle> displaylist = new ArrayList<Bundle>();
+
+    //    private SwipeMenuListView uploadList;
+    private ListView uploadList;
+    private uploadAdapter adapter;
+//    private ArrayList<Bundle> arr;
+
+    private RAUploadManager uploadManager;
+    private ApexDriversBackgroundService.MyBinder binder;
+    private ApexDriversBackgroundService uploadServiceservice;
+    private boolean serviceConnectionFlag = false; // 绑定服务标识
+    private ServiceConnection serviceConnection;
+    private RAUploadManager.UIUpdateListener uiUpdateListener = new RAUploadManager.UIUpdateListener() {
+
+        @Override
+        public void updateCell(long index, final Bundle taskinfo)
+        {
+            if(true)
+                return;
+
+            if(true) {
+
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+
+                        adapter.notifyDataSetChanged();
+                    }
+                });
+
+                return;
+            }
+
+            final int position = (int)index;
+
+            runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+
+                    double percent = taskinfo.getDouble("progress",0);
+
+                    long timeStart = System.currentTimeMillis();
+
+                    int startShowIndex = uploadList.getFirstVisiblePosition();
+                    int lastShowIndex = uploadList.getLastVisiblePosition();
+
+                    if (position >= startShowIndex && position <= lastShowIndex) {
+                        View cell = uploadList.getChildAt((int)position - startShowIndex);
+                        uploadAdapter.ViewHolder holder= (uploadAdapter.ViewHolder)cell.getTag();
+
+
+
+                        RAUploadManager.TaskStatus istatus = RAUploadManager.TaskStatus.values()[taskinfo.getInt("status",0)];
+                        String status="";
+                        switch(istatus)
+                        {
+                            case TaskStatusStart:
+                                status="uploading";
+                                break;
+                            case TaskStatusStop:
+                                status="stop";
+                                break;
+                            case TaskStatusError:
+                                status="warning";
+                                break;
+                            case TaskStatusWait:
+                                status="waiting";
+                                break;
+                            case TaskStatusFinish:
+                                status="finish";
+                                break;
+
+                            default:
+                                status="warning";
+                                break;
+                        }
+
+
+
+                        String err = taskinfo.getString("msg");
+                        String name = taskinfo.getString("order","");
+                        String action = taskinfo.getString("action","");
+                        String time = taskinfo.getString("time");
+                        String photoName = taskinfo.getString("name");
+
+                        holder.name_tv.setText(name);
+                        holder.progressBar.setProgress((int)(100 *percent));
+                        holder.state_tv.setText(status);
+                        holder.action_tv.setText(action);
+                        holder.time_tv.setText(time);
+                        holder.progress_tv.setText(String.format("%.2f%%",100*percent));
+                        holder.err_tv.setText(err);
+                        holder.photoName_tv.setText(photoName);
+                    }
+
+                }
+            });
+        }
+
+        @Override
+        public void updateList(final ArrayList<Bundle> newlist)
+        {
+
+
+            runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+
+                    Log.d("_LIST","running  tid="+Thread.currentThread().getId());
+                    Log.d("_LIST", "uploadlist UI  size:  "+ newlist.size());
+                    long timeStart = System.currentTimeMillis();
+                    displaylist = (ArrayList<Bundle>) newlist.clone();
+                    adapter.notifyDataSetChanged();
+
+                }
+            });
+
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_upload_list);
+
+        ActionBar mActionBar = getSupportActionBar();
+        mActionBar.setHomeButtonEnabled(true);
+        mActionBar.setDisplayHomeAsUpEnabled(true);
+        mActionBar.setTitle("Apex Drivers");
+
+
+        // 初始化视图
+        uploadList = (ListView)findViewById(R.id.upload_list);
+        adapter = new uploadAdapter(R.layout.upload_cell);
+        uploadList.setAdapter(adapter);
+        serviceConnection = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder iBinder) {
+                serviceConnectionFlag = true;
+
+                binder = (ApexDriversBackgroundService.MyBinder)iBinder;
+                uploadServiceservice = (ApexDriversBackgroundService)binder.getService();
+
+
+
+
+                uploadManager = uploadServiceservice.getUploadManager();
+                if (uploadManager == null) {
+                    return;
+                }
+                uploadManager.uiUpdateListener = uiUpdateListener;
+                displaylist = (ArrayList<Bundle>) uploadManager.get_arr_queue().clone(); //uploadManager.arr_queue;
+                adapter.notifyDataSetChanged();
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                serviceConnectionFlag = false;
+                uploadManager.uiUpdateListener = null;
+            }
+        };
+
+        bindService();
+
+    }
+
+    @Override
+    protected void onDestroy() {
+
+        unbindService(serviceConnection);
+
+        super.onDestroy();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.upload_list_menu,menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home: {
+                finish();
+            }
+            break;
+            case R.id.clear_upload_list_btn: {
+
+                clearUploadList();
+            }
+            break;
+        }
+        return true;
+    }
+
+    private void clearUploadList() {
+        if (displaylist.size() == 0) {
+            new AlertDialog.Builder(this)
+                    .setTitle("Warning")
+                    .setMessage("Upload list is empty.")
+                    .setPositiveButton("OK",null)
+                    .show();
+            return;
+        }
+
+        new AlertDialog.Builder(this)
+                .setTitle("Clear upload list")
+                .setMessage("Are you sure remove all error/finish task?")
+                .setPositiveButton("YES", new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        // clear
+                        if (serviceConnectionFlag == true && uploadServiceservice != null) {
+                            uploadManager.clearTask();
+//                            adapter.notifyDataSetChanged();
+                        }
+
+                    }
+                })
+                .setNegativeButton("NO", new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+
+                    }
+                })
+                .show();
+
+    }
+
+
+    private class uploadAdapter extends BaseAdapter {
+        private int resourceId;
+        public uploadAdapter(int resourceID) {
+
+            resourceId = resourceID;
+        }
+
+        @Override
+        public int getCount() {
+            return displaylist.size();
+        }
+
+        @Override
+        public Bundle getItem(int position) {
+            return displaylist.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+//        @Override
+//        public int getViewTypeCount() {
+//            return 4;
+//        }
+//
+//        @Override
+//        public int getItemViewType(int position) {
+//            /**
+//             * 0. <finish>      remove              </finish>
+//             * 1. <error>       start  remove       </error>
+//             * 2. <stop>        start               </stop>
+//             * 3. <start\wait>  none                </start\wait>
+//             * */
+//            return taskSwipeType(position);
+//        }
+
+        @NonNull
+        @Override
+        public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
+
+            Log.d("_LIST","running  tid="+Thread.currentThread().getId());
+            long timeStart = System.currentTimeMillis();
+
+            final Bundle item = displaylist.get(position);
+
+            RAUploadManager.TaskStatus istatus = RAUploadManager.TaskStatus.values()[item.getInt("status",0)];
+            String status="";
+            switch(istatus)
+            {
+                case TaskStatusStart:
+                    status="uploading";
+                    break;
+                case TaskStatusStop:
+                    status="stop";
+                    break;
+                case TaskStatusError:
+                    status="warning";
+                    break;
+                case TaskStatusWait:
+                    status="waiting";
+                    break;
+                case TaskStatusFinish:
+                    status="finish";
+                    break;
+
+                default:
+                    status="warning";
+                    break;
+            }
+
+            double percent = item.getDouble("progress",0);
+            String err = item.getString("msg");
+            String name = item.getString("order","");
+            String action = item.getString("action","");
+            String time = item.getString("time");
+            String photoName = item.getString("name");
+
+            View cell;
+            ViewHolder holder;
+            if (convertView != null) {
+
+                cell = convertView;
+                holder = (ViewHolder)cell.getTag();
+
+            } else  {
+
+                cell = View.inflate(getApplicationContext(),resourceId,null);
+                holder = new ViewHolder();
+                holder.name_tv = (TextView)cell.findViewById(R.id.upload_cell_title_tv);
+                holder.photoName_tv = cell.findViewById(R.id.upload_cell_name_tv);
+                holder.action_tv = cell.findViewById(R.id.upload_cell_action_tv);
+                holder.time_tv = cell.findViewById(R.id.upload_cell_time_tv);
+                holder.progressBar = (ProgressBar)cell.findViewById(R.id.upload_progressBar);
+                holder.state_tv = (TextView)cell.findViewById(R.id.upload_state_tv);
+                holder.progress_tv = (TextView)cell.findViewById(R.id.upload_progress_tv);
+                holder.err_tv = (TextView)cell.findViewById(R.id.upload_err_tv);
+                holder.btn_reload=(ImageButton) cell.findViewById(R.id.btn_reload);
+
+
+                holder.btn_reload.setImageResource(R.drawable.ic_action_reload);
+                if(istatus!= RAUploadManager.TaskStatus.TaskStatusError)
+                {
+                    holder.btn_reload.setVisibility(View.GONE);
+                }
+                else
+                {
+                    holder.btn_reload.setVisibility(View.VISIBLE);
+                    holder.btn_reload.setOnClickListener(new View.OnClickListener() {
+                        @Override
+                        public void onClick(View v) {
+
+                            uploadManager.startTask(item);
+                            v.setVisibility(View.GONE);
+                        }
+                    });
+                }
+                cell.setTag(holder);
+            }
+
+            holder.name_tv.setText(name);
+            holder.action_tv.setText(action);
+            holder.time_tv.setText(time);
+            holder.progressBar.setProgress((int) (100 * percent));
+            holder.state_tv.setText(status);
+            holder.progress_tv.setText(String.format("%.2f%%",100*percent));
+            holder.err_tv.setText(err);
+            holder.photoName_tv.setText(photoName);
+
+
+
+            Log.d("_LIST", "getView(): POSITION   "+position+" COST   " + (System.currentTimeMillis() - timeStart));
+            return cell;
+        }
+
+        private class ViewHolder {
+            public TextView name_tv;
+            public TextView photoName_tv;
+            public TextView action_tv;
+            public TextView time_tv;
+            public ProgressBar progressBar;
+            public TextView state_tv;
+            public TextView progress_tv;
+            public TextView err_tv;
+            public ImageButton btn_reload;
+        }
+    }
+
+
+    /** Service */
+    private void bindService() {
+        Intent intent = new Intent(UploadListActivity.this, ApexDriversBackgroundService.class);
+
+        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
+    }
+
+    private int dp2px(int dp) {
+        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,getResources().getDisplayMetrics());
+    }
+}

+ 79 - 0
ApexDrivers/app/src/main/java/com/usai/redant/apexdrivers/utils/OperationQueue.java

@@ -0,0 +1,79 @@
+package com.usai.redant.apexdrivers.utils;
+
+import android.os.AsyncTask;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class OperationQueue {
+
+    /**
+     * 根据CPU核心数控制线程数
+     * */
+    private static final int count = Runtime.getRuntime().availableProcessors() * 3;
+
+    private static ExecutorService limitedTaskExecutor = null;
+
+    public OperationQueue() {
+        limitedTaskExecutor = Executors.newFixedThreadPool(count);
+    }
+
+    public void addOperationTask(OperationBackgroundCallBack backgroundCallBack, OperationCompletionCallBack completion, OperationCancelCallBack cancelCallBack) {
+
+        OperationTask task = new OperationTask(backgroundCallBack,completion,cancelCallBack);
+        task.executeOnExecutor(limitedTaskExecutor);
+    }
+
+    private class OperationTask extends AsyncTask<Void, Void, Object> {
+
+        private OperationBackgroundCallBack mBackgroundCallBack;
+        private OperationCompletionCallBack mCompletion;
+        private OperationCancelCallBack mCancelCallBack;
+
+        OperationTask(OperationBackgroundCallBack backgroundCallBack, OperationCompletionCallBack completion, OperationCancelCallBack cancelCallBack) {
+            mBackgroundCallBack = backgroundCallBack;
+            mCompletion = completion;
+            mCancelCallBack = cancelCallBack;
+        }
+
+        @Override
+        protected Object doInBackground(Void... voids) {
+
+            if (mBackgroundCallBack != null) {
+                return mBackgroundCallBack.operationDoInBackground();
+            }
+
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Object object) {
+
+            if (mCompletion != null) {
+                mCompletion.operationCompletion(object);
+            }
+        }
+
+        @Override
+        protected void onCancelled() {
+            super.onCancelled();
+
+            if (mCancelCallBack != null) {
+                mCancelCallBack.operationCancelled();
+            }
+        }
+    }
+
+    public interface OperationBackgroundCallBack {
+        Object operationDoInBackground();
+    }
+
+    public interface OperationCompletionCallBack {
+        void operationCompletion(Object object);
+    }
+
+    public interface OperationCancelCallBack {
+        void operationCancelled();
+    }
+
+}

+ 1 - 1
ApexDrivers/app/src/main/res/layout/activity_detail.xml

@@ -6,7 +6,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="#ffffff"
-    tools:context=".Detail.DetailActivity">
+    tools:context=".detail.DetailActivity">
 
     <android.support.v4.widget.SwipeRefreshLayout
         android:id="@+id/detail_refresh"

+ 1 - 1
ApexDrivers/app/src/main/res/layout/activity_home_more.xml

@@ -5,7 +5,7 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    tools:context=".Home.HomeMoreActivity">
+    tools:context=".home.HomeMoreActivity">
 
     <android.support.v4.widget.SwipeRefreshLayout
         android:id="@+id/home_more_refresh"

+ 1 - 1
ApexDrivers/app/src/main/res/layout/activity_message.xml

@@ -5,6 +5,6 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    tools:context=".Message.MessageActivity">
+    tools:context=".message.MessageActivity">
 
 </android.support.constraint.ConstraintLayout>

+ 1 - 1
ApexDrivers/app/src/main/res/layout/activity_order_filter.xml

@@ -5,7 +5,7 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    tools:context=".Filter.OrderFilterActivity">
+    tools:context=".filter.OrderFilterActivity">
 
     <ExpandableListView
         android:id="@+id/order_filter_listView"

+ 1 - 1
ApexDrivers/app/src/main/res/layout/activity_photo_preview.xml

@@ -7,7 +7,7 @@
     android:layout_height="match_parent"
     tools:context=".Update.PhotoPreviewActivity">
 
-    <com.usai.redant.apexdrivers.Update.XuanImageView.XuanImageView
+    <com.usai.redant.apexdrivers.update.xuanimageview.XuanImageView
         android:id="@+id/preview_image_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"/>

+ 1 - 1
ApexDrivers/app/src/main/res/layout/activity_signature.xml

@@ -7,7 +7,7 @@
     android:layout_height="match_parent"
     tools:context=".Signature.SignatureActivity">
 
-    <com.usai.redant.apexdrivers.Signature.SignatureView
+    <com.usai.redant.apexdrivers.signature.SignatureView
         android:id="@+id/signature_content_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"

+ 1 - 1
ApexDrivers/app/src/main/res/layout/activity_upload_list.xml

@@ -3,7 +3,7 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    tools:context="com.usai.redant.apexdrivers.Upload.UploadListActivity">
+    tools:context="com.usai.redant.apexdrivers.upload.UploadListActivity">
 
     <ListView
         android:id="@+id/upload_list"

+ 1 - 1
ApexDrivers/app/src/main/res/layout/capture.xml

@@ -20,7 +20,7 @@
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"/>
 
-  <com.usai.redant.apexdrivers.CodeScanner.ViewfinderView
+  <com.usai.redant.apexdrivers.codescanner.ViewfinderView
       android:id="@+id/viewfinder_view"
       android:layout_width="fill_parent"
       android:layout_height="fill_parent"/>

+ 2 - 2
ApexDrivers/app/src/main/res/layout/home_header_view.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<com.usai.redant.apexdrivers.Home.HomeHeaderView xmlns:android="http://schemas.android.com/apk/res/android"
+<com.usai.redant.apexdrivers.home.HomeHeaderView xmlns:android="http://schemas.android.com/apk/res/android"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
     android:background="@color/ApexDriverOrange"
@@ -251,4 +251,4 @@
 
     </LinearLayout>
 
-</com.usai.redant.apexdrivers.Home.HomeHeaderView>
+</com.usai.redant.apexdrivers.home.HomeHeaderView>

+ 2 - 2
ApexDrivers/app/src/main/res/layout/home_order_cell.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<com.usai.redant.apexdrivers.Home.HomeCellLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.usai.redant.apexdrivers.home.HomeCellLayout xmlns:android="http://schemas.android.com/apk/res/android"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content">
 
@@ -160,4 +160,4 @@
     </RelativeLayout>
 
 
-</com.usai.redant.apexdrivers.Home.HomeCellLayout>
+</com.usai.redant.apexdrivers.home.HomeCellLayout>

+ 1 - 1
ApexDrivers/app/src/main/res/layout/section_header.xml

@@ -49,7 +49,7 @@
                 android:text="New Order"
                 />
 
-            <com.usai.redant.apexdrivers.BadgeView.BadgeView
+            <com.usai.redant.apexdrivers.badgeview.BadgeView
                 android:id="@+id/header_badge_tv"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"