From d215329e289ce4067716341d493595a43b4f31e8 Mon Sep 17 00:00:00 2001 From: tongtongstudio Date: Wed, 23 Oct 2024 09:19:18 +0800 Subject: [PATCH] =?UTF-8?q?version:1.3.4=20fix:=E4=BF=AE=E5=A4=8D=E9=97=AA?= =?UTF-8?q?=E9=80=80=20update:=E4=BC=98=E5=8C=96=E8=81=94=E7=B3=BB?= =?UTF-8?q?=E4=BA=BA=E6=B7=BB=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 11 +- app/src/main/AndroidManifest.xml | 12 +- .../alarm/list/AlarmListActivity.java | 2 +- .../alarm/list/AlarmListViewModel.java | 6 +- .../activity/contact/AddContactActivity.java | 317 ++++++++++---- .../activity/contact/AddContactViewModel.java | 116 +++++ .../contact/AddWechatContactActivity.java | 209 --------- .../contact/AddWechatContactViewModel.java | 144 ------- ...Activity.java => ContactListActivity.java} | 16 +- ...ewModel.java => ContactListViewModel.java} | 8 +- .../activity/contact/EditContactActivity.java | 2 +- .../contact/EditContactViewModel.java | 6 +- .../xxpatx/os/activity/dock/DockActivity.java | 2 +- .../os/activity/dock/DockViewModel.java | 6 +- .../activity/emergency/EmergencyActivity.java | 6 +- .../emergency/EmergencyViewModel.java | 8 +- .../os/activity/location/LocationAcivity.java | 2 +- .../activity/location/LocationViewModel.java | 6 +- .../xxpatx/os/activity/main/MainActivity.java | 66 ++- .../os/activity/main/MainViewModel.java | 34 +- .../selectnumber/SelectNumberActivity.java | 57 ++- .../os/activity/setting/SettingActivity.java | 7 +- .../os/activity/setting/SettingViewModel.java | 6 +- .../os/activity/sim/SimCardActivity.java | 85 ++++ .../os/activity/sim/SimCardViewModel.java | 18 + .../os/activity/weather/WeatherActivity.java | 9 +- .../os/activity/weather/WeatherViewModel.java | 30 +- .../xxpatx/os/adapter/CallRecordAdapter.java | 46 +- .../com/xxpatx/os/adapter/ContactAdapter.java | 4 +- .../xxpatx/os/adapter/DailyAppAdapter.java | 4 +- .../com/xxpatx/os/adapter/PhoneAdapter.java | 16 +- .../xxpatx/os/adapter/PhoneSetAdapter.java | 93 ++++ .../os/adapter/WechatContactAdapter.java | 6 +- .../main/java/com/xxpatx/os/bean/Contact.java | 16 + .../java/com/xxpatx/os/bean/ContactId.java | 63 +++ .../java/com/xxpatx/os/bean/TestAppInfo.java | 89 ++++ .../com/xxpatx/os/config/CommonConfig.java | 6 + .../com/xxpatx/os/dialog/CallPhoneDialog.java | 82 ++++ .../xxpatx/os/dialog/EditContactDialog.java | 8 +- .../os/fragment/app/AppListFragment.java | 4 +- .../os/fragment/contact/ContactFragment.java | 8 +- .../os/fragment/contact/ContactViewModel.java | 22 +- .../xxpatx/os/fragment/home/HomeFragment.java | 4 +- .../phone/contact/ContactFragment.java | 8 +- .../phone/contact/ContactViewModel.java | 25 +- .../fragment/phone/dialer/DialerFragment.java | 4 +- .../fragment/phone/record/RecordFragment.java | 51 ++- .../phone/record/RecordViewModel.java | 15 +- .../os/network/NetInterfaceManager.java | 39 ++ .../com/xxpatx/os/network/UrlAddress.java | 2 + .../os/network/api/uiui/TestAppApi.java | 18 + .../java/com/xxpatx/os/utils/ApkUtils.java | 10 +- .../com/xxpatx/os/utils/ContactsUtils.java | 391 +++++++++++++++++ .../xxpatx/os/utils/LocalContactUtils.java | 84 ++++ .../xxpatx/os/utils/MobileNetworkUtils.java | 47 ++ .../xxpatx/os/utils/MobileNumberUtils.java | 56 +++ .../main/res/drawable-hdpi/default_avatar.png | Bin 8557 -> 10802 bytes .../res/drawable-hdpi/icon_call_incoming.png | Bin 1213 -> 1496 bytes .../res/drawable-hdpi/icon_call_missed.png | Bin 1218 -> 1558 bytes .../res/drawable-hdpi/icon_call_outgoing.png | Bin 1181 -> 1429 bytes .../res/drawable-hdpi/icon_call_phone.png | Bin 0 -> 12340 bytes .../main/res/drawable-hdpi/icon_select.png | Bin 0 -> 1469 bytes .../main/res/drawable-hdpi/icon_unselect.png | Bin 0 -> 1151 bytes .../main/res/layout/activity_add_contact.xml | 210 ++++++--- .../layout/activity_add_wechat_contact.xml | 405 ------------------ ..._contact.xml => activity_contact_list.xml} | 4 +- app/src/main/res/layout/activity_setting.xml | 50 +++ app/src/main/res/layout/activity_sim_card.xml | 78 ++++ app/src/main/res/layout/dialog_call_phone.xml | 49 +++ app/src/main/res/layout/item_call_record.xml | 33 +- app/src/main/res/layout/item_phone.xml | 66 +-- app/src/main/res/layout/item_phone_set.xml | 76 ++++ app/src/main/res/values/styles.xml | 2 + build.gradle | 4 +- 74 files changed, 2251 insertions(+), 1138 deletions(-) delete mode 100644 app/src/main/java/com/xxpatx/os/activity/contact/AddWechatContactActivity.java delete mode 100644 app/src/main/java/com/xxpatx/os/activity/contact/AddWechatContactViewModel.java rename app/src/main/java/com/xxpatx/os/activity/contact/{ContactActivity.java => ContactListActivity.java} (93%) rename app/src/main/java/com/xxpatx/os/activity/contact/{ContactViewModel.java => ContactListViewModel.java} (94%) create mode 100644 app/src/main/java/com/xxpatx/os/activity/sim/SimCardActivity.java create mode 100644 app/src/main/java/com/xxpatx/os/activity/sim/SimCardViewModel.java create mode 100644 app/src/main/java/com/xxpatx/os/adapter/PhoneSetAdapter.java create mode 100644 app/src/main/java/com/xxpatx/os/bean/ContactId.java create mode 100644 app/src/main/java/com/xxpatx/os/bean/TestAppInfo.java create mode 100644 app/src/main/java/com/xxpatx/os/dialog/CallPhoneDialog.java create mode 100644 app/src/main/java/com/xxpatx/os/network/api/uiui/TestAppApi.java create mode 100644 app/src/main/java/com/xxpatx/os/utils/ContactsUtils.java create mode 100644 app/src/main/java/com/xxpatx/os/utils/LocalContactUtils.java create mode 100644 app/src/main/java/com/xxpatx/os/utils/MobileNetworkUtils.java create mode 100644 app/src/main/java/com/xxpatx/os/utils/MobileNumberUtils.java create mode 100644 app/src/main/res/drawable-hdpi/icon_call_phone.png create mode 100644 app/src/main/res/drawable-hdpi/icon_select.png create mode 100644 app/src/main/res/drawable-hdpi/icon_unselect.png delete mode 100644 app/src/main/res/layout/activity_add_wechat_contact.xml rename app/src/main/res/layout/{activity_contact.xml => activity_contact_list.xml} (97%) create mode 100644 app/src/main/res/layout/activity_sim_card.xml create mode 100644 app/src/main/res/layout/dialog_call_phone.xml create mode 100644 app/src/main/res/layout/item_phone_set.xml diff --git a/app/build.gradle b/app/build.gradle index dd5936e..ebbba13 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,8 +15,8 @@ android { applicationId "com.xxpatx.os" minSdkVersion 24 targetSdkVersion 29 - versionCode 1030 - versionName "1.2.9" + versionCode 1035 + versionName "1.3.4" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -72,6 +72,7 @@ android { buildTypes { debug { + buildConfigField "String", "platform", '"MTK01"' versionNameSuffix "-debug" //Zipalign优化 zipAlignEnabled true @@ -93,6 +94,7 @@ android { } release { + buildConfigField "String", "platform", '"MTK01"' //Zipalign优化 zipAlignEnabled true shrinkResources true @@ -164,6 +166,11 @@ dependencies { //Gson implementation 'com.google.code.gson:gson:2.9.0' implementation 'com.google.zxing:core:3.5.0' + //libphonenumber + implementation 'com.googlecode.libphonenumber:libphonenumber:8.13.47' + implementation 'com.googlecode.libphonenumber:carrier:1.230' + implementation 'com.googlecode.libphonenumber:geocoder:2.241' + implementation 'com.googlecode.libphonenumber:prefixmapper:2.241' //生命周期管理 implementation 'com.trello.rxlifecycle4:rxlifecycle:4.0.2' implementation 'com.trello.rxlifecycle4:rxlifecycle-android:4.0.2' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c76d78d..73b984b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -155,7 +155,11 @@ android:launchMode="singleTask" android:screenOrientation="portrait" /> + @@ -167,10 +171,6 @@ android:name=".activity.setting.TouchActivity" android:launchMode="singleTask" android:screenOrientation="portrait" /> - diff --git a/app/src/main/java/com/xxpatx/os/activity/alarm/list/AlarmListActivity.java b/app/src/main/java/com/xxpatx/os/activity/alarm/list/AlarmListActivity.java index 04b6cdb..ea86dab 100644 --- a/app/src/main/java/com/xxpatx/os/activity/alarm/list/AlarmListActivity.java +++ b/app/src/main/java/com/xxpatx/os/activity/alarm/list/AlarmListActivity.java @@ -121,7 +121,7 @@ public class AlarmListActivity extends BaseMvvmActivity>() { + mViewModel.mAlarmClockData.observe(this, new androidx.lifecycle.Observer>() { @Override public void onChanged(List alarmClockData) { setAlarmClock(alarmClockData); diff --git a/app/src/main/java/com/xxpatx/os/activity/alarm/list/AlarmListViewModel.java b/app/src/main/java/com/xxpatx/os/activity/alarm/list/AlarmListViewModel.java index 436ae4d..7b63cb3 100644 --- a/app/src/main/java/com/xxpatx/os/activity/alarm/list/AlarmListViewModel.java +++ b/app/src/main/java/com/xxpatx/os/activity/alarm/list/AlarmListViewModel.java @@ -32,11 +32,7 @@ public class AlarmListViewModel extends BaseViewModel> mAlarmClockData = new MutableLiveData<>(); - - public MutableLiveData> getAlarmClockData() { - return mAlarmClockData; - } + public MutableLiveData> mAlarmClockData = new MutableLiveData<>(); public void getAlarmClock() { NetInterfaceManager.getInstance().getAlarmClock(getLifecycle(), new NetInterfaceManager.AlarmClockCallback() { diff --git a/app/src/main/java/com/xxpatx/os/activity/contact/AddContactActivity.java b/app/src/main/java/com/xxpatx/os/activity/contact/AddContactActivity.java index 8fe6048..b8c847f 100644 --- a/app/src/main/java/com/xxpatx/os/activity/contact/AddContactActivity.java +++ b/app/src/main/java/com/xxpatx/os/activity/contact/AddContactActivity.java @@ -1,46 +1,57 @@ package com.xxpatx.os.activity.contact; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.graphics.Bitmap; import android.net.Uri; +import android.provider.ContactsContract; import android.text.TextUtils; import android.util.Log; import android.view.View; +import androidx.annotation.Nullable; +import androidx.lifecycle.Observer; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.resource.bitmap.RoundedCorners; +import com.bumptech.glide.request.RequestOptions; +import com.bumptech.glide.request.target.SimpleTarget; +import com.bumptech.glide.request.transition.Transition; import com.hjq.toast.Toaster; import com.luck.picture.lib.basic.PictureSelector; import com.luck.picture.lib.config.SelectMimeType; import com.luck.picture.lib.entity.LocalMedia; import com.luck.picture.lib.interfaces.OnResultCallbackListener; -import com.trello.rxlifecycle4.RxLifecycle; -import com.trello.rxlifecycle4.android.ActivityEvent; +import com.tencent.mmkv.MMKV; +import com.xiasuhuei321.loadingdialog.view.LoadingDialog; import com.xxpatx.os.R; import com.xxpatx.os.base.mvvm.BaseMvvmActivity; -import com.xxpatx.os.bean.BaseResponse; +import com.xxpatx.os.bean.Contact; +import com.xxpatx.os.config.CommonConfig; import com.xxpatx.os.custom.GlideEngine; import com.xxpatx.os.databinding.ActivityAddContactBinding; -import com.xxpatx.os.network.NetInterfaceManager; -import com.xxpatx.os.utils.FileUtil; -import com.xxpatx.os.utils.GlideLoadUtils; -import com.xxpatx.os.utils.Utils; +import com.xxpatx.os.db.ContactCacheUtils; +import com.xxpatx.os.utils.ContactsUtils; +import com.xxpatx.os.utils.LocalContactUtils; +import com.xxpatx.os.utils.ScreenUtil; +import com.zackratos.ultimatebarx.ultimatebarx.java.UltimateBarX; +import java.io.ByteArrayOutputStream; import java.io.File; import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -import io.reactivex.rxjava3.annotations.NonNull; -import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.Disposable; -import okhttp3.MediaType; -import okhttp3.MultipartBody; -import okhttp3.RequestBody; +import java.util.concurrent.ThreadLocalRandom; public class AddContactActivity extends BaseMvvmActivity { private static final String TAG = "AddContactActivity"; - private String avatarFilePath; + private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); + + private LoadingDialog mLoadingDialog; +// public String avatarFilePath = ""; @Override - public boolean setfitWindow() { + public boolean setNightMode() { return true; } @@ -52,113 +63,229 @@ public class AddContactActivity extends BaseMvvmActivity() { + @Override + public void onChanged(Boolean aBoolean) { + if (aBoolean) { + mLoadingDialog.loadSuccess(); + finish(); + } else { + mLoadingDialog.loadFailed(); + } + } + }); + mViewModel.mContactData.observe(this, new Observer() { + @Override + public void onChanged(Contact contact) { + ContactCacheUtils.getInstance().addContact(contact); + Toaster.showLong("已保存至本地"); + finish(); + } + }); } - private void selectPicture() { - PictureSelector.create(this) - .openGallery(SelectMimeType.ofImage()) + @Override + protected void onDestroy() { + super.onDestroy(); + mLoadingDialog.close(); + mLoadingDialog = null; + } + + @Override + protected void onPause() { + super.onPause(); + } + + private void openSelector() { + PictureSelector.create(AddContactActivity.this) + .openGallery(SelectMimeType.ofAll()) .setSelectionMode(1) .setImageEngine(GlideEngine.createGlideEngine()) .forResult(new OnResultCallbackListener() { @Override public void onResult(ArrayList result) { - avatarFilePath = result.get(0).getPath(); - Log.e("selectPicture", "onResult: " + avatarFilePath); - GlideLoadUtils.getInstance().glideLoad(AddContactActivity.this, avatarFilePath, mViewDataBinding.nvAvatar, R.drawable.default_avatar); - } - - @Override - public void onCancel() { - - } - }); - } - - - private void checkContact() { - String name = mViewDataBinding.etName.getText().toString(); - if (TextUtils.isEmpty(name)) { - Toaster.show("请输入联系人姓名"); - return; - } - String phone = mViewDataBinding.etPhone.getText().toString(); - if (TextUtils.isEmpty(phone)) { - Toaster.show("请输入手机号码"); - return; - } - File avatarFile; - Log.e("checkContact", "avatarFilePath: " + avatarFilePath); - if (TextUtils.isEmpty(avatarFilePath)) { - avatarFile = FileUtil.drawableToFile(AddContactActivity.this, R.drawable.default_avatar, "avatar"); - } else { - Uri uri = Uri.parse(avatarFilePath); - avatarFile = FileUtil.uriToFile(uri, AddContactActivity.this); - } - MediaType mediaType = MediaType.Companion.parse("image/png"); - RequestBody requestBody = RequestBody.Companion.create(avatarFile, mediaType); - MultipartBody.Part body = MultipartBody.Part.createFormData("avatar", avatarFile.getName(), requestBody); - Map params = new HashMap<>(); - params.put("sn", Utils.getSerial()); - params.put("name", name); - params.put("mobile", phone); - params.put("is_urgent", String.valueOf(mViewDataBinding.toggleButton.isToggleOn())); - NetInterfaceManager.getInstance() - .getMailListAddObservable(params, body) - .compose(RxLifecycle.bindUntilEvent(getLifecycleSubject(), ActivityEvent.DESTROY)) - .subscribe(new Observer() { - @Override - public void onSubscribe(@NonNull Disposable d) { - Log.e("checkContact", "onSubscribe: "); - } - - @Override - public void onNext(@NonNull BaseResponse baseResponse) { - Log.e("checkContact", "onNext: " + baseResponse); - if (baseResponse.code == 200) { - Toaster.show("已添加"); - finish(); + mViewModel.avatarFilePath = result.get(0).getRealPath(); + File file = new File(mViewModel.avatarFilePath); + if (file.exists()) { + RequestOptions options = new RequestOptions().transform(new RoundedCorners(ScreenUtil.dip2px(AddContactActivity.this, 8F))); + Glide.with(mViewDataBinding.nvAvatar).load(file).apply(options).into(mViewDataBinding.nvAvatar); } else { - Toaster.show(baseResponse.msg); + mViewModel.avatarFilePath = ""; } } @Override - public void onError(@NonNull Throwable e) { - Log.e("checkContact", "onError: " + e.getMessage()); - } - - @Override - public void onComplete() { - Log.e("checkContact", "onComplete: "); + public void onCancel() { + Log.e(TAG, "onCancel: "); } }); + } + + public void testAddContacts() { + //插入raw_contacts表,并获取_id属性 + Uri uri = Uri.parse("content://icc/adn"); + ContentResolver resolver = getContentResolver(); + ContentValues values = new ContentValues(); + long contact_id = ContentUris.parseId(resolver.insert(uri, values)); + //插入data表 + uri = Uri.parse("content://com.android.contacts/data"); + //add Name + values.put("raw_contact_id", contact_id); + values.put(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/name"); + values.put("data2", "zdong"); + values.put("data1", "xzdong"); + resolver.insert(uri, values); + values.clear(); + //add Phone + values.put("raw_contact_id", contact_id); + values.put(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/phone_v2"); + values.put("data2", "2"); //手机 + values.put("data1", "87654321"); + resolver.insert(uri, values); + values.clear(); + //add email + values.put("raw_contact_id", contact_id); + values.put(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/email_v2"); + values.put("data2", "2"); //单位 + values.put("data1", "xzdong@xzdong.com"); + resolver.insert(uri, values); + } + + private void addLocalContact() { + Contact contact = new Contact(); + ThreadLocalRandom random = ThreadLocalRandom.current(); + int fakeId = random.nextInt(Integer.MAX_VALUE); + Log.e(TAG, "addLocalContact: fakeId = " + fakeId); + contact.setId(fakeId); + contact.setMobile(mViewDataBinding.etPhone.getText().toString()); + contact.setName(mViewDataBinding.etName.getText().toString()); + contact.setTag(mViewDataBinding.etTag.getText().toString()); + contact.setIs_urgent(mViewDataBinding.toggleButton.isToggleOn()); + contact.setAvatar(mViewModel.avatarFilePath); + LocalContactUtils.addLocalContact(contact); + + if (ContactsUtils.isExist(AddContactActivity.this, contact.getMobile())) { + long rawContactId = ContactsUtils.getContactId(AddContactActivity.this, contact.getMobile()); + Glide.with(AddContactActivity.this).asBitmap().load(contact.getAvatar()).override(200, 200).into(new SimpleTarget() { + @Override + public void onResourceReady(@androidx.annotation.NonNull Bitmap resource, @Nullable Transition transition) { + ContentResolver resolver = AddContactActivity.this.getContentResolver(); + ContentValues nameValues = new ContentValues(); + //add Name + nameValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); + nameValues.put(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/name"); + nameValues.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, contact.getName()); + resolver.update(ContactsContract.Data.CONTENT_URI, nameValues, "raw_contact_id=?" + rawContactId, new String[]{ContactsContract.Data.CONTACT_ID}); + + ContentValues photoValues = new ContentValues(); + photoValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + resource.compress(Bitmap.CompressFormat.PNG, 100, out); + photoValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE); + photoValues.put(ContactsContract.CommonDataKinds.Photo.PHOTO, out.toByteArray()); + resolver.update(ContactsContract.Data.CONTENT_URI, photoValues, "raw_contact_id=?" + rawContactId, new String[]{ContactsContract.Data.CONTACT_ID}); + + ContentValues phoneValues = new ContentValues(); + phoneValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); + phoneValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE); + phoneValues.put(ContactsContract.CommonDataKinds.Phone.NUMBER, contact.getMobile()); + phoneValues.put(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE); + resolver.update(ContactsContract.Data.CONTENT_URI, phoneValues, "raw_contact_id=?" + rawContactId, new String[]{ContactsContract.Data.CONTACT_ID}); + } + }); + } else { + ContentValues values = new ContentValues(); + long rawContactId = ContentUris.parseId(AddContactActivity.this.getContentResolver().insert(ContactsContract.RawContacts.CONTENT_URI, values)); + Glide.with(AddContactActivity.this).asBitmap().load(contact.getAvatar()).override(200, 200).into(new SimpleTarget() { + @Override + public void onResourceReady(@androidx.annotation.NonNull Bitmap resource, @Nullable Transition transition) { + ContentResolver resolver = AddContactActivity.this.getContentResolver(); + ContentValues nameValues = new ContentValues(); + //add Name + nameValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); + nameValues.put(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/name"); + nameValues.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, contact.getName()); + resolver.insert(ContactsContract.Data.CONTENT_URI, nameValues); + + ContentValues photoValues = new ContentValues(); + photoValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + resource.compress(Bitmap.CompressFormat.PNG, 100, out); + photoValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE); + photoValues.put(ContactsContract.CommonDataKinds.Photo.PHOTO, out.toByteArray()); + resolver.insert(ContactsContract.Data.CONTENT_URI, photoValues); + + ContentValues phoneValues = new ContentValues(); + phoneValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); + phoneValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE); + phoneValues.put(ContactsContract.CommonDataKinds.Phone.NUMBER, contact.getMobile()); + phoneValues.put(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE); + resolver.insert(ContactsContract.Data.CONTENT_URI, phoneValues); + } + }); + } } + public void createContact(String name, String phoneNumber) { + Uri simUri = Uri.parse("content://icc/adn"); + ContentValues values = new ContentValues(); + values.put("tag", name); + values.put("number", phoneNumber); + getContentResolver().insert(simUri, values); + finish(); + } + public class BtnClick { + public void selectPic(View view) { + openSelector(); + } + public void exit(View view) { finish(); } - public void selectPic(View v) { - selectPicture(); - } - - public void confirm(View v) { - checkContact(); + public void addContact(View view) { + String name = mViewDataBinding.etName.getText().toString(); + if (TextUtils.isEmpty(name)) { + Toaster.show("请输入联系人姓名"); + return; + } + String phone = mViewDataBinding.etPhone.getText().toString(); + if (TextUtils.isEmpty(phone)) { + Toaster.show("请输入手机号码"); + return; + } + if (mViewDataBinding.tbSim.isToggleOn() == 1) { + createContact(name, phone); + } else { + Log.e("checkContact", "avatarFilePath: " + mViewModel.avatarFilePath); + mLoadingDialog.setLoadingText("正在上传"); + mLoadingDialog.show(); +// addLocalContact(); + mViewModel.checkContact(); + } } } } diff --git a/app/src/main/java/com/xxpatx/os/activity/contact/AddContactViewModel.java b/app/src/main/java/com/xxpatx/os/activity/contact/AddContactViewModel.java index 943e818..18dd01d 100644 --- a/app/src/main/java/com/xxpatx/os/activity/contact/AddContactViewModel.java +++ b/app/src/main/java/com/xxpatx/os/activity/contact/AddContactViewModel.java @@ -1,10 +1,37 @@ package com.xxpatx.os.activity.contact; +import android.net.Uri; +import android.text.TextUtils; +import android.util.Log; + +import androidx.lifecycle.MutableLiveData; + +import com.hjq.toast.Toaster; +import com.trello.rxlifecycle4.RxLifecycle; import com.trello.rxlifecycle4.android.ActivityEvent; +import com.xxpatx.os.R; import com.xxpatx.os.base.mvvm.BaseViewModel; +import com.xxpatx.os.bean.BaseResponse; +import com.xxpatx.os.bean.Contact; import com.xxpatx.os.databinding.ActivityAddContactBinding; +import com.xxpatx.os.network.NetInterfaceManager; +import com.xxpatx.os.utils.FileUtil; +import com.xxpatx.os.utils.Utils; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.Disposable; +import okhttp3.MediaType; +import okhttp3.MultipartBody; +import okhttp3.RequestBody; public class AddContactViewModel extends BaseViewModel { + private static final String TAG = "AddContactViewModel"; @Override public ActivityAddContactBinding getVDBinding() { @@ -15,4 +42,93 @@ public class AddContactViewModel extends BaseViewModel mAddData = new MutableLiveData<>(); + + public MutableLiveData mContactData = new MutableLiveData<>(); + + public void checkContact() { + String name = binding.etName.getText().toString(); + String phone = binding.etPhone.getText().toString(); + String tag = binding.etTag.getText().toString(); + File avatarFile; + Log.e("checkContact", "avatarFilePath: " + avatarFilePath); + if (TextUtils.isEmpty(avatarFilePath)) { + avatarFile = FileUtil.drawableToFile(getCtx(), R.drawable.default_avatar, "avatar"); + } else { + Uri uri = Uri.parse(avatarFilePath); + avatarFile = FileUtil.uriToFile(uri, getCtx()); + } + if (avatarFile == null) { + Toaster.showShort("图片加载失败"); + return; + } + MediaType mediaType = MediaType.Companion.parse("image/png"); + RequestBody requestBody = RequestBody.Companion.create(avatarFile, mediaType); + MultipartBody.Part body = MultipartBody.Part.createFormData("avatar", avatarFile.getName(), requestBody); + Map params = new HashMap<>(); + + params.put("sn", Utils.getSerial()); + params.put("name", name); + params.put("mobile", phone); + params.put("tag", tag); + params.put("is_urgent", String.valueOf(binding.toggleButton.isToggleOn())); + NetInterfaceManager.getInstance() + .getMailListAddObservable(params, body) + .compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY)) + .subscribe(new Observer() { + @Override + public void onSubscribe(@NonNull Disposable d) { + Log.e("checkContact", "onSubscribe: "); + } + + @Override + public void onNext(@NonNull BaseResponse baseResponse) { + Log.e("checkContact", "onNext: " + baseResponse); + if (baseResponse.code == 200) { + Toaster.show("已添加"); + mAddData.setValue(true); + } else { + Contact contact = new Contact(); + ThreadLocalRandom random = ThreadLocalRandom.current(); + int fakeId = random.nextInt(Integer.MAX_VALUE); + Log.e(TAG, "checkContent: fakeId = " + fakeId); + contact.setId(fakeId); + contact.setMobile(phone); + contact.setName(name); + contact.setTag(tag); + contact.setIs_urgent(binding.toggleButton.isToggleOn()); + contact.setAvatar(avatarFilePath); + mContactData.setValue(contact); + Toaster.show(baseResponse.msg); + mAddData.setValue(false); + } + } + + @Override + public void onError(@NonNull Throwable e) { + Log.e("checkContact", "onError: " + e.getMessage()); + Contact contact = new Contact(); + ThreadLocalRandom random = ThreadLocalRandom.current(); + int fakeId = random.nextInt(Integer.MAX_VALUE); + Log.e(TAG, "checkContent: fakeId = " + fakeId); + contact.setId(fakeId); + contact.setMobile(phone); + contact.setName(name); + contact.setTag(tag); + contact.setIs_urgent(binding.toggleButton.isToggleOn()); + contact.setAvatar(avatarFilePath); + mContactData.setValue(contact); + mAddData.setValue(false); + } + + @Override + public void onComplete() { + Log.e("checkContact", "onComplete: "); + } + }); + } + } diff --git a/app/src/main/java/com/xxpatx/os/activity/contact/AddWechatContactActivity.java b/app/src/main/java/com/xxpatx/os/activity/contact/AddWechatContactActivity.java deleted file mode 100644 index 14e63a6..0000000 --- a/app/src/main/java/com/xxpatx/os/activity/contact/AddWechatContactActivity.java +++ /dev/null @@ -1,209 +0,0 @@ -package com.xxpatx.os.activity.contact; - -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; -import android.net.Uri; -import android.provider.ContactsContract; -import android.text.TextUtils; -import android.util.Log; -import android.view.View; - -import androidx.lifecycle.Observer; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.load.resource.bitmap.RoundedCorners; -import com.bumptech.glide.request.RequestOptions; -import com.hjq.toast.Toaster; -import com.luck.picture.lib.basic.PictureSelector; -import com.luck.picture.lib.config.SelectMimeType; -import com.luck.picture.lib.entity.LocalMedia; -import com.luck.picture.lib.interfaces.OnResultCallbackListener; -import com.xiasuhuei321.loadingdialog.view.LoadingDialog; -import com.xxpatx.os.R; -import com.xxpatx.os.base.mvvm.BaseMvvmActivity; -import com.xxpatx.os.bean.Contact; -import com.xxpatx.os.custom.GlideEngine; -import com.xxpatx.os.databinding.ActivityAddWechatContactBinding; -import com.xxpatx.os.db.ContactCacheUtils; -import com.xxpatx.os.utils.ScreenUtil; -import com.zackratos.ultimatebarx.ultimatebarx.java.UltimateBarX; - -import java.io.File; -import java.util.ArrayList; - -public class AddWechatContactActivity extends BaseMvvmActivity { - private static final String TAG = "AddWechatContactActivity"; - - private LoadingDialog mLoadingDialog; -// private String mPictrueFilePath; - - @Override - public boolean setNightMode() { - return true; - } - -// @Override -// public boolean setfitWindow() { -// return true; -// } - - @Override - protected int getLayoutId() { - return R.layout.activity_add_wechat_contact; - } - - @Override - protected void initDataBinding() { - mViewModel.setCtx(this); - mViewModel.setVDBinding(mViewDataBinding); - mViewModel.setLifecycle(getLifecycleSubject()); - mViewDataBinding.setClick(new BtnClick()); - } - - @Override - protected void initView() { - UltimateBarX.addStatusBarTopPadding(mViewDataBinding.clExit); - UltimateBarX.addNavigationBarBottomPadding(mViewDataBinding.clBottom); - - mLoadingDialog = new LoadingDialog(this); - mLoadingDialog.setLoadingText("正在上传") - .setSuccessText("添加成功") - .setFailedText("添加失败") - .setInterceptBack(true) - .setLoadSpeed(LoadingDialog.Speed.SPEED_TWO) - .closeSuccessAnim() - .closeFailedAnim(); - } - - @Override - protected void initData() { - mViewModel.getAddData().observe(this, new Observer() { - @Override - public void onChanged(Boolean aBoolean) { - if (aBoolean) { - mLoadingDialog.loadSuccess(); - finish(); - } else { - mLoadingDialog.loadFailed(); - } - } - }); - mViewModel.getContactData().observe(this, new Observer() { - @Override - public void onChanged(Contact contact) { - ContactCacheUtils.getInstance().addContact(contact); - Toaster.showLong("已保存至本地"); - finish(); - } - }); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - mLoadingDialog.close(); - mLoadingDialog = null; - } - - @Override - protected void onPause() { - super.onPause(); - } - - private void openSelector() { - PictureSelector.create(AddWechatContactActivity.this) - .openGallery(SelectMimeType.ofAll()) - .setSelectionMode(1) - .setImageEngine(GlideEngine.createGlideEngine()) - .forResult(new OnResultCallbackListener() { - @Override - public void onResult(ArrayList result) { - mViewModel.avatarFilePath = result.get(0).getRealPath(); - File file = new File(mViewModel.avatarFilePath); - if (file.exists()) { - RequestOptions options = new RequestOptions().transform(new RoundedCorners(ScreenUtil.dip2px(AddWechatContactActivity.this, 8F))); - Glide.with(mViewDataBinding.nvAvatar).load(file).apply(options).into(mViewDataBinding.nvAvatar); - } else { - mViewModel.avatarFilePath = ""; - } - } - - @Override - public void onCancel() { - Log.e(TAG, "onCancel: "); - } - }); - } - - public void testAddContacts() { - //插入raw_contacts表,并获取_id属性 - Uri uri = Uri.parse("content://icc/adn"); - ContentResolver resolver = getContentResolver(); - ContentValues values = new ContentValues(); - long contact_id = ContentUris.parseId(resolver.insert(uri, values)); - //插入data表 - uri = Uri.parse("content://com.android.contacts/data"); - //add Name - values.put("raw_contact_id", contact_id); - values.put(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/name"); - values.put("data2", "zdong"); - values.put("data1", "xzdong"); - resolver.insert(uri, values); - values.clear(); - //add Phone - values.put("raw_contact_id", contact_id); - values.put(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/phone_v2"); - values.put("data2", "2"); //手机 - values.put("data1", "87654321"); - resolver.insert(uri, values); - values.clear(); - //add email - values.put("raw_contact_id", contact_id); - values.put(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/email_v2"); - values.put("data2", "2"); //单位 - values.put("data1", "xzdong@xzdong.com"); - resolver.insert(uri, values); - } - - public void createContact(String name, String phoneNumber) { - Uri simUri = Uri.parse("content://icc/adn"); - ContentValues values = new ContentValues(); - values.put("tag", name); - values.put("number", phoneNumber); - getContentResolver().insert(simUri, values); - finish(); - } - - - public class BtnClick { - public void selectPic(View view) { - openSelector(); - } - - public void exit(View view) { - finish(); - } - - public void addContact(View view) { - String name = mViewDataBinding.etName.getText().toString(); - if (TextUtils.isEmpty(name)) { - Toaster.show("请输入联系人姓名"); - return; - } - String phone = mViewDataBinding.etPhone.getText().toString(); - if (TextUtils.isEmpty(phone)) { - Toaster.show("请输入手机号码"); - return; - } - if (mViewDataBinding.tbSim.isToggleOn() == 1) { - createContact(name, phone); - } else { - Log.e("checkContact", "avatarFilePath: " + mViewModel.avatarFilePath); - mLoadingDialog.setLoadingText("正在上传"); - mLoadingDialog.show(); - mViewModel.checkContact(); - } - } - } -} diff --git a/app/src/main/java/com/xxpatx/os/activity/contact/AddWechatContactViewModel.java b/app/src/main/java/com/xxpatx/os/activity/contact/AddWechatContactViewModel.java deleted file mode 100644 index 440888e..0000000 --- a/app/src/main/java/com/xxpatx/os/activity/contact/AddWechatContactViewModel.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.xxpatx.os.activity.contact; - -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.net.Uri; -import android.text.TextUtils; -import android.util.Log; - -import androidx.lifecycle.MutableLiveData; - -import com.hjq.toast.Toaster; -import com.trello.rxlifecycle4.RxLifecycle; -import com.trello.rxlifecycle4.android.ActivityEvent; -import com.xxpatx.os.R; -import com.xxpatx.os.base.mvvm.BaseViewModel; -import com.xxpatx.os.bean.BaseResponse; -import com.xxpatx.os.bean.Contact; -import com.xxpatx.os.databinding.ActivityAddWechatContactBinding; -import com.xxpatx.os.network.NetInterfaceManager; -import com.xxpatx.os.utils.FileUtil; -import com.xxpatx.os.utils.Utils; - -import java.io.File; -import java.io.FileOutputStream; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ThreadLocalRandom; - -import io.reactivex.rxjava3.annotations.NonNull; -import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.Disposable; -import okhttp3.MediaType; -import okhttp3.MultipartBody; -import okhttp3.RequestBody; - -public class AddWechatContactViewModel extends BaseViewModel { - private static final String TAG = "AddWechatContactViewModel"; - - @Override - public ActivityAddWechatContactBinding getVDBinding() { - return binding; - } - - @Override - public void onDestroy() { - - } - - public String avatarFilePath = ""; - - private MutableLiveData mAddData = new MutableLiveData<>(); - - public MutableLiveData getAddData() { - return mAddData; - } - - private MutableLiveData mContactData = new MutableLiveData<>(); - - public MutableLiveData getContactData() { - return mContactData; - } - - public void checkContact() { - String name = binding.etName.getText().toString(); - String phone = binding.etPhone.getText().toString(); - String tag = binding.etTag.getText().toString(); - File avatarFile; - Log.e("checkContact", "avatarFilePath: " + avatarFilePath); - if (TextUtils.isEmpty(avatarFilePath)) { - avatarFile = FileUtil.drawableToFile(getCtx(), R.drawable.default_avatar, "avatar"); - } else { - Uri uri = Uri.parse(avatarFilePath); - avatarFile = FileUtil.uriToFile(uri, getCtx()); - } - if (avatarFile == null) { - Toaster.showShort("图片加载失败"); - return; - } - MediaType mediaType = MediaType.Companion.parse("image/png"); - RequestBody requestBody = RequestBody.Companion.create(avatarFile, mediaType); - MultipartBody.Part body = MultipartBody.Part.createFormData("avatar", avatarFile.getName(), requestBody); - Map params = new HashMap<>(); - params.put("sn", Utils.getSerial()); - params.put("name", name); - params.put("mobile", phone); - params.put("tag", tag); - params.put("is_urgent", String.valueOf(binding.toggleButton.isToggleOn())); - NetInterfaceManager.getInstance() - .getMailListAddObservable(params, body) - .compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY)) - .subscribe(new Observer() { - @Override - public void onSubscribe(@NonNull Disposable d) { - Log.e("checkContact", "onSubscribe: "); - } - - @Override - public void onNext(@NonNull BaseResponse baseResponse) { - Log.e("checkContact", "onNext: " + baseResponse); - if (baseResponse.code == 200) { - Toaster.show("已添加"); - mAddData.setValue(true); - } else { - Contact contact = new Contact(); - ThreadLocalRandom random = ThreadLocalRandom.current(); - int fakeId = random.nextInt(Integer.MAX_VALUE); - Log.e(TAG, "checkContent: fakeId = " + fakeId); - contact.setId(fakeId); - contact.setMobile(phone); - contact.setName(name); - contact.setTag(tag); - contact.setIs_urgent(binding.toggleButton.isToggleOn()); - contact.setAvatar(avatarFilePath); - mContactData.setValue(contact); - Toaster.show(baseResponse.msg); - mAddData.setValue(false); - } - } - - @Override - public void onError(@NonNull Throwable e) { - Log.e("checkContact", "onError: " + e.getMessage()); - Contact contact = new Contact(); - ThreadLocalRandom random = ThreadLocalRandom.current(); - int fakeId = random.nextInt(Integer.MAX_VALUE); - Log.e(TAG, "checkContent: fakeId = " + fakeId); - contact.setId(fakeId); - contact.setMobile(phone); - contact.setName(name); - contact.setTag(tag); - contact.setIs_urgent(binding.toggleButton.isToggleOn()); - contact.setAvatar(avatarFilePath); - mContactData.setValue(contact); - mAddData.setValue(false); - } - - @Override - public void onComplete() { - Log.e("checkContact", "onComplete: "); - } - }); - } - -} diff --git a/app/src/main/java/com/xxpatx/os/activity/contact/ContactActivity.java b/app/src/main/java/com/xxpatx/os/activity/contact/ContactListActivity.java similarity index 93% rename from app/src/main/java/com/xxpatx/os/activity/contact/ContactActivity.java rename to app/src/main/java/com/xxpatx/os/activity/contact/ContactListActivity.java index 87f3f73..cbe8293 100644 --- a/app/src/main/java/com/xxpatx/os/activity/contact/ContactActivity.java +++ b/app/src/main/java/com/xxpatx/os/activity/contact/ContactListActivity.java @@ -21,15 +21,15 @@ import com.xxpatx.os.base.mvvm.BaseMvvmActivity; import com.xxpatx.os.bean.BaseResponse; import com.xxpatx.os.bean.Contact; import com.xxpatx.os.config.CommonConfig; -import com.xxpatx.os.databinding.ActivityContactBinding; +import com.xxpatx.os.databinding.ActivityContactListBinding; import com.xxpatx.os.dialog.EditContactDialog; import com.zackratos.ultimatebarx.ultimatebarx.java.UltimateBarX; import java.util.ArrayList; import java.util.List; -public class ContactActivity extends BaseMvvmActivity { - private static final String TAG = "ContactActivity"; +public class ContactListActivity extends BaseMvvmActivity { + private static final String TAG = "ContactListActivity"; private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); @@ -37,7 +37,7 @@ public class ContactActivity extends BaseMvvmActivity { - private static final String TAG = "ContactViewModel"; +public class ContactListViewModel extends BaseViewModel { + private static final String TAG = "ContactListViewModel"; private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); @Override - public ActivityContactBinding getVDBinding() { + public ActivityContactListBinding getVDBinding() { return binding; } diff --git a/app/src/main/java/com/xxpatx/os/activity/contact/EditContactActivity.java b/app/src/main/java/com/xxpatx/os/activity/contact/EditContactActivity.java index c05aeed..127483a 100644 --- a/app/src/main/java/com/xxpatx/os/activity/contact/EditContactActivity.java +++ b/app/src/main/java/com/xxpatx/os/activity/contact/EditContactActivity.java @@ -80,7 +80,7 @@ public class EditContactActivity extends BaseMvvmActivity() { + mViewModel.mBaseResponseMutableLiveData.observe(this, new Observer() { @Override public void onChanged(BaseResponse baseResponse) { if (baseResponse.code == 200) { diff --git a/app/src/main/java/com/xxpatx/os/activity/contact/EditContactViewModel.java b/app/src/main/java/com/xxpatx/os/activity/contact/EditContactViewModel.java index f328187..bac480b 100644 --- a/app/src/main/java/com/xxpatx/os/activity/contact/EditContactViewModel.java +++ b/app/src/main/java/com/xxpatx/os/activity/contact/EditContactViewModel.java @@ -30,11 +30,7 @@ public class EditContactViewModel extends BaseViewModel mBaseResponseMutableLiveData = new MutableLiveData<>(); - - public MutableLiveData getBaseResponseMutableLiveData() { - return mBaseResponseMutableLiveData; - } + public MutableLiveData mBaseResponseMutableLiveData = new MutableLiveData<>(); public void editContact(Map params, MultipartBody.Part body) { NetInterfaceManager.getInstance() diff --git a/app/src/main/java/com/xxpatx/os/activity/dock/DockActivity.java b/app/src/main/java/com/xxpatx/os/activity/dock/DockActivity.java index e61c205..e16bcfb 100644 --- a/app/src/main/java/com/xxpatx/os/activity/dock/DockActivity.java +++ b/app/src/main/java/com/xxpatx/os/activity/dock/DockActivity.java @@ -76,7 +76,7 @@ public class DockActivity extends BaseMvvmActivity>() { + mViewModel.mAppSelectBeanListData.observe(this, new Observer>() { @Override public void onChanged(List dockApps) { mDockAppSelectedAdapter.setDockAppList(dockApps); diff --git a/app/src/main/java/com/xxpatx/os/activity/dock/DockViewModel.java b/app/src/main/java/com/xxpatx/os/activity/dock/DockViewModel.java index 462b19f..480843b 100644 --- a/app/src/main/java/com/xxpatx/os/activity/dock/DockViewModel.java +++ b/app/src/main/java/com/xxpatx/os/activity/dock/DockViewModel.java @@ -28,11 +28,7 @@ public class DockViewModel extends BaseViewModel> mAppSelectBeanListData = new MutableLiveData<>(); - - public MutableLiveData> getAppSelectBeanListData() { - return mAppSelectBeanListData; - } + public MutableLiveData> mAppSelectBeanListData = new MutableLiveData<>(); public void getPackageList() { PackageManager pm = getCtx().getPackageManager(); diff --git a/app/src/main/java/com/xxpatx/os/activity/emergency/EmergencyActivity.java b/app/src/main/java/com/xxpatx/os/activity/emergency/EmergencyActivity.java index 0b2efcf..3851911 100644 --- a/app/src/main/java/com/xxpatx/os/activity/emergency/EmergencyActivity.java +++ b/app/src/main/java/com/xxpatx/os/activity/emergency/EmergencyActivity.java @@ -1,10 +1,7 @@ package com.xxpatx.os.activity.emergency; -import android.content.ComponentName; import android.content.Intent; import android.net.Uri; -import android.os.Handler; -import android.text.TextUtils; import android.util.Log; import android.view.View; @@ -23,7 +20,6 @@ import com.xxpatx.os.config.CommonConfig; import com.xxpatx.os.databinding.ActivityEmergencyBinding; import com.xxpatx.os.manager.AmapManager; import com.xxpatx.os.network.NetInterfaceManager; -import com.xxpatx.os.receiver.BootReceiver; import java.util.List; import java.util.concurrent.TimeUnit; @@ -156,7 +152,7 @@ public class EmergencyActivity extends BaseMvvmActivity>() { + mViewModel.mContactListData.observe(this, new androidx.lifecycle.Observer>() { @Override public void onChanged(List contacts) { phoneList = contacts; diff --git a/app/src/main/java/com/xxpatx/os/activity/emergency/EmergencyViewModel.java b/app/src/main/java/com/xxpatx/os/activity/emergency/EmergencyViewModel.java index d897437..47c861f 100644 --- a/app/src/main/java/com/xxpatx/os/activity/emergency/EmergencyViewModel.java +++ b/app/src/main/java/com/xxpatx/os/activity/emergency/EmergencyViewModel.java @@ -10,7 +10,6 @@ import com.google.gson.reflect.TypeToken; import com.tencent.mmkv.MMKV; import com.trello.rxlifecycle4.RxLifecycle; import com.trello.rxlifecycle4.android.ActivityEvent; -import com.trello.rxlifecycle4.android.FragmentEvent; import com.xxpatx.os.base.mvvm.BaseViewModel; import com.xxpatx.os.bean.BaseResponse; import com.xxpatx.os.bean.Contact; @@ -18,7 +17,6 @@ import com.xxpatx.os.config.CommonConfig; import com.xxpatx.os.databinding.ActivityEmergencyBinding; import com.xxpatx.os.gson.GsonUtils; import com.xxpatx.os.network.NetInterfaceManager; -import com.xxpatx.os.network.UrlAddress; import java.lang.reflect.Type; import java.util.ArrayList; @@ -44,11 +42,7 @@ public class EmergencyViewModel extends BaseViewModel> mContactListData = new MutableLiveData<>(); - - public MutableLiveData> getContactListData() { - return mContactListData; - } + public MutableLiveData> mContactListData = new MutableLiveData<>(); public void getCacheContact() { String jsonString = mMMKV.getString(CommonConfig.EMERGENCY_CONTACT_LIST_KEY, null); diff --git a/app/src/main/java/com/xxpatx/os/activity/location/LocationAcivity.java b/app/src/main/java/com/xxpatx/os/activity/location/LocationAcivity.java index 0974d56..c071b7d 100644 --- a/app/src/main/java/com/xxpatx/os/activity/location/LocationAcivity.java +++ b/app/src/main/java/com/xxpatx/os/activity/location/LocationAcivity.java @@ -150,7 +150,7 @@ public class LocationAcivity extends BaseMvvmActivity>() { + mViewModel.mAddressBeanListData.observe(this, new Observer>() { @Override public void onChanged(List addressBeans) { mDistrict = addressBeans; diff --git a/app/src/main/java/com/xxpatx/os/activity/location/LocationViewModel.java b/app/src/main/java/com/xxpatx/os/activity/location/LocationViewModel.java index 25149e3..b340533 100644 --- a/app/src/main/java/com/xxpatx/os/activity/location/LocationViewModel.java +++ b/app/src/main/java/com/xxpatx/os/activity/location/LocationViewModel.java @@ -38,11 +38,7 @@ public class LocationViewModel extends BaseViewModel> mAddressBeanListData =new MutableLiveData<>(); - - public MutableLiveData> getAddressBeanListData() { - return mAddressBeanListData; - } + public MutableLiveData> mAddressBeanListData = new MutableLiveData<>(); public void initJsonData() { if (getCtx() == null) return; diff --git a/app/src/main/java/com/xxpatx/os/activity/main/MainActivity.java b/app/src/main/java/com/xxpatx/os/activity/main/MainActivity.java index 1dc3a6e..6759b4e 100644 --- a/app/src/main/java/com/xxpatx/os/activity/main/MainActivity.java +++ b/app/src/main/java/com/xxpatx/os/activity/main/MainActivity.java @@ -33,6 +33,8 @@ import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import androidx.lifecycle.Observer; +import com.arialyy.aria.core.Aria; +import com.arialyy.aria.core.download.DownloadEntity; import com.blankj.utilcode.util.NetworkUtils; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @@ -54,6 +56,7 @@ import com.xxpatx.os.bean.AppInfo; import com.xxpatx.os.bean.DesktopIcon; import com.xxpatx.os.bean.DockApp; import com.xxpatx.os.bean.MapBean; +import com.xxpatx.os.bean.TestAppInfo; import com.xxpatx.os.config.CommonConfig; import com.xxpatx.os.databinding.ActivityMainBinding; import com.xxpatx.os.fragment.app.AppListFragment; @@ -64,6 +67,7 @@ import com.xxpatx.os.manager.AmapManager; import com.xxpatx.os.service.NotificationService; import com.xxpatx.os.utils.ApkUtils; import com.xxpatx.os.utils.AppUsedTimeUtils; +import com.xxpatx.os.utils.ContactsUtils; import com.xxpatx.os.utils.DayUtils; import com.xxpatx.os.utils.TimeUtils; import com.xxpatx.os.utils.Utils; @@ -78,6 +82,8 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; +import static com.arialyy.aria.core.inf.IEntity.STATE_RUNNING; + public class MainActivity extends BaseMvvmActivity implements NetworkUtils.OnNetworkStatusChangedListener, NotificationService.NotificationListener { private static final String TAG = "MainActivity"; @@ -98,8 +104,8 @@ public class MainActivity extends BaseMvvmActivity testAppInfoBaseResponse) { + Log.e("getTestApp", "onNext: " + testAppInfoBaseResponse); + if (testAppInfoBaseResponse.code == 200) { + TestAppInfo testAppInfo = testAppInfoBaseResponse.data; + mTestAppInfoData.setValue(testAppInfo); + } + } + + @Override + public void onError(@NonNull Throwable e) { + Log.e("getTestApp", "onError: " + e.getMessage()); + } + + @Override + public void onComplete() { + Log.e("getTestApp", "onComplete: "); + } + }); + + } + public void uploadContacts() { List contactList = ContactCacheUtils.getInstance().getDatabaseContact(); diff --git a/app/src/main/java/com/xxpatx/os/activity/selectnumber/SelectNumberActivity.java b/app/src/main/java/com/xxpatx/os/activity/selectnumber/SelectNumberActivity.java index 9ee0c9a..37bc548 100644 --- a/app/src/main/java/com/xxpatx/os/activity/selectnumber/SelectNumberActivity.java +++ b/app/src/main/java/com/xxpatx/os/activity/selectnumber/SelectNumberActivity.java @@ -2,7 +2,7 @@ package com.xxpatx.os.activity.selectnumber; import android.content.Context; import android.content.Intent; -import android.telecom.PhoneAccountHandle; +import android.net.Uri; import android.telecom.TelecomManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; @@ -15,23 +15,34 @@ import android.view.View; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import com.tencent.mmkv.MMKV; import com.xxpatx.os.R; import com.xxpatx.os.adapter.PhoneAdapter; import com.xxpatx.os.base.mvvm.BaseMvvmActivity; +import com.xxpatx.os.config.CommonConfig; import com.xxpatx.os.databinding.ActivitySelectNumberBinding; +import com.xxpatx.os.utils.MobileNetworkUtils; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; public class SelectNumberActivity extends BaseMvvmActivity { private static final String TAG = "SelectNumberActivity"; + //指定SIM卡拨打 + public static final String[] DUAL_SIM_TYPES = {"subscription", "Subscription", + "com.android.phone.extra.slot", + "phone", "com.android.phone.DialingMode", + "simId", "simnum", "phone_type", + "simSlot"}; + private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); + private TelephonyManager mTelephonyManager; private SubscriptionManager subscriptionManager; private TelecomManager telecomManager; private PhoneAdapter mPhoneAdapter; + private String mDefaultNumber; + private String mPhone; @Override public boolean setNightMode() { @@ -66,11 +77,12 @@ public class SelectNumberActivity extends BaseMvvmActivity() { + mViewModel.mAppInfoData.observe(this, new Observer() { @Override public void onChanged(AppInfo appInfo) { if (appInfo == null) { @@ -302,6 +303,10 @@ public class SettingActivity extends BaseMvvmActivity mAppInfoData = new MutableLiveData<>(); - - public MutableLiveData getAppInfoData() { - return mAppInfoData; - } + public MutableLiveData mAppInfoData = new MutableLiveData<>(); public void checkUpdate() { NetInterfaceManager.getInstance().getUpdateObservable() diff --git a/app/src/main/java/com/xxpatx/os/activity/sim/SimCardActivity.java b/app/src/main/java/com/xxpatx/os/activity/sim/SimCardActivity.java new file mode 100644 index 0000000..3f1bc4a --- /dev/null +++ b/app/src/main/java/com/xxpatx/os/activity/sim/SimCardActivity.java @@ -0,0 +1,85 @@ +package com.xxpatx.os.activity.sim; + +import android.content.Context; +import android.telecom.TelecomManager; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.util.Log; +import android.view.View; + +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.xxpatx.os.BuildConfig; +import com.xxpatx.os.R; +import com.xxpatx.os.adapter.PhoneSetAdapter; +import com.xxpatx.os.base.mvvm.BaseMvvmActivity; +import com.xxpatx.os.databinding.ActivitySimCardBinding; + +import java.util.ArrayList; +import java.util.List; + +public class SimCardActivity extends BaseMvvmActivity { + private static final String TAG = "SimCardActivity"; + + private TelephonyManager mTelephonyManager; + private SubscriptionManager subscriptionManager; + private TelecomManager telecomManager; + + private PhoneSetAdapter mPhoneSetAdapter; + + + @Override + protected int getLayoutId() { + return R.layout.activity_sim_card; + } + + @Override + protected void initDataBinding() { + mViewModel.setCtx(this); + mViewModel.setVDBinding(mViewDataBinding); + mViewModel.setLifecycle(getLifecycleSubject()); + mViewDataBinding.setClick(new BtnClick()); + } + + @Override + protected void initView() { + mPhoneSetAdapter = new PhoneSetAdapter(); + + LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this); + linearLayoutManager.setOrientation(RecyclerView.VERTICAL); + mViewDataBinding.recyclerView.setLayoutManager(linearLayoutManager); + mViewDataBinding.recyclerView.setAdapter(mPhoneSetAdapter); + + mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); + subscriptionManager = (SubscriptionManager) getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); + telecomManager = (TelecomManager) getSystemService(Context.TELECOM_SERVICE); + + } + + @Override + protected void initData() { + List subscriptionInfos = subscriptionManager.getActiveSubscriptionInfoList(); + List stringList = new ArrayList<>(); + for (SubscriptionInfo subscriptionInfo : subscriptionInfos) { + Log.e(TAG, "initData: id = " + subscriptionInfo.getSubscriptionId()); + Log.e(TAG, "initData: mCardString = " + subscriptionInfo.getCardString()); + Log.e(TAG, "initData: phone = " + mTelephonyManager.getLine1Number(subscriptionInfo.getSubscriptionId())); + stringList.add(mTelephonyManager.getLine1Number(subscriptionInfo.getSubscriptionId())); + } + if (BuildConfig.DEBUG) { +// stringList.add("+8613220282310"); +// stringList.add("+8618108347897"); + } + stringList.add(stringList.size(), "默认手动选择"); + mPhoneSetAdapter.setPhoneList(stringList); + } + + public class BtnClick { + public void exit(View view) { + finish(); + } + } + +} diff --git a/app/src/main/java/com/xxpatx/os/activity/sim/SimCardViewModel.java b/app/src/main/java/com/xxpatx/os/activity/sim/SimCardViewModel.java new file mode 100644 index 0000000..ab85502 --- /dev/null +++ b/app/src/main/java/com/xxpatx/os/activity/sim/SimCardViewModel.java @@ -0,0 +1,18 @@ +package com.xxpatx.os.activity.sim; + +import com.trello.rxlifecycle4.android.ActivityEvent; +import com.xxpatx.os.base.mvvm.BaseViewModel; +import com.xxpatx.os.databinding.ActivitySimCardBinding; + +public class SimCardViewModel extends BaseViewModel { + + @Override + public ActivitySimCardBinding getVDBinding() { + return binding; + } + + @Override + public void onDestroy() { + + } +} diff --git a/app/src/main/java/com/xxpatx/os/activity/weather/WeatherActivity.java b/app/src/main/java/com/xxpatx/os/activity/weather/WeatherActivity.java index 3b2aba9..967b90c 100644 --- a/app/src/main/java/com/xxpatx/os/activity/weather/WeatherActivity.java +++ b/app/src/main/java/com/xxpatx/os/activity/weather/WeatherActivity.java @@ -37,7 +37,6 @@ import com.xxpatx.os.databinding.ActivityWeatherBinding; import com.xxpatx.os.manager.AmapManager; import com.xxpatx.os.utils.DataUtil; import com.xxpatx.os.utils.DayUtils; -import com.xxpatx.os.utils.TimeUtils; import com.xxpatx.os.view.FadeInOutAnimator; import com.xxpatx.os.view.HorizontalItemDecoration; @@ -154,7 +153,7 @@ public class WeatherActivity extends BaseMvvmActivity() { + mViewModel.mQweatherLocationData.observe(this, new Observer() { @Override public void onChanged(QweatherLocation qweatherLocation) { if (qweatherLocation != null) { @@ -166,7 +165,7 @@ public class WeatherActivity extends BaseMvvmActivity() { + mViewModel.mLocationData.observe(this, new Observer() { @Override public void onChanged(String s) { mViewDataBinding.tvLocation.setText(s); @@ -174,7 +173,7 @@ public class WeatherActivity extends BaseMvvmActivity() { + mViewModel.mWeatherNowData.observe(this, new Observer() { @Override public void onChanged(WeatherNowBean weatherNowBean) { //先判断返回的status是否正确,当status正确时获取数据,若status不正确,可查看status对应的Code值找到原因 @@ -195,7 +194,7 @@ public class WeatherActivity extends BaseMvvmActivity() { + mViewModel.mWeatherDailyData.observe(this, new Observer() { @Override public void onChanged(WeatherDailyBean weatherDailyBean) { if (weatherDailyBean != null) { diff --git a/app/src/main/java/com/xxpatx/os/activity/weather/WeatherViewModel.java b/app/src/main/java/com/xxpatx/os/activity/weather/WeatherViewModel.java index 5e2f658..94ecbf7 100644 --- a/app/src/main/java/com/xxpatx/os/activity/weather/WeatherViewModel.java +++ b/app/src/main/java/com/xxpatx/os/activity/weather/WeatherViewModel.java @@ -50,11 +50,7 @@ public class WeatherViewModel extends BaseViewModel mGeoResultData = new MutableLiveData<>(); - - public MutableLiveData getGeoResultData() { - return mGeoResultData; - } + public MutableLiveData mGeoResultData = new MutableLiveData<>(); @Deprecated public void decodeGeo(String address) { @@ -92,11 +88,7 @@ public class WeatherViewModel extends BaseViewModel mQweatherLocationData = new MutableLiveData<>(); - - public MutableLiveData getQweatherLocationData() { - return mQweatherLocationData; - } + public MutableLiveData mQweatherLocationData = new MutableLiveData<>(); public void cityLookup(String city) { NetInterfaceManager.getInstance().getLookupObservable(city) @@ -155,11 +147,7 @@ public class WeatherViewModel extends BaseViewModel mLocationData = new MutableLiveData<>(); - - public MutableLiveData getLocationData() { - return mLocationData; - } + public MutableLiveData mLocationData = new MutableLiveData<>(); public void getLocation() { String location = AmapManager.getInstance().getDistrict(); @@ -182,11 +170,7 @@ public class WeatherViewModel extends BaseViewModel mWeatherNowData = new MutableLiveData<>(); - - public MutableLiveData getWeatherNowData() { - return mWeatherNowData; - } + public MutableLiveData mWeatherNowData = new MutableLiveData<>(); public void getWeatherNow(String locationTude) { Log.e(TAG, "getweather: " + locationTude); @@ -219,11 +203,7 @@ public class WeatherViewModel extends BaseViewModel mWeatherDailyData = new MutableLiveData<>(); - - public MutableLiveData getWeatherDailyData() { - return mWeatherDailyData; - } + public MutableLiveData mWeatherDailyData = new MutableLiveData<>(); public void getWeather7D(String locationTude) { QWeather.getWeather7D(getCtx(), locationTude, new QWeather.OnResultWeatherDailyListener() { diff --git a/app/src/main/java/com/xxpatx/os/adapter/CallRecordAdapter.java b/app/src/main/java/com/xxpatx/os/adapter/CallRecordAdapter.java index 9ae2cc1..ca09fd6 100644 --- a/app/src/main/java/com/xxpatx/os/adapter/CallRecordAdapter.java +++ b/app/src/main/java/com/xxpatx/os/adapter/CallRecordAdapter.java @@ -20,6 +20,7 @@ import com.xxpatx.os.R; import com.xxpatx.os.activity.selectnumber.SelectNumberActivity; import com.xxpatx.os.bean.Contact; import com.xxpatx.os.bean.RecordsInfo; +import com.xxpatx.os.dialog.CallPhoneDialog; import com.xxpatx.os.utils.GlideLoadUtils; import com.xxpatx.os.utils.Utils; @@ -35,6 +36,7 @@ public class CallRecordAdapter extends RecyclerView.Adapter mRecordsInfoList; private SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat("HH:mm"); + private CallPhoneDialog mCallPhoneDialog; public void setRecordsInfoList(List recordsInfoList) { this.mRecordsInfoList = recordsInfoList; @@ -48,6 +50,16 @@ public class CallRecordAdapter extends RecyclerView.Adapter call(phone)); - holder.cl_info.setOnClickListener(view -> call(phone)); holder.iv_remove.setOnClickListener(new View.OnClickListener() { @Override @@ -101,6 +111,30 @@ public class CallRecordAdapter extends RecyclerView.Adapter { } else if (position == 1) { holder.iv_card.setImageDrawable(mContext.getDrawable(R.drawable.sim_card_2)); } - holder.tv_number.setText(phone); + if (TextUtils.isEmpty(phone)) { + holder.tv_number.setText("未知号码"); + } else { + holder.tv_number.setText(phone); + } + if (TextUtils.isEmpty(MobileNumberUtils.getCarrier(phone))) { + holder.tv_carrier.setText("未知"); + } else { + holder.tv_carrier.setText(MobileNumberUtils.getCarrier(phone)); + } holder.root.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -90,13 +101,14 @@ public class PhoneAdapter extends RecyclerView.Adapter { public class Holder extends RecyclerView.ViewHolder { ConstraintLayout root; - TextView tv_number; + TextView tv_number, tv_carrier; ImageView iv_card; public Holder(@NonNull View itemView) { super(itemView); root = itemView.findViewById(R.id.root); iv_card = itemView.findViewById(R.id.iv_card); + tv_carrier = itemView.findViewById(R.id.tv_carrier); tv_number = itemView.findViewById(R.id.tv_number); } } diff --git a/app/src/main/java/com/xxpatx/os/adapter/PhoneSetAdapter.java b/app/src/main/java/com/xxpatx/os/adapter/PhoneSetAdapter.java new file mode 100644 index 0000000..2fd7873 --- /dev/null +++ b/app/src/main/java/com/xxpatx/os/adapter/PhoneSetAdapter.java @@ -0,0 +1,93 @@ +package com.xxpatx.os.adapter; + +import android.content.Context; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.recyclerview.widget.RecyclerView; + +import com.tencent.mmkv.MMKV; +import com.xxpatx.os.R; +import com.xxpatx.os.config.CommonConfig; +import com.xxpatx.os.utils.MobileNumberUtils; + +import java.util.List; +import java.util.Objects; + +public class PhoneSetAdapter extends RecyclerView.Adapter { + private static final String TAG = "PhoneSetAdapter"; + + private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); + + private Context mContext; + private List mPhoneList; + private String mDefaultNumber; + + public void setPhoneList(List phoneList) { + mPhoneList = phoneList; + notifyDataSetChanged(); + } + + @NonNull + @Override + public Holder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + mContext = parent.getContext(); + mDefaultNumber = mMMKV.decodeString(CommonConfig.DEFAULT_PHONE_NUMBER_KEY, ""); + return new Holder(LayoutInflater.from(mContext).inflate(R.layout.item_phone_set, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull Holder holder, int position) { + String phone = mPhoneList.get(position); + if (Objects.equals(mDefaultNumber, phone)) { + holder.iv_select.setImageDrawable(mContext.getDrawable(R.drawable.icon_select)); + } else { + holder.iv_select.setImageDrawable(mContext.getDrawable(R.drawable.icon_unselect)); + } + holder.tv_number.setText(phone); + holder.tv_carrier.setText(MobileNumberUtils.getCarrier(phone)); + holder.root.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Log.e(TAG, "onClick: " + phone); + mDefaultNumber = phone; + mMMKV.encode(CommonConfig.DEFAULT_PHONE_NUMBER_KEY, phone); + notifyDataSetChanged(); + } + }); + holder.iv_select.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Log.e(TAG, "onClick: " + phone); + mDefaultNumber = phone; + mMMKV.encode(CommonConfig.DEFAULT_PHONE_NUMBER_KEY, phone); + notifyDataSetChanged(); + } + }); + } + + @Override + public int getItemCount() { + return mPhoneList == null ? 0 : mPhoneList.size(); + } + + public class Holder extends RecyclerView.ViewHolder { + ConstraintLayout root; + TextView tv_number, tv_carrier; + ImageView iv_select; + + public Holder(@NonNull View itemView) { + super(itemView); + root = itemView.findViewById(R.id.root); + tv_carrier = itemView.findViewById(R.id.tv_carrier); + tv_number = itemView.findViewById(R.id.tv_number); + iv_select = itemView.findViewById(R.id.iv_select); + } + } +} diff --git a/app/src/main/java/com/xxpatx/os/adapter/WechatContactAdapter.java b/app/src/main/java/com/xxpatx/os/adapter/WechatContactAdapter.java index f7d1db4..2b5fac6 100644 --- a/app/src/main/java/com/xxpatx/os/adapter/WechatContactAdapter.java +++ b/app/src/main/java/com/xxpatx/os/adapter/WechatContactAdapter.java @@ -19,7 +19,7 @@ import com.bumptech.glide.Glide; import com.shehuan.niv.NiceImageView; import com.xxpatx.os.R; import com.xxpatx.os.activity.callwechat.CallWechatActivity; -import com.xxpatx.os.activity.contact.AddWechatContactActivity; +import com.xxpatx.os.activity.contact.AddContactActivity; import com.xxpatx.os.bean.Contact; import com.xxpatx.os.utils.AccessibilityUtils; @@ -62,7 +62,7 @@ public class WechatContactAdapter extends RecyclerView.Adapter>() { + mViewModel.mContactListData.observe(this, new Observer>() { @Override public void onChanged(List contacts) { if (contacts == null || contacts.size() == 0) { @@ -212,7 +212,7 @@ public class ContactFragment extends BaseMvvmFragment() { + mViewModel.mDeleteData.observe(this, new Observer() { @Override public void onChanged(BaseResponse baseResponse) { if (baseResponse.code == 200) { @@ -299,7 +299,7 @@ public class ContactFragment extends BaseMvvmFragment { + private static final String TAG = "ContactListViewModel"; private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); @Override @@ -41,11 +43,7 @@ public class ContactViewModel extends BaseViewModel> mContactListData = new MutableLiveData<>(); - - public MutableLiveData> getContactListData() { - return mContactListData; - } + public MutableLiveData> mContactListData = new MutableLiveData<>(); public void getCacheContact() { String jsonString = mMMKV.getString(UrlAddress.GET_MAIL_LIST, null); @@ -76,8 +74,13 @@ public class ContactViewModel extends BaseViewModel> listBaseResponse) { Log.e("getContactList", "onNext: " + listBaseResponse); if (listBaseResponse.code == 200) { + List contactList = listBaseResponse.data; + String oldCache = mMMKV.getString(UrlAddress.GET_MAIL_LIST, ""); + if (!oldCache.equals(GsonUtils.toJSONString(listBaseResponse.data))) { + ContactsUtils.saveContactPhone(getCtx(), contactList); + } + mContactListData.setValue(contactList); mMMKV.putString(UrlAddress.GET_MAIL_LIST, GsonUtils.toJSONString(listBaseResponse.data)); - mContactListData.setValue(listBaseResponse.data); } else { mMMKV.putString(UrlAddress.GET_MAIL_LIST, ""); mContactListData.setValue(new ArrayList<>()); @@ -108,12 +111,7 @@ public class ContactViewModel extends BaseViewModel mDeleteData =new MutableLiveData<>(); - - public MutableLiveData getDeleteData() { - return mDeleteData; - } + public MutableLiveData mDeleteData = new MutableLiveData<>(); public void deleteContact(long id) { NetInterfaceManager.getInstance().getMailListDeleteObservable(id) diff --git a/app/src/main/java/com/xxpatx/os/fragment/home/HomeFragment.java b/app/src/main/java/com/xxpatx/os/fragment/home/HomeFragment.java index 80d6cb7..a27ed22 100644 --- a/app/src/main/java/com/xxpatx/os/fragment/home/HomeFragment.java +++ b/app/src/main/java/com/xxpatx/os/fragment/home/HomeFragment.java @@ -27,7 +27,7 @@ import com.xxpatx.os.BuildConfig; import com.xxpatx.os.R; import com.xxpatx.os.activity.FlashlightActivity; import com.xxpatx.os.activity.NetworkActivity; -import com.xxpatx.os.activity.contact.AddWechatContactActivity; +import com.xxpatx.os.activity.contact.AddContactActivity; import com.xxpatx.os.activity.setting.SettingActivity; import com.xxpatx.os.activity.weather.WeatherActivity; import com.xxpatx.os.alarm.AlarmClockData; @@ -460,7 +460,7 @@ public class HomeFragment extends BaseMvvmFragment>() { + mViewModel.mContactListData.observe(this, new Observer>() { @Override public void onChanged(List contacts) { List sim = getSIMContacts(); @@ -112,7 +112,7 @@ public class ContactFragment extends BaseMvvmFragment() { + mViewModel.mDeleteData.observe(this, new Observer() { @Override public void onChanged(BaseResponse baseResponse) { if (baseResponse.code == 200) { @@ -275,7 +275,7 @@ public class ContactFragment extends BaseMvvmFragment { - private static final String TAG = "ContactViewModel"; + private static final String TAG = "ContactListViewModel"; private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); @Override @@ -53,11 +54,7 @@ public class ContactViewModel extends BaseViewModel> mContactListData = new MutableLiveData<>(); - - public MutableLiveData> getContactListData() { - return mContactListData; - } + public MutableLiveData> mContactListData = new MutableLiveData<>(); public void getCacheContact() { String jsonString = mMMKV.getString(UrlAddress.GET_MAIL_LIST, null); @@ -87,8 +84,13 @@ public class ContactViewModel extends BaseViewModel> listBaseResponse) { Log.e("getContactList", "onNext: " + listBaseResponse); if (listBaseResponse.code == 200) { - mMMKV.putString(UrlAddress.GET_MAIL_LIST, GsonUtils.toJSONString(listBaseResponse.data)); - mContactListData.setValue(listBaseResponse.data); + List contactList = listBaseResponse.data; + String oldCache = mMMKV.getString(UrlAddress.GET_MAIL_LIST, ""); + if (!oldCache.equals(GsonUtils.toJSONString(contactList))) { + ContactsUtils.saveContactPhone(getCtx(), contactList); + } + mContactListData.setValue(contactList); + mMMKV.putString(UrlAddress.GET_MAIL_LIST, GsonUtils.toJSONString(contactList)); } else { mMMKV.putString(UrlAddress.GET_MAIL_LIST, ""); mContactListData.setValue(new ArrayList<>()); @@ -118,11 +120,7 @@ public class ContactViewModel extends BaseViewModel mDeleteData = new MutableLiveData<>(); - - public MutableLiveData getDeleteData() { - return mDeleteData; - } + public MutableLiveData mDeleteData = new MutableLiveData<>(); public void deleteContact(Contact contact) { Log.e(TAG, "deleteContact: " + contact.getId()); @@ -153,7 +151,6 @@ public class ContactViewModel extends BaseViewModel dataList = new ArrayList<>(); + private DeleteDialog mDeleteDialog; // TODO: Rename parameter arguments, choose names that match // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER @@ -92,6 +96,28 @@ public class RecordFragment extends BaseMvvmFragment>() { + mViewModel.mRecordsInfoData.observe(this, new Observer>() { @Override public void onChanged(List recordsInfos) { dataList = recordsInfos; @@ -121,12 +147,13 @@ public class RecordFragment extends BaseMvvmFragment>() { + mViewModel.mContactMapData.observe(this, new Observer>() { @Override public void onChanged(Map stringContactMap) { mCallRecordAdapter.setContactMap(stringContactMap); } }); + mViewModel.getContact(); mViewModel.getRecordsInfoList(); } @@ -144,6 +171,24 @@ public class RecordFragment extends BaseMvvmFragment> mRecordsInfoData = new MutableLiveData<>(); - - public MutableLiveData> getRecordsInfoData() { - return mRecordsInfoData; - } + public MutableLiveData> mRecordsInfoData = new MutableLiveData<>(); /** * 读取数据 @@ -115,11 +110,7 @@ public class RecordViewModel extends BaseViewModel> mContactMapData = new MutableLiveData(); - - public MutableLiveData> getContactMapData() { - return mContactMapData; - } + public MutableLiveData> mContactMapData = new MutableLiveData(); public void getContact() { String jsonString = mMMKV.decodeString(UrlAddress.GET_MAIL_LIST); @@ -139,7 +130,7 @@ public class RecordViewModel extends BaseViewModel contact)); mContactMapData.setValue(map); } } diff --git a/app/src/main/java/com/xxpatx/os/network/NetInterfaceManager.java b/app/src/main/java/com/xxpatx/os/network/NetInterfaceManager.java index e2c365c..a86adcc 100644 --- a/app/src/main/java/com/xxpatx/os/network/NetInterfaceManager.java +++ b/app/src/main/java/com/xxpatx/os/network/NetInterfaceManager.java @@ -37,6 +37,7 @@ import com.xxpatx.os.bean.RegionInfo; import com.xxpatx.os.bean.ServeBean; import com.xxpatx.os.bean.SnInfo; import com.xxpatx.os.bean.SystemSettings; +import com.xxpatx.os.bean.TestAppInfo; import com.xxpatx.os.bean.UserAvatarInfo; import com.xxpatx.os.bean.UserId; import com.xxpatx.os.bean.WxpayBean; @@ -65,6 +66,7 @@ import com.xxpatx.os.network.api.uiui.RegionListApi; import com.xxpatx.os.network.api.uiui.RegionListCall; import com.xxpatx.os.network.api.uiui.ServeApi; import com.xxpatx.os.network.api.uiui.SnIsActivationApi; +import com.xxpatx.os.network.api.uiui.TestAppApi; import com.xxpatx.os.network.api.uiui.UpdateAppIconApi; import com.xxpatx.os.network.api.uiui.UserInfoControl; import com.xxpatx.os.network.api.uiui.alarmclock.AlarmClockAddApi; @@ -600,6 +602,13 @@ public class NetInterfaceManager { .observeOn(AndroidSchedulers.mainThread()); } + public Observable> getTestAppObservable() { + return mRetrofit.create(TestAppApi.class) + .getTestApp(Utils.getSerial(), BuildConfig.platform, BuildConfig.APPLICATION_ID) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } + public RegionListApi getRegionListApi() { return mRetrofit.create(RegionListApi.class); } @@ -1466,4 +1475,34 @@ public class NetInterfaceManager { } }; } + + public void getTestApp() { + getTestAppObservable().subscribe(getTestAppObserver()); + } + + public Observer> getTestAppObserver() { + return new Observer>() { + @Override + public void onSubscribe(@NonNull Disposable d) { + Log.e("getTestAppObserver", "onSubscribe: "); + } + + @Override + public void onNext(@NonNull BaseResponse testAppInfoBaseResponse) { + Log.e("getTestAppObserver", "onNext: " + testAppInfoBaseResponse); + } + + @Override + public void onError(@NonNull Throwable e) { + Log.e("getTestAppObserver", "onError: " + e.getMessage()); + } + + @Override + public void onComplete() { + Log.e("getTestAppObserver", "onComplete: "); + } + }; + } + + } diff --git a/app/src/main/java/com/xxpatx/os/network/UrlAddress.java b/app/src/main/java/com/xxpatx/os/network/UrlAddress.java index e6c9378..95093e0 100644 --- a/app/src/main/java/com/xxpatx/os/network/UrlAddress.java +++ b/app/src/main/java/com/xxpatx/os/network/UrlAddress.java @@ -35,6 +35,8 @@ public class UrlAddress { public static final String FAMILY_ADDRESS = "sn/family-address"; /*根据包名获取更新*/ public final static String GET_NEWESTAPPUPDATE = "app/newestAppUpdate"; + /*获取灰度测试应用*/ + public static final String GET_TEST_APP = "app/test-app"; /*获取抢购列表*/ public static final String GET_GOODS_LIST = "getGoodsList"; diff --git a/app/src/main/java/com/xxpatx/os/network/api/uiui/TestAppApi.java b/app/src/main/java/com/xxpatx/os/network/api/uiui/TestAppApi.java new file mode 100644 index 0000000..95bdb8e --- /dev/null +++ b/app/src/main/java/com/xxpatx/os/network/api/uiui/TestAppApi.java @@ -0,0 +1,18 @@ +package com.xxpatx.os.network.api.uiui; + +import com.xxpatx.os.bean.BaseResponse; +import com.xxpatx.os.bean.TestAppInfo; +import com.xxpatx.os.network.UrlAddress; + +import io.reactivex.rxjava3.core.Observable; +import retrofit2.http.GET; +import retrofit2.http.Query; + +public interface TestAppApi { + @GET(UrlAddress.GET_TEST_APP) + Observable> getTestApp( + @Query("sn") String sn, + @Query("model_name") String model_name, + @Query("app_package") String app_package + ); +} diff --git a/app/src/main/java/com/xxpatx/os/utils/ApkUtils.java b/app/src/main/java/com/xxpatx/os/utils/ApkUtils.java index da53a4a..2fcbfe5 100644 --- a/app/src/main/java/com/xxpatx/os/utils/ApkUtils.java +++ b/app/src/main/java/com/xxpatx/os/utils/ApkUtils.java @@ -28,6 +28,7 @@ import com.xxpatx.os.R; import com.xxpatx.os.activity.quickapp.QuickAppActivity; import com.xxpatx.os.bean.AppInfo; import com.xxpatx.os.bean.DesktopIcon; +import com.xxpatx.os.bean.TestAppInfo; import com.xxpatx.os.config.CommonConfig; import com.xxpatx.os.gson.GsonUtils; import com.xxpatx.os.manager.AppManager; @@ -442,7 +443,8 @@ public class ApkUtils { DesktopIcon contactIcon = new DesktopIcon(); contactIcon.setTitle("联系人"); contactIcon.setPackage(AppManager.CONTACT_PACKAGE); - desktopIcons.add(1, contactIcon); + // TODO: 2024/10/22 2024-10-22 18:30:22 隐藏了客服中心将会报错 + desktopIcons.add(contactIcon); } List shortcutPkgInfos = ShortcutUtils.getInstance().getShortcutList(); @@ -964,6 +966,12 @@ public class ApkUtils { return checkAppUpdate(context, packageName, versionCode); } + public static boolean checkAppUpdate(Context context, TestAppInfo appUpdateInfo) { + String packageName = appUpdateInfo.getApp_package(); + long versionCode = appUpdateInfo.getApp_version_code(); + return checkAppUpdate(context, packageName, versionCode); + } + public static boolean checkAppUpdate(Context context, String packageName, long versionCode) { PackageInfo packageInfo = null; try { diff --git a/app/src/main/java/com/xxpatx/os/utils/ContactsUtils.java b/app/src/main/java/com/xxpatx/os/utils/ContactsUtils.java new file mode 100644 index 0000000..2d56683 --- /dev/null +++ b/app/src/main/java/com/xxpatx/os/utils/ContactsUtils.java @@ -0,0 +1,391 @@ +package com.xxpatx.os.utils; + +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.provider.ContactsContract; +import android.text.TextUtils; +import android.util.Log; + +import androidx.annotation.Nullable; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.target.SimpleTarget; +import com.bumptech.glide.request.transition.Transition; +import com.xxpatx.os.R; +import com.xxpatx.os.bean.Contact; +import com.xxpatx.os.bean.ContactId; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.ObservableEmitter; +import io.reactivex.rxjava3.core.ObservableOnSubscribe; +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.schedulers.Schedulers; + +public class ContactsUtils { + private static final String TAG = "ContactsUtils"; + + + public static void saveContactPhone(Context context, List contactList) { + Log.e(TAG, "saveContactPhone: "); +// List contactIdList = ContactsUtils.getLocalContacts(context); + Observable.create(new ObservableOnSubscribe() { + @Override + public void subscribe(@NonNull ObservableEmitter emitter) throws Throwable { + for (Contact contact : contactList) { + if (TextUtils.isEmpty(contact.getMobile())) { + continue; + } + if (ContactsUtils.isExist(context, contact.getMobile())) { + long rawContactId = ContactsUtils.getContactId(context, contact.getMobile()); + Glide.with(context).asBitmap().load(contact.getAvatar()).override(200, 200).into(new SimpleTarget() { + @Override + public void onResourceReady(@androidx.annotation.NonNull Bitmap resource, @Nullable Transition transition) { + ContentResolver resolver = context.getContentResolver(); + ContentValues nameValues = new ContentValues(); + //add Name + nameValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); + nameValues.put(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/name"); + nameValues.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, contact.getName()); + resolver.update(ContactsContract.Data.CONTENT_URI, nameValues, "raw_contact_id=?" + rawContactId, new String[]{ContactsContract.Data.CONTACT_ID}); + + ContentValues photoValues = new ContentValues(); + photoValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + resource.compress(Bitmap.CompressFormat.PNG, 100, out); + photoValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE); + photoValues.put(ContactsContract.CommonDataKinds.Photo.PHOTO, out.toByteArray()); + resolver.update(ContactsContract.Data.CONTENT_URI, photoValues, "raw_contact_id=?" + rawContactId, new String[]{ContactsContract.Data.CONTACT_ID}); + + ContentValues phoneValues = new ContentValues(); + phoneValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); + phoneValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE); + phoneValues.put(ContactsContract.CommonDataKinds.Phone.NUMBER, contact.getMobile()); + phoneValues.put(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE); + resolver.update(ContactsContract.Data.CONTENT_URI, phoneValues, "raw_contact_id=?" + rawContactId, new String[]{ContactsContract.Data.CONTACT_ID}); + } + }); + } else { + ContentValues values = new ContentValues(); + long rawContactId = ContentUris.parseId(context.getContentResolver().insert(ContactsContract.RawContacts.CONTENT_URI, values)); + Glide.with(context).asBitmap().load(contact.getAvatar()).override(200, 200).into(new SimpleTarget() { + @Override + public void onResourceReady(@androidx.annotation.NonNull Bitmap resource, @Nullable Transition transition) { + ContentResolver resolver = context.getContentResolver(); + ContentValues nameValues = new ContentValues(); + //add Name + nameValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); + nameValues.put(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/name"); + nameValues.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, contact.getName()); + resolver.insert(ContactsContract.Data.CONTENT_URI, nameValues); + + ContentValues photoValues = new ContentValues(); + photoValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + resource.compress(Bitmap.CompressFormat.PNG, 100, out); + photoValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE); + photoValues.put(ContactsContract.CommonDataKinds.Photo.PHOTO, out.toByteArray()); + resolver.insert(ContactsContract.Data.CONTENT_URI, photoValues); + + ContentValues phoneValues = new ContentValues(); + phoneValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); + phoneValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE); + phoneValues.put(ContactsContract.CommonDataKinds.Phone.NUMBER, contact.getMobile()); + phoneValues.put(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE); + resolver.insert(ContactsContract.Data.CONTENT_URI, phoneValues); + } + }); + } + } + emitter.onNext(1L); + } + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Observer() { + @Override + public void onSubscribe(@NonNull Disposable d) { + Log.e("saveContactPhone", "onSubscribe: "); + } + + @Override + public void onNext(@NonNull Long aLong) { + Log.e("saveContactPhone", "onNext: " + aLong); + } + + @Override + public void onError(@NonNull Throwable e) { + Log.e("saveContactPhone", "onError: " + e.getMessage()); + } + + @Override + public void onComplete() { + Log.e("saveContactPhone", "onComplete: "); + } + }); + } + + /** + * 添加联系人信息 + */ + public static void insertConstacts(Context context, String name, String phone, List list) { + long contactId = getContactId(context, name); + if (contactId == -1) { + ContentResolver resolver = context.getContentResolver(); + + //插入raw_contacts表,并获取_id属性 + ContentValues nameValues = new ContentValues(); + long rawContactId = ContentUris.parseId(resolver.insert(ContactsContract.RawContacts.CONTENT_URI, nameValues)); + + //插入data表 + //add Name + nameValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); + nameValues.put(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/name"); + nameValues.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, name); + resolver.insert(ContactsContract.Data.CONTENT_URI, nameValues); + + + try { + //写入头像 + Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.default_avatar); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); + ContentValues photoValues = new ContentValues(); + photoValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); + photoValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE); + photoValues.put(ContactsContract.CommonDataKinds.Photo.PHOTO, out.toByteArray()); + context.getContentResolver().insert(ContactsContract.Data.CONTENT_URI, photoValues); + out.flush(); + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + ContentValues phoneValues = new ContentValues(); + //写入手机号码 + phoneValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); + phoneValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE); + phoneValues.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone); + phoneValues.put(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE); + //插入data表 + context.getContentResolver().insert(ContactsContract.Data.CONTENT_URI, phoneValues); + } + } + + /** + * 判断某个手机号是否存在 + */ + public static boolean isThePhoneExist(Context context, String phoneNum) { + //uri= content://com.android.contacts/data/phones/filter/# + Cursor cursor = null; + try { + Uri uri = Uri.parse("content://com.android.contacts/data/phones/filter/" + phoneNum); + ContentResolver resolver = context.getContentResolver(); + cursor = resolver.query(uri, new String[]{ContactsContract.Data.DISPLAY_NAME}, + null, null, null); //从raw_contact表中返回display_name + for (String columnName : cursor.getColumnNames()) { + Log.e("isThePhoneExist: ", columnName); + } + while (cursor.moveToNext()) { + for (String columnName : cursor.getColumnNames()) { + Log.e("isThePhoneExist: ", columnName); + Log.e("isThePhoneExist: ", cursor.getColumnIndex(columnName) + ""); + if (cursor.getColumnIndex(columnName) != -1) { + Log.e("isThePhoneExist: getString = ", " - " + cursor.getString(cursor.getColumnIndex(columnName))); + } + } + } + if (cursor.moveToFirst()) { + //Log.i(TAG, "name=" + cursor.getString(0) + " , phoneNum = " + phoneNum); + cursor.close(); + return true; + } + } catch (Exception e) { + //Log.i(TAG, "163 e =" + e.getMessage()); + e.printStackTrace(); + } finally { + if (cursor != null) { + cursor.close(); + } + } + + return false; + } + + /** + * 获取所有联系人信息 + */ +// public static List getAllSysContacts(Context context) { +// List list = new ArrayList<>(); +// try { +// ContentResolver resolver = context.getContentResolver(); +// SysContactsListBean sysContactsListBean; +// Uri uri = Uri.parse("content://com.android.contacts/data/phones"); +// +// Cursor cursor1 = resolver.query(uri, +// new String[]{ContactsContract.Data.RAW_CONTACT_ID, +// ContactsContract.Data.DISPLAY_NAME, +// ContactsContract.CommonDataKinds.Phone.NUMBER}, +// null, null, null); +// +// while (cursor1.moveToNext()) { +// sysContactsListBean = new SysContactsListBean(); +// sysContactsListBean.setCustomerId(cursor1.getLong(0)); +// sysContactsListBean.setCustomerName(cursor1.getString(1)); +// sysContactsListBean.setPhoneNumber(cursor1.getString(2)); +// list.add(sysContactsListBean); +// } +// } catch (Exception e) { +// e.printStackTrace(); +// } +// +// if (!CollectorUtils.isEmpty(list)) { +// Collections.sort(list, new Comparator() { +// @Override +// public int compare(SysContactsListBean o1, SysContactsListBean o2) { +// return o2.getCustomerName().compareTo(o1.getCustomerName()); +// } +// }); +// } +// return list; +// } + public static void getContacts(Context context) { + //https://blog.csdn.net/luofeixiongsix/article/details/48849511 + //查询raw_contacts表获得联系人 + ContentResolver resolver = context.getContentResolver(); +// Uri uri = ContactsContract.Data.CONTENT_URI;//content://com.android.contacts/data 有电话+86 +// Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;//content://com.android.contacts/data/phones 有电话+86 +// Uri uri = ContactsContract.RawContacts.CONTENT_URI;//content://com.android.contacts/raw_contacts 无电话 + Uri uri = ContactsContract.Contacts.CONTENT_URI;//content://com.android.contacts/contacts 无电话 + + Log.e(TAG, "getContacts: " + uri.toString()); + //查询联系人 + Cursor cursor1 = resolver.query(ContactsContract.Data.CONTENT_URI, null, null, null, null); + Log.e("getContacts: cursor1 = ", Arrays.toString(cursor1.getColumnNames())); + Cursor cursor2 = resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null); + Log.e("getContacts: cursor2 = ", Arrays.toString(cursor1.getColumnNames())); + Cursor cursor3 = resolver.query(ContactsContract.RawContacts.CONTENT_URI, null, null, null, null); + Log.e("getContacts: cursor3 = ", Arrays.toString(cursor1.getColumnNames())); + Cursor cursor4 = resolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null); + Log.e("getContacts: cursor4 = ", Arrays.toString(cursor1.getColumnNames())); + +// while (cursor.moveToNext()) { +// String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); +// String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); +// Log.e(TAG, "getContacts: 联系人:" + name); +// Log.e(TAG, "getContacts: 电话:" + number); +// } +// for (String columnName : cursor.getColumnNames()) { +// Log.e("getContacts: ", columnName); +// } +// while (cursor.moveToNext()) { +// for (String columnName : cursor.getColumnNames()) { +// Log.e("getContacts: ", cursor.getColumnIndex(columnName) + "\t" + columnName); +// if (cursor.getColumnIndex(columnName) != -1) { +// Log.e("getContacts: getString =", " " + cursor.getString(cursor.getColumnIndex(columnName))); +// } +// } +// } +// cursor.close(); + } + + public static List getLocalContacts(Context context) { + ContentResolver resolver = context.getContentResolver(); + Uri uri = ContactsContract.Contacts.CONTENT_URI; + Cursor cursor = resolver.query(uri, null, null, null, null); + List contactIds = new ArrayList<>(); + while (cursor.moveToNext()) { + long id = cursor.getLong(cursor.getColumnIndexOrThrow("name_raw_contact_id")); + int in_phone = cursor.getInt(cursor.getColumnIndexOrThrow("indicate_phone_or_sim_contact")); + String display_name = cursor.getString(cursor.getColumnIndexOrThrow("display_name")); + String photo_uri = cursor.getString(cursor.getColumnIndexOrThrow("photo_uri")); + + ContactId contactId = new ContactId(id, in_phone, display_name, photo_uri); + contactIds.add(contactId); + } + cursor.close(); + List filter = contactIds.stream().filter(new Predicate() { + @Override + public boolean test(ContactId contactId) { + return contactId.getInPhone() == -1; + } + }).collect(Collectors.toList()); + Log.e(TAG, "getLocalContacts: " + filter); + return filter; + } + + + /** + * 判断某个手机号是否存在 + */ + public static boolean isExist(Context context, String phoneNum) { + //uri= content://com.android.contacts/data/phones/filter/# + Cursor cursor = null; + try { + Uri uri = Uri.parse("content://com.android.contacts/data/phones/filter/" + phoneNum); + ContentResolver resolver = context.getContentResolver(); + cursor = resolver.query(uri, new String[]{ContactsContract.Data.DISPLAY_NAME}, + null, null, null); //从raw_contact表中返回display_name + if (cursor.moveToFirst()) { + cursor.close(); + return true; + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (cursor != null) { + cursor.close(); + } + } + return false; + } + + /** + * @param context + * @param phoneNum + * @return 获取联系人id + */ + public static long getContactId(Context context, String phoneNum) { + Cursor cursor = null; + try { + Uri uri = Uri.parse("content://com.android.contacts/data/phones/filter/" + phoneNum); + ContentResolver resolver = context.getContentResolver(); + cursor = resolver.query(uri, null, null, null, null); //从raw_contact表中返回display_name + if (cursor.moveToFirst()) { + for (String columnName : cursor.getColumnNames()) { + Log.e("getContactId: ", cursor.getColumnIndex(columnName) + "\t" + columnName); + if (cursor.getColumnIndex(columnName) != -1) { + Log.e("getContactId: getString =", " " + cursor.getString(cursor.getColumnIndex(columnName))); + } + } + return cursor.getLong(cursor.getColumnIndexOrThrow(ContactsContract.Data.CONTACT_ID)); + } + cursor.close(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (cursor != null) { + cursor.close(); + } + } + return -1; + } + + +} diff --git a/app/src/main/java/com/xxpatx/os/utils/LocalContactUtils.java b/app/src/main/java/com/xxpatx/os/utils/LocalContactUtils.java new file mode 100644 index 0000000..51bfe37 --- /dev/null +++ b/app/src/main/java/com/xxpatx/os/utils/LocalContactUtils.java @@ -0,0 +1,84 @@ +package com.xxpatx.os.utils; + +import android.text.TextUtils; +import android.util.Log; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.tencent.mmkv.MMKV; +import com.xxpatx.os.bean.Contact; +import com.xxpatx.os.config.CommonConfig; +import com.xxpatx.os.gson.GsonUtils; + +import java.lang.reflect.Type; +import java.util.HashSet; +import java.util.Set; + +public class LocalContactUtils { + private static final String TAG = "LocalContactUtils"; + + private static MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); + + + public static Set getLocalContact() { + Set contacts = new HashSet<>(); + String jsonString = mMMKV.decodeString(CommonConfig.CONTACT_LOCAL_CACHE_KEY, ""); + if (TextUtils.isEmpty(jsonString)) { + return contacts; + } else { + Gson gson = new Gson(); + Type type = new TypeToken>() { + }.getType(); + try { + contacts = gson.fromJson(jsonString, type); + return contacts; + } catch (Exception e) { + Log.e(TAG, "getLocalContact: " + e.getMessage()); + } + } + return contacts; + } + + public static boolean addLocalContact(Contact contact) { + String jsonString = mMMKV.decodeString(CommonConfig.CONTACT_LOCAL_CACHE_KEY, ""); + if (TextUtils.isEmpty(jsonString)) { + Set contacts = new HashSet<>(); + contacts.add(contact); + return mMMKV.encode(CommonConfig.CONTACT_LOCAL_CACHE_KEY, GsonUtils.toJSONString(contacts)); + } else { + Gson gson = new Gson(); + Type type = new TypeToken>() { + }.getType(); + try { + Set contacts = gson.fromJson(jsonString, type); + if (contacts == null) { + contacts = new HashSet<>(); + } + contacts.add(contact); + return mMMKV.encode(CommonConfig.CONTACT_LOCAL_CACHE_KEY, GsonUtils.toJSONString(contacts)); + } catch (Exception e) { + Log.e(TAG, "addLocalContact: " + e.getMessage()); + mMMKV.encode(CommonConfig.CONTACT_LOCAL_CACHE_KEY, ""); + } + } + return false; + } + + + public static void delLocalContact(Contact contact) { + String jsonString = mMMKV.decodeString(CommonConfig.CONTACT_LOCAL_CACHE_KEY, ""); + if (TextUtils.isEmpty(jsonString)) { + return; + } + Gson gson = new Gson(); + Type type = new TypeToken>() { + }.getType(); + try { + Set contacts = gson.fromJson(jsonString, type); + contacts.remove(contact); + } catch (Exception e) { + Log.e(TAG, "getLocalContact: " + e.getMessage()); + } + } + +} diff --git a/app/src/main/java/com/xxpatx/os/utils/MobileNetworkUtils.java b/app/src/main/java/com/xxpatx/os/utils/MobileNetworkUtils.java new file mode 100644 index 0000000..18fdeaa --- /dev/null +++ b/app/src/main/java/com/xxpatx/os/utils/MobileNetworkUtils.java @@ -0,0 +1,47 @@ +package com.xxpatx.os.utils; + +import android.content.Context; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; + +import java.util.List; + +public class MobileNetworkUtils { + public static CharSequence getCurrentCarrierNameForDisplay(Context context, int subId) { + final SubscriptionManager sm = context.getSystemService(SubscriptionManager.class); + if (sm != null) { + final SubscriptionInfo subInfo = getSubscriptionInfo(sm, subId); + if (subInfo != null) { + return subInfo.getCarrierName(); + } + } + return getOperatorNameFromTelephonyManager(context); + } + + private static SubscriptionInfo getSubscriptionInfo(SubscriptionManager subManager, + int subId) { + List subInfos = subManager.getAccessibleSubscriptionInfoList(); + if (subInfos == null) { + subInfos = subManager.getActiveSubscriptionInfoList(); + } + if (subInfos == null) { + return null; + } + for (SubscriptionInfo subInfo : subInfos) { + if (subInfo.getSubscriptionId() == subId) { + return subInfo; + } + } + return null; + } + + private static String getOperatorNameFromTelephonyManager(Context context) { + final TelephonyManager tm = + (TelephonyManager) context.getSystemService(TelephonyManager.class); + if (tm == null) { + return null; + } + return tm.getNetworkOperatorName(); + } +} diff --git a/app/src/main/java/com/xxpatx/os/utils/MobileNumberUtils.java b/app/src/main/java/com/xxpatx/os/utils/MobileNumberUtils.java new file mode 100644 index 0000000..24d52e9 --- /dev/null +++ b/app/src/main/java/com/xxpatx/os/utils/MobileNumberUtils.java @@ -0,0 +1,56 @@ +package com.xxpatx.os.utils; + +import com.google.i18n.phonenumbers.NumberParseException; +import com.google.i18n.phonenumbers.PhoneNumberToCarrierMapper; +import com.google.i18n.phonenumbers.PhoneNumberUtil; +import com.google.i18n.phonenumbers.Phonenumber; +import com.google.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder; + +import java.util.Locale; + +public class MobileNumberUtils { + private static final String TAG = "MobileNumberUtils"; + private static final String LANGUAGE = "CN"; + + //获取手机号码运营商 + public static String getCarrier(String phoneNumber) { + PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance(); + PhoneNumberToCarrierMapper carrierMapper = PhoneNumberToCarrierMapper.getInstance(); + Phonenumber.PhoneNumber referencePhonenumber = new Phonenumber.PhoneNumber(); + try { + referencePhonenumber = phoneNumberUtil.parse(phoneNumber, LANGUAGE); + } catch (NumberParseException e) { + e.printStackTrace(); + } + //返回结果只有英文,自己转成成中文 + String carrierEn = carrierMapper.getNameForNumber(referencePhonenumber, Locale.CHINA); +// if (Locale.CHINA.getCountry().equals(Locale.getDefault().getCountry())) { +// switch (carrierEn) { +// case "China Mobile": +// return "中国移动"; +// case "China Unicom": +// return "中国联通"; +// case "China Telecom": +// return "中国电信"; +// default: +// break; +// } +// } + return carrierEn; + } + + //获取手机号码归属地 + public static String getGeo(String phoneNumber) { + PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance(); + PhoneNumberOfflineGeocoder geocoder = PhoneNumberOfflineGeocoder.getInstance(); + Phonenumber.PhoneNumber referencePhonenumber = null; + try { + referencePhonenumber = phoneNumberUtil.parse(phoneNumber, LANGUAGE); + //手机号码归属城市 referenceRegion + return geocoder.getDescriptionForNumber(referencePhonenumber, Locale.CHINA); + } catch (NumberParseException e) { + e.printStackTrace(); + } + return "未知"; + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable-hdpi/default_avatar.png b/app/src/main/res/drawable-hdpi/default_avatar.png index cf4cb6a2f82f8ccfdbb9cc11d798acbcbd4078f7..5a952272bcd7bb1f2eef49b1b1204f89c129509d 100644 GIT binary patch literal 10802 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 literal 8557 zcmV-zA(GySP)PyA07*naRCr$PeGQZx)ph2*)!oxQT|J|os-77kX+R^0O-N7<8@$$d4JKY#1%hpf zAK4q@c;g>}f%sTbj2(l$U>k2FB8dYyCb8liC-PxE*a*U3SXkR2Kq4ToEv?0`GzudW zM$=W(Gt<@G)7@2X_w`JTdS<%2e!8l={@yu02Kv4G?)~2T=Dz#ht@jXuVjBXP4Czr6 zX#fCM5`;5|49hWuy@V*e2*?2;Ymi}L1V|?k(g}c_v&xV>Am$M;PZXs9h{p&J5s?{3 zf~N>k#$<$lCqUkbGUd@^GC5(J5k41;KDWPV^QWgtZ@FCfZADSui-^BP2ssCkTnvDG z)4~l2AOa!y93s92guKZx%TL$?OBzOrSMsH+fB-Yi%uLmY{86sQ@1p3@O;DB(7 z5O@$2`RVCt;iFPUkiPlIsxRDhG{)V^tvp z9s+`UQmOb}$0}))V!k1WAPC)v(HjWhp8>!coAC6y*kcIfZwZ;XpXd20uNzD4^E^R> zTy6mJ$`=9fCP0`wi1WkH+GisnkVhcjg-+%kKJM+n-m?Umn8>e|iLw;|Znkf#1d<8B zeIaCa^z?Kd_aYjeBgoiTwm%$(uK?m^FAkVvZ6ah>xeVL-`x9f1)zO4vo+5}KNM9!a zUqghL2`qvp%_C3%0^i|z_FK)1a)ty?5F|a3{Szd3I{*wi16JQ_F+z~sp6W^bquY^i zH9^KR((f}0d>a7%(Cq`^QT_i#Vc@pjBzw@U_PLfIq9}cv0Nm}_te z*>wa-X9}01Qr-c8YuyOAe|08@2ov6#O2(dX69%p#NLtKpL%>_y1aP41LIenq?I|wt z6}RBx3WAJ}OBXUBxDNo{?-pPKJsTqMk|M*+y}j%!PRGgd1QA8)CVCj>mrkI}*ZZ_Vf^(keGP|=zkwb zkjlTWVwL%SUe~A0{A~`~%0uKZl63hB>S_k|6Jj+4CcaKL7$3 zIt(wv>QF)q9}B~QF{VNas|zL3P`?b6IFvL){f?Yxjm<`Qr9{YQmUD^2R^!vGJjW3v zJ(0T#3H%U%B$_p@UF}s;{Nz}8frn^?K9n%jllIvLUyNH6Abr{ zF|c)iwB_Ey$Rwg<*?fKpu06YKq*;6F60(cu6I$NG=s=fJ zTlRO@5?dJ2!$@69lpRAej`t`8@JA^wew`89?5NvTf{cui*s^80pAvv8>@dzwnRGd| z>rlkbE^L!cNkW%hTh^9r>%$_!PfSh5uO1x4wp~rsHiAq}PI6^A^spOm5ZUx})Ky-i zQH(NtX~E%vJlPix$s3j}TP9jWIzwXGLXgTwO&$1{+qQAtxTMmW4X*Ge2(OfjsxiFaLWiHh|=TW!rdyb1mt>Y=LV7hz!N;j z{*hjrnyJ?kf~3XV9(3V3hnu%@o0$}pBrk5rHzUIKwg|X~zD?!|xTjjT<)#6D;y@>PANa$;IxP|BAyX+!~Z3LOk z$|XOwOL05pK6o@-Ng?rjp=tEPm(4gH1_ohcPY?UB9g5mQkjnQQz`W?zl=XoTm)v&Np#mFtkA+jrEnWlwU*fs=I>!@4 z5TqZt`7_xMJlyH%v~{Z|fR;R$Z2QidL0~t}v!B5U+EZuK*=5)< zR5UWL+Ey0krZFn7bbNVV<^k_&oz62G!y4p~MJ zL6i=-{obk|IIlmYToSCS2E4#?EPcbi>0${%yl~@Tu)JzKmt1jhSY?7$_W?IeklDL( z<)@pbYMTXY-QNMjN5h7+_+Wnt?j6d)14pB<{jj&UhTXH9b$SRHWG?CH>3r7Epf0N1 zG(iM0`)e;;ByDu}ijw1zODE^y+;%ALX_B}t4w>E5f`ciJZR&2drU>GtCkTwhA7`>M z{GSaIOa3rDKNf+HJ+rKtKUj`#>#{gx7E1!R^E`WpSzt{QWbW}A$6r3I2Ns55oGR7z#p3F(1~q~1n7;t$70*)lIzd+Cp_a# z+!&~~lP1K=fZ;%0&X;OjgWKi77G(5IFG z;hRP~a&mpWJ6JR@rvbW|LWx4X3H2&&EpFV&s>T|De3j!8+l=6E$b(d_?1|uU0B^lR zu9qOfC(}l-tsBI0JT!V;PRk6(|E>^ncQ2pVtVdB@1QBw%0Vt5S^$2uh9W8=b zy}UAlkxH7kvkXwe(EnGw1BGl$afz>L%~?%?2!hZ} zAoeaIc=P6F0MH@`Jvpad#7l*GrVAeKF=Udv&p{TNN(e}bEG_Tr>uZ=6p-B)?%-%u( z4ecEpLfrwCwNg!*Isb5gd39AV8i#m^AeChnkXw4W#9fWXNRuFfC>;fWHBKJrW_8md zNGk&LW^FLj-Y7xb^liExJP`0G$HmVvO^|dtw-FiQsAqZg7;dyqqiaSNfA?Wjwv+1{ z4bNNBqX+ioaB3$L&L)@C~ z-jE;!9!l}-)rJXj^5j%18VilvJxETGUoTMi;tp0d=KPORx`$x@hP=byGt_O$f{HT* zxp&2i?wWZ-bv=kEN;eXK@0#n@O<}8b#ofmi$m;6F)^p5^{w=thj=YOUx*|*%yWsgx(SlWCAB^=-OWw|J{@qDhiB?@u&@&Wi#1 z0)r8^@;&jLHI`pXaS_Dqb8dWWvl(*Gwu^VOyu8I*jg_9=EU(5Nr*Rs`0M1qeWBa4_tAoQjJ7dAdIo=S)DMRdUZg6$2gAtNL_-AjEuy(m-RXO z{a?M-Hd*hN@1Fj+s7X<-YQFefWY_yLuxa(|lpvuFAYsoYbysYhnmpAxI5;?;y1ihD zl}=B644IHEk0H8hmH>ubY!h@Fxr<*ciP$H~eQLd~<6nR4`)%}R7 z{6d0=(tY$)(Uq22taK;I1Jdh{eOPF~zWq=q?4Mg&%|n0;C>>p%MucXMq&Rl|)A9ub z5hkQDAkgP&1aunbXM$9gQ-r`wsk{U&CFIeOGusGQ#kq7VYb#Y-$71nCu0AnxIM;-6aRK}3lrYOQYJ z9CUP7ekX{mC{QXd^q_v&=AE0A$9aw&oF~ZiH0dpta%4@dPvWI#PN|Ng0^^^2! zTNz|N)YH@XT!kQlAblPX{>~2ruh&KzpwnfRUrQ~5cz%m_!CwQop6A)`&k{t;Z3iG< z_UmM*Ww+l6LIZRfsH??C@khmE1iXvm;}6assBp1o?$gJjbH$os~4ord}~AB)0MC@ze@Dk&y-`r>Sz5W2V;5qK_< znj?th&!i)b;q&WO@8TUSzb&uSX~AkAO+E2yq27afk)Tu~K&EMs zPSZAnKOGt~154A;84h`J{!q_BJxEZNAW#knqIXu53($dO2s7SG`3}5C-AO2VzpBn|^9Bcn`bWK-2at=<5e~UN775NhbPPK^C=60fbO5DRk&96A zB8aZ{A~P5T8N9hGXJACNsy-PcD{4Vh{ku{IC`pz7LU*&MCvjsbDhTCJaB8kKcC*Qf zLAtvZPN#n-N+nRIMV4iVhC|R1an~BYR4zjSq7aHi_1o>&nn=^#)6&SdP)u`t+ zs>KHc(FZmmus5YXe;|sv?Id_WM;p0v0YF~(>Qp0=P=S;vbcc&Bz3eKK7&9})x?Uq7 zh#BPZE?#{=CwPKQCxG-6oyH-H3MC0$ZdE@xv$zfo;A7DaONnBH7(tM(bBxt$r0NN_ z;1N$9;8mrW)T&JhLW$CdB&tVgcIj0~7nv!-Owsr^^1%{I$5>O(U;T(@@C>vL&=$oX zwOY{h6B@j0kwk4vmtS=06^Vo^=_>T!)wGrB7iK9T6q&wTUS6Y>bM*LAvccV+LR$&nB;vf`h z-S%^`a5;w3?IpW2!=$!SZjNHh)+Hfh!z&$kOP zp>vYS*wF<9sl*|{4JS(n-=z0gEXYaTBoqokw7b`5&pPYmHNq*rhn>&nTyTF)T9ocX zfSYuR@<%lT!MPqloD!SFyE%Yxu*_OKW(457DUQ8a{qGA2lAih)GNFg{476|R%uHHt zD)lDnSBaJj*D0_w9tVte2VP_mUPO^Mq*C3F)FjBr$VjYvSzlf!#@?@HvG;J&vMUma zK%}!P@FI%_Woq(N=iuPre5U7;D*{DPdXxZM>HXQS!$KRpN3CAl6`xeR2wi3cORdVV zAi!fB$9|-GjFu22ElOWNfWOf(eY{=8X7AwyqI5)~5T;3#p=dx7n&1v_V~S(H*N`BB zkQ)FbZ+mt2pU5JNN(d*iv$wR-Gaann-EC{6N$xR)pmF-9@+VuF4%RX{kK1A%@wugweLhWBa^mKfZWo{CGKj9 zAc7!t1H|411k2y-+CRp?*8Nc!&1&-0EC!agb9>OP_q5zn&@2>Yr*;LR=ADkea-rMSdbwdRDbKixtu zHvom^6Bt{0j-h3EGrzknCZXim^ehjflD=HDX0T~x+h&$tiVQ1zd)bp(`syMG6_OUE zdl6v%Q$#Jni+^`RoMFT!x9o0Fa%_1y+0w&6BRE=7*C6fPA7?%mQ`9~L4T)28NRfjeL_!gfal{U^vKjrkhxf7Cm`x#9+ zuTcw7N|I~N$~!Lm*NwYXdM2z^9W8G{0Jo>O_*b;#sJ|N`$kH*w23hZVTjldkZg1j;cth{RR+n5aZaAHhjX=K2aS20bdWH#7A&Dr_Z$&DX`>Mr{UZW9F zHlH`$(scR2Yt$Z%)1Cxh=ehW~MhHghnj%PgBKs#u@P}$=e)bw#%PzZ#5_pZqQ=X=) z3|}@wPtE_hmrFd*5`xTmkta`N@ekd4;Aa%5X6ma;uT86SmL)w>kMWa#>-5~&Ml(3q zYCC#ej^0-CXo_P$YDTsx4?;T{&q%-jiKn8k=)DNRjB;LULy58~F2lOsA`JD-z)-LL z7ow=ApgU8eQrH!VhE5o};mZn@TUZdnYdJ1@*o;o52|_#j-)|P~{m7G1(}#y<6!uaZ zN*G#HqO6QBO{qUH-r?y(KD)+KU0VHr_i;PVvv-*7rzwKaPInxbdHd`C+A&~OfuLQ0 zfE@_TEv*iwIQGJ3udznr4<6)$#Lv$&1!22L8uZxK3q8Rr$3MWcC!iv?E?hN zB0$qj<_-p5;5qh#X6013vy33)|g%y?& zgevpctNCy2IuyAhh)1j#nm~pT1k@9A1o#5SvG*Hk+o-Ok1fd=6J6afC|9JFTs}v06 z1PEv$Fu$}Su$$-E&ui5;^1I~(A%rkH_7{(SWcw4*y;EUka$ZRYWpPBGs4#?xx_19?p&Fc)`BK_ooHcSVmhEpU+lTL?n?c;Im1 zhTTWP-wWaqtA{L*ry{U(s93oWn?cyv)5F^G_^fRNp+-FMTK?OcUyR-woR0*P+G+$g zuPs64o-Be}cs{<%YWb=>TM0rt`T3jq-8bxuel{S9T|?+Uxu^52$WES5Y_$^s+X*ta zv$c2qnhCC84T8Ti0*^4w%*Z2jQiV4)Qjwu~Uu zn90dW?(6>=KfL?ZuyqgOwF0G}kG3OF+0I?Aypqe7*AEOV6I-QFO9?`aqE`pqc_{kJ zfF!LNV)yr>lA~NCSN8WO##(7n%Lx*Yq?N<#{=V8t4vJn|2tqp^9i8O1y~_Ub2d{#CfB%Mk(a!}Z?pj0U z-QQbzLarA@#}I@n{P>alx3(OJ-WHHV@9=r2-uxAzHs@9uMa)qIq00ZyLxt;ql@8r= z&;JcajT#fwcP9dKiHB1R!l&)Z$~B9O;|M|xx#RiLWkL@AVfSm{m1YeK+PMP(^RKL4ZalO{%cW9XMG&g~_Ycl|{IJ06 z+WlJij8=gZ^kW_Y)oZNlR}O}d+1l(~x#l%7*V1(aQO$hnjl!-kKHu@Bz?+yG_*S&C z>Is5Y=vuE2*Aj#p^j9yI)*qWB_w0Ey5`2W!&RJBWZyJbiUR!#Uf$*(nAFpwONvUS(0aQQhK#8$&P(F56mwcO%Zly{-=>#x-lo(pd@RTSlK+b}}}eA&3g{#)ru7os%qo=bw*c26lvjK3Z#*Q_bS}-p^{TCA?FtB@fpVfoqW}N_8%ab#RNUIX zFTA&h4d0%bVJ>=e5?%d;BPGOzy>bBo4&ObLzjJz8c&eRO>X@Cht!x(%#30&#`Loi8 z#tP)q@8&}nXN$1%m2oC!P((|rQ!>ykSw04t-{r{59~~_HI0T_zB$KhDEm6QDodg7F zav-OtN$&%16#w9*Q+WBYsnCVfMOc1hf=M+g)=+awhBM-%^qyt%(F;=Wt4(W4KVul? z^>8@$YF8JI8xj^&UVuPA5Su0h%?*1GFQ0!N2fd{t`Q7dqE}JUCS(zf95oJg$!eECm z0~`auoXlb5q=c0RPgf-fAq2<@mXrwr<%~F)>4_@iMH%ul3M26yPx)l1W5CRCr$Pom*DhFc5|%1M6s5NnstNr|wvDLVI3q<5yr&{ zwly;zjmEZk={b!o&CI8Nbd6(8{!ejLAThb%G2e15p+MW#(jsWFX2Spv;`C zkPJjw1eBSR6_SA{i-0n7vO+QtWf5RD=k)aJY`tC|?d|P7+}wO$VykCW$fr-|r_R}# zbM`|~Z2sKd{<^^$H01KH{$G0^oHI*ni?hYz`vb-&79hS?JUwmhtu?cHqU-S8e zpJ-gZbrySbclYBA;#VvfBp~zo`Mojr2x7SAfu0H^CJZ!?lanvk*1FR^FtnsE3Xs?^ zP(Y~TMi;!4=?e=)0tN~Qr#dt&9x*_qV4#4^=NFD6RW;R-LMc!nk}$wP+E=1{h@_!F zfJnmt1BsQcc5zBV2?0p~0|BI5(emk`y(TFrT|iR700W6Rexw>x+JK~l0Rj?Zx(iWS zY^5{-@n<;CXE`tD8TCKvMj^z{cWs3pN(IE*6%)#PATMV(@6xg)4AlzRUeV)E-ohPi zdoQc0yflnK0I8I6clYx!6lBcjy9!B}rnb^!$cdCGYuh&b>06g?T|j)QeBM9>pM^gw z>Cu?t0ed>wsLG)L;Z7eRg@od=`?-h6q2k~Hl@^6?1BtQwu)kj%_Bjp)jUjyxq_91$ ztvrXb^}4zJ5>tqGqB?u#3NE&u=eu6u(>zSw)ulFnmkje~E%s3KjMx`m}UABOM2&OI64Z@)W8fqsPh+x{vK%n$uY&X1b+dV86zmB0cjjbF? zArfh8$}=qRI;7qUC2pW_ZzK*$MJRV8kbp?3WQvPK zG7yOwS2-B-o~3Ia;K@L|V-hnipSlhXisQaVquN2BHy4{6l2XZ(p!8CRha|IlFK;n& z-0IM-l-@wvUaJwqac-rQ3O6%r*E8q#mK^u0`ug1)h&Mngb@{fqpfugfqP{?&FNFH@ zEI`U>u*`W$1%>)jo3it)uiL)b9rJ=W1RNWXkfRV|2xU-P^-vpALSX|Uow_P`(BMH~ zVCyAxqRvNHfk@7}cD?EXgN2b23Of*~1!G$`E5W#>bA|u+@wMFlcbZ^(G*p4udfUoJ z0U|jS<_bhtJ(5tO0I9~4rq}*S1TaP$Qc$7+Nj1pFfFU`iLWG=XM1Yh^kz2{f0#UCw78Efc849eMhyr=tg!FHuBd-f!ZpoG6R*EQ) z+RQ6MY3VcKKq@=(n9@un8IU}tCjz_yK(^DEyX5*k6WY*Sf2u!>K?B)VOzPPf4~$U&NvW6^ zm$L^qhJ;ef+iUyG=f1y{6=;jJ5)auwIv{n2-%cpqM=m&i1%7Z~uVQ`0Zqz__;rL&1 z-fW7G)^27{K-JY$W9%QU7hQI#WHLZ@gW%Ou{a^Jhs(P7J->8}_kWem9^?3%8D)KGu yW*`}evIrPx(cS%G+RCr$PoNZd-Fc8O+BKN?Pi07dDEPl}S7V0fr{Q&z|&LMjexrfj!v74oI z+h&rPNm~eC&CBGM|4cGTsub~uA|O$G00nix-%ifgXu&mv2p1b zVqC~FX?puEgq-R=^;lM`mrF?hUI5FT)pYU%ojr(wA<9(8qm*2(RzGhbeLDlRz%zXY zhp5BJcAL+oRGYWA*Ck}|y3GIMQR^&wy@u9z0tkfH3B+UDl50pz#=_GB1p}c*x*3V2|EX?i~uB7X-$s}>6a(FGtCJO_Y>6{@cgp#Ul2fdI~CKNkDI zJof}wuEwfF2taChK!A52Qi`t)Gr^Ayq75JiJS)I&-)`qh$pry8b|tm}W!m|Q&-1oT(l@wwfKyG*dfQP-6D;;2YGKf`x`cS~+wuLQj83xe)_KkGeC}@Ko zv zt+{X@7j`;-YZ@fpR@ebVwf|lKqzLe{8E(Vxat2t2XaR`Mfk9s3YkawJz3I<$`QVc^ zVd8KrK*Bt@5v0rUIcPQl7qYqV(Y*(?ZJ3#-w#BzS3qaI4V5?210MELO`Fqds;X6C+ z_54;n9$WldwI1nnH7^FZAlKOt+R8$BCs3B6T5b}b+FEWLz$HTUEMkVAz-cV%aQ9Q(o{XC}@7nf8Uc*+K3Ny#Q(zetzcfK8Kg>am)V#sCa&- zuJ&r4?5{J|>W=xRxaOZ>2^XA#(jW!oN=}fK$c- z5e<+d%>x4QDMk*F1R!TT^r2*_Lkf`$;JHF11<1Xq5UY_yiKGAziwfV$+&75i01ZT* zOobR<(Hu&NECA05A{)SSfyfH*08u>n%V7vUt!`{p!*?wl!6B zih0#Wda1ywk>;Q3P-ZezUkMBXVA-{IyWJF{O6Zj-ROf>NSRPcNN->dgH_7wlYX806 zv}r43pb3$zv7glr(uJXFFk2#19-2+Kw14sdF))1U4%UTz2Fp<=0z^`c03$#o0c7>4 bKqSCF2Efd3Cys~I00000NkvXXu0mjfJ5w#U diff --git a/app/src/main/res/drawable-hdpi/icon_call_missed.png b/app/src/main/res/drawable-hdpi/icon_call_missed.png index 03305eb416d47762ae2509f46d99ea505d7d525c..ae7a67f744d9a649213a7da0da7fab47a99c007a 100644 GIT binary patch literal 1558 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 diff --git a/app/src/main/res/drawable-hdpi/icon_call_outgoing.png b/app/src/main/res/drawable-hdpi/icon_call_outgoing.png index c1392c1ae5a2651b48d2aee47f8b5342b6d205e2..12dd83c6129c3fc1284200f9bc67b62c015c96a7 100644 GIT binary patch literal 1429 zcmV;G1#0?Px)Pf0{URCr$PolREUFbsg@fqQgV_Ce18oTRYpE8L;w4$#*fa1!7Qh1X@%dvwOF zG7XanNo?7&Y$wik99z<7$?{J;3d)m|1yoYf6k(tMNvsnTAO%Qb7D!B-;voe{VirhD zoZ=w`NMaU9Oq}8&1xR8RNKBmKAq7Zc7U+uea{1L;LcOlw=-Zu3J-iC1KP_Rs_t2wE1)%QX@Y=uEQ8lBM|8aQ& z3ynH3KI)39yFU=KB6&dBbSm@^#zz{68AhXAMo{+8>X2*yoD7FKNg(zx@`7Ry0-zo< z)$Q;rQb6c1rU%6c167s);vZ}C2w!8KG7O{I4PO#Ko-ZCi0mea<0T5XzzA$D2WCLZb&f&xLqm5fRmEnL<1HtWI{pD2#Tuvcw z<(fCB;)Sf;$)m$|s1+P87}(hE!fT){+%$jytko&ImvF*p=p&Rr`%70@bMcUWk$~?0 zqaHHU(YV{Oa?@?0`2y+2d$dVp7$|}ni2sHZv5yQCU#t$`{de2|;(5AyB>=+?$R?eW zoT>L2{05I2AlFtf; zeaMQF`pyo9JrMbUeU>onfk=-d+!>Wjcx7qDZVah2t5UIg^re5L(qKI2H8=g3`!^E^LhlO8<63_+zUEdOSd6_e(ZVTrah$PnzuAj(pnf zUN?iQ@BuL^${=pH3gS||y^PVI@BxtzT@4*OBB`MQRgW{jH-%c?Y|wkOod?qE?R_H*5ZP3i$rPj2h(QShWILYpUHd0tzz}VS zKnVvV-XI?WhS-=A76_^#vv(2_f_?J9AVqi}aWgLl6ZKXREPTlTsWUIJlaHYwhL$oGj-btQ=q)DR6Gx9 zM493|#p7<4_&}z*%;I=AOBz6?2*vSkmUMtjE(0U(X8sT1X#ts52Ih1OHccRnw~QY~ zu+8P-lyf}2pDvKRLzwfSzO;cfeLJ@uNXG#k)y)G)PTt}HBQGF3-je8Am?w-pf$WAc zTs7wUKNG*fp8-bRK-yqpXJbq-rU4|b$FLvH&Sy}!4JggEeV{%v{8!dUt8O!KlN!?l z($=`yq15`A5zZYfR#%}uv70uKE*!k@68}j}!;%1~g8FExdZPZXt;rkuexDDJuJ-6X z&4V{l<5yJeC(;*H^9AD4eFferKm?3TX`%q3SWBR60U}^zN)rVL#aaSo3lIS#Q<^A1 jDAp1vTYv}{nbO2xgQdoU7lUCp00000NkvXXu0mjf{U?Tz literal 1181 zcmV;O1Y-M%P)Px(S4l)cRCr$PoL^eoFbu%$ti4A$32=^P-^v4wTQF|H;Gx||dJejiz&&W=p(Tb; zl32E+{AtZ=EnEIsl4aRJtN2GEph?nESVe$DBZvSaKq3L6c(lL>kVt?i9xX5eBoZKs zM+@u|;9_w-gYmapHOsQQ4N(_1@ie_YGbUeZt^Vuh^Jk2D(OUhO@ z#tz~;Fh;r1HuL$c^I-?LAM;$wmB06H ziA{RCjDZy(F{TU-;#YWP13RxVdMOeEU8{w##=$_WgO_Cw=ik3yE<^xQQ-TWI-XS)@ z104kcUOk5-q(e(_+Te)+++P)ht74jTZiy~#XPI@(CX-~fTHU>j2B1& zVQ3)Z#zzDYAPS^2VpW(>R}7VNy0C;5jy?CQ!bSlE0shV8Fx(goko$h*P^=IQP{Ine zHW!bN53A7lGXyzLfV?RUSmAVZ# zaqBzvc4u*bmD37*0PTBv=#ZTq%I~tP6M$AVsp11CW#0;UrvS&3Y~!u4bAV&mTqcv` z1Y3;l1>n%*BKBizm`85_KjLEezbN61eU-kvcH-Uv4$B(-?_>r6(5`;J#Rz|RgK*(G zH|!o{7`(v(v`~vmxB4M<=TeU274!BNd{8djv?-JqNUSFru1_pR0k9e);lB}4tP*== zirwKy1+Y4)!Yb8F>)kBRlhe0PRL`1TN?2%0B^&NfD@F^j&E4&9*wX~o+UnXJ96$<; v5ZMSYgjz)=0*nBO1c>6%0wX{o0iyUn@5ae*ca5xp00000NkvXXu0mjfijoW% diff --git a/app/src/main/res/drawable-hdpi/icon_call_phone.png b/app/src/main/res/drawable-hdpi/icon_call_phone.png new file mode 100644 index 0000000000000000000000000000000000000000..481115ba268d502278abe8cc935065d55bfc5a3c GIT binary patch literal 12340 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 XkLU7kZCSF>00000NkvXXu0mjfaU;l& literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-hdpi/icon_unselect.png b/app/src/main/res/drawable-hdpi/icon_unselect.png new file mode 100644 index 0000000000000000000000000000000000000000..2826cb1527e9654422b1e9daa534dcaf65380657 GIT binary patch literal 1151 zcmV-_1c3XAP)Px(IY~r8RCr$Pn^AJ&FbsxcCY)pGGhv1s6i%|`BwbEY_9Ue@V21Eq_8bJf5##Ix zN3v|kCdmRX?LY|97wMPmC?a|Amw0ghlP7>@1l)nZd_JEPMezndC8hi!gqTPvC+hDJ z-WNg~rIgqIN2Adf{GaFfHnw@uQlN5LO8FBU^>IW2*$N?k$AS?YK$c~{$_xGOx7!^dRlv!jD6X0S|6eYb z*YdclW>7BxF&d55`~7~?CLCG;FkSWPN`b>ZMgo}g`Mj`zvCi|{J1x8cWLfr^B*}uH z-^wWcoV6-~YD-yBHiyGuLHGWi$OtfvBw$!k@MR;&zB(_@;L{k4KA8s1%Ocd zPMxm>8xderJ6(+E@>snBV1iDq1_v*MwwpQ>09^+Lmj)f&bM06@^i1>WlegtntQ)&Qs}jIK1nFcre)eO;5R0l=3`lUY79dcNXFjO{=~ zI`=A005}Nc@Z@$p9Km|cP0MQs-4L!}x{ho>2S4SQ;kC)kv=C`2#Dpr_#+U0_rDI`3MG5gVWdCtYE0>aiqrO;ZYj(SPeTaAEGO5 z4WJ59HQN4e@!Wu~_FcahI>mwjs667v+S_;@+VRSkpaAL))?mDv(!&I$avIVn)&eXV z03Am4^as3edTg%D>pyTznx>mr + @@ -12,11 +13,11 @@ + android:layout_height="match_parent" + android:background="#FAF8F8"> - - + app:layout_constraintBottom_toTopOf="@+id/cl_bottom" + app:layout_constraintTop_toBottomOf="@+id/cl_exit" + tools:layout_editor_absoluteX="16dp"> + android:layout_height="wrap_content" + android:orientation="vertical"> - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -296,5 +395,12 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_add_wechat_contact.xml b/app/src/main/res/layout/activity_add_wechat_contact.xml deleted file mode 100644 index 20e9fdf..0000000 --- a/app/src/main/res/layout/activity_add_wechat_contact.xml +++ /dev/null @@ -1,405 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_contact.xml b/app/src/main/res/layout/activity_contact_list.xml similarity index 97% rename from app/src/main/res/layout/activity_contact.xml rename to app/src/main/res/layout/activity_contact_list.xml index 37c3107..c3581b1 100644 --- a/app/src/main/res/layout/activity_contact.xml +++ b/app/src/main/res/layout/activity_contact_list.xml @@ -2,13 +2,13 @@ + tools:context=".activity.contact.ContactListActivity"> + type="com.xxpatx.os.activity.contact.ContactListActivity.BtnClick" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_call_phone.xml b/app/src/main/res/layout/dialog_call_phone.xml new file mode 100644 index 0000000..3f70ef6 --- /dev/null +++ b/app/src/main/res/layout/dialog_call_phone.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_call_record.xml b/app/src/main/res/layout/item_call_record.xml index 64ee993..5a55893 100644 --- a/app/src/main/res/layout/item_call_record.xml +++ b/app/src/main/res/layout/item_call_record.xml @@ -40,10 +40,11 @@ @@ -62,31 +63,33 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="4dp" - android:textColor="@color/noti_font_color" - android:textSize="14sp" + android:textColor="@color/black" + android:textSize="16sp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tv_name" tools:text="12345665432" /> - - + + @@ -13,48 +14,55 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> - + android:layout_weight="2"> + - - - - - - + + android:textSize="16sp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@+id/iv_card" + tools:text="1234567890" /> + - + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index d1734ea..1e88e32 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -109,6 +109,8 @@ @android:color/transparent true + + true @null diff --git a/build.gradle b/build.gradle index ac3b968..c504270 100644 --- a/build.gradle +++ b/build.gradle @@ -7,8 +7,8 @@ buildscript { mavenCentral() maven { url "https://jitpack.io" } maven { url 'https://developer.huawei.com/repo/' } - maven { url 'https://maven.aliyun.com/repository/central/' } maven { url 'https://maven.aliyun.com/repository/public/' } + maven { url 'https://maven.aliyun.com/repository/central/' } } dependencies { classpath 'com.android.tools.build:gradle:3.6.4' @@ -29,8 +29,8 @@ allprojects { mavenCentral() maven { url "https://jitpack.io" } maven { url 'https://developer.huawei.com/repo/' } - maven { url 'https://maven.aliyun.com/repository/central/' } maven { url 'https://maven.aliyun.com/repository/public/' } + maven { url 'https://maven.aliyun.com/repository/central/' } } gradle.projectsEvaluated { tasks.withType(JavaCompile) {