Răsfoiți Sursa

1.修改Android RAUtils重命名部分工具包。

Pen Li 7 ani în urmă
părinte
comite
41ecfee4e0
85 a modificat fișierele cu 4328 adăugiri și 133 ștergeri
  1. 2 2
      ApexDrivers/RAUtilsLibrary/src/main/AndroidManifest.xml
  2. 132 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/actionsheet/ActionSheet.java
  3. 47 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/Contact.java
  4. 1769 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/ContactHelper.java
  5. 449 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/ContactUIHelper.java
  6. 19 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/element/BaseElement.java
  7. 21 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/element/EmailAddress.java
  8. 32 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/element/Name.java
  9. 10 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/element/Note.java
  10. 14 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/element/Organization.java
  11. 39 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/element/PhoneNumber.java
  12. 16 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/element/Photo.java
  13. 25 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/element/PostalAddress.java
  14. 48 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/element/SocialProfile.java
  15. 10 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/element/WebSite.java
  16. 297 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/datetimepicker/DatePickerView.java
  17. 246 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/datetimepicker/DateTimePickerDialog.java
  18. 138 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/datetimepicker/TimePickerView.java
  19. 147 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/fileviewer/FileViewerActivity.java
  20. 121 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/fileviewer/FileViewerAdapter.java
  21. 180 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/infinitephoto/InfinitePhotoActivity.java
  22. 125 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/infinitephoto/InfinitePhotoItem.java
  23. 92 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/infinitephoto/InfinitePhotoItemCell.java
  24. 8 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/infinitephoto/InfinitePhotoItemUIDelegate.java
  25. 92 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/operationqueue/OperationQueue.java
  26. 120 0
      ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/roundcornerimageview/RoundCornerImageView.java
  27. 1 1
      ApexDrivers/RAUtilsLibrary/src/main/res/layout/file_viewer_activity.xml
  28. 1 1
      ApexDrivers/RAUtilsLibrary/src/main/res/layout/file_viewer_cell.xml
  29. 5 5
      ApexDrivers/RAUtilsLibrary/src/main/res/layout/ra_date_picker_compact_view.xml
  30. 5 5
      ApexDrivers/RAUtilsLibrary/src/main/res/layout/ra_date_picker_regular_view.xml
  31. 5 5
      ApexDrivers/RAUtilsLibrary/src/main/res/layout/ra_infinite_photo_cell.xml
  32. 5 5
      ApexDrivers/RAUtilsLibrary/src/main/res/layout/ra_time_picker_view.xml
  33. 4 6
      ApexDrivers/apexcrm/src/main/java/com/usai/apex/apexcrm/MainActivity.java
  34. 1 1
      ApexDrivers/apexcrm/src/main/java/com/usai/apex/apexcrm/dataProvider/DataProvider.java
  35. 1 1
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/ApexDriverApplication.java
  36. 1 1
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailPhotoModel.java
  37. 1 1
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/detail/model/DetailSignatureModel.java
  38. 1 1
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/home/HomeFragment.java
  39. 1 1
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/home/HomeOrderModel.java
  40. 1 1
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/message/MessageActivity.java
  41. 1 1
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/photoCell/PhotoItemModel.java
  42. 2 2
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/SettingActivity.java
  43. 1 1
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/SettingAdapter.java
  44. 1 1
      ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/setting/changepassword/ChangePasswordDialog.java
  45. 1 1
      ApexDrivers/apexdriverslib/src/main/res/layout/setting_about_cell.xml
  46. 2 2
      ApexDrivers/apexmobile/src/main/AndroidManifest.xml
  47. 1 1
      ApexDrivers/apexmobile/src/main/java/com/usai/apex/apexresult/ApexResultActivity.java
  48. 1 1
      ApexDrivers/apexmobile/src/main/java/com/usai/apex/apexresult/ApexResultPresenter.java
  49. 1 1
      ApexDrivers/apexmobile/src/main/res/layout/activity_about.xml
  50. 8 8
      ApexDrivers/apexmobile/src/main/res/layout/activity_function_select.xml
  51. 1 1
      ApexDrivers/apexmobile/src/main/res/layout/activity_help.xml
  52. 1 1
      ApexDrivers/apexmobile/src/main/res/layout/activity_history.xml
  53. 1 1
      ApexDrivers/apexmobile/src/main/res/layout/activity_retrieve_password.xml
  54. 1 1
      ApexDrivers/apexmobile/src/main/res/layout/activity_search_list.xml
  55. 1 1
      ApexDrivers/apexmobile/src/main/res/layout/announcement_cell.xml
  56. 1 1
      ApexDrivers/apexmobile/src/main/res/layout/announcement_item.xml
  57. 1 1
      ApexDrivers/apexmobile/src/main/res/layout/apex_result_document_cell.xml
  58. 1 1
      ApexDrivers/apexmobile/src/main/res/layout/commu_item_header.xml
  59. 1 1
      ApexDrivers/apexmobile/src/main/res/layout/detail_tracking_cell.xml
  60. 2 2
      ApexDrivers/apexmobile/src/main/res/layout/documents_list_item.xml
  61. 1 1
      ApexDrivers/apexmobile/src/main/res/layout/drag_list_item.xml
  62. 1 1
      ApexDrivers/apexmobile/src/main/res/layout/fragment_direct_tracking.xml
  63. 1 1
      ApexDrivers/apexmobile/src/main/res/layout/fragment_login.xml
  64. 2 2
      ApexDrivers/apexmobile/src/main/res/layout/fragment_tools.xml
  65. 1 1
      ApexDrivers/apexmobile/src/main/res/layout/group_tag.xml
  66. 1 1
      ApexDrivers/apexmobile/src/main/res/layout/kpi_item_cell.xml
  67. 1 1
      ApexDrivers/apexmobile/src/main/res/layout/saved_cell.xml
  68. 2 2
      ApexDrivers/apexmobile/src/main/res/layout/saved_doc.xml
  69. 2 2
      ApexDrivers/apexmobile/src/main/res/layout/search_item_datepicker.xml
  70. 1 1
      ApexDrivers/apexmobile/src/main/res/layout/static_modelist_cell.xml
  71. 3 3
      ApexDrivers/apexmobile/src/main/res/menu/about.xml
  72. 3 3
      ApexDrivers/apexmobile/src/main/res/menu/apex.xml
  73. 3 3
      ApexDrivers/apexmobile/src/main/res/menu/cargo_tracking.xml
  74. 3 3
      ApexDrivers/apexmobile/src/main/res/menu/change_password.xml
  75. 3 3
      ApexDrivers/apexmobile/src/main/res/menu/customize_fields.xml
  76. 3 3
      ApexDrivers/apexmobile/src/main/res/menu/function_select.xml
  77. 3 3
      ApexDrivers/apexmobile/src/main/res/menu/help.xml
  78. 3 3
      ApexDrivers/apexmobile/src/main/res/menu/inner_tools.xml
  79. 3 3
      ApexDrivers/apexmobile/src/main/res/menu/location_detail.xml
  80. 5 5
      ApexDrivers/apexmobile/src/main/res/menu/message.xml
  81. 7 7
      ApexDrivers/apexmobile/src/main/res/menu/result.xml
  82. 3 3
      ApexDrivers/apexmobile/src/main/res/menu/retrieve_password.xml
  83. 5 5
      ApexDrivers/apexmobile/src/main/res/menu/search.xml
  84. 3 3
      ApexDrivers/apexmobile/src/main/res/menu/search_list.xml
  85. 3 3
      ApexDrivers/apexmobile/src/main/res/menu/web.xml

+ 2 - 2
ApexDrivers/RAUtilsLibrary/src/main/AndroidManifest.xml

@@ -65,7 +65,7 @@
             android:exported="false">
         </receiver>
 
-        <activity android:name=".fileViewer.FileViewerActivity">
+        <activity android:name=".fileviewer.FileViewerActivity">
         </activity>
 
         <activity
@@ -82,7 +82,7 @@
 
         <activity android:name=".preview.RAPDFPreviewActivity"/>
 
-        <activity android:name=".InfinitePhoto.InfinitePhotoActivity"
+        <activity android:name=".infinitephoto.InfinitePhotoActivity"
             android:theme="@style/Theme.AppCompat.Light.NoActionBar.FullScreen"/>
 
         <activity android:name=".signature.SignatureActivity"

+ 132 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/actionsheet/ActionSheet.java

@@ -0,0 +1,132 @@
+package com.usai.redant.rautils.actionsheet;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.graphics.Color;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.text.TextPaint;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import com.usai.redant.rautils.R;
+import com.usai.redant.rautils.button.RAButton;
+
+import java.util.HashMap;
+
+public class ActionSheet extends Dialog implements View.OnClickListener {
+
+    private Context mCtx;
+    private LinearLayout mRootView;
+    private HashMap<Button,View.OnClickListener> buttonListenerMap = new HashMap<>();
+
+    public ActionSheet(@NonNull Context context) {
+        this(context,R.style.actionSheet);
+
+    }
+
+    public ActionSheet(@NonNull Context context, int themeResId) {
+        super(context, themeResId);
+
+        mCtx = context;
+        init();
+    }
+
+    protected ActionSheet(@NonNull Context context, boolean cancelable, @Nullable OnCancelListener cancelListener) {
+        super(context, cancelable, cancelListener);
+
+        mCtx = context;
+        init();
+    }
+
+    private void init() {
+        mRootView = (LinearLayout) LayoutInflater.from(mCtx).inflate(R.layout.action_sheet, null);
+        setContentView(mRootView);
+    }
+
+    public void addAction(String title, ActionType actionType, View.OnClickListener clickListener) {
+
+        RAButton button = new RAButton(mCtx);
+        button.setText(title);
+        button.setTextColor(Color.parseColor("#2577ff"));
+        button.setTextSize(TypedValue.COMPLEX_UNIT_SP,20);
+        button.setAllCaps(false);
+        button.setBackgroundResource(R.drawable.actionsheet_round_corner_normal_bg);
+        button.setOnClickListener(this);
+        if (clickListener != null) {
+            buttonListenerMap.put(button,clickListener);
+        }
+
+        button.setTitleColorForState(RAButton.RAButtonState.RAButtonStateHighlight,Color.RED);
+        button.setBackgroundDrawableForState(RAButton.RAButtonState.RAButtonStateNormal,mCtx.getResources().getDrawable(R.drawable.actionsheet_round_corner_normal_bg));
+
+        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,dp2px(mCtx,50));
+
+        int marginH = dp2px(mCtx,10);
+        int marginV = dp2px(mCtx,5);
+        switch (actionType) {
+            case ACtionTypeCancel: {
+                layoutParams.setMargins(marginH,marginV,marginH,marginV);
+
+                TextPaint tp = button.getPaint();
+                tp.setFakeBoldText(true);
+            }
+            break;
+            case ActionTypeDefault: {
+                layoutParams.setMargins(marginH,0,marginH,marginV);
+            }
+            break;
+        }
+
+        mRootView.addView(button,layoutParams);
+    }
+
+    private static int dp2px(Context context, float dpValue) {
+        float scale = context.getResources().getDisplayMetrics().density;
+        return (int) (dpValue * scale + 0.5f);
+    }
+
+    @Override
+    public void show() {
+
+        Window dialogWindow = getWindow();
+        if (dialogWindow != null) {
+
+            dialogWindow.setGravity(Gravity.BOTTOM);
+            WindowManager.LayoutParams layoutParams = dialogWindow.getAttributes(); // 获取对话框当前的参数值
+            layoutParams.x = 0; // 新位置X坐标
+            layoutParams.y = 0; // 新位置Y坐标
+            layoutParams.width = (int) mCtx.getResources().getDisplayMetrics().widthPixels; // 宽度
+            mRootView.measure(0, 0);
+            layoutParams.height = mRootView.getMeasuredHeight();
+
+            layoutParams.alpha = 9f; // 透明度
+            dialogWindow.setAttributes(layoutParams);
+        }
+
+        super.show();
+    }
+
+    @Override
+    public void onClick(View v) {
+
+        dismiss();
+
+        View.OnClickListener listener = buttonListenerMap.get(v);
+        if (listener != null) {
+            listener.onClick(v);
+        }
+    }
+
+    public enum ActionType {
+        ActionTypeDefault,
+        ACtionTypeCancel
+    }
+}

+ 47 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/Contact.java

@@ -0,0 +1,47 @@
+package com.usai.redant.rautils.contactkit;
+
+import com.usai.redant.rautils.contactkit.element.EmailAddress;
+import com.usai.redant.rautils.contactkit.element.Name;
+import com.usai.redant.rautils.contactkit.element.Note;
+import com.usai.redant.rautils.contactkit.element.Organization;
+import com.usai.redant.rautils.contactkit.element.PhoneNumber;
+import com.usai.redant.rautils.contactkit.element.Photo;
+import com.usai.redant.rautils.contactkit.element.PostalAddress;
+import com.usai.redant.rautils.contactkit.element.SocialProfile;
+import com.usai.redant.rautils.contactkit.element.WebSite;
+
+import java.util.ArrayList;
+
+public class Contact {
+
+    public static final Integer NEW_CONTACT_ID = -1;
+
+    private Integer ID;
+
+    public Name name;
+    public Photo photo;
+    public Organization organization;
+    public ArrayList<PhoneNumber>       phoneNumbers;
+    public ArrayList<EmailAddress>      emailAddresses;
+    public ArrayList<PostalAddress>     postalAddresses;
+    public ArrayList<SocialProfile>     socialProfiles;
+    public ArrayList<WebSite>           webSites;
+    public Note note;
+
+    public Contact() {
+        setID(NEW_CONTACT_ID);
+    }
+
+    public Contact(Integer ID) {
+        setID(ID);
+    }
+
+    private void setID(Integer ID) {
+        this.ID = ID;
+    }
+
+    public Integer getID() {
+        return ID;
+    }
+
+}

+ 1769 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/ContactHelper.java

@@ -0,0 +1,1769 @@
+package com.usai.redant.rautils.contactkit;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.util.Log;
+
+import com.usai.redant.rautils.contactkit.element.BaseElement;
+import com.usai.redant.rautils.contactkit.element.EmailAddress;
+import com.usai.redant.rautils.contactkit.element.Name;
+import com.usai.redant.rautils.contactkit.element.Note;
+import com.usai.redant.rautils.contactkit.element.Organization;
+import com.usai.redant.rautils.contactkit.element.PhoneNumber;
+import com.usai.redant.rautils.contactkit.element.Photo;
+import com.usai.redant.rautils.contactkit.element.PostalAddress;
+import com.usai.redant.rautils.contactkit.element.SocialProfile;
+import com.usai.redant.rautils.contactkit.element.WebSite;
+
+import java.util.ArrayList;
+
+public class ContactHelper {
+
+
+    private static volatile ContactHelper sharedHelper;
+    private ContentResolver mContentResolver;
+    private Context mContext;
+
+    //region Construction
+    /**
+     * context should be ApplicationContext
+     * */
+    public static ContactHelper sharedHelper(Context context) {
+        if (context == null) {
+            return null;
+        }
+
+        if (sharedHelper == null) {
+            synchronized (ContactHelper.class) {
+                if (sharedHelper == null) {
+                    sharedHelper = new ContactHelper(context);
+                }
+            }
+        }
+        return sharedHelper;
+    }
+
+    private ContactHelper(Context context) {
+        mContext = context;
+        mContentResolver = context.getContentResolver();
+    }
+    // endregion
+
+    //region Look Up
+    /**
+     * 查询信息
+     * */
+    private ArrayList<PhoneNumber> traversePhoneNumbersByID(Integer ID) {
+
+        Uri phoneNumberUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
+
+        String[] phoneNumberProjections = new String[] {
+                ContactsContract.CommonDataKinds.Phone.NUMBER,
+                ContactsContract.CommonDataKinds.Phone.TYPE,
+                ContactsContract.CommonDataKinds.Phone.LABEL,
+                ContactsContract.Data._ID
+        };
+
+        String phoneNumberSelection = ContactsContract.Data.CONTACT_ID + "='" + ID + "'";
+
+        Cursor phoneCur = mContentResolver.query(phoneNumberUri, phoneNumberProjections, phoneNumberSelection, null, null);
+        if (phoneCur != null) {
+
+            ArrayList<PhoneNumber> phoneNumberArray = new ArrayList<>();
+            while(phoneCur.moveToNext()) {
+
+                String phoneNumber = phoneCur.getString(0);
+                Integer phoneType = phoneCur.getInt(1);
+                String phoneLabel = phoneCur.getString(2);
+                Integer id = phoneCur.getInt(3);
+
+                PhoneNumber phone = new PhoneNumber(id);
+                phone.value = phoneNumber;
+                phone.label = phoneLabel;
+                phone.type = PhoneNumber.PhoneNumberType.values()[phoneType];
+                phone.localizedLabel = ContactsContract.CommonDataKinds.Phone.getTypeLabel(mContext.getResources(), phoneType, phoneLabel).toString();
+
+                phoneNumberArray.add(phone);
+
+            }
+            phoneCur.close();
+            return phoneNumberArray;
+        }
+
+        return null;
+    }
+
+    private ArrayList<EmailAddress> traverseEmailAddressByID(Integer ID) {
+
+        Uri emailUri = ContactsContract.CommonDataKinds.Email.CONTENT_URI;
+
+        String[] emailProjections = new String[]{
+                ContactsContract.CommonDataKinds.Email.DATA,
+                ContactsContract.CommonDataKinds.Email.TYPE,
+                ContactsContract.CommonDataKinds.Email.LABEL,
+                ContactsContract.Data._ID
+        };
+
+        String emailSelection = ContactsContract.Data.CONTACT_ID + "='" + ID + "'";
+
+        Cursor emailCur = mContentResolver.query(emailUri, emailProjections, emailSelection, null, null);
+
+        if (emailCur != null) {
+
+            ArrayList<EmailAddress> emailAddressArray = new ArrayList<>();
+            while(emailCur.moveToNext()) {
+
+                String email = emailCur.getString(0);
+                Integer emailType = emailCur.getInt(1);
+                String emailLabel = emailCur.getString(2);
+                Integer id = emailCur.getInt(3);
+
+                EmailAddress emailAddress = new EmailAddress(id);
+                emailAddress.value = email;
+                emailAddress.type = EmailAddress.EmailType.values()[emailType];
+                emailAddress.label = emailLabel;
+                emailAddress.localizedLabel = ContactsContract.CommonDataKinds.Email.getTypeLabel(mContext.getResources(), emailType, emailLabel).toString();
+
+                emailAddressArray.add(emailAddress);
+            }
+            emailCur.close();
+            return emailAddressArray;
+        }
+        return null;
+    }
+
+    private ArrayList<PostalAddress> traversePostalAddressByID(Integer ID) {
+
+        Uri addressUri = ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_URI;
+
+        String[] addressProjections = null;
+
+        String addressSelection = ContactsContract.Data.CONTACT_ID + "='" + ID + "'";
+
+        Cursor addressCur = mContentResolver.query(addressUri, addressProjections, addressSelection, null, null);
+
+        if (addressCur != null) {
+
+            ArrayList<PostalAddress> postalAddressArray = new ArrayList<>();
+            while (addressCur.moveToNext()) {
+
+                String country = addressCur.getString(addressCur.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY));
+                String state = addressCur.getString(addressCur.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.REGION));
+                String city = addressCur.getString(addressCur.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.CITY));
+                String street = addressCur.getString(addressCur.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.STREET));
+                String postalCode = addressCur.getString(addressCur.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE));
+                String formatAddress = addressCur.getString(addressCur.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS));
+
+                Integer addressType = addressCur.getInt(addressCur.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.TYPE));
+                String addressLabel = addressCur.getString(addressCur.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.LABEL));
+
+                Integer id = addressCur.getInt(addressCur.getColumnIndex(ContactsContract.Data._ID));
+
+                PostalAddress address = new PostalAddress(id);
+                address.country = country;
+                address.state = state;
+                address.city = city;
+                address.street = street;
+                address.postalCode = postalCode;
+                address.formatAddress = formatAddress;
+                address.type = PostalAddress.AddressType.values()[addressType];
+                address.label = addressLabel;
+                address.localizedLabel = ContactsContract.CommonDataKinds.StructuredPostal.getTypeLabel(mContext.getResources(), addressType, addressLabel).toString();
+
+                postalAddressArray.add(address);
+            }
+            addressCur.close();
+            return postalAddressArray;
+        }
+
+        return null;
+    }
+
+    private ArrayList<SocialProfile> traverseSocialProfileByID(Integer ID) {
+
+        Uri socialUri = ContactsContract.Data.CONTENT_URI;
+
+        String[] socialProjections = new String[] {
+                ContactsContract.CommonDataKinds.Im.DATA,
+                ContactsContract.CommonDataKinds.Im.TYPE,
+                ContactsContract.CommonDataKinds.Im.LABEL,
+                ContactsContract.CommonDataKinds.Im.PROTOCOL,
+                ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL,
+                ContactsContract.Data._ID
+        };
+
+        String socialSelection = ContactsContract.Data.CONTACT_ID + "='" + ID + "'" + " And " + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE + "'";
+
+        Cursor socialCur = mContentResolver.query(socialUri, socialProjections, socialSelection, null, null);
+        if (socialCur != null) {
+
+            ArrayList<SocialProfile> socialProfileArray = new ArrayList<>();
+            while (socialCur.moveToNext()) {
+
+
+                String socialValue = socialCur.getString(0);
+                Integer socialType = socialCur.getInt(1);
+                String socialLabel = socialCur.getString(2);
+                Integer socialProtocol = socialCur.getInt(3);
+                String socialCustomProtocol = socialCur.getString(4);
+                Integer id = socialCur.getInt(5);
+
+                SocialProfile socialProfile = new SocialProfile(id);
+                socialProfile.value = socialValue;
+                socialProfile.label = socialLabel;
+                socialProfile.type = SocialProfile.SocialProfileType.values()[socialType];
+                socialProfile.localizedLabel = ContactsContract.CommonDataKinds.Im.getTypeLabel(mContext.getResources(), socialType, socialLabel).toString();
+                socialProfile.setSocialProtocol(SocialProfile.SocialProfileProtocol.values()[socialProtocol + 1]);
+                socialProfile.localizedProtocol = ContactsContract.CommonDataKinds.Im.getProtocolLabel(mContext.getResources(), socialProtocol, socialCustomProtocol).toString();
+
+                socialProfileArray.add(socialProfile);
+            }
+            socialCur.close();
+            return socialProfileArray;
+        }
+
+        return null;
+    }
+
+    private ArrayList<WebSite> traverseWebSiteByID(Integer ID) {
+
+        Uri webUri = ContactsContract.Data.CONTENT_URI;
+
+        String[] webProjections = new String[] {
+                ContactsContract.CommonDataKinds.Website.URL,
+                ContactsContract.Data._ID
+        };
+
+        String webSelection = ContactsContract.Data.CONTACT_ID + "='" + ID + "'" + " And " + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE + "'";
+
+        Cursor webCur = mContentResolver.query(webUri, webProjections, webSelection, null, null);
+        if (webCur != null) {
+
+            ArrayList<WebSite> webArray = new ArrayList<>();
+            while (webCur.moveToNext()) {
+
+                String web = webCur.getString(0);
+                Integer id = webCur.getInt(1);
+
+                WebSite webSite = new WebSite(id);
+                webSite.url = web;
+
+                webArray.add(webSite);
+            }
+            webCur.close();
+            return webArray;
+        }
+
+        return null;
+    }
+
+    private Note traverseNoteByID(Integer ID) {
+
+        Uri noteUri = ContactsContract.Data.CONTENT_URI;
+        String[] noteProjections = new String[] {
+                ContactsContract.CommonDataKinds.Note.NOTE,
+                ContactsContract.Data._ID
+        };
+
+        String noteSelection = ContactsContract.Data.CONTACT_ID + "='" + ID + "'" + " And " + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE + "'";
+
+        Cursor noteCur = mContentResolver.query(noteUri, noteProjections, noteSelection, null, null);
+        if (noteCur != null) {
+            Note note = null;
+            if (noteCur.moveToFirst()) {
+
+                String noteStr = noteCur.getString(0);
+                Integer id = noteCur.getInt(1);
+
+                note = new Note(id);
+                note.note = noteStr;
+            }
+            noteCur.close();
+            return note;
+        }
+
+        return null;
+    }
+
+    private Organization traverseOrganizationByID(Integer ID) {
+
+        Uri jobUri = ContactsContract.Data.CONTENT_URI;
+
+        String[] jobProjections = new String[]{
+                ContactsContract.CommonDataKinds.Organization.COMPANY,
+                ContactsContract.CommonDataKinds.Organization.DEPARTMENT,
+                ContactsContract.CommonDataKinds.Organization.TITLE,
+                ContactsContract.Data._ID
+        };
+
+        String jobSelection = ContactsContract.Data.RAW_CONTACT_ID + "='" + ID + "'" + " AND " + ContactsContract.Contacts.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE + "'";
+
+        Cursor jobCur = mContentResolver.query(jobUri, jobProjections, jobSelection, null, null);
+
+        if (jobCur != null) {
+
+            Organization organization = null;
+            if (jobCur.moveToFirst()) {
+
+                String organizationName = jobCur.getString(0);
+                String department = jobCur.getString(1);
+                String jobTitle = jobCur.getString(2);
+                Integer id = jobCur.getInt(3);
+
+                organization = new Organization(id);
+                organization.organizationName = organizationName;
+                organization.department = department;
+                organization.jobTitle = jobTitle;
+            }
+            jobCur.close();
+            return organization;
+        }
+        return null;
+    }
+
+    private Photo traversePhotoByID(Integer ID) {
+
+        Uri photoUri = ContactsContract.Data.CONTENT_URI;
+
+        String[] photoProjections = new String[]{
+                ContactsContract.CommonDataKinds.Photo.PHOTO_FILE_ID,
+                ContactsContract.CommonDataKinds.Photo.PHOTO,
+                ContactsContract.Data._ID
+        };
+
+        String photoSelection = ContactsContract.Data.RAW_CONTACT_ID + "='" + ID + "'" + " AND " + ContactsContract.Contacts.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE + "'";
+
+        Cursor photoCur = mContentResolver.query(photoUri, photoProjections, photoSelection, null, null);
+
+        if (photoCur != null) {
+
+            Photo photo = null;
+            if (photoCur.moveToFirst()) {
+
+                String photoFileID = photoCur.getString(0);
+                byte[] thumbnail = photoCur.getBlob(1);
+                Integer id = photoCur.getInt(2);
+
+                Uri uri = Uri.withAppendedPath(ContactsContract.DisplayPhoto.CONTENT_URI, photoFileID);
+
+                photo = new Photo(id);
+                photo.uri = uri;
+                photo.thumbnail = thumbnail;
+            }
+            photoCur.close();
+            return photo;
+        }
+
+        return null;
+    }
+
+    private Name traverseNameByID(Integer ID) {
+
+        Uri nameUri = ContactsContract.Data.CONTENT_URI;
+
+        String[] nameProjections = new String[]{
+                ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,
+                ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME,
+                ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME,
+                ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
+                ContactsContract.Data._ID
+        };
+
+        String nameSelection = ContactsContract.Data.RAW_CONTACT_ID + "='" + ID + "'" + " AND " + ContactsContract.Contacts.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE + "'";
+
+        Cursor nameCur = mContentResolver.query(nameUri, nameProjections, nameSelection, null, null);
+
+        if (nameCur != null) {
+
+            Name name = null;
+            if (nameCur.moveToFirst()) {
+
+                String givenName = nameCur.getString(0);
+                String middleName = nameCur.getString(1);
+                String familyName = nameCur.getString(2);
+                String displayName = nameCur.getString(3);
+                Integer id = nameCur.getInt(4);
+
+                name = new Name(id);
+                name.displayName = displayName;
+                name.givenName = givenName;
+                name.middleName = middleName;
+                name.familyName = familyName;
+            }
+            nameCur.close();
+            return name;
+        }
+
+        return null;
+    }
+
+    public Contact searchContactByID(Integer ID) {
+
+        Contact contact = new Contact(ID);
+
+        // name
+        contact.name = traverseNameByID(ID);
+
+        // Photo
+        contact.photo =  traversePhotoByID(ID);
+
+        // Fetch Job
+        contact.organization = traverseOrganizationByID(ID);
+
+        //Fetch Phone Number
+        contact.phoneNumbers = traversePhoneNumbersByID(ID);
+
+        //Fetch email
+        contact.emailAddresses = traverseEmailAddressByID(ID);
+
+        // Fetch Address
+        contact.postalAddresses = traversePostalAddressByID(ID);
+
+        // Fetch Social
+        contact.socialProfiles = traverseSocialProfileByID(ID);
+
+        // Fetch URL
+        contact.webSites = traverseWebSiteByID(ID);
+
+        // Fetch Note
+        contact.note = traverseNoteByID(ID);
+
+        return contact;
+    }
+
+    public Contact searchContactByUri(Uri uri) {
+        if (uri != null) {
+
+            String[] projections = new String[] {
+                    ContactsContract.Contacts._ID
+            };
+
+            Cursor cursor = mContentResolver.query(uri, projections, null, null, null);
+            if (cursor != null) {
+
+                if (cursor.moveToFirst()) {
+                    Integer ID = cursor.getInt(0);
+                    cursor.close();
+                    return searchContactByID(ID);
+                }
+
+            }
+
+        }
+        return null;
+    }
+
+    public Uri getUriOfContact(Contact contact) {
+
+        if (contact != null && contact.getID() != Contact.NEW_CONTACT_ID) {
+
+            Integer ID = contact.getID();
+
+            Uri uri = ContactsContract.Contacts.CONTENT_URI;
+
+            String[] projections = new String[]{
+                    ContactsContract.Contacts.LOOKUP_KEY
+            };
+
+            String selection = ContactsContract.Data._ID + "='" + ID + "'";
+
+            Cursor cursor = mContentResolver.query(uri, projections, selection, null, null);
+            if (cursor != null) {
+
+                if (cursor.moveToFirst()) {
+                    String lookupKey = cursor.getString(0);
+                    cursor.close();
+                    return ContactsContract.Contacts.getLookupUri(ID, lookupKey);
+                }
+            }
+
+        }
+        return null;
+    }
+
+    private Integer getRawContactID(Contact contact) {
+
+        if (contact != null && contact.getID() != Contact.NEW_CONTACT_ID) {
+
+            Integer ID = contact.getID();
+
+            Uri uri = ContactsContract.Data.CONTENT_URI;
+
+            String[] projections = new String[]{
+                    ContactsContract.Data.RAW_CONTACT_ID
+            };
+
+            String selection = ContactsContract.Data._ID + "='" + ID + "'";
+
+            Cursor cursor = mContentResolver.query(uri, projections, selection, null, null);
+            if (cursor != null) {
+
+                if (cursor.moveToFirst()) {
+                    Integer rawContactID = cursor.getInt(0);
+                    cursor.close();
+                    return rawContactID;
+                }
+            }
+
+        }
+        return Contact.NEW_CONTACT_ID;
+    }
+
+    /**
+     * 遍历所有联系人
+     * */
+    public ArrayList<Contact> traverseAllContacts() {
+
+        Uri uri = ContactsContract.Contacts.CONTENT_URI;
+        String[] projections = new String[] {
+                ContactsContract.Contacts._ID
+        };
+
+        Cursor cursor = mContentResolver.query(uri, projections, null, null,null);
+        if (cursor != null) {
+
+            ArrayList<Contact> contactArray = new ArrayList<>();
+            while (cursor.moveToNext()) {
+
+                Integer ID = cursor.getInt(0);
+
+                Contact contact = searchContactByID(ID);
+
+                contactArray.add(contact);
+            }
+            cursor.close();
+            return contactArray;
+        }
+
+        return null;
+    }
+
+    /**
+     * 查询联系人
+     * */
+    public ArrayList<Contact> searchContactsByKeyword(String keyword) {
+
+        if (keyword != null) {
+
+            Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_FILTER_URI, keyword);
+            String[] projection = new String[]{
+                    ContactsContract.Contacts._ID
+            };
+
+            Cursor cursor = mContentResolver.query(uri, projection, null, null, null);
+            if (cursor != null) {
+
+                ArrayList<Contact> contacts = new ArrayList<>();
+                while (cursor.moveToNext()) {
+
+                    Integer ID = cursor.getInt(0);
+                    Contact contact = searchContactByID(ID);
+                    if (contact != null) {
+                        contacts.add(contact);
+                    }
+                }
+                cursor.close();
+                return contacts;
+            }
+        }
+
+        return null;
+    }
+    // endregion
+
+
+    //region Insert
+    /**
+     * 插入信息
+     * */
+
+    /**
+     * name
+     * */
+    private ContentProviderOperation insertNameForContact(Name name, int rawContactID) {
+
+        if (name != null && !name.isEmpty()) {
+            ContentValues values = new ContentValues();
+            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
+
+            // given name
+            if (name.givenName != null) {
+                values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, name.givenName);
+            }
+
+            // middle name
+            if (name.middleName != null) {
+                values.put(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, name.middleName);
+            }
+
+            // family name
+            if (name.familyName != null) {
+                values.put(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, name.familyName);
+            }
+
+            return ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+                    .withValueBackReference(ContactsContract.CommonDataKinds.StructuredName.RAW_CONTACT_ID, rawContactID) // RAW_CONTACT_ID是第一个事务添加得到的,因此这里传入0,applyBatch返回的ContentProviderResult[]数组中第一项
+                    .withValues(values)
+                    .build();
+        }
+        return null;
+    }
+
+    private ContentProviderOperation insertName(Name name) {
+        return insertNameForContact(name, 0);
+    }
+
+    /**
+     * Photo
+     * */
+    private  ContentProviderOperation insertPhotoForContact(Photo photo, int rawContactID) {
+        if (photo != null && photo.thumbnail != null) {
+
+            return ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+                    .withValueBackReference(ContactsContract.CommonDataKinds.Photo.RAW_CONTACT_ID, rawContactID)
+                    .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
+                    .withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, photo.thumbnail)
+                    .build();
+
+        }
+
+        return null;
+    }
+
+    private ContentProviderOperation insertPhoto(Photo photo) {
+        return insertPhotoForContact(photo, 0);
+    }
+
+    /**
+     * Job
+     * */
+
+    private ContentProviderOperation insertJobForContact(Organization organization, int rawContactID) {
+        if (organization != null && !organization.isEmpty()) {
+
+            ContentValues values = new ContentValues();
+            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE);
+
+            if (organization.organizationName != null) {
+                values.put(ContactsContract.CommonDataKinds.Organization.COMPANY, organization.organizationName);
+            }
+
+            if (organization.department != null) {
+                values.put(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, organization.department);
+            }
+
+            if (organization.jobTitle != null) {
+                values.put(ContactsContract.CommonDataKinds.Organization.TITLE, organization.jobTitle);
+            }
+
+            return ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+                    .withValueBackReference(ContactsContract.CommonDataKinds.Organization.RAW_CONTACT_ID, rawContactID)
+                    .withValues(values)
+                    .build();
+        }
+
+        return null;
+    }
+
+    private ContentProviderOperation insertJob(Organization organization) {
+
+        return insertJobForContact(organization, 0);
+    }
+
+    /**
+     * Phone
+     * */
+    private ContentProviderOperation insertPhoneNumberForContact(PhoneNumber phone, int rawContactID) {
+
+        if (phone != null) {
+
+            ContentValues values = new ContentValues();
+            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
+
+            if (phone.type == null) {
+                phone.type = PhoneNumber.PhoneNumberType.TYPE_MAIN;
+            }
+            values.put(ContactsContract.CommonDataKinds.Phone.TYPE, phone.type.ordinal());
+
+            if (phone.type == PhoneNumber.PhoneNumberType.TYPE_CUSTOM && phone.localizedLabel != null) {
+                values.put(ContactsContract.CommonDataKinds.Phone.LABEL, phone.localizedLabel);
+            }
+
+            if (phone.value != null) {
+                values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone.value);
+            }
+
+            if (phone.label != null) {
+                values.put(ContactsContract.CommonDataKinds.Phone.LABEL, phone.label);
+            }
+
+            return ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+                    .withValueBackReference(ContactsContract.CommonDataKinds.Phone.RAW_CONTACT_ID, rawContactID)
+                    .withValues(values)
+                    .build();
+        }
+
+        return null;
+    }
+
+    private ArrayList<ContentProviderOperation> insertPhoneNumbers(ArrayList<PhoneNumber> phoneNumbers) {
+
+        if (phoneNumbers != null && phoneNumbers.size() > 0) {
+
+            ArrayList<ContentProviderOperation> ops = new ArrayList<>();
+
+            for (PhoneNumber phone : phoneNumbers) {
+                ops.add(insertPhoneNumberForContact(phone, 0));
+            }
+
+            return ops;
+        }
+
+        return null;
+    }
+
+    /**
+     * Email
+     * */
+    private ContentProviderOperation insertEmailAddressForContact(EmailAddress email, int rawContactID) {
+
+        if (email != null) {
+
+            ContentValues values = new ContentValues();
+            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
+
+            if (email.type == null) {
+                email.type = EmailAddress.EmailType.TYPE_HOME;
+            }
+            values.put(ContactsContract.CommonDataKinds.Email.TYPE, email.type.ordinal());
+
+            if (email.type == EmailAddress.EmailType.TYPE_CUSTOM && email.localizedLabel != null) {
+                values.put(ContactsContract.CommonDataKinds.Email.LABEL, email.localizedLabel);
+            }
+
+            if (email.value != null) {
+                values.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email.value);
+            }
+
+            if (email.label != null) {
+                values.put(ContactsContract.CommonDataKinds.Email.LABEL, email.label);
+            }
+
+            return ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+                    .withValueBackReference(ContactsContract.CommonDataKinds.Email.RAW_CONTACT_ID, rawContactID)
+                    .withValues(values)
+                    .build();
+        }
+        return null;
+    }
+
+    private ArrayList<ContentProviderOperation> insertEmailAddresses(ArrayList<EmailAddress> emailAddresses) {
+
+        if (emailAddresses != null && emailAddresses.size() > 0) {
+
+            ArrayList<ContentProviderOperation> ops = new ArrayList<>();
+
+            for (EmailAddress email : emailAddresses) {
+                ops.add(insertEmailAddressForContact(email, 0));
+            }
+
+            return ops;
+        }
+
+        return null;
+    }
+
+    /**
+     * Postal Address
+     * */
+    private ContentProviderOperation insertPostalAddressForContact(PostalAddress address, int rawContactID) {
+
+        if (address != null && !address.isEmpty()) {
+
+            ContentValues values = new ContentValues();
+            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE);
+
+            if (address.type == null) {
+                address.type = PostalAddress.AddressType.TYPE_HOME;
+            }
+            values.put(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, address.type.ordinal());
+
+            if (address.type == PostalAddress.AddressType.TYPE_CUSTOM && address.localizedLabel != null) {
+                values.put(ContactsContract.CommonDataKinds.StructuredPostal.LABEL, address.localizedLabel);
+            }
+
+            if (address.country != null) {
+                values.put(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, address.country);
+            }
+
+            if (address.state != null) {
+                values.put(ContactsContract.CommonDataKinds.StructuredPostal.REGION, address.state);
+            }
+
+            if (address.city != null) {
+                values.put(ContactsContract.CommonDataKinds.StructuredPostal.CITY, address.city);
+            }
+
+            if (address.street != null) {
+                values.put(ContactsContract.CommonDataKinds.StructuredPostal.STREET, address.street);
+            }
+
+            if (address.postalCode != null) {
+                values.put(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, address.postalCode);
+            }
+
+            return ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+                    .withValueBackReference(ContactsContract.CommonDataKinds.StructuredPostal.RAW_CONTACT_ID, rawContactID)
+                    .withValues(values)
+                    .build();
+        }
+
+        return null;
+    }
+
+    private ArrayList<ContentProviderOperation> insertPostalAddresses(ArrayList<PostalAddress> postalAddresses) {
+
+        if (postalAddresses != null && postalAddresses.size() > 0) {
+
+            ArrayList<ContentProviderOperation> ops = new ArrayList<>();
+
+            for (PostalAddress address : postalAddresses) {
+
+                if (!address.isEmpty()) {
+                    ops.add(insertPostalAddressForContact(address, 0));
+                }
+
+            }
+
+            return ops;
+
+        }
+        return null;
+    }
+
+    /**
+     * Social Profile
+     * */
+    private ContentProviderOperation insertSocialProfileForContact(SocialProfile socialProfile, int rawContactID) {
+
+        if (socialProfile != null && socialProfile.value != null) {
+
+            ContentValues values = new ContentValues();
+            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE);
+
+            values.put(ContactsContract.CommonDataKinds.Im.DATA, socialProfile.value);
+
+            if (socialProfile.type == null) {
+                socialProfile.type = SocialProfile.SocialProfileType.TYPE_HOME;
+            }
+            values.put(ContactsContract.CommonDataKinds.Im.TYPE, socialProfile.type.ordinal());
+
+            if (socialProfile.type == SocialProfile.SocialProfileType.TYPE_CUSTOM && socialProfile.localizedLabel != null) {
+                values.put(ContactsContract.CommonDataKinds.Im.LABEL, socialProfile.localizedLabel);
+            }
+
+            int protocol = socialProfile.getImProtocol();
+            values.put(ContactsContract.CommonDataKinds.Im.PROTOCOL, protocol);
+            if (protocol == ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM && socialProfile.localizedProtocol != null) {
+                values.put(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL, socialProfile.localizedProtocol);
+            }
+
+            return ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+                    .withValueBackReference(ContactsContract.CommonDataKinds.Im.RAW_CONTACT_ID, rawContactID)
+                    .withValues(values)
+                    .build();
+        }
+        return null;
+    }
+
+    private ArrayList<ContentProviderOperation> insertSocialProfiles(ArrayList<SocialProfile> socialProfiles) {
+
+        if (socialProfiles != null && socialProfiles.size() > 0) {
+
+            ArrayList<ContentProviderOperation> ops = new ArrayList<>();
+
+            for (SocialProfile socialProfile : socialProfiles) {
+
+                if (socialProfile.value != null) {
+                    ops.add(insertSocialProfileForContact(socialProfile, 0));
+                }
+
+            }
+
+            return ops;
+
+        }
+
+        return null;
+    }
+
+    /**
+     * Website
+     * */
+    private ContentProviderOperation insertWebSiteForContact(WebSite web, int rawContactID) {
+
+        if (web != null && web.url != null) {
+
+            ContentValues values = new ContentValues();
+            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE);
+
+            values.put(ContactsContract.CommonDataKinds.Website.URL, web.url);
+
+            return ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+                    .withValueBackReference(ContactsContract.CommonDataKinds.Website.RAW_CONTACT_ID, rawContactID)
+                    .withValues(values)
+                    .build();
+        }
+        return null;
+    }
+
+    private ArrayList<ContentProviderOperation> insertWebSites(ArrayList<WebSite> websites) {
+        if (websites != null && websites.size() > 0) {
+
+            ArrayList<ContentProviderOperation> ops = new ArrayList<>();
+
+            for (WebSite web : websites) {
+
+                if (web.url != null) {
+                    ops.add(insertWebSiteForContact(web, 0));
+                }
+            }
+
+            return ops;
+        }
+        return null;
+    }
+
+    /**
+     * Note
+     * */
+    private ContentProviderOperation insertNoteForContact(Note note, int rawContactID) {
+        if (note != null && note.note != null) {
+            ContentValues values = new ContentValues();
+            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE);
+
+            values.put(ContactsContract.CommonDataKinds.Note.NOTE, note.note);
+
+            return ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+                    .withValueBackReference(ContactsContract.CommonDataKinds.Note.RAW_CONTACT_ID, rawContactID)
+                    .withValues(values)
+                    .build();
+        }
+        return null;
+    }
+
+    private ContentProviderOperation insertNote(Note note) {
+        if (note != null && note.note != null) {
+            return insertNoteForContact(note, 0);
+        }
+        return null;
+    }
+
+    /**
+     * 新增联系人
+     * */
+    public void insertContact(ArrayList<Contact> contacts) {
+
+        if (contacts != null && contacts.size() > 0) {
+
+            for (Contact contact : contacts) {
+
+                if (contact.getID() == Contact.NEW_CONTACT_ID) {
+
+                    ArrayList<ContentProviderOperation> operations = new ArrayList<>();
+
+
+                    // 添加到账号
+                    operations.add(
+                            ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
+                                    .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null)
+                                    .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null)
+                                    .build()
+                    );
+
+                    // insert name
+                    ContentProviderOperation nameOp = insertName(contact.name);
+                    if (nameOp != null) {
+                        operations.add(nameOp);
+                    }
+
+                    // insert photo
+                    ContentProviderOperation photoOperation = insertPhoto(contact.photo);
+                    if (photoOperation != null) {
+                        operations.add(photoOperation);
+                    }
+
+                    // insert job
+                    ContentProviderOperation jobOp = insertJob(contact.organization);
+                    if (jobOp != null) {
+                        operations.add(jobOp);
+                    }
+
+                    // insert phone number
+                    ArrayList<ContentProviderOperation> phoneOps = insertPhoneNumbers(contact.phoneNumbers);
+                    if (phoneOps != null && phoneOps.size() > 0) {
+                        operations.addAll(phoneOps);
+                    }
+
+                    // insert email
+                    ArrayList<ContentProviderOperation> emailOps = insertEmailAddresses(contact.emailAddresses);
+                    if (emailOps != null && emailOps.size() > 0) {
+                        operations.addAll(emailOps);
+                    }
+
+                    // insert postal address
+                    ArrayList<ContentProviderOperation> addressOps = insertPostalAddresses(contact.postalAddresses);
+                    if (addressOps != null && addressOps.size() > 0) {
+                        operations.addAll(addressOps);
+                    }
+
+                    // insert social profile
+                    ArrayList<ContentProviderOperation> socialProfileOps = insertSocialProfiles(contact.socialProfiles);
+                    if (socialProfileOps != null && socialProfileOps.size() > 0) {
+                        operations.addAll(socialProfileOps);
+                    }
+
+                    // insert url
+                    ArrayList<ContentProviderOperation> urlOps = insertWebSites(contact.webSites);
+                    if (urlOps != null && urlOps.size() > 0) {
+                        operations.addAll(urlOps);
+                    }
+
+                    // insert note
+                    ContentProviderOperation noteOp = insertNote(contact.note);
+                    if (noteOp != null) {
+                        operations.add(noteOp);
+                    }
+
+
+                    try {
+                        ContentProviderResult[] results = mContentResolver.applyBatch(ContactsContract.AUTHORITY, operations);
+                        for (ContentProviderResult result : results) {
+                            Log.i("Insert Contact", result.uri.toString());
+                        }
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+
+                } // if
+
+            }  // for
+        }
+    }
+
+    /**
+     * 已有联系人新增信息
+     * */
+    public void insertContactElements(Contact contact, ArrayList<BaseElement> elements) {
+
+        if (contact != null && contact.getID() != null && contact.getID() != Contact.NEW_CONTACT_ID && elements != null && elements.size() > 0) {
+
+            ArrayList<ContentProviderOperation> ops = new ArrayList<>();
+
+            for (BaseElement element : elements) {
+
+                if (element.getId().intValue() == BaseElement.NEW_ELEMENT_ID.intValue()) {
+
+                    ContentProviderOperation op = null;
+
+                    if (element instanceof Name) {
+
+                        Name name = (Name)element;
+                        op = insertNameForContact(name, contact.getID());
+
+                    } else if (element instanceof Photo) {
+
+                        Photo photo = (Photo)element;
+                        op = insertPhotoForContact(photo, contact.getID());
+
+                    } else if (element instanceof Organization) {
+
+                        Organization organization = (Organization)element;
+                        op = insertJobForContact(organization, contact.getID());
+
+                    } else if (element instanceof PhoneNumber) {
+
+                        PhoneNumber phone = (PhoneNumber)element;
+                        op = insertPhoneNumberForContact(phone, contact.getID());
+
+                    } else if (element instanceof EmailAddress) {
+
+                        EmailAddress email = (EmailAddress)element;
+                        op = insertEmailAddressForContact(email, contact.getID());
+
+                    } else if (element instanceof PostalAddress) {
+
+                        PostalAddress postalAddress = (PostalAddress)element;
+                        op = insertPostalAddressForContact(postalAddress, contact.getID());
+
+                    } else if (element instanceof SocialProfile) {
+
+                        SocialProfile socialProfile = (SocialProfile)element;
+                        op = insertSocialProfileForContact(socialProfile, contact.getID());
+
+                    } else if (element instanceof WebSite) {
+
+                        WebSite web = (WebSite)element;
+                        op = insertWebSiteForContact(web, contact.getID());
+
+                    } else if (element instanceof Note) {
+
+                        Note note = (Note)element;
+                        op = insertNoteForContact(note, contact.getID());
+                    }
+
+                    if (op != null) {
+                        ops.add(op);
+                    }
+
+                }// if
+            } // for
+
+            if (ops.size() > 0) {
+                try {
+                    ContentProviderResult[] results = mContentResolver.applyBatch(ContactsContract.AUTHORITY, ops);
+                    for (ContentProviderResult result : results) {
+                        Log.i("Update Contact Name", result.toString());
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+
+        }
+    }
+
+    // endregion
+
+    //region Delete
+    /**
+     * 删除联系人
+     * */
+    public void deleteContact(ArrayList<Contact> contacts) {
+        if (contacts != null && contacts.size() > 0) {
+
+            ArrayList<ContentProviderOperation> operations = new ArrayList<>();
+            for (Contact contact : contacts) {
+
+                Integer ID = contact.getID();
+                if (ID != Contact.NEW_CONTACT_ID) {
+
+                    //delete contact
+                    operations.add(ContentProviderOperation.newDelete(ContactsContract.RawContacts.CONTENT_URI).withSelection(ContactsContract.RawContacts.CONTACT_ID + "=" + ID, null).build());
+
+                    //delete contact information such as phone number,email
+                    operations.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).withSelection(ContactsContract.Data.CONTACT_ID + "=" + ID, null).build());
+                }
+
+            }
+
+            try {
+                mContentResolver.applyBatch(ContactsContract.AUTHORITY, operations);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+    // endregion
+
+    //region Update
+    /**
+     * 更新联系人
+     *
+     * 方式一:
+     *  Uri uri = ContactsContract.Data.CONTENT_URI;
+     *
+     *  String nameSelection = ContactsContract.Data._ID + "='" + name.id + "'" + " AND " + ContactsContract.Contacts.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE + "'";
+     *
+     *  int result = mContentResolver.update(uri, values, nameSelection, null);
+     *
+     *  方式二:
+     *  ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
+     *                     .withSelection(ContactsContract.Data._ID + "=?", new String[]{String.valueOf(name.getId())})
+     *                     .withValues(values)
+     *                     .build();
+     * */
+
+    private ContentProviderOperation updateName(Name name) {
+        if (name != null && name.getId() != null && name.getId().intValue() != BaseElement.NEW_ELEMENT_ID.intValue()) {
+
+            ContentValues values = new ContentValues();
+
+            // given name
+            if (name.givenName == null) {
+                name.givenName = "";
+            }
+            values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, name.givenName);
+
+            // middle name
+            if (name.middleName == null) {
+                name.middleName = "";
+            }
+            values.put(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, name.middleName);
+
+            // family name
+            if (name.familyName == null) {
+                name.familyName = "";
+            }
+            values.put(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, name.familyName);
+
+            return ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
+                    .withSelection(ContactsContract.Data._ID + "=?", new String[]{String.valueOf(name.getId())})
+                    .withValues(values)
+                    .build();
+
+
+//            Uri uri = ContactsContract.Data.CONTENT_URI;
+//
+//            String nameSelection = ContactsContract.Data._ID + "='" + name.id + "'" + " AND " + ContactsContract.Contacts.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE + "'";
+//
+//            int result = mContentResolver.update(uri, values, nameSelection, null);
+//
+//            Log.d("Update Contact", "updateName: " + result);
+        }
+        return null;
+    }
+
+    private ContentProviderOperation updatePhoto(Photo photo) {
+
+        if (photo != null && photo.getId() != null && photo.getId().intValue() != BaseElement.NEW_ELEMENT_ID.intValue()) {
+
+            if (photo.thumbnail != null) {
+                return ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
+                        .withSelection(ContactsContract.Data._ID + "=?", new String[]{String.valueOf(photo.getId())})
+                        .withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, photo.thumbnail)
+                        .build();
+            } else {
+                return ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
+                        .withSelection(ContactsContract.Data._ID + "=?", new String[]{String.valueOf(photo.getId())})
+                        .build();
+            }
+
+        }
+
+        return null;
+    }
+
+    private ContentProviderOperation updateJob(Organization organization) {
+
+        if (organization != null && organization.getId() != null && organization.getId().intValue() != BaseElement.NEW_ELEMENT_ID.intValue()) {
+
+            if (organization.isEmpty()) {
+
+                return ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
+                        .withSelection(ContactsContract.Data._ID + "=?", new String[]{String.valueOf(organization.getId())})
+                        .build();
+
+            } else {
+                ContentValues values = new ContentValues();
+
+                if (organization.organizationName != null) {
+                    values.put(ContactsContract.CommonDataKinds.Organization.COMPANY, organization.organizationName);
+                }
+
+                if (organization.department != null) {
+                    values.put(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, organization.department);
+                }
+
+                if (organization.jobTitle != null) {
+                    values.put(ContactsContract.CommonDataKinds.Organization.TITLE, organization.jobTitle);
+                }
+
+                return ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
+                        .withSelection(ContactsContract.Data._ID + "=?", new String[]{String.valueOf(organization.getId())})
+                        .withValues(values)
+                        .build();
+            }
+        }
+
+        return null;
+    }
+
+    private ContentProviderOperation updatePhoneNumber(PhoneNumber phone) {
+
+        if (phone != null && phone.getId() != null && phone.getId().intValue() != BaseElement.NEW_ELEMENT_ID.intValue()) {
+
+            if (phone.value != null) {
+
+                ContentValues values = new ContentValues();
+
+                if (phone.type == null) {
+                    phone.type = PhoneNumber.PhoneNumberType.TYPE_MAIN;
+                }
+                values.put(ContactsContract.CommonDataKinds.Phone.TYPE, phone.type.ordinal());
+
+                if (phone.type == PhoneNumber.PhoneNumberType.TYPE_CUSTOM && phone.localizedLabel != null) {
+                    values.put(ContactsContract.CommonDataKinds.Phone.LABEL, phone.localizedLabel);
+                }
+
+                if (phone.value != null) {
+                    values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone.value);
+                }
+
+                if (phone.label != null) {
+                    values.put(ContactsContract.CommonDataKinds.Phone.LABEL, phone.label);
+                }
+
+                return ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
+                        .withSelection(ContactsContract.Data._ID + "=?", new String[]{String.valueOf(phone.getId())})
+                        .withValues(values)
+                        .build();
+
+            } else {
+
+                return ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
+                        .withSelection(ContactsContract.Data._ID + "=?", new String[]{String.valueOf(phone.getId())})
+                        .build();
+
+            }
+        }
+
+        return null;
+    }
+
+    private ContentProviderOperation updateEmailAddress(EmailAddress emailAddress) {
+
+        if (emailAddress != null && emailAddress.getId() != null && emailAddress.getId().intValue() != BaseElement.NEW_ELEMENT_ID.intValue()) {
+
+            if (emailAddress.value != null) {
+
+                ContentValues values = new ContentValues();
+
+                if (emailAddress.type == null) {
+                    emailAddress.type = EmailAddress.EmailType.TYPE_HOME;
+                }
+                values.put(ContactsContract.CommonDataKinds.Email.TYPE, emailAddress.type.ordinal());
+
+                if (emailAddress.type == EmailAddress.EmailType.TYPE_CUSTOM && emailAddress.localizedLabel != null) {
+                    values.put(ContactsContract.CommonDataKinds.Email.LABEL, emailAddress.localizedLabel);
+                }
+
+                if (emailAddress.value != null) {
+                    values.put(ContactsContract.CommonDataKinds.Email.ADDRESS, emailAddress.value);
+                }
+
+                if (emailAddress.label != null) {
+                    values.put(ContactsContract.CommonDataKinds.Email.LABEL, emailAddress.label);
+                }
+
+                return ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
+                        .withSelection(ContactsContract.Data._ID + "=?", new String[]{String.valueOf(emailAddress.getId())})
+                        .withValues(values)
+                        .build();
+
+            } else {
+
+                return ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
+                        .withSelection(ContactsContract.Data._ID + "=?", new String[]{String.valueOf(emailAddress.getId())})
+                        .build();
+            }
+
+        }
+
+        return null;
+    }
+
+    private ContentProviderOperation updatePostalAddress(PostalAddress postalAddress) {
+
+        if (postalAddress != null && postalAddress.getId() != null && postalAddress.getId().intValue() != BaseElement.NEW_ELEMENT_ID.intValue()) {
+
+            if (!postalAddress.isEmpty()) {
+
+                ContentValues values = new ContentValues();
+
+                if (postalAddress.type == null) {
+                    postalAddress.type = PostalAddress.AddressType.TYPE_HOME;
+                }
+                values.put(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, postalAddress.type.ordinal());
+
+                if (postalAddress.type == PostalAddress.AddressType.TYPE_CUSTOM && postalAddress.localizedLabel != null) {
+                    values.put(ContactsContract.CommonDataKinds.StructuredPostal.LABEL, postalAddress.localizedLabel);
+                }
+
+                if (postalAddress.country != null) {
+                    values.put(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, postalAddress.country);
+                }
+
+                if (postalAddress.state != null) {
+                    values.put(ContactsContract.CommonDataKinds.StructuredPostal.REGION, postalAddress.state);
+                }
+
+                if (postalAddress.city != null) {
+                    values.put(ContactsContract.CommonDataKinds.StructuredPostal.CITY, postalAddress.city);
+                }
+
+                if (postalAddress.street != null) {
+                    values.put(ContactsContract.CommonDataKinds.StructuredPostal.STREET, postalAddress.street);
+                }
+
+                if (postalAddress.postalCode != null) {
+                    values.put(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, postalAddress.postalCode);
+                }
+
+                return ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
+                        .withSelection(ContactsContract.Data._ID + "=?", new String[]{String.valueOf(postalAddress.getId())})
+                        .withValues(values)
+                        .build();
+            } else {
+
+                return ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
+                        .withSelection(ContactsContract.Data._ID + "=?", new String[]{String.valueOf(postalAddress.getId())})
+                        .build();
+            }
+
+        }
+        return null;
+    }
+
+    private ContentProviderOperation updateSocialProfile(SocialProfile socialProfile) {
+
+        if (socialProfile != null && socialProfile.getId() != null && socialProfile.getId().intValue() != BaseElement.NEW_ELEMENT_ID.intValue()) {
+
+            if (socialProfile.value != null) {
+
+                ContentValues values = new ContentValues();
+
+                values.put(ContactsContract.CommonDataKinds.Im.DATA, socialProfile.value);
+
+                if (socialProfile.type == null) {
+                    socialProfile.type = SocialProfile.SocialProfileType.TYPE_HOME;
+                }
+                values.put(ContactsContract.CommonDataKinds.Im.TYPE, socialProfile.type.ordinal());
+
+                if (socialProfile.type == SocialProfile.SocialProfileType.TYPE_CUSTOM && socialProfile.localizedLabel != null) {
+                    values.put(ContactsContract.CommonDataKinds.Im.LABEL, socialProfile.localizedLabel);
+                }
+
+                int protocol = socialProfile.getImProtocol();
+                values.put(ContactsContract.CommonDataKinds.Im.PROTOCOL, protocol);
+                if (protocol == ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM && socialProfile.localizedProtocol != null) {
+                    values.put(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL, socialProfile.localizedProtocol);
+                }
+
+                return ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
+                        .withSelection(ContactsContract.Data._ID + "=?", new String[]{String.valueOf(socialProfile.getId())})
+                        .withValues(values)
+                        .build();
+            } else {
+
+                return ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
+                        .withSelection(ContactsContract.Data._ID + "=?", new String[]{String.valueOf(socialProfile.getId())})
+                        .build();
+            }
+
+        }
+
+        return null;
+    }
+
+    private ContentProviderOperation updateWebSite(WebSite website) {
+        if (website != null && website.getId() != null && website.getId().intValue() != BaseElement.NEW_ELEMENT_ID.intValue()) {
+
+            if (website.url != null) {
+                ContentValues values = new ContentValues();
+                values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE);
+
+                values.put(ContactsContract.CommonDataKinds.Website.URL, website.url);
+
+                return ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
+                        .withSelection(ContactsContract.Data._ID + "=?", new String[]{String.valueOf(website.getId())})
+                        .withValues(values)
+                        .build();
+            } else {
+
+                return ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
+                        .withSelection(ContactsContract.Data._ID + "=?", new String[]{String.valueOf(website.getId())})
+                        .build();
+            }
+        }
+        return null;
+    }
+
+    private ContentProviderOperation updateNote(Note note) {
+        if (note != null && note.getId() != null && note.getId().intValue() != BaseElement.NEW_ELEMENT_ID.intValue()) {
+
+            if (note.note != null) {
+
+                ContentValues values = new ContentValues();
+                values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE);
+
+                values.put(ContactsContract.CommonDataKinds.Note.NOTE, note.note);
+
+                return ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
+                        .withSelection(ContactsContract.Data._ID + "=?", new String[]{String.valueOf(note.getId())})
+                        .withValues(values)
+                        .build();
+            } else {
+
+                return ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
+                        .withSelection(ContactsContract.Data._ID + "=?", new String[]{String.valueOf(note.getId())})
+                        .build();
+            }
+
+        }
+        return null;
+    }
+
+    /**
+     * 如果要删除,只需将其值设置为null
+     * 比如:删除note, note.note = null
+     * */
+    public void updateContact(Contact contact) {
+        if (contact != null && contact.getID() != Contact.NEW_CONTACT_ID) {
+
+            Integer ID = getRawContactID(contact);
+            if (ID.intValue() == Contact.NEW_CONTACT_ID) {
+                return;
+            }
+
+            ArrayList<ContentProviderOperation> operations = new ArrayList<>();
+
+            // update name
+            if (contact.name != null) {
+                ContentProviderOperation nameOp;
+                if (contact.name.getId().intValue() != BaseElement.NEW_ELEMENT_ID.intValue()) {
+                    nameOp = updateName(contact.name);
+                } else {
+                    nameOp = insertNameForContact(contact.name, contact.getID());
+                }
+                if (nameOp != null) {
+                    operations.add(nameOp);
+                }
+            }
+
+            // update photo
+            if (contact.photo != null) {
+                ContentProviderOperation photoOperation;
+                if (contact.photo.getId().intValue() != BaseElement.NEW_ELEMENT_ID.intValue()) {
+                    photoOperation = updatePhoto(contact.photo);
+                } else {
+                    photoOperation = insertPhotoForContact(contact.photo, contact.getID());
+                }
+                if (photoOperation != null) {
+                    operations.add(photoOperation);
+                }
+            }
+
+            // update job
+            if (contact.organization != null) {
+
+                ContentProviderOperation jobOp;
+
+                if (contact.organization.getId().intValue() != BaseElement.NEW_ELEMENT_ID.intValue()) {
+                    jobOp = updateJob(contact.organization);
+                } else {
+                    jobOp = insertJobForContact(contact.organization, contact.getID());
+                }
+
+                if (jobOp != null) {
+                    operations.add(jobOp);
+                }
+            }
+
+            // update phone number
+            if (contact.phoneNumbers != null && contact.phoneNumbers.size() > 0) {
+                for (PhoneNumber phone : contact.phoneNumbers) {
+
+                    ContentProviderOperation phoneOp;
+
+                    if (phone.getId().intValue() != BaseElement.NEW_ELEMENT_ID.intValue()) {
+
+                        phoneOp = updatePhoneNumber(phone);
+                    } else {
+
+                        phoneOp = insertPhoneNumberForContact(phone, contact.getID());
+                    }
+
+                    if (phoneOp != null) {
+                        operations.add(phoneOp);
+                    }
+                }
+            }
+
+
+            // update email
+            if (contact.emailAddresses != null && contact.emailAddresses.size() > 0) {
+                for (EmailAddress emailAddress : contact.emailAddresses) {
+
+                    ContentProviderOperation emailOp;
+                    if (emailAddress.getId().intValue() != BaseElement.NEW_ELEMENT_ID.intValue()) {
+
+                        emailOp = updateEmailAddress(emailAddress);
+                    } else {
+
+                        emailOp = insertEmailAddressForContact(emailAddress, contact.getID());
+                    }
+
+                    if (emailOp != null) {
+                        operations.add(emailOp);
+                    }
+                }
+            }
+
+            // update postal address
+            if (contact.postalAddresses != null && contact.postalAddresses.size() > 0) {
+                for (PostalAddress postalAddress : contact.postalAddresses) {
+
+                    ContentProviderOperation postalAddressOp;
+                    if (postalAddress.getId().intValue() != BaseElement.NEW_ELEMENT_ID.intValue()) {
+
+                        postalAddressOp = updatePostalAddress(postalAddress);
+                    } else {
+                        postalAddressOp = insertPostalAddressForContact(postalAddress, contact.getID());
+                    }
+                    if (postalAddressOp != null) {
+                        operations.add(postalAddressOp);
+                    }
+                }
+            }
+
+            // update social profile
+            if (contact.socialProfiles != null && contact.socialProfiles.size() > 0) {
+                for (SocialProfile socialProfile : contact.socialProfiles) {
+
+                    ContentProviderOperation socialProfileOp;
+                    if (socialProfile.getId().intValue() != BaseElement.NEW_ELEMENT_ID.intValue()) {
+
+                        socialProfileOp = updateSocialProfile(socialProfile);
+                    } else {
+                        socialProfileOp = insertSocialProfileForContact(socialProfile, contact.getID());
+                    }
+
+                    if (socialProfileOp != null) {
+                        operations.add(socialProfileOp);
+                    }
+                }
+            }
+
+            // update url
+            if (contact.webSites != null && contact.webSites.size() > 0) {
+                for (WebSite webSite : contact.webSites) {
+
+                    ContentProviderOperation webSiteOp;
+                    if (webSite.getId().intValue() != BaseElement.NEW_ELEMENT_ID.intValue()) {
+
+                        webSiteOp = updateWebSite(webSite);
+                    } else {
+
+                        webSiteOp = insertWebSiteForContact(webSite, contact.getID());
+                    }
+
+                    if (webSiteOp != null) {
+                        operations.add(webSiteOp);
+                    }
+                }
+            }
+
+            // update note
+            ContentProviderOperation noteOp;
+            if (contact.note.getId().intValue() != BaseElement.NEW_ELEMENT_ID.intValue()) {
+
+                noteOp = updateNote(contact.note);
+            } else {
+
+                noteOp = insertNoteForContact(contact.note, contact.getID());
+            }
+            if (noteOp != null) {
+                operations.add(noteOp);
+            }
+
+            try {
+                ContentProviderResult[] results = mContentResolver.applyBatch(ContactsContract.AUTHORITY, operations);
+                for (ContentProviderResult result : results) {
+                    Log.i("Update Contact", result.toString());
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+
+        }
+    }
+
+    public void updateContactElements(ArrayList<BaseElement> elements) {
+
+        if (elements != null && elements.size() > 0) {
+
+            ArrayList<ContentProviderOperation> ops = new ArrayList<>();
+
+            for (BaseElement element : elements) {
+
+                if (element.getId().intValue() != BaseElement.NEW_ELEMENT_ID.intValue()) {
+
+                    ContentProviderOperation op = null;
+
+                    if (element instanceof Name) {
+
+                        Name name = (Name)element;
+                        op = updateName(name);
+
+                    } else if (element instanceof Photo) {
+
+                        Photo photo = (Photo)element;
+                        op = updatePhoto(photo);
+
+                    } else if (element instanceof Organization) {
+
+                        Organization organization = (Organization)element;
+                        op = updateJob(organization);
+
+                    } else if (element instanceof PhoneNumber) {
+
+                        PhoneNumber phone = (PhoneNumber)element;
+                        op = updatePhoneNumber(phone);
+
+                    } else if (element instanceof EmailAddress) {
+
+                        EmailAddress email = (EmailAddress)element;
+                        op = updateEmailAddress(email);
+
+                    } else if (element instanceof PostalAddress) {
+
+                        PostalAddress postalAddress = (PostalAddress)element;
+                        op = updatePostalAddress(postalAddress);
+
+                    } else if (element instanceof SocialProfile) {
+
+                        SocialProfile socialProfile = (SocialProfile)element;
+                        op = updateSocialProfile(socialProfile);
+
+                    } else if (element instanceof WebSite) {
+
+                        WebSite web = (WebSite)element;
+                        op = updateWebSite(web);
+
+                    } else if (element instanceof Note) {
+
+                        Note note = (Note)element;
+                        op = updateNote(note);
+                    }
+
+                    if (op != null) {
+                        ops.add(op);
+                    }
+
+                }// if
+            } // for
+
+            if (ops.size() > 0) {
+                try {
+                    ContentProviderResult[] results = mContentResolver.applyBatch(ContactsContract.AUTHORITY, ops);
+                    for (ContentProviderResult result : results) {
+                        Log.i("Update Contact Name", result.toString());
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+
+        }
+    }
+
+    // endregion
+
+}

+ 449 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/ContactUIHelper.java

@@ -0,0 +1,449 @@
+package com.usai.redant.rautils.contactkit;
+
+import android.app.Activity;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.provider.ContactsContract;
+
+import com.usai.redant.rautils.contactkit.element.EmailAddress;
+import com.usai.redant.rautils.contactkit.element.Name;
+import com.usai.redant.rautils.contactkit.element.Note;
+import com.usai.redant.rautils.contactkit.element.Organization;
+import com.usai.redant.rautils.contactkit.element.PhoneNumber;
+import com.usai.redant.rautils.contactkit.element.Photo;
+import com.usai.redant.rautils.contactkit.element.PostalAddress;
+import com.usai.redant.rautils.contactkit.element.SocialProfile;
+import com.usai.redant.rautils.contactkit.element.WebSite;
+
+import java.util.ArrayList;
+
+public class ContactUIHelper {
+
+    // region Content Values
+
+    /**
+     * name
+     * */
+    private static ContentValues valuesForName(Name name) {
+
+        if (name != null && !name.isEmpty()) {
+            ContentValues values = new ContentValues();
+            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
+
+            // given name
+            if (name.givenName != null) {
+                values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, name.givenName);
+            }
+
+            // middle name
+            if (name.middleName != null) {
+                values.put(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, name.middleName);
+            }
+
+            // family name
+            if (name.familyName != null) {
+                values.put(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, name.familyName);
+            }
+
+           return values;
+        }
+        return null;
+    }
+
+    /**
+     * Photo
+     * */
+    private static ContentValues valuesForPhoto(Photo photo) {
+        if (photo != null && photo.thumbnail != null) {
+
+            ContentValues values = new ContentValues();
+
+            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE);
+            values.put(ContactsContract.CommonDataKinds.Photo.PHOTO, photo.thumbnail);
+
+            return values;
+        }
+
+        return null;
+    }
+
+    /**
+     * Job
+     * */
+
+    private static ContentValues valuesForJob(Organization organization) {
+        if (organization != null && !organization.isEmpty()) {
+
+            ContentValues values = new ContentValues();
+            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE);
+
+            if (organization.organizationName != null) {
+                values.put(ContactsContract.CommonDataKinds.Organization.COMPANY, organization.organizationName);
+            }
+
+            if (organization.department != null) {
+                values.put(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, organization.department);
+            }
+
+            if (organization.jobTitle != null) {
+                values.put(ContactsContract.CommonDataKinds.Organization.TITLE, organization.jobTitle);
+            }
+
+            return values;
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Phone
+     * */
+    private static ContentValues valuesForPhoneNumber(PhoneNumber phone) {
+
+        if (phone != null) {
+
+            ContentValues values = new ContentValues();
+            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
+
+            if (phone.type == null) {
+                phone.type = PhoneNumber.PhoneNumberType.TYPE_MAIN;
+            }
+            values.put(ContactsContract.CommonDataKinds.Phone.TYPE, phone.type.ordinal());
+
+            if (phone.type == PhoneNumber.PhoneNumberType.TYPE_CUSTOM && phone.localizedLabel != null) {
+                values.put(ContactsContract.CommonDataKinds.Phone.LABEL, phone.localizedLabel);
+            }
+
+            if (phone.value != null) {
+                values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone.value);
+            }
+
+            if (phone.label != null) {
+                values.put(ContactsContract.CommonDataKinds.Phone.LABEL, phone.label);
+            }
+
+            return values;
+        }
+
+        return null;
+    }
+
+    /**
+     * Email
+     * */
+    private static ContentValues valuesForEmailAddress(EmailAddress email) {
+
+        if (email != null) {
+
+            ContentValues values = new ContentValues();
+            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
+
+            if (email.type == null) {
+                email.type = EmailAddress.EmailType.TYPE_HOME;
+            }
+            values.put(ContactsContract.CommonDataKinds.Email.TYPE, email.type.ordinal());
+
+            if (email.type == EmailAddress.EmailType.TYPE_CUSTOM && email.localizedLabel != null) {
+                values.put(ContactsContract.CommonDataKinds.Email.LABEL, email.localizedLabel);
+            }
+
+            if (email.value != null) {
+                values.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email.value);
+            }
+
+            if (email.label != null) {
+                values.put(ContactsContract.CommonDataKinds.Email.LABEL, email.label);
+            }
+
+            return values;
+        }
+        return null;
+    }
+
+    /**
+     * Postal Address
+     * */
+    private static ContentValues valuesForPostalAddress(PostalAddress address) {
+
+        if (address != null && !address.isEmpty()) {
+
+            ContentValues values = new ContentValues();
+            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE);
+
+            if (address.type == null) {
+                address.type = PostalAddress.AddressType.TYPE_HOME;
+            }
+            values.put(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, address.type.ordinal());
+
+            if (address.type == PostalAddress.AddressType.TYPE_CUSTOM && address.localizedLabel != null) {
+                values.put(ContactsContract.CommonDataKinds.StructuredPostal.LABEL, address.localizedLabel);
+            }
+
+            if (address.country != null) {
+                values.put(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, address.country);
+            }
+
+            if (address.state != null) {
+                values.put(ContactsContract.CommonDataKinds.StructuredPostal.REGION, address.state);
+            }
+
+            if (address.city != null) {
+                values.put(ContactsContract.CommonDataKinds.StructuredPostal.CITY, address.city);
+            }
+
+            if (address.street != null) {
+                values.put(ContactsContract.CommonDataKinds.StructuredPostal.STREET, address.street);
+            }
+
+            if (address.postalCode != null) {
+                values.put(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, address.postalCode);
+            }
+
+            return values;
+        }
+
+        return null;
+    }
+
+    /**
+     * Social Profile
+     * */
+    private static ContentValues valuesForSocialProfile(SocialProfile socialProfile) {
+
+        if (socialProfile != null && socialProfile.value != null) {
+
+            ContentValues values = new ContentValues();
+            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE);
+
+            values.put(ContactsContract.CommonDataKinds.Im.DATA, socialProfile.value);
+
+            if (socialProfile.type == null) {
+                socialProfile.type = SocialProfile.SocialProfileType.TYPE_HOME;
+            }
+            values.put(ContactsContract.CommonDataKinds.Im.TYPE, socialProfile.type.ordinal());
+
+            if (socialProfile.type == SocialProfile.SocialProfileType.TYPE_CUSTOM && socialProfile.localizedLabel != null) {
+                values.put(ContactsContract.CommonDataKinds.Im.LABEL, socialProfile.localizedLabel);
+            }
+
+            int protocol = socialProfile.getImProtocol();
+            values.put(ContactsContract.CommonDataKinds.Im.PROTOCOL, protocol);
+            if (protocol == ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM && socialProfile.localizedProtocol != null) {
+                values.put(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL, socialProfile.localizedProtocol);
+            }
+
+            return values;
+        }
+        return null;
+    }
+
+    /**
+     * Website
+     * */
+    private static ContentValues valuesForWebsite(WebSite web) {
+
+        if (web != null && web.url != null) {
+
+            ContentValues values = new ContentValues();
+            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE);
+
+            values.put(ContactsContract.CommonDataKinds.Website.URL, web.url);
+
+            return values;
+        }
+        return null;
+    }
+
+    /**
+     * Note
+     * */
+    private static ContentValues valuesForNote(Note note) {
+        if (note != null && note.note != null) {
+            ContentValues values = new ContentValues();
+            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE);
+
+            values.put(ContactsContract.CommonDataKinds.Note.NOTE, note.note);
+
+            return values;
+        }
+        return null;
+    }
+
+    // endregion
+
+    // region New Contact
+    /**
+     * 新建联系人
+     * */
+    public static void startActivityForNewContact(Activity context, int requestCode) {
+
+        if (context != null) {
+            Intent intent = new Intent(ContactsContract.Intents.Insert.ACTION);
+            intent.setType(ContactsContract.RawContacts.CONTENT_TYPE);
+            context.startActivityForResult(intent, requestCode);
+        }
+    }
+
+    public static void startActivityForNewContact(Activity context, Contact contact, int requestCode) {
+
+        if (context != null) {
+
+            Intent intent = new Intent(ContactsContract.Intents.Insert.ACTION);
+            intent.setType(ContactsContract.RawContacts.CONTENT_TYPE);
+
+            if (contact != null) {
+
+                // info
+                if (contact.name != null) {
+                    intent.putExtra(ContactsContract.Intents.Insert.NAME, contact.name.fullName());
+                }
+//
+//            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 nameValues = valuesForName(contact.name);
+//                if (nameValues != null) {
+//                    data.add(nameValues);
+//                }
+
+                ContentValues photoValues = valuesForPhoto(contact.photo);
+                if (photoValues != null) {
+                    data.add(photoValues);
+                }
+
+                ContentValues jobValues = valuesForJob(contact.organization);
+                if (jobValues != null) {
+                    data.add(jobValues);
+                }
+
+                if (contact.phoneNumbers != null && contact.phoneNumbers.size() > 0) {
+                    for (PhoneNumber phoneNumber : contact.phoneNumbers) {
+
+                        ContentValues phoneValues = valuesForPhoneNumber(phoneNumber);
+                        if (phoneValues != null) {
+                            data.add(phoneValues);
+                        }
+
+                    }
+                }
+
+                if (contact.emailAddresses != null && contact.emailAddresses.size() > 0) {
+
+                    for (EmailAddress email : contact.emailAddresses) {
+
+                        ContentValues emailValues = valuesForEmailAddress(email);
+                        if (emailValues != null) {
+                            data.add(emailValues);
+                        }
+                    }
+                }
+
+                if (contact.postalAddresses != null && contact.postalAddresses.size() > 0) {
+
+                    for (PostalAddress address : contact.postalAddresses) {
+
+                        ContentValues addressValues = valuesForPostalAddress(address);
+                        if (addressValues != null) {
+                            data.add(addressValues);
+                        }
+                    }
+                }
+
+                if (contact.socialProfiles != null && contact.socialProfiles.size() > 0) {
+
+                    for (SocialProfile socialProfile : contact.socialProfiles) {
+
+                        ContentValues socialProfileValues = valuesForSocialProfile(socialProfile);
+                        if (socialProfileValues != null) {
+                            data.add(socialProfileValues);
+                        }
+                    }
+                }
+
+                if (contact.webSites != null && contact.webSites.size() > 0) {
+
+                    for (WebSite webSite : contact.webSites) {
+
+                        ContentValues webValues = valuesForWebsite(webSite);
+                        if (webValues != null) {
+                            data.add(webValues);
+                        }
+                    }
+                }
+
+                if (contact.note != null) {
+                    ContentValues noteValues = valuesForNote(contact.note);
+                    if (noteValues != null) {
+                        data.add(noteValues);
+                    }
+                }
+
+                intent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, data);
+
+            } // if contact != null
+
+            context.startActivityForResult(intent, requestCode);
+        }
+
+    }
+
+    // endregion
+
+
+    // region Edit Contact
+    /**
+     * 编辑联系人
+     * */
+    public static void startActivityForEditContact(Context context, Contact contact) {
+
+        if (contact != null) {
+
+            Uri uri = ContactHelper.sharedHelper(context).getUriOfContact(contact);
+            if (uri != null) {
+                Intent editIntent = new Intent(Intent.ACTION_EDIT);
+                editIntent.setDataAndType(uri,ContactsContract.Contacts.CONTENT_ITEM_TYPE);
+                context.startActivity(editIntent);
+            }
+        }
+    }
+
+    // endregion
+
+
+    // region Contact Picker
+    /**
+     * 选择联系人
+     * */
+    public static void startContactPickerActivity(Activity activity, int requestCode) {
+
+        if (activity != null) {
+            Intent intent = new Intent(Intent.ACTION_PICK);
+            intent.setData(ContactsContract.Contacts.CONTENT_URI);
+            activity.startActivityForResult(intent, requestCode);
+        }
+    }
+
+    public static Contact pickContactForIntent(Context context,Intent data) {
+        if (data != null && context != null) {
+            return ContactHelper.sharedHelper(context).searchContactByUri(data.getData());
+        }
+        return null;
+    }
+    // endregion
+}

+ 19 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/element/BaseElement.java

@@ -0,0 +1,19 @@
+package com.usai.redant.rautils.contactkit.element;
+
+public class BaseElement {
+
+    public static final Integer NEW_ELEMENT_ID = 0;
+
+    private Integer id;
+
+    /**
+     * 如果是新建的元素,那么ID应当传入 NEW_ELEMENT_ID
+     * */
+    public BaseElement(Integer id) {
+        this.id = id;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+}

+ 21 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/element/EmailAddress.java

@@ -0,0 +1,21 @@
+package com.usai.redant.rautils.contactkit.element;
+
+public class EmailAddress extends BaseElement {
+
+    public enum EmailType {
+        TYPE_CUSTOM,
+        TYPE_HOME,
+        TYPE_WORK,
+        TYPE_OTHER,
+        TYPE_MOBILE
+    }
+
+    public String value;
+    public EmailType type;
+    public String label;
+    public String localizedLabel;
+
+    public EmailAddress(Integer id) {
+        super(id);
+    }
+}

+ 32 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/element/Name.java

@@ -0,0 +1,32 @@
+package com.usai.redant.rautils.contactkit.element;
+
+public class Name extends BaseElement {
+
+    public String givenName, middleName, familyName, displayName;
+
+    public boolean isEmpty() {
+        return givenName == null && middleName == null && familyName == null;
+    }
+
+    public Name(Integer id) {
+        super(id);
+    }
+
+    public String fullName() {
+
+        StringBuffer stringBuffer = new StringBuffer();
+        if (givenName != null) {
+            stringBuffer.append(givenName);
+        }
+
+        if (middleName != null) {
+            stringBuffer.append(" " + middleName);
+        }
+
+        if (familyName != null) {
+            stringBuffer.append(" " + familyName);
+        }
+
+        return stringBuffer.toString();
+    }
+}

+ 10 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/element/Note.java

@@ -0,0 +1,10 @@
+package com.usai.redant.rautils.contactkit.element;
+
+public class Note extends BaseElement {
+
+    public String note;
+
+    public Note(Integer id) {
+        super(id);
+    }
+}

+ 14 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/element/Organization.java

@@ -0,0 +1,14 @@
+package com.usai.redant.rautils.contactkit.element;
+
+public class Organization extends BaseElement {
+
+    public String organizationName, department, jobTitle;
+
+    public boolean isEmpty() {
+        return organizationName == null && department == null && jobTitle == null;
+    }
+
+    public Organization(Integer id) {
+        super(id);
+    }
+}

+ 39 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/element/PhoneNumber.java

@@ -0,0 +1,39 @@
+package com.usai.redant.rautils.contactkit.element;
+
+public class PhoneNumber extends BaseElement {
+
+
+    public enum PhoneNumberType {
+        TYPE_CUSTOM,
+        TYPE_HOME,
+        TYPE_MOBILE,
+        TYPE_WORK,
+        TYPE_FAX_WORK,
+        TYPE_FAX_HOME,
+        TYPE_PAGER,
+        TYPE_OTHER,
+        TYPE_CALLBACK,
+        TYPE_CAR,
+        TYPE_COMPANY_MAIN,
+        TYPE_ISDN,
+        TYPE_MAIN,
+        TYPE_OTHER_FAX,
+        TYPE_RADIO,
+        TYPE_TELEX,
+        TYPE_TTY_TDD,
+        TYPE_WORK_MOBILE,
+        TYPE_WORK_PAGER,
+        TYPE_ASSISTANT,
+        TYPE_MMS
+    }
+
+    public PhoneNumberType type;
+    public String label;
+    public String value;
+    public String localizedLabel;
+
+    public PhoneNumber(Integer id) {
+        super(id);
+    }
+
+}

+ 16 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/element/Photo.java

@@ -0,0 +1,16 @@
+package com.usai.redant.rautils.contactkit.element;
+
+import android.net.Uri;
+
+/**
+ * 修改Contact Photo时,应当修改thumbnail的值
+ * */
+public class Photo extends BaseElement {
+
+    public Uri uri;
+    public byte[] thumbnail;
+
+    public Photo(Integer id) {
+        super(id);
+    }
+}

+ 25 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/element/PostalAddress.java

@@ -0,0 +1,25 @@
+package com.usai.redant.rautils.contactkit.element;
+
+public class PostalAddress extends BaseElement {
+
+    public enum AddressType {
+        TYPE_CUSTOM,
+        TYPE_HOME,
+        TYPE_WORK,
+        TYPE_OTHER
+    }
+
+    public String country, state, city, street, postalCode;
+    public String formatAddress;
+    public AddressType type;
+    public String label;
+    public String localizedLabel;
+
+    public boolean isEmpty() {
+        return country == null && state == null && city == null && street == null && postalCode == null;
+    }
+
+    public PostalAddress(Integer id) {
+        super(id);
+    }
+}

+ 48 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/element/SocialProfile.java

@@ -0,0 +1,48 @@
+package com.usai.redant.rautils.contactkit.element;
+
+public class SocialProfile extends BaseElement {
+
+    public enum SocialProfileType {
+        TYPE_CUSTOM,
+        TYPE_HOME,
+        TYPE_WORK,
+        TYPE_OTHER
+    }
+
+    public enum SocialProfileProtocol {
+        PROTOCOL_CUSTOM,
+        PROTOCOL_AIM,
+        PROTOCOL_MSN,
+        PROTOCOL_YAHOO,
+        PROTOCOL_SKYPE,
+        PROTOCOL_QQ,
+        PROTOCOL_GOOGLE_TALK,
+        PROTOCOL_ICQ,
+        PROTOCOL_JABBER,
+        PROTOCOL_NETMEETING
+    }
+
+    private int imProtocol;
+
+    public String value;
+    public SocialProfileType type;
+    public String localizedProtocol;
+    public String label;
+    public String localizedLabel;
+
+    public void setSocialProtocol(SocialProfileProtocol protocol) {
+        imProtocol = protocol.ordinal() - 1;
+    }
+
+    public SocialProfileProtocol getSocialProtocol() {
+        return SocialProfileProtocol.values()[imProtocol + 1];
+    }
+
+    public int getImProtocol() {
+        return imProtocol;
+    }
+
+    public SocialProfile(Integer id) {
+        super(id);
+    }
+}

+ 10 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/contactkit/element/WebSite.java

@@ -0,0 +1,10 @@
+package com.usai.redant.rautils.contactkit.element;
+
+public class WebSite extends BaseElement {
+
+    public String url;
+
+    public WebSite(Integer id) {
+        super(id);
+    }
+}

+ 297 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/datetimepicker/DatePickerView.java

@@ -0,0 +1,297 @@
+package com.usai.redant.rautils.datetimepicker;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.widget.LinearLayout;
+import android.widget.NumberPicker;
+import android.widget.TextView;
+
+import com.usai.redant.rautils.R;
+import com.usai.redant.rautils.utils.RAUtil;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+
+import static com.usai.redant.rautils.datetimepicker.DatePickerView.DatePickerViewLayoutMode.DATE_PICKER_VIEW_LAYOUT_MODE_COMPACT;
+import static com.usai.redant.rautils.datetimepicker.DatePickerView.DatePickerViewLayoutMode.DATE_PICKER_VIEW_LAYOUT_MODE_REGULAR;
+
+public class DatePickerView extends LinearLayout {
+
+    public enum DatePickerViewLayoutMode {
+        DATE_PICKER_VIEW_LAYOUT_MODE_COMPACT,
+        DATE_PICKER_VIEW_LAYOUT_MODE_REGULAR
+    }
+
+    private DatePickerViewLayoutMode mMode = DATE_PICKER_VIEW_LAYOUT_MODE_REGULAR;
+
+    public static DatePickerView buildDatePickerView(Context context, DatePickerViewLayoutMode layoutMode) {
+
+//        DatePickerView datePickerView = new DatePickerView(context);
+
+        int layoutId = R.layout.ra_date_picker_regular_view;
+        if (layoutMode == DATE_PICKER_VIEW_LAYOUT_MODE_COMPACT) {
+            layoutId = R.layout.ra_date_picker_compact_view;
+        }
+
+        DatePickerView datePickerView = (DatePickerView) LayoutInflater.from(context).inflate(layoutId,null);
+        datePickerView.mMode = layoutMode;
+        datePickerView.init();
+        return datePickerView;
+    }
+
+    public DatePickerView(Context context) {
+        super(context);
+    }
+
+    public DatePickerView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public DatePickerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    /**
+     * Calendar
+     * year:    从1开始
+     * month:   从0开始
+     * day:     从1开始
+     * */
+    private  Calendar mCalendar = Calendar.getInstance();
+    private boolean initialized = false;
+    private void init() {
+        if (!initialized) {
+            setupView();
+        }
+        initialized = true;
+    }
+
+    NumberPicker mYearPicker, mMonthPicker, mDayPicker;
+    private void setupView() {
+
+//        String languageCode = RAUtil.getSystemLanguageCode(getContext());
+//        Log.d("DatePicker", "setupView: " + languageCode);
+//
+//        if (languageCode.equals("zh")) {
+//
+//        } else {
+//
+//        }
+
+        /**
+         * View
+         * */
+        // year
+//        mYearPicker = new NumberPicker(getContext());
+//        int yearWidth = RAUtil.dp2px(getContext(), 60);
+//        int yearWeight = 1;
+//        if (mMode == DATE_PICKER_VIEW_LAYOUT_MODE_REGULAR) {
+//            yearWidth = 0;
+//            yearWeight = 2;
+//        }
+//        setupPicker(mYearPicker, 10000, yearWidth, yearWeight,"-");
+//
+//
+//        // month
+//        mMonthPicker = new NumberPicker(getContext());
+//        int monthWidth = RAUtil.dp2px(getContext(), 30);
+//        int monthWeight = 1;
+//        if (mMode == DATE_PICKER_VIEW_LAYOUT_MODE_REGULAR) {
+//            monthWidth = 0;
+//            monthWeight = 1;
+//        }
+//        setupPicker(mMonthPicker, 12, monthWidth, monthWeight, "-");
+//
+//        // day
+//        mDayPicker = new NumberPicker(getContext());
+//        int dayWidth = RAUtil.dp2px(getContext(), 30);
+//        int dayWeight = 1;
+//        if (mMode == DATE_PICKER_VIEW_LAYOUT_MODE_REGULAR) {
+//            dayWidth = 0;
+//            dayWeight = 1;
+//        }
+//        setupPicker(mDayPicker, mCalendar.getActualMaximum(Calendar.DAY_OF_MONTH), dayWidth, dayWeight, "");
+
+        mYearPicker = findViewById(R.id.ra_date_year_picker);
+        initPicker(mYearPicker,3000);
+
+        mMonthPicker = findViewById(R.id.ra_date_month_picker);
+        initPicker(mMonthPicker, 12);
+
+        mDayPicker = findViewById(R.id.ra_date_day_picker);
+        initPicker(mDayPicker, mCalendar.getActualMaximum(Calendar.DAY_OF_MONTH));
+
+        /**
+         * setup
+         * */
+        // setup
+        setYear(mCalendar.get(Calendar.YEAR));
+        mYearPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
+            @Override
+            public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
+                p_setYear(newVal + 1);
+            }
+        });
+
+        mMonthPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
+            @Override
+            public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
+                p_setMonth(newVal + 1);
+            }
+        });
+        setMonth(mCalendar.get(Calendar.MONTH) + 1);
+
+
+        mDayPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
+            @Override
+            public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
+                p_setDay(newVal + 1);
+            }
+        });
+        setDay(mCalendar.get(Calendar.DAY_OF_MONTH));
+    }
+
+    private void setupPicker(NumberPicker picker, int max, int width, int weight ,String unit) {
+
+        initPicker(picker, max);
+
+        TextView unitTv = new TextView(getContext());
+        unitTv.setTextColor(Color.BLACK);
+        unitTv.setGravity(Gravity.CENTER);
+        unitTv.setText(unit);
+
+        LayoutParams pickerParams;
+        if (width != 0) {
+            pickerParams = new LayoutParams(width, LayoutParams.MATCH_PARENT);
+        } else {
+            pickerParams = new LayoutParams(width, LayoutParams.MATCH_PARENT,weight);
+        }
+
+        addView(picker, pickerParams);
+
+        LayoutParams unitParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
+        int margin= RAUtil.dp2px(getContext(), 5);
+        unitParams.setMargins(margin,margin,margin,margin);
+
+        addView(unitTv, unitParams);
+    }
+
+    private static String[] setupRangeArray(int max) {
+
+        ArrayList<String> hourArr = new ArrayList<>();
+        for (int i = 0; i < max; i++) {
+            hourArr.add(String.valueOf(i + 1));
+        }
+        String[] values = RAUtil.list2Array(hourArr);
+
+        return values;
+    }
+
+    private void initPicker(NumberPicker picker, int max) {
+
+        String[] values = setupRangeArray(max);
+
+        int oldMax = picker.getMaxValue();
+
+        picker.setMinValue(0);
+        picker.setMaxValue(0);
+        if (oldMax > max) {
+
+            picker.setMaxValue(max - 1);
+            picker.setDisplayedValues(values);
+        } else {
+
+            picker.setDisplayedValues(values);
+            picker.setMaxValue(max - 1);
+        }
+        picker.setValue(0);
+
+
+        // 禁止编辑输入
+        picker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
+    }
+
+    private int year, month, day;
+
+    private int getNumberOfDays(int year, int month) {
+        Calendar c = Calendar.getInstance();
+        c.clear();
+
+        c.set(Calendar.YEAR, year);
+        c.set(Calendar.MONTH, month - 1);
+
+        return c.getActualMaximum(Calendar.DAY_OF_MONTH);
+    }
+
+    private void resetCalendar() {
+        mCalendar.clear();
+        int maxDay = getNumberOfDays(year, month);
+        if (maxDay < day) {
+            day = maxDay;
+        }
+
+        int curMaxDay = mDayPicker.getMaxValue() + 1;
+        if (maxDay != curMaxDay) {
+            initPicker(mDayPicker, maxDay);
+        }
+        mDayPicker.setValue(day - 1);
+
+        mCalendar.set(year, month - 1, day);
+    }
+
+    private void p_setYear(int year) {
+        this.year = year;
+
+        if (initialized) {
+            resetCalendar();
+        }
+    }
+
+    private void p_setMonth(int month) {
+        this.month = month;
+
+        if (initialized) {
+            resetCalendar();
+        }
+    }
+
+    private void p_setDay(int day) {
+        this.day = day;
+
+        if (initialized) {
+            resetCalendar();
+        }
+    }
+
+
+    public void setYear(int year) {
+        p_setYear(year);
+        mYearPicker.setValue(year - 1);
+    }
+
+    public void setMonth(int month) {
+        p_setMonth(month);
+        mMonthPicker.setValue(month - 1);
+    }
+
+    public void setDay(int day) {
+        p_setDay(day);
+        mDayPicker.setValue(day - 1);
+    }
+
+    public int getYear() {
+        return year;
+    }
+
+    public int getMonth() {
+        return month;
+    }
+
+    public int getDay() {
+        return day;
+    }
+}

+ 246 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/datetimepicker/DateTimePickerDialog.java

@@ -0,0 +1,246 @@
+package com.usai.redant.rautils.datetimepicker;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.usai.redant.rautils.R;
+import com.usai.redant.rautils.base.BaseDialog;
+import com.usai.redant.rautils.utils.RAUtil;
+
+import java.util.Calendar;
+
+public class DateTimePickerDialog extends BaseDialog {
+
+    private Context mContext;
+    private DateTimePickerDialog self = this;
+    private boolean initialized = false;
+
+    public enum DateTimePickerMode {
+        DATE_TIME_PICKER_MODE_DATE,
+        DATE_TIME_PICKER_MODE_TIME,
+        DATE_TIME_PICKER_MODE_DATETIME
+    }
+
+    public interface DateTimePickerClickListener {
+
+        void dateTimePickerDialogCanceled(DateTimePickerDialog dateTimePickerDialog);
+        void dateTimePickerDialogConfirmed(DateTimePickerDialog dateTimePickerDialog, Calendar calendar);
+    }
+
+    private DateTimePickerMode mMode = DateTimePickerMode.DATE_TIME_PICKER_MODE_DATE;
+    private DateTimePickerClickListener mClickListener = null;
+
+    public static DateTimePickerDialog dateTimePickerDialog(Context context, DateTimePickerMode mode, DateTimePickerClickListener listener) {
+
+        DateTimePickerDialog dialog = new DateTimePickerDialog(context);
+        dialog.mClickListener = listener;
+        dialog.mMode = mode;
+        dialog.init();
+
+        return dialog;
+    }
+
+    private DateTimePickerDialog(@NonNull final Context context) {
+        super(context, new BasicDialogSetupCallBack() {
+
+            @Override
+            public View createContentView(BaseDialog dialog) {
+                return setupRootView(context);
+            }
+
+            @Override
+            public int dialogGravity(BaseDialog dialog) {
+                return BasicDialogContentGravityCenter;
+            }
+        });
+
+        mContext = context;
+    }
+
+    @Override
+    public void setTitle(@Nullable CharSequence title) {
+        mTitleTv.setText(title);
+    }
+
+    public void setMode(DateTimePickerMode mode) {
+        mMode = mode;
+    }
+
+    private TextView mTitleTv;
+    private DatePickerView mDatePicker;
+    private TimePickerView mTimePicker;
+    private Button mCancelBtn, mOkBtn;
+
+    private void init() {
+
+        View rootView = getRootView();
+
+        mTitleTv = rootView.findViewById(R.id.ra_date_time_picker_title_tv);
+
+        LinearLayout loopPanel = rootView.findViewById(R.id.ra_date_time_picker_loop_panel);
+        setupPicker(loopPanel);
+
+        mCancelBtn = rootView.findViewById(R.id.ra_date_time_picker_cancel_btn);
+        mCancelBtn.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+
+                if (mClickListener != null) {
+                    mClickListener.dateTimePickerDialogCanceled(self);
+                }
+
+                dismiss();
+            }
+        });
+
+        mOkBtn = rootView.findViewById(R.id.ra_date_time_picker_ok_btn);
+        mOkBtn.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+
+                if (mClickListener != null) {
+
+                    Calendar calendar = Calendar.getInstance();
+                    calendar.clear();
+
+                    int year, month, day, hour, minute, second;
+                    year = month = day = hour = minute = second = 0;
+
+                    if (mDatePicker != null) {
+                        year = mDatePicker.getYear();
+                        month = mDatePicker.getMonth();
+                        day = mDatePicker.getDay();
+                    }
+                    if (mTimePicker != null) {
+                        hour = mTimePicker.getHour();
+                        minute = mTimePicker.getMinute();
+                        second = mTimePicker.getSecond();
+                    }
+
+                    calendar.set(Calendar.YEAR, year);
+                    calendar.set(Calendar.MONTH, month - 1);
+                    calendar.set(Calendar.DAY_OF_MONTH, day);
+                    calendar.set(Calendar.HOUR_OF_DAY, hour);
+                    calendar.set(Calendar.MINUTE, minute);
+                    calendar.set(Calendar.SECOND, second);
+
+                    mClickListener.dateTimePickerDialogConfirmed(self, calendar);
+                }
+
+                dismiss();
+            }
+        });
+
+
+        initialized = true;
+    }
+
+    private void setupPicker(LinearLayout panel) {
+        if (panel != null) {
+
+            switch (mMode) {
+                case DATE_TIME_PICKER_MODE_DATE: {
+
+                    mDatePicker = DatePickerView.buildDatePickerView(mContext, DatePickerView.DatePickerViewLayoutMode.DATE_PICKER_VIEW_LAYOUT_MODE_REGULAR);
+
+                    LinearLayout.LayoutParams dateLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
+                    dateLayoutParams.setMargins(RAUtil.dp2px(mContext, 20),0,RAUtil.dp2px(mContext, 20), 0);
+                    panel.addView(mDatePicker, dateLayoutParams);
+                }
+                break;
+                case DATE_TIME_PICKER_MODE_TIME: {
+
+                    mTimePicker = TimePickerView.buildTimePickerView(mContext);
+                    LinearLayout.LayoutParams timeLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
+                    timeLayoutParams.setMargins(RAUtil.dp2px(mContext, 20),0,RAUtil.dp2px(mContext, 20), 0);
+                    panel.addView(mTimePicker, timeLayoutParams);
+                }
+                break;
+                case DATE_TIME_PICKER_MODE_DATETIME: {
+
+                    // Date
+                    mDatePicker = DatePickerView.buildDatePickerView(mContext, DatePickerView.DatePickerViewLayoutMode.DATE_PICKER_VIEW_LAYOUT_MODE_COMPACT);
+                    LinearLayout.LayoutParams dateLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT);
+                    dateLayoutParams.setMargins(0,0,RAUtil.dp2px(mContext, 5), 0);
+                    panel.addView(mDatePicker, dateLayoutParams);
+
+
+                    // Time
+                    mTimePicker = TimePickerView.buildTimePickerView(mContext);
+                    LinearLayout.LayoutParams timeLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT);
+                    panel.addView(mTimePicker, timeLayoutParams);
+                }
+                break;
+            }
+        }
+    }
+
+
+    @Override
+    public void show() {
+
+        if (!initialized) {
+            init();
+        }
+
+        super.show();
+    }
+
+    public void setCalendar(Calendar calendar) {
+        if (calendar != null) {
+
+            if (mDatePicker != null) {
+                int year = calendar.get(Calendar.YEAR);
+                int month = calendar.get(Calendar.MONTH) + 1;
+                int day = calendar.get(Calendar.DAY_OF_MONTH);
+
+                mDatePicker.setYear(year);
+                mDatePicker.setMonth(month);
+                mDatePicker.setDay(day);
+            }
+
+            if (mTimePicker != null) {
+
+                int hour = calendar.get(Calendar.HOUR_OF_DAY);
+                int minute = calendar.get(Calendar.MINUTE);
+                int second = calendar.get(Calendar.SECOND);
+
+                mTimePicker.setHour(hour);
+                mTimePicker.setMinute(minute);
+                mTimePicker.setSecond(second);
+            }
+
+        }
+    }
+
+    private static View setupRootView(Context context) {
+
+        if (context == null) {
+            return null;
+        }
+
+        RelativeLayout root = new RelativeLayout(context);
+
+        int margin = RAUtil.dp2px(context, 20);
+        int widthPixels = RAUtil.getScreenWidthPixels(context);
+        widthPixels = widthPixels - 2 * margin;
+        int heightPixels = widthPixels + RAUtil.dp2px(context, 25);
+
+        View contentView = (View)LayoutInflater.from(context).inflate(R.layout.ra_date_time_picker_dialog_content_view,null);
+        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(widthPixels, heightPixels);
+        layoutParams.setMargins(margin, 0 , margin, 0);
+
+        root.addView(contentView, layoutParams);
+
+        return root;
+    }
+
+
+}

+ 138 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/datetimepicker/TimePickerView.java

@@ -0,0 +1,138 @@
+package com.usai.redant.rautils.datetimepicker;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.widget.LinearLayout;
+import android.widget.NumberPicker;
+
+import com.usai.redant.rautils.R;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+
+public class TimePickerView extends LinearLayout {
+
+    public static TimePickerView buildTimePickerView(Context context) {
+
+        TimePickerView pickerView = (TimePickerView)LayoutInflater.from(context).inflate(R.layout.ra_time_picker_view,null);
+        pickerView.init();
+        return pickerView;
+    }
+
+    public TimePickerView(Context context) {
+        super(context);
+    }
+
+    public TimePickerView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public TimePickerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    private NumberPicker mHourPicker, mMinutePicker, mSecondPicker;
+    private void init() {
+
+        initView();
+
+        initTime();
+    }
+
+    private void initTime() {
+
+        Calendar calendar = Calendar.getInstance();
+        setHour(calendar.get(Calendar.HOUR_OF_DAY));
+        setMinute(calendar.get(Calendar.MINUTE));
+        setSecond(calendar.get(Calendar.SECOND));
+    }
+
+    private void initView() {
+
+        // hour
+        mHourPicker = findViewById(R.id.ra_time_picker_hour);
+        initPicker(mHourPicker, 24);
+
+        // minute
+        mMinutePicker = findViewById(R.id.ra_time_picker_minute);
+        initPicker(mMinutePicker, 60);
+
+        // second
+        mSecondPicker = findViewById(R.id.ra_time_picker_second);
+        initPicker(mSecondPicker, 60);
+    }
+
+    private void initPicker(NumberPicker picker, int max) {
+
+        ArrayList<String> hourArr = new ArrayList<>();
+        for (int i = 0; i < max; i++) {
+            hourArr.add(String.format("%02d",i));
+        }
+        String[] values = list2Array(hourArr);
+        picker.setDisplayedValues(values);
+        picker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
+        picker.setMinValue(0);
+        picker.setMaxValue(max - 1);
+        picker.setValue(0);
+    }
+
+    private String[] list2Array(List list) {
+        if (list != null) {
+            return (String[])list.toArray(new String[list.size()]);
+        }
+        return null;
+    }
+
+    private int hour, minute, second;
+
+    public void setHour(int hour) {
+        if (hour >= 24) {
+            hour = hour % 24;
+        }
+        this.hour = hour;
+
+        mHourPicker.setValue(hour);
+    }
+
+    public void setMinute(int minute) {
+        if (minute >= 60) {
+            minute = minute % 60;
+        }
+        this.minute = minute;
+
+        mMinutePicker.setValue(minute);
+    }
+
+    public void setSecond(int second) {
+        if (second >= 60) {
+            second = second % 60;
+        }
+        this.second = second;
+
+        mSecondPicker.setValue(second);
+    }
+
+    public int getHour() {
+        if (mHourPicker != null) {
+            hour = mHourPicker.getValue();
+        }
+        return hour;
+    }
+
+    public int getMinute() {
+        if (mMinutePicker != null) {
+            minute = mMinutePicker.getValue();
+        }
+        return minute;
+    }
+
+    public int getSecond() {
+        if (mSecondPicker != null) {
+            second = mSecondPicker.getValue();
+        }
+        return second;
+    }
+}

+ 147 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/fileviewer/FileViewerActivity.java

@@ -0,0 +1,147 @@
+package com.usai.redant.rautils.fileviewer;
+
+import android.app.Application;
+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.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ListView;
+
+import com.usai.redant.rautils.R;
+import com.usai.redant.rautils.utils.FileManager;
+import com.usai.redant.rautils.utils.RAProviderHelper;
+import com.usai.redant.rautils.utils.RAUtil;
+
+import java.io.File;
+
+public class FileViewerActivity extends AppCompatActivity {
+
+    final static String TAG = "FileViewer";
+
+    private final static String File_Key = "File_Key";
+    public static void start(Context context, File file) {
+        if (context == null) {
+            Log.d(TAG, "start fileviewer context is null");
+            return;
+        }
+        if (file == null || !file.exists() || file.isFile()) {
+            Log.d(TAG, "start fileviewer file is null or file not exist or file is not a directory ");
+            return;
+        }
+
+        Intent intent = new Intent(context, FileViewerActivity.class);
+        intent.putExtra(File_Key, file.getAbsolutePath());
+
+        context.startActivity(intent);
+    }
+
+    private ListView mListView;
+    private File mFile;
+    private FileViewerAdapter mAdapter;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.file_viewer_activity);
+
+        ActionBar actionBar = getSupportActionBar();
+        if (actionBar != null) {
+            actionBar.setHomeButtonEnabled(true);
+            actionBar.setDisplayHomeAsUpEnabled(true);
+        }
+
+        Intent intent = getIntent();
+        if (intent != null) {
+            String filePath = intent.getStringExtra(File_Key);
+            if (filePath != null) {
+                mFile = new File(filePath);
+            }
+        }
+
+        if (savedInstanceState != null) {
+            String filePath = savedInstanceState.getString(File_Key);
+            if (filePath != null) {
+                mFile = new File(filePath);
+            }
+        }
+
+        mAdapter = new FileViewerAdapter(this,mFile);
+
+        mListView = findViewById(R.id.file_list_view);
+        mListView.setAdapter(mAdapter);
+        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+
+                File file = (File) mAdapter.getItem(position);
+                clickFileCell(file);
+            }
+        });
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        if (mFile != null) {
+            outState.putString(File_Key, mFile.getAbsolutePath());
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home: {
+                finish();
+                return true;
+            }
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    private void clickFileCell(File file) {
+        if (file != null && file.exists()) {
+
+            if (file.isDirectory()) {
+
+                start(this,file);
+
+            } else {
+
+                viewFile(file);
+            }
+        }
+    }
+
+    private void viewFile(File file) {
+        if (file != null && file.exists() && file.isFile()) {
+
+            String name = file.getName();
+            String oldPath = file.getAbsolutePath();
+            String newDir = FileManager.SDCardRoot() + RAUtil.getApplicationName(getApplicationContext());
+            File dirF = new File(newDir);
+            if (!dirF.exists()) {
+                dirF.mkdirs();
+            }
+
+            String newPath = newDir  + File.separator + name;
+            FileManager.copyFile(oldPath, newPath);
+
+            Application application = getApplication();
+            if (application != null && application instanceof RAProviderHelper.ProviderHelperDelegate) {
+
+                String authority = ((RAProviderHelper.ProviderHelperDelegate) application).getProviderAuthorities();
+                FileManager.openFile(getApplicationContext(), authority, new File(newPath));
+            }
+
+
+        }
+    }
+
+
+}

+ 121 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/fileviewer/FileViewerAdapter.java

@@ -0,0 +1,121 @@
+package com.usai.redant.rautils.fileviewer;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.usai.redant.rautils.R;
+
+import java.io.File;
+import java.lang.ref.WeakReference;
+
+public class FileViewerAdapter extends BaseAdapter {
+
+    private WeakReference<FileViewerActivity> mActivity;
+    private File[] mFileList;
+
+    public FileViewerAdapter(FileViewerActivity activity, File file) {
+        if (activity == null) {
+            mActivity = null;
+        } else {
+            mActivity = new WeakReference<>(activity);
+        }
+
+        if (file == null) {
+            mFileList = null;
+        } else {
+            mFileList = file.listFiles();
+        }
+    }
+
+    private FileViewerActivity getActivity() {
+        if (mActivity == null) {
+            return null;
+        }
+        return mActivity.get();
+    }
+
+    private int getFileCount() {
+        if (mFileList == null || getActivity() == null) {
+            return 0;
+        }
+        return mFileList.length;
+    }
+
+    private File getFile(int index) {
+        int count = getFileCount();
+        if (index >= count || index < 0) {
+            return null;
+        }
+        return mFileList[index];
+    }
+
+    @Override
+    public int getCount() {
+        return getFileCount();
+    }
+
+    @Override
+    public Object getItem(int position) {
+        return getFile(position);
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return position;
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+
+        FileCellHolder holder;
+        if (convertView == null) {
+
+            convertView = LayoutInflater.from(getActivity()).inflate(R.layout.file_viewer_cell,null);
+            holder = new FileCellHolder(convertView);
+
+        } else {
+
+            holder = (FileCellHolder)convertView.getTag();
+        }
+
+        File file = getFile(position);
+        holder.setFile(file);
+
+        return convertView;
+    }
+
+    private class FileCellHolder {
+
+        private ImageView iconView;
+        private TextView  nameTv;
+        public FileCellHolder(View view) {
+            view.setTag(this);
+
+            iconView = view.findViewById(R.id.file_cell_icon);
+            nameTv = view.findViewById(R.id.file_cell_name_tv);
+        }
+
+        public void setFile(File f) {
+            if (f == null) {
+                iconView.setImageDrawable(null);
+                nameTv.setText(null);
+            } else {
+
+                FileViewerActivity activity = getActivity();
+                if (activity != null) {
+                    if (f.isFile()) {
+                        iconView.setImageDrawable(activity.getDrawable(R.drawable.ic_file));
+                    } else {
+                        iconView.setImageDrawable(activity.getDrawable(R.drawable.ic_folder));
+                    }
+                }
+
+                nameTv.setText(f.getName());
+            }
+        }
+    }
+}

+ 180 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/infinitephoto/InfinitePhotoActivity.java

@@ -0,0 +1,180 @@
+package com.usai.redant.rautils.infinitephoto;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
+import com.usai.redant.rautils.R;
+import com.usai.redant.rautils.carousel.CarouselView;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+
+public class InfinitePhotoActivity extends AppCompatActivity {
+
+    private final static String ImagesKey = "ImagesKey";
+    private final static String OffsetKey = "OffsetKey";
+
+    private final static String PhotoItemKeyPath_URL = "url";
+    private final static String PhotoItemKeyPath_Local = "local";
+    private final static String PhotoItemKeyPath_PlaceHolder = "placeHolder";
+
+    public static JSONObject buildPhotoItem(String url, boolean local, int placeHolderResId) {
+        JSONObject json = new JSONObject();
+        try {
+
+            if (url != null) {
+                json.put(PhotoItemKeyPath_URL, url);
+            }
+            json.put(PhotoItemKeyPath_Local,local);
+            json.put(PhotoItemKeyPath_PlaceHolder, placeHolderResId);
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+        return json;
+    }
+
+    public static void startInfinitePhotoActivity(Activity context, JSONArray images, int offset) {
+        if (context == null || images == null || images.length() == 0) {
+            return;
+        }
+
+        Intent intent = new Intent(context, InfinitePhotoActivity.class);
+        intent.putExtra(ImagesKey, images.toString());
+        intent.putExtra(OffsetKey, offset);
+
+        context.startActivity(intent);
+    }
+
+    private CarouselView mCarouselView;
+    private TextView mIndicatorTv;
+    private ArrayList<InfinitePhotoItem> mPhotoItems;
+    private int mOffset;
+    private Context mCtx = this;
+    private InfinitePhotoActivity self = this;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ra_infinite_photo_activity);
+
+        mCarouselView = findViewById(R.id.ra_infinite_photo_carousel);
+        mIndicatorTv = findViewById(R.id.ra_infinite_photo_indicator_tv);
+
+        Intent intent = getIntent();
+        if (intent != null) {
+
+            String imagesStr = intent.getStringExtra(ImagesKey);
+            int offset = intent.getIntExtra(OffsetKey, 0);
+
+            try {
+                JSONArray images = new JSONArray(imagesStr);
+                if (images.length() > 0) {
+
+                    ArrayList<InfinitePhotoItem> tmpArr = new ArrayList<>();
+                    for (int i = 0; i < images.length(); i++) {
+
+                        JSONObject itemJson = images.getJSONObject(i);
+                        InfinitePhotoItem photoItem = new InfinitePhotoItem(self);
+                        photoItem.setValuesForKeysWithJSON(itemJson);
+                        tmpArr.add(photoItem);
+                    }
+
+                    mPhotoItems = tmpArr;
+                }
+
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+
+            mOffset = offset;
+        }
+
+        updateIndicator();
+
+        // Carousel
+        mCarouselView.setAutoScroll(false);
+        mCarouselView.registResourceId(R.layout.ra_infinite_photo_cell);
+        mCarouselView.setDelegate(new CarouselView.CarouselDelegate() {
+            @Override
+            public void carouselWillShowItem(CarouselView carousel, View cell, int index) {
+
+                Log.d("Carousel", "WillDisplay: " + index + " " + cell);
+                updateViewForIndex(cell, index);
+
+            }
+
+            @Override
+            public void carouselDidShowItem(CarouselView carousel, View cell, int index) {
+                Log.d("Carousel", "DidScrollTo: " + index);
+                mOffset = index;
+                updateIndicator();
+
+                /** 使用数据与Cell绑定后,
+                 *  如果图片有两张那么排列位置: 2, 1, 2, 1
+                 *  当前位置为2的时候,会预加载位置1、3,那么会将其中一个重置为数据为空。
+                 *  滑动到3位置时,会跳转到1位置,此时1位置已经被清空了,所以应当对当前
+                 *  显示Cell重新绑定数据
+                 * */
+                updateViewForIndex(cell, index);
+            }
+
+            @Override
+            public void carouselWillEndDisplayItem(CarouselView carousel, View cell, int index) {
+                Log.d("Carousel", "WillEndDisplay: " + index + " " + cell);
+                if (cell instanceof InfinitePhotoItemCell) {
+                    InfinitePhotoItemCell photoItemCell = (InfinitePhotoItemCell) cell;
+                    photoItemCell.clear();
+                }
+            }
+
+            @Override
+            public int carouselNumberOfItems(CarouselView carousel) {
+                if (mPhotoItems == null) {
+                    return 0;
+                }
+                return mPhotoItems.size();
+            }
+        });
+        mCarouselView.reloadDataWithOffset(mOffset);
+    }
+
+    /**
+     * Private
+     * */
+
+    private void updateIndicator() {
+
+        int index = mOffset + 1;
+        if (index > mPhotoItems.size()) {
+            index = mPhotoItems.size();
+        }
+        String offsetStr = String.format("%d/%d",index, mPhotoItems.size());
+        mIndicatorTv.setText(offsetStr);
+    }
+
+    private void updateViewForIndex(View cell, int index) {
+
+        if (cell instanceof InfinitePhotoItemCell) {
+
+            InfinitePhotoItemCell photoItemCell = (InfinitePhotoItemCell) cell;
+            photoItemCell.setActivity(self);
+
+            if (index >= mPhotoItems.size()) {
+                photoItemCell.setPhotoItem(null);
+            } else {
+                InfinitePhotoItem photoItem = mPhotoItems.get(index);
+                photoItemCell.setPhotoItem(photoItem);
+            }
+        }
+    }
+}

+ 125 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/infinitephoto/InfinitePhotoItem.java

@@ -0,0 +1,125 @@
+package com.usai.redant.rautils.infinitephoto;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.util.Log;
+
+import com.usai.redant.rautils.base.BaseObject;
+import com.usai.redant.rautils.operationqueue.OperationQueue;
+import com.usai.redant.rautils.utils.ImageUtil;
+
+import org.json.JSONObject;
+
+import java.io.File;
+import java.lang.ref.WeakReference;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+public   class InfinitePhotoItem extends BaseObject {
+
+    public final static int PlaceHolderResIdNone = -1;
+
+    public String url;
+    public boolean local;
+    public Bitmap image;
+    private WeakReference<InfinitePhotoItemUIDelegate> mWeakdelegate;
+    private InfinitePhotoItem self = this;
+
+    private WeakReference<Activity> mWeakCtx;
+    public InfinitePhotoItem(Activity context) {
+        if (context != null) {
+            mWeakCtx = new WeakReference<>(context);
+        } else {
+            mWeakCtx = null;
+        }
+    }
+
+    @Override
+    public void setValuesForKeysWithJSON(JSONObject json) {
+        super.setValuesForKeysWithJSON(json);
+
+        fetchPhoto();
+    }
+
+    private InfinitePhotoItemUIDelegate getDelegate() {
+        if (mWeakdelegate == null) {
+            return null;
+        } else {
+            return mWeakdelegate.get();
+        }
+    }
+
+    private void setWeakDelegate(InfinitePhotoItemUIDelegate delegate) {
+        if (delegate == null) {
+            mWeakdelegate = null;
+        } else {
+            mWeakdelegate = new WeakReference<>(delegate);
+        }
+    }
+
+    public void setImage(Bitmap image) {
+        this.image = image;
+
+        InfinitePhotoItemUIDelegate oldDelegate = getDelegate();
+        if (oldDelegate != null) {
+            oldDelegate.refreshUI();
+        }
+    }
+
+    public Bitmap getImage() {
+        return image;
+    }
+
+    public void setDelegate(InfinitePhotoItemUIDelegate delegate) {
+        InfinitePhotoItemUIDelegate oldDelegate = getDelegate();
+        if (oldDelegate != null) {
+            oldDelegate.unbind();
+        }
+
+        setWeakDelegate(delegate);
+    }
+
+    private void fetchPhoto() {
+        OperationQueue.sharedQueue().addOperationTask(new OperationQueue.OperationBackgroundCallBack() {
+            @Override
+            public Object operationDoInBackground() {
+
+                Bitmap photo = null;
+                if (url != null) {
+
+                    if (local) {
+
+                        photo = ImageUtil.loadImageFromFile(null, new File(url));
+
+                    } else {
+
+                        if (mWeakCtx != null) {
+                            try {
+                                photo = ImageUtil.loadImageFromURL(mWeakCtx.get(),new URI(url),0,0);
+                            } catch (URISyntaxException e) {
+                                e.printStackTrace();
+                            }
+                        }
+                    }
+
+                }
+
+                Log.d("Load Photo", "operationDoInBackground: " + photo);
+                return photo;
+            }
+        }, new OperationQueue.OperationCompletionCallBack() {
+            @Override
+            public void operationCompletion(Object object) {
+
+                if (object != null && object instanceof Bitmap) {
+
+                    setImage((Bitmap) object);
+
+                } else {
+                    setImage(null);
+                }
+
+            }
+        }, null);
+    }
+}

+ 92 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/infinitephoto/InfinitePhotoItemCell.java

@@ -0,0 +1,92 @@
+package com.usai.redant.rautils.infinitephoto;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+
+import com.usai.redant.rautils.R;
+
+import java.lang.ref.WeakReference;
+
+public class InfinitePhotoItemCell extends RelativeLayout implements InfinitePhotoItemUIDelegate {
+
+    private WeakReference<Activity> mWeakActivity;
+
+    public InfinitePhotoItemCell(Context context) {
+        super(context);
+    }
+
+    public InfinitePhotoItemCell(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public InfinitePhotoItemCell(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    private InfinitePhotoItem mPhotoItem;
+    private ImageView mPhotoIv;
+
+    public void setActivity(Activity activity) {
+
+        if (activity == null) {
+            mWeakActivity = null;
+        } else {
+            mWeakActivity = new WeakReference<>(activity);
+        }
+    }
+
+    public void setPhotoItem(InfinitePhotoItem photoItem) {
+        if (mPhotoItem != null) {
+            mPhotoItem.setDelegate(null);
+        }
+
+        mPhotoItem = photoItem;
+        mPhotoItem.setDelegate(this);
+
+        refreshUI();
+    }
+
+    @Override
+    public void refreshUI() {
+        if (mWeakActivity != null) {
+            Activity activity = mWeakActivity.get();
+            if (activity != null) {
+                activity.runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (mPhotoIv == null) {
+                            mPhotoIv = findViewById(R.id.ra_infinite_photo_iv);
+                        }
+
+                        Bitmap bitmap = null;
+                        if (mPhotoItem != null) {
+                             bitmap = mPhotoItem.getImage();
+                        }
+                        mPhotoIv.setImageBitmap(bitmap);
+                    }
+                });
+            }
+        }
+    }
+
+    @Override
+    public void unbind() {
+        if (mPhotoItem != null) {
+            mPhotoItem = null;
+        }
+        refreshUI();
+    }
+
+    public void clear() {
+        if (mPhotoItem != null) {
+            mPhotoItem.setDelegate(null);
+        }
+        mPhotoItem = null;
+        refreshUI();
+
+    }
+}

+ 8 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/infinitephoto/InfinitePhotoItemUIDelegate.java

@@ -0,0 +1,8 @@
+package com.usai.redant.rautils.infinitephoto;
+
+public interface InfinitePhotoItemUIDelegate {
+
+    void refreshUI();
+    void unbind();
+
+}

+ 92 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/operationqueue/OperationQueue.java

@@ -0,0 +1,92 @@
+package com.usai.redant.rautils.operationqueue;
+
+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;
+
+    private static volatile OperationQueue instance;
+
+    public static OperationQueue sharedQueue() {
+        if (instance == null) {
+            synchronized (OperationQueue.class) {
+                if (instance == null) {
+                    instance = new OperationQueue();
+                }
+            }
+        }
+        return instance;
+    }
+
+    private 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();
+    }
+
+}

+ 120 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/roundcornerimageview/RoundCornerImageView.java

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

+ 1 - 1
ApexDrivers/RAUtilsLibrary/src/main/res/layout/file_viewer_activity.xml

@@ -5,7 +5,7 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    tools:context=".fileViewer.FileViewerActivity">
+    tools:context=".fileviewer.FileViewerActivity">
 
     <ListView
         android:id="@+id/file_list_view"

+ 1 - 1
ApexDrivers/RAUtilsLibrary/src/main/res/layout/file_viewer_cell.xml

@@ -10,7 +10,7 @@
 
     <ImageView
         android:id="@+id/file_cell_icon"
-        android:layout_width="50dp"
+        android:layout_width="50dp" android:contentDescription="TODO" 
         android:layout_height="50dp"
         android:layout_centerVertical="true"
         android:layout_marginLeft="10dp"

+ 5 - 5
ApexDrivers/RAUtilsLibrary/src/main/res/layout/ra_date_picker_compact_view.xml

@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
-<com.usai.redant.rautils.dateTimePicker.DatePickerView xmlns:android="http://schemas.android.com/apk/res/android"
-              android:orientation="horizontal"
-              android:layout_width="match_parent"
-              android:layout_height="match_parent">
+<com.usai.redant.rautils.datetimepicker.DatePickerView xmlns:android="http://schemas.android.com/apk/res/android"
+                                                       android:orientation="horizontal"
+                                                       android:layout_width="match_parent"
+                                                       android:layout_height="match_parent">
 
     <NumberPicker
         android:id="@+id/ra_date_year_picker"
@@ -52,4 +52,4 @@
         android:text=""
         />
 
-</com.usai.redant.rautils.dateTimePicker.DatePickerView>
+</com.usai.redant.rautils.datetimepicker.DatePickerView>

+ 5 - 5
ApexDrivers/RAUtilsLibrary/src/main/res/layout/ra_date_picker_regular_view.xml

@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
-<com.usai.redant.rautils.dateTimePicker.DatePickerView xmlns:android="http://schemas.android.com/apk/res/android"
-              android:orientation="horizontal"
-              android:layout_width="match_parent"
-              android:layout_height="match_parent">
+<com.usai.redant.rautils.datetimepicker.DatePickerView xmlns:android="http://schemas.android.com/apk/res/android"
+                                                       android:orientation="horizontal"
+                                                       android:layout_width="match_parent"
+                                                       android:layout_height="match_parent">
 
     <NumberPicker
         android:id="@+id/ra_date_year_picker"
@@ -52,4 +52,4 @@
         <!--android:text=""-->
         <!--/>-->
 
-</com.usai.redant.rautils.dateTimePicker.DatePickerView>
+</com.usai.redant.rautils.datetimepicker.DatePickerView>

+ 5 - 5
ApexDrivers/RAUtilsLibrary/src/main/res/layout/ra_infinite_photo_cell.xml

@@ -1,14 +1,14 @@
 <?xml version="1.0" encoding="utf-8"?>
-<com.usai.redant.rautils.InfinitePhoto.InfinitePhotoItemCell xmlns:android="http://schemas.android.com/apk/res/android"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent">
+<com.usai.redant.rautils.infinitephoto.InfinitePhotoItemCell xmlns:android="http://schemas.android.com/apk/res/android"
+                                                             android:layout_width="match_parent"
+                                                             android:layout_height="match_parent">
 
     <ImageView
         android:id="@+id/ra_infinite_photo_iv"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:scaleType="fitCenter"
+        android:contentDescription="TODO" android:scaleType="fitCenter"
         android:background="#aaffaa"
         />
 
-</com.usai.redant.rautils.InfinitePhoto.InfinitePhotoItemCell>
+</com.usai.redant.rautils.infinitephoto.InfinitePhotoItemCell>

+ 5 - 5
ApexDrivers/RAUtilsLibrary/src/main/res/layout/ra_time_picker_view.xml

@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
-<com.usai.redant.rautils.dateTimePicker.TimePickerView xmlns:android="http://schemas.android.com/apk/res/android"
-              android:orientation="horizontal"
-              android:layout_width="match_parent"
-              android:layout_height="match_parent">
+<com.usai.redant.rautils.datetimepicker.TimePickerView xmlns:android="http://schemas.android.com/apk/res/android"
+                                                       android:orientation="horizontal"
+                                                       android:layout_width="match_parent"
+                                                       android:layout_height="match_parent">
 
     <NumberPicker
         android:id="@+id/ra_time_picker_hour"
@@ -45,4 +45,4 @@
 
 
 
-</com.usai.redant.rautils.dateTimePicker.TimePickerView>
+</com.usai.redant.rautils.datetimepicker.TimePickerView>

+ 4 - 6
ApexDrivers/apexcrm/src/main/java/com/usai/apex/apexcrm/MainActivity.java

@@ -8,7 +8,6 @@ import android.content.Intent;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
-import android.provider.ContactsContract;
 import android.support.v4.content.PermissionChecker;
 import android.support.v7.app.ActionBar;
 import android.support.v7.app.AlertDialog;
@@ -20,12 +19,12 @@ import android.webkit.ValueCallback;
 import android.widget.Toast;
 
 import com.usai.apex.apexcrm.dataProvider.DataProvider;
-import com.usai.redant.rautils.InfinitePhoto.InfinitePhotoActivity;
-import com.usai.redant.rautils.actionSheet.ActionSheet;
+import com.usai.redant.rautils.infinitephoto.InfinitePhotoActivity;
+import com.usai.redant.rautils.actionsheet.ActionSheet;
 import com.usai.redant.rautils.calendar_event.CalendarEventManager;
 import com.usai.redant.rautils.camera.CameraHelper;
 import com.usai.redant.rautils.contacts.ContactsManager;
-import com.usai.redant.rautils.dateTimePicker.DateTimePickerDialog;
+import com.usai.redant.rautils.datetimepicker.DateTimePickerDialog;
 import com.usai.redant.rautils.email.EmailHelper;
 import com.usai.redant.rautils.map.MapHelper;
 import com.usai.redant.rautils.preview.RAPDFPreviewActivity;
@@ -40,7 +39,6 @@ import org.json.JSONException;
 import org.json.JSONObject;
 
 import java.io.File;
-import java.net.URL;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -48,7 +46,7 @@ import java.util.Calendar;
 import java.util.Date;
 
 import static com.usai.apex.apexcrm.RAWebView.RALocalFileScheme;
-import static com.usai.redant.rautils.InfinitePhoto.InfinitePhotoItem.PlaceHolderResIdNone;
+import static com.usai.redant.rautils.infinitephoto.InfinitePhotoItem.PlaceHolderResIdNone;
 import static com.usai.redant.rautils.utils.Network.RESULT_TRUE;
 
 

+ 1 - 1
ApexDrivers/apexcrm/src/main/java/com/usai/apex/apexcrm/dataProvider/DataProvider.java

@@ -1,6 +1,6 @@
 package com.usai.apex.apexcrm.dataProvider;
 
-import com.usai.redant.rautils.operationQueue.OperationQueue;
+import com.usai.redant.rautils.operationqueue.OperationQueue;
 import com.usai.redant.rautils.utils.Network;
 
 import org.json.JSONObject;

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

@@ -32,7 +32,7 @@ import com.usai.redant.apexdrivers.home.HomeFragment;
 import com.usai.redant.apexdrivers.network.Network;
 import com.usai.redant.apexdrivers.offline.OfflineHandler;
 import com.usai.redant.apexdrivers.receiver.ApexDriverAlarmReceiver;
-import com.usai.redant.rautils.operationQueue.OperationQueue;
+import com.usai.redant.rautils.operationqueue.OperationQueue;
 import com.usai.redant.rautils.receiver.RABroadcast;
 import com.usai.redant.rautils.utils.AESUtil;
 

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

@@ -5,7 +5,7 @@ import android.graphics.Bitmap;
 import android.text.TextUtils;
 
 import com.usai.redant.apexdrivers.ApexDriverApplication;
-import com.usai.redant.rautils.operationQueue.OperationQueue;
+import com.usai.redant.rautils.operationqueue.OperationQueue;
 import com.usai.redant.rautils.utils.ImageUtil;
 
 import java.net.URI;

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

@@ -5,7 +5,7 @@ import android.graphics.Bitmap;
 import android.text.TextUtils;
 
 import com.usai.redant.apexdrivers.ApexDriverApplication;
-import com.usai.redant.rautils.operationQueue.OperationQueue;
+import com.usai.redant.rautils.operationqueue.OperationQueue;
 import com.usai.redant.rautils.utils.ImageUtil;
 
 import java.net.URI;

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

@@ -33,7 +33,7 @@ import com.usai.redant.apexdrivers.SaveInstanceHelper;
 import com.usai.redant.apexdrivers.badgeview.BadgeView;
 import com.usai.redant.apexdrivers.detail.DetailActivity;
 import com.usai.redant.apexdrivers.message.MessageActivity;
-import com.usai.redant.rautils.operationQueue.OperationQueue;
+import com.usai.redant.rautils.operationqueue.OperationQueue;
 import com.usai.redant.rautils.utils.Network;
 
 import org.json.JSONArray;

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

@@ -3,7 +3,7 @@ import android.content.Context;
 import android.graphics.Bitmap;
 
 import com.usai.redant.apexdrivers.ApexDriverApplication;
-import com.usai.redant.rautils.operationQueue.OperationQueue;
+import com.usai.redant.rautils.operationqueue.OperationQueue;
 import com.usai.redant.rautils.utils.ImageUtil;
 import com.usai.redant.rautils.utils.RAUtil;
 

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

@@ -28,7 +28,7 @@ import com.usai.redant.apexdrivers.base.BasicActivity;
 import com.usai.redant.apexdrivers.detail.DetailActivity;
 import com.usai.redant.apexdrivers.message.model.MessageModel;
 import com.usai.redant.apexdrivers.network.Network;
-import com.usai.redant.rautils.operationQueue.OperationQueue;
+import com.usai.redant.rautils.operationqueue.OperationQueue;
 
 import org.json.JSONArray;
 import org.json.JSONException;

+ 1 - 1
ApexDrivers/apexdriverslib/src/main/java/com/usai/redant/apexdrivers/photoCell/PhotoItemModel.java

@@ -6,7 +6,7 @@ import android.graphics.BitmapFactory;
 
 import com.usai.redant.apexdrivers.ApexDriverApplication;
 import com.usai.redant.apexdrivers.base.BasicObject;
-import com.usai.redant.rautils.operationQueue.OperationQueue;
+import com.usai.redant.rautils.operationqueue.OperationQueue;
 import com.usai.redant.rautils.utils.ImageUtil;
 
 import org.json.JSONException;

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

@@ -26,8 +26,8 @@ import com.usai.redant.apexdrivers.setting.model.LinkModel;
 import com.usai.redant.apexdrivers.setting.model.OptionModel;
 import com.usai.redant.apexdrivers.setting.model.SectionModel;
 import com.usai.redant.apexdrivers.setting.option.OptionActivity;
-import com.usai.redant.rautils.operationQueue.OperationQueue;
-import com.usai.redant.rautils.fileViewer.FileViewerActivity;
+import com.usai.redant.rautils.operationqueue.OperationQueue;
+import com.usai.redant.rautils.fileviewer.FileViewerActivity;
 import com.usai.redant.rautils.utils.FileManager;
 import com.usai.redant.rautils.utils.ImageUtil;
 import com.usai.redant.rautils.utils.RAUtil;

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

@@ -11,7 +11,7 @@ import android.widget.Switch;
 import android.widget.TextView;
 
 import com.usai.redant.apexdrivers.R;
-import com.usai.redant.rautils.roundCornerImageView.RoundCornerImageView;
+import com.usai.redant.rautils.roundcornerimageview.RoundCornerImageView;
 import com.usai.redant.apexdrivers.setting.model.AboutModel;
 import com.usai.redant.apexdrivers.setting.model.ActionModel;
 import com.usai.redant.apexdrivers.setting.model.BaseModel;

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

@@ -21,7 +21,7 @@ import android.widget.TextView;
 import com.usai.redant.apexdrivers.ApexDriverApplication;
 import com.usai.redant.apexdrivers.R;
 import com.usai.redant.apexdrivers.network.Network;
-import com.usai.redant.rautils.operationQueue.OperationQueue;
+import com.usai.redant.rautils.operationqueue.OperationQueue;
 
 import org.json.JSONObject;
 

+ 1 - 1
ApexDrivers/apexdriverslib/src/main/res/layout/setting_about_cell.xml

@@ -12,7 +12,7 @@
         android:layout_height="wrap_content"
         >
 
-        <com.usai.redant.rautils.roundCornerImageView.RoundCornerImageView
+        <com.usai.redant.rautils.roundcornerimageview.RoundCornerImageView
             android:id="@+id/setting_about_icon_view"
             android:layout_width="50dp"
             android:layout_height="50dp"

+ 2 - 2
ApexDrivers/apexmobile/src/main/AndroidManifest.xml

@@ -268,7 +268,7 @@
                 <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
             </intent-filter>
         </receiver>
-        <receiver android:name=".Alarmreceiver">
+        <receiver android:name=".Alarmreceiver" android:permission="">
             <intent-filter>
                 <action android:name="com.usai.apex.push"/>
                 <action android:name="com.usai.apex.push.cancel"/>
@@ -306,7 +306,7 @@
         <service
             android:name="com.baidu.location.f"
             android:enabled="true"
-            android:process=":remote">
+            android:permission="" android:process=":remote">
             <intent-filter>
                 <action android:name="com.baidu.location.service_v2.2">
                 </action>

+ 1 - 1
ApexDrivers/apexmobile/src/main/java/com/usai/apex/apexresult/ApexResultActivity.java

@@ -27,7 +27,7 @@ import com.usai.apex.CustomizeFieldsActivity;
 import com.usai.apex.R;
 import com.usai.apex.mainframe.NewDetailActivity;
 import com.usai.apex.pdf.PDFPreviewActivity;
-import com.usai.redant.rautils.actionSheet.ActionSheet;
+import com.usai.redant.rautils.actionsheet.ActionSheet;
 import com.usai.util.Network;
 import com.usai.util.commonUtil;
 

+ 1 - 1
ApexDrivers/apexmobile/src/main/java/com/usai/apex/apexresult/ApexResultPresenter.java

@@ -7,7 +7,7 @@ import com.usai.apex.ApexTrackingApplication;
 import com.usai.apex.apexresult.model.ApexResultBaseModel;
 import com.usai.apex.apexresult.model.ApexResultDocumentModel;
 import com.usai.apex.apexresult.model.ApexResultShipModel;
-import com.usai.redant.rautils.operationQueue.OperationQueue;
+import com.usai.redant.rautils.operationqueue.OperationQueue;
 import com.usai.util.Network;
 import com.usai.util.RAUtil;
 

+ 1 - 1
ApexDrivers/apexmobile/src/main/res/layout/activity_about.xml

@@ -65,7 +65,7 @@
         android:layout_marginEnd="32dp"
         android:layout_marginStart="32dp"
         android:layout_marginTop="52dp"
-        android:src="@drawable/apexlogo_large"
+        android:contentDescription="TODO" android:src="@drawable/apexlogo_large"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent" />

+ 8 - 8
ApexDrivers/apexmobile/src/main/res/layout/activity_function_select.xml

@@ -15,16 +15,16 @@
 
         <ImageButton
             android:id="@+id/ibtn_booking"
-            android:contentDescription=""
+            android:contentDescription="TODO"
             android:src="@drawable/ic_oceanbooking" />
 
         <ImageButton
             android:id="@+id/ibtn_info"
-            android:src="@drawable/ic_ocean_blinfo" />
+            android:contentDescription="TODO" android:src="@drawable/ic_ocean_blinfo" />
 
         <ImageButton
             android:id="@+id/ibtn_detail"
-            android:src="@drawable/ic_container_detail" />
+            android:contentDescription="TODO" android:src="@drawable/ic_container_detail" />
     </TableRow>
 
     <TableRow
@@ -34,15 +34,15 @@
 
         <ImageButton
             android:id="@+id/ibtn_cargo"
-            android:src="@drawable/ic_cargo_tracking" />
+            android:contentDescription="TODO" android:src="@drawable/ic_cargo_tracking" />
 
         <ImageButton
             android:id="@+id/ibtn_doc"
-            android:src="@drawable/ic_down_doc" />
+            android:contentDescription="TODO" android:src="@drawable/ic_down_doc" />
 
         <ImageButton
             android:id="@+id/ibtn_password"
-            android:src="@drawable/ic_password" />
+            android:contentDescription="TODO" android:src="@drawable/ic_password" />
     </TableRow>
 
     <TableRow
@@ -54,11 +54,11 @@
             android:id="@+id/ibtn_location"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:src="@drawable/ic_test" />
+            android:contentDescription="TODO" android:src="@drawable/ic_test" />
 
         <ImageButton
             android:id="@+id/ibtn_exit"
-            android:src="@drawable/ic_exit" />
+            android:contentDescription="TODO" android:src="@drawable/ic_exit" />
     </TableRow>
 
 </TableLayout>

+ 1 - 1
ApexDrivers/apexmobile/src/main/res/layout/activity_help.xml

@@ -11,7 +11,7 @@
         android:layout_height="fill_parent"
         android:layout_alignParentTop="true"
         android:layout_centerHorizontal="true"
-        android:scaleType="fitXY"
+        android:contentDescription="TODO" android:scaleType="fitXY"
         android:src="@drawable/help_fields" />
 
 </RelativeLayout>

+ 1 - 1
ApexDrivers/apexmobile/src/main/res/layout/activity_history.xml

@@ -20,7 +20,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginTop="13dp"
-            android:src="@drawable/apexlogo"
+            android:contentDescription="TODO" android:src="@drawable/apexlogo"
             app:layout_constraintBottom_toTopOf="@+id/webView1"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent"

+ 1 - 1
ApexDrivers/apexmobile/src/main/res/layout/activity_retrieve_password.xml

@@ -47,7 +47,7 @@
                 android:imeActionId="@+id/login"
                 android:imeActionLabel="@string/action_sign_in_short"
                 android:imeOptions="actionUnspecified"
-                android:maxLines="1"
+                android:inputType="" android:maxLines="1"
                 android:singleLine="true" />
 
             <EditText

+ 1 - 1
ApexDrivers/apexmobile/src/main/res/layout/activity_search_list.xml

@@ -21,7 +21,7 @@
             android:hint="Keyword"
             android:imeOptions="actionSearch"
             android:singleLine="true"
-            android:maxLines="1"
+            android:inputType="" android:maxLines="1"
             />
 
         <Button

+ 1 - 1
ApexDrivers/apexmobile/src/main/res/layout/announcement_cell.xml

@@ -34,7 +34,7 @@
             android:layout_width="42dp"
             android:layout_height="42dp"
             android:layout_marginStart="14dp"
-            android:src="@drawable/rect_market_news"
+            android:contentDescription="TODO" android:src="@drawable/rect_market_news"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent" />

+ 1 - 1
ApexDrivers/apexmobile/src/main/res/layout/announcement_item.xml

@@ -9,7 +9,7 @@
         android:layout_height="60dp"
         android:layout_alignParentLeft="true"
         android:layout_alignParentTop="true"
-        android:src="@drawable/ic_launcher" />
+        android:contentDescription="TODO" android:src="@drawable/ic_launcher" />
 
     <TextView
         android:id="@+id/tv_title"

+ 1 - 1
ApexDrivers/apexmobile/src/main/res/layout/apex_result_document_cell.xml

@@ -19,7 +19,7 @@
             android:layout_height="48dp"
             android:layout_marginLeft="10dp"
             android:layout_centerVertical="true"
-            android:scaleType="fitCenter"
+            android:contentDescription="TODO" android:scaleType="fitCenter"
             android:src="@drawable/mode_document"
             />
 

+ 1 - 1
ApexDrivers/apexmobile/src/main/res/layout/commu_item_header.xml

@@ -64,7 +64,7 @@
         android:layout_height="30dp"
         android:layout_marginTop="8dp"
         android:background="@drawable/bg_button"
-        android:src="@drawable/ic_contact"
+        android:contentDescription="TODO" android:src="@drawable/ic_contact"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintTop_toTopOf="@+id/comm_cc_tv"
         app:srcCompat="@drawable/ic_contact"/>

+ 1 - 1
ApexDrivers/apexmobile/src/main/res/layout/detail_tracking_cell.xml

@@ -37,7 +37,7 @@
         android:layout_height="42dp"
         android:layout_marginLeft="16dp"
         android:layout_marginStart="16dp"
-        android:src="@drawable/ic_launcher"
+        android:contentDescription="TODO" android:src="@drawable/ic_launcher"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent" />

+ 2 - 2
ApexDrivers/apexmobile/src/main/res/layout/documents_list_item.xml

@@ -7,7 +7,7 @@
         android:id="@+id/iv_thumb"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:src="@drawable/ic_launcher" />
+        android:contentDescription="TODO" android:src="@drawable/ic_launcher" />
 
     <TextView
         android:id="@+id/tv_toolname"
@@ -23,6 +23,6 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
-        android:src="@android:drawable/ic_menu_share" />
+        android:contentDescription="TODO" android:src="@android:drawable/ic_menu_share" />
 
 </LinearLayout>

+ 1 - 1
ApexDrivers/apexmobile/src/main/res/layout/drag_list_item.xml

@@ -21,6 +21,6 @@
         android:layout_alignParentRight="true"
         android:layout_marginRight="10dp"
         android:layout_centerVertical="true"
-        android:src="@drawable/ic_drag_new" />
+        android:contentDescription="TODO" android:src="@drawable/ic_drag_new" />
 
 </RelativeLayout>

+ 1 - 1
ApexDrivers/apexmobile/src/main/res/layout/fragment_direct_tracking.xml

@@ -45,7 +45,7 @@
 
         <EditText
             android:id="@+id/et_from"
-            android:layout_width="wrap_content"
+            android:layout_width="wrap_content" android:inputType="" 
             android:layout_height="wrap_content"
             android:ems="12" >
 

+ 1 - 1
ApexDrivers/apexmobile/src/main/res/layout/fragment_login.xml

@@ -122,6 +122,6 @@
         android:layout_centerHorizontal="true"
         android:layout_below="@id/tv_ver"
         android:layout_above="@id/login_form"
-        android:src="@drawable/apexlogo_large"/>
+        android:contentDescription="TODO" android:src="@drawable/apexlogo_large"/>
 
 </RelativeLayout>

+ 2 - 2
ApexDrivers/apexmobile/src/main/res/layout/fragment_tools.xml

@@ -7,7 +7,7 @@
         android:id="@+id/iv_thumb"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:src="@drawable/ic_launcher"
+        android:contentDescription="TODO" android:src="@drawable/ic_launcher"
         />
 
     <TextView
@@ -25,6 +25,6 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
-        android:src="@drawable/ic_arrow" />
+        android:contentDescription="TODO" android:src="@drawable/ic_arrow" />
 
 </LinearLayout>

+ 1 - 1
ApexDrivers/apexmobile/src/main/res/layout/group_tag.xml

@@ -28,7 +28,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         app:layout_constraintEnd_toEndOf="parent"
-        android:src="@drawable/ic_expand_more"
+        android:contentDescription="TODO" android:src="@drawable/ic_expand_more"
         />
 
 </android.support.constraint.ConstraintLayout>

+ 1 - 1
ApexDrivers/apexmobile/src/main/res/layout/kpi_item_cell.xml

@@ -2,7 +2,7 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                 android:layout_width="match_parent"
                 android:layout_height="40dp"
-                android:minHeight="40dp"
+                android:baselineAligned="false" android:minHeight="40dp"
     android:orientation="horizontal">
 
     <RelativeLayout

+ 1 - 1
ApexDrivers/apexmobile/src/main/res/layout/saved_cell.xml

@@ -34,7 +34,7 @@
             android:layout_width="42dp"
             android:layout_height="42dp"
             android:layout_marginStart="16dp"
-            android:src="@drawable/ic_launcher"
+            android:contentDescription="TODO" android:src="@drawable/ic_launcher"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent" />

+ 2 - 2
ApexDrivers/apexmobile/src/main/res/layout/saved_doc.xml

@@ -34,7 +34,7 @@
             android:layout_width="42dp"
             android:layout_height="42dp"
             android:layout_marginStart="16dp"
-            android:src="@drawable/ic_launcher"
+            android:contentDescription="TODO" android:src="@drawable/ic_launcher"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent" />
@@ -57,7 +57,7 @@
             android:layout_marginBottom="8dp"
             android:layout_marginEnd="16dp"
             android:layout_marginTop="8dp"
-            android:src="@drawable/ic_share_new"
+            android:contentDescription="TODO" android:src="@drawable/ic_share_new"
             android:visibility="invisible"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintEnd_toEndOf="parent"

+ 2 - 2
ApexDrivers/apexmobile/src/main/res/layout/search_item_datepicker.xml

@@ -32,7 +32,7 @@
         android:hint="@string/str_from"
         app:layout_constraintEnd_toStartOf="@+id/btn_clear_from"
         app:layout_constraintStart_toStartOf="@+id/aname"
-        app:layout_constraintTop_toBottomOf="@+id/aname" />
+        app:layout_constraintTop_toBottomOf="@+id/aname" android:inputType=""  />
 
     <Button
         android:id="@+id/btn_clear_from"
@@ -57,7 +57,7 @@
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toStartOf="@+id/btn_clear_to"
         app:layout_constraintStart_toStartOf="@+id/et_from"
-        app:layout_constraintTop_toBottomOf="@+id/et_from">
+        app:layout_constraintTop_toBottomOf="@+id/et_from" android:inputType="">
 
         <requestFocus
             android:layout_width="wrap_content"

+ 1 - 1
ApexDrivers/apexmobile/src/main/res/layout/static_modelist_cell.xml

@@ -34,7 +34,7 @@
             android:layout_width="42dp"
             android:layout_height="42dp"
             android:layout_marginStart="16dp"
-            android:src="@drawable/ic_launcher"
+            android:contentDescription="TODO" android:src="@drawable/ic_launcher"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent" />

+ 3 - 3
ApexDrivers/apexmobile/src/main/res/menu/about.xml

@@ -1,9 +1,9 @@
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"  >
 
     <item
         android:id="@+id/action_settings"
         android:orderInCategory="100"
-        android:showAsAction="never"
-        android:title="@string/action_settings"/>
+        
+        app:showAsAction="never" android:title="@string/action_settings"/>
 
 </menu>

+ 3 - 3
ApexDrivers/apexmobile/src/main/res/menu/apex.xml

@@ -1,9 +1,9 @@
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"  >
 
     <item
         android:id="@+id/forget_password"
         android:orderInCategory="100"
-        android:showAsAction="never"
-        android:title="@string/action_forget_password"/>
+        
+        app:showAsAction="never" android:title="@string/action_forget_password"/>
 
 </menu>

+ 3 - 3
ApexDrivers/apexmobile/src/main/res/menu/cargo_tracking.xml

@@ -1,9 +1,9 @@
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"  >
 
     <item
         android:id="@+id/action_settings"
         android:orderInCategory="100"
-        android:showAsAction="never"
-        android:title="@string/action_settings"/>
+        
+        app:showAsAction="never" android:title="@string/action_settings"/>
 
 </menu>

+ 3 - 3
ApexDrivers/apexmobile/src/main/res/menu/change_password.xml

@@ -1,8 +1,8 @@
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"  >
 
     <item
         android:id="@+id/action_forgot_password"
-        android:showAsAction="never"
-        android:title="@string/action_forgot_password"/>
+        
+        app:showAsAction="never" android:title="@string/action_forgot_password"/>
 
 </menu>

+ 3 - 3
ApexDrivers/apexmobile/src/main/res/menu/customize_fields.xml

@@ -1,4 +1,4 @@
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"  >
 
 <!--     <item
         android:id="@+id/action_settings"
@@ -8,7 +8,7 @@
     <item
         android:id="@+id/action_help"
         android:orderInCategory="200"
-        android:showAsAction="always"
+        
         android:icon="@android:drawable/ic_menu_help"
-        android:title="@string/action_help"/>
+        app:showAsAction="always" android:title="@string/action_help"/>
 </menu>

+ 3 - 3
ApexDrivers/apexmobile/src/main/res/menu/function_select.xml

@@ -1,10 +1,10 @@
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"  >
 
     <item
         android:id="@+id/action_settings"
         android:orderInCategory="100"
-        android:showAsAction="always"
-        android:title="@string/action_settings"
+        
+        app:showAsAction="always" android:title="@string/action_settings"
 
         android:icon="@drawable/rect_setting"/>
 

+ 3 - 3
ApexDrivers/apexmobile/src/main/res/menu/help.xml

@@ -1,9 +1,9 @@
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"  >
 
     <item
         android:id="@+id/action_settings"
         android:orderInCategory="100"
-        android:showAsAction="never"
-        android:title="@string/action_settings"/>
+        
+        app:showAsAction="never" android:title="@string/action_settings"/>
 
 </menu>

+ 3 - 3
ApexDrivers/apexmobile/src/main/res/menu/inner_tools.xml

@@ -1,11 +1,11 @@
 <menu xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
+    xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" 
     tools:context="com.usai.apex.InnerToolsActivity" >
 
     <item
         android:id="@+id/action_settings"
         android:orderInCategory="100"
-        android:showAsAction="never"
-        android:title="@string/action_settings"/>
+        
+        app:showAsAction="never" android:title="@string/action_settings"/>
 
 </menu>

+ 3 - 3
ApexDrivers/apexmobile/src/main/res/menu/location_detail.xml

@@ -1,9 +1,9 @@
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"  >
 
     <item
         android:id="@+id/action_settings"
         android:orderInCategory="100"
-        android:showAsAction="never"
-        android:title="@string/action_settings"/>
+        
+        app:showAsAction="never" android:title="@string/action_settings"/>
 
 </menu>

+ 5 - 5
ApexDrivers/apexmobile/src/main/res/menu/message.xml

@@ -1,16 +1,16 @@
 <menu xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
+    xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" 
     tools:context="com.usai.apex.BlankActivity" >
    <item
         android:id="@+id/action_markallread"
         android:orderInCategory="100"
-        android:showAsAction="never"
-        android:title="@string/action_markallread"/>
+        
+        app:showAsAction="never" android:title="@string/action_markallread"/>
     
     <item
         android:id="@+id/action_deleteall"
         android:orderInCategory="200"
-        android:showAsAction="never"
-        android:title="@string/action_deleteall"/>
+        
+        app:showAsAction="never" android:title="@string/action_deleteall"/>
 
 </menu>

+ 7 - 7
ApexDrivers/apexmobile/src/main/res/menu/result.xml

@@ -1,19 +1,19 @@
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"  >
 
     <item
         android:id="@+id/action_custom_fields"
         android:orderInCategory="100"
-        android:showAsAction="never"
-        android:title="@string/action_custom_fields"/>
+        
+        app:showAsAction="never" android:title="@string/action_custom_fields"/>
     <item
         android:id="@+id/action_help"
         android:orderInCategory="200"
-        android:showAsAction="ifRoom" 
+         
         android:icon="@android:drawable/ic_menu_help"
-        android:title="@string/action_help"/>
+        app:showAsAction="ifRoom" android:title="@string/action_help"/>
         <item
         android:id="@+id/action_save_search"
         android:orderInCategory="300"
-        android:showAsAction="never"
-        android:title="@string/actoin_save_search"/>
+        
+        app:showAsAction="never" android:title="@string/actoin_save_search"/>
 </menu>

+ 3 - 3
ApexDrivers/apexmobile/src/main/res/menu/retrieve_password.xml

@@ -1,8 +1,8 @@
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"  >
 
     <item
         android:id="@+id/action_forgot_password"
-        android:showAsAction="never"
-        android:title="@string/action_forgot_password"/>
+        
+        app:showAsAction="never" android:title="@string/action_forgot_password"/>
 
 </menu>

+ 5 - 5
ApexDrivers/apexmobile/src/main/res/menu/search.xml

@@ -1,16 +1,16 @@
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"  >
 
     <item
         android:id="@+id/action_custom_fields"
         android:orderInCategory="100"
-        android:showAsAction="never"
-        android:title="@string/action_custom_fields"/>
+        
+        app:showAsAction="never" android:title="@string/action_custom_fields"/>
     <item
         android:id="@+id/action_help"
         android:icon="@android:drawable/ic_menu_help"
         android:orderInCategory="200"
-        android:showAsAction="ifRoom"
-        android:title="@string/action_help"/>
+        
+        app:showAsAction="ifRoom" android:title="@string/action_help"/>
 
 
 </menu>

+ 3 - 3
ApexDrivers/apexmobile/src/main/res/menu/search_list.xml

@@ -1,10 +1,10 @@
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"  >
 
     <item
         android:id="@+id/action_custom_fields"
         android:orderInCategory="100"
-        android:showAsAction="never"
-        android:title="@string/action_custom_fields"/>
+        
+        app:showAsAction="never" android:title="@string/action_custom_fields"/>
     <!--<item-->
         <!--android:id="@+id/action_help"-->
         <!--android:orderInCategory="200"-->

+ 3 - 3
ApexDrivers/apexmobile/src/main/res/menu/web.xml

@@ -1,11 +1,11 @@
 <menu xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
+    xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" 
     tools:context="com.usai.apex.WebActivity" >
 
     <item
         android:id="@+id/action_settings"
         android:orderInCategory="100"
-        android:showAsAction="never"
-        android:title="@string/action_settings"/>
+        
+        app:showAsAction="never" android:title="@string/action_settings"/>
 
 </menu>