diff --git a/app/build.gradle b/app/build.gradle index 7b15d1b..08d2729 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,8 +15,8 @@ android { applicationId "com.xxpatx.os" minSdkVersion 24 targetSdkVersion 29 - versionCode 1055 - versionName "1.5.5" + versionCode 1056 + versionName "1.5.6" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/com/xxpatx/os/activity/control/ControlActivity.java b/app/src/main/java/com/xxpatx/os/activity/control/ControlActivity.java index 3f318b0..69c7fb8 100644 --- a/app/src/main/java/com/xxpatx/os/activity/control/ControlActivity.java +++ b/app/src/main/java/com/xxpatx/os/activity/control/ControlActivity.java @@ -29,7 +29,6 @@ import com.xxpatx.os.R; import com.xxpatx.os.base.mvvm.BaseMvvmActivity; import com.xxpatx.os.config.CommonConfig; import com.xxpatx.os.databinding.ActivityControlBinding; -import com.xxpatx.os.manager.AmapManager; import com.xxpatx.os.utils.BrightnessUtils; import java.lang.reflect.InvocationTargetException; @@ -674,7 +673,7 @@ public class ControlActivity extends BaseMvvmActivity() { + .observeSticky(this, new Observer() { @Override public void onChanged(@Nullable MapBean mapBean) { + Log.e(TAG, "observeSticky onChanged: "); mMMKV.encode(CommonConfig.MANUALLY_SELECT_LOCATION_DISTRICT, mapBean.getDistrict()); mMMKV.encode(CommonConfig.MANUALLY_SELECT_LOCATION_TUDE, mapBean.getLongitude() + "," + mapBean.getLatitude()); // mViewDataBinding.tvLocation.setText(mapBean.getDistrict()); @@ -725,9 +724,6 @@ public class MainActivity extends BaseMvvmActivity hourlyBeanList) { +// if (hourlyBeanList != null && !hourlyBeanList.isEmpty()) { +// LiveEventBus +// .get("getWeather24HourlyKey") +// .post(hourlyBeanList.get(0)); +// } +// } +// }); + weatherManager.getWeather7Day(false, location, new WeatherManager.WeatherDailyCallback() { + @Override + public void onWeatherDaily(List dailyBeanList) { + if (dailyBeanList != null && !dailyBeanList.isEmpty()) { + LiveEventBus + .get("getWeather7DKey") + .post(dailyBeanList.get(0)); + } + } + }); +// getWeatherNow(location); +// getWeather24Hourly(location); +// getWeather7D(location); } + @Deprecated private MutableLiveData mNowBaseBeanData = new MutableLiveData<>(); - public void getWeatherNow(String location) { Log.e(TAG, "getWeatherNow: " + location); /** @@ -136,9 +167,9 @@ public class MainViewModel extends BaseViewModel mHourlyBeanData = new MutableLiveData<>(); - public void getWeather24Hourly(String location) { QWeather.getWeather24Hourly(getCtx(), location, new QWeather.OnResultWeatherHourlyListener() { @Override @@ -167,9 +198,9 @@ public class MainViewModel extends BaseViewModel DailyBeanData = new MutableLiveData<>(); - public void getWeather7D(String location) { QWeather.getWeather7D(getCtx(), location, new QWeather.OnResultWeatherDailyListener() { @Override 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 967b90c..8408461 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 @@ -159,7 +159,7 @@ public class WeatherActivity extends BaseMvvmActivity() { + @Override + public void onChanged(WeatherNowBean.NowBaseBean nowBaseBean) { + Log.d("getWeatherNowData: ", "onSuccess: now " + new Gson().toJson(nowBaseBean)); + mViewDataBinding.setNowBaseBean(nowBaseBean); + Toaster.show("刷新成功"); + } + }); + mViewModel.mDailyBeanListMutableLiveData.observe(this, new Observer>() { + @Override + public void onChanged(List dailyBeanList) { + if (dailyBeanList != null) { + mWeatherDayApdapter.setDailyBeans(dailyBeanList); + WeatherDailyBean.DailyBean dailyBean = dailyBeanList.get(0); + mViewDataBinding.tvMinMax.setText(dailyBean.getTempMin() + "℃ - " + dailyBean.getTempMax() + "℃"); + } + } + }); // mViewModel.loadProvince(); - getWeather(); + getWeather(false); } - private void getWeather() { + private void getWeather(boolean refresh) { // mViewModel.getWeatherCache(); String district = mMMKV.decodeString(CommonConfig.MANUALLY_SELECT_LOCATION_DISTRICT, CommonConfig.DEFAULT_LOCATION_DISTRICT); String tude = mMMKV.decodeString(CommonConfig.MANUALLY_SELECT_LOCATION_TUDE, CommonConfig.DEFAULT_LOCATION_TUDE); @@ -218,7 +235,7 @@ public class WeatherActivity extends BaseMvvmActivity() { + .observeSticky(this, new Observer() { @Override public void onChanged(@Nullable WeatherNowBean.NowBaseBean nowBaseBean) { mViewDataBinding.tvTemp.setText(nowBaseBean.getTemp() + "℃"); @@ -262,7 +262,7 @@ public class HomeFragment extends BaseMvvmFragment() { + .observeSticky(this, new Observer() { @Override public void onChanged(@Nullable WeatherDailyBean.DailyBean dailyBean) { mViewDataBinding.tvWeather.setText(dailyBean.getTempMin() + "℃ - " + dailyBean.getTempMax() + "℃"); @@ -442,7 +442,7 @@ public class HomeFragment extends BaseMvvmFragment() { + .observeSticky(this, new Observer() { @Override public void onChanged(@Nullable MapBean mapBean) { mViewDataBinding.tvLocation.setText(mapBean.getDistrict()); diff --git a/app/src/main/java/com/xxpatx/os/fragment/settings/SettingsFragment.java b/app/src/main/java/com/xxpatx/os/fragment/settings/SettingsFragment.java index 6720d3a..40350c1 100644 --- a/app/src/main/java/com/xxpatx/os/fragment/settings/SettingsFragment.java +++ b/app/src/main/java/com/xxpatx/os/fragment/settings/SettingsFragment.java @@ -117,7 +117,7 @@ public class SettingsFragment extends BaseMvvmFragment() { + .observeSticky(this, new Observer() { @Override public void onChanged(@Nullable WeatherNowBean.NowBaseBean nowBaseBean) { mViewDataBinding.tvTemp.setText(nowBaseBean.getTemp() + "℃"); @@ -164,7 +164,7 @@ public class SettingsFragment extends BaseMvvmFragment() { + .observeSticky(this, new Observer() { @Override public void onChanged(@Nullable WeatherDailyBean.DailyBean dailyBean) { mViewDataBinding.tvWeather.setText(dailyBean.getTempMin() + "℃ - " + dailyBean.getTempMax() + "℃"); @@ -199,7 +199,7 @@ public class SettingsFragment extends BaseMvvmFragment() { + .observeSticky(this, new Observer() { @Override public void onChanged(@Nullable MapBean mapBean) { mViewDataBinding.tvLocation2.setText(mapBean.getAddress()); diff --git a/app/src/main/java/com/xxpatx/os/manager/AmapManager.java b/app/src/main/java/com/xxpatx/os/manager/AmapManager.java index 05a88e5..93cda79 100644 --- a/app/src/main/java/com/xxpatx/os/manager/AmapManager.java +++ b/app/src/main/java/com/xxpatx/os/manager/AmapManager.java @@ -145,8 +145,7 @@ public class AmapManager { private void updateAddress(AMapLocation aMapLocation) { NetInterfaceManager.getInstance().getUpdateAddressObservable(aMapLocation.getAddress() - , aMapLocation.getLongitude(), aMapLocation.getLatitude() - ) + , aMapLocation.getLongitude(), aMapLocation.getLatitude()) .subscribe(new Observer() { @Override public void onSubscribe(@NonNull Disposable d) { diff --git a/app/src/main/java/com/xxpatx/os/manager/WeatherManager.java b/app/src/main/java/com/xxpatx/os/manager/WeatherManager.java new file mode 100644 index 0000000..d221a4f --- /dev/null +++ b/app/src/main/java/com/xxpatx/os/manager/WeatherManager.java @@ -0,0 +1,215 @@ +package com.xxpatx.os.manager; + +import android.content.Context; +import android.text.TextUtils; +import android.util.Log; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.qweather.sdk.bean.base.Code; +import com.qweather.sdk.bean.base.Lang; +import com.qweather.sdk.bean.base.Unit; +import com.qweather.sdk.bean.weather.WeatherDailyBean; +import com.qweather.sdk.bean.weather.WeatherHourlyBean; +import com.qweather.sdk.bean.weather.WeatherNowBean; +import com.qweather.sdk.view.QWeather; +import com.tencent.mmkv.MMKV; +import com.xxpatx.os.config.CommonConfig; +import com.xxpatx.os.gson.GsonUtils; + +import java.lang.reflect.Type; +import java.util.List; + +public class WeatherManager { + private static final String TAG = "WeatherManager"; + + private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); + + private Context mContext; + private Gson mGson; + + + /** + * 上次获取实时天气时间戳 3小时更新 + */ + private static final String GET_WEATHER_NOW_LAST_TIME_KEY = "get_weather_now_last_time_key"; + + /** + * 上次获取24小时天气时间戳 8小时更新 + */ + private static final String GET_WEATHER_24_HOURLY_LAST_TIME_KEY = "get_weather_24_hourly_last_time_key"; + + /** + * 上次获取7天天气时间戳 24小时更新 + */ + private static final String GET_WEATHER_7_DAY_LAST_TIME_KEY = "get_weather_7_day_last_time_key"; + + /*实时天气缓存*/ + private static final String CACHE_WEATHER_NOW_BEAN_KEY = "cache_weather_now_bean_key"; + /*24小时天气缓存*/ + private static final String CACHE_WEATHER_HOURLY_BEAN_KEY = "cache_weather_hourly_bean_key"; + /*7天天气缓存*/ + private static final String CACHE_WEATHER_DAILY_BEAN_KEY = "cache_weather_daily_bean_key"; + + private static final long MINUTE_1_INTERVAL_MS = 60 * 1000; // 1分钟毫秒数 + private static final long HOURLY_3_INTERVAL_MS = 3 * 60 * 60 * 1000; // 3小时毫秒数 + private static final long HOURLY_8_INTERVAL_MS = 8 * 60 * 60 * 1000; // 8小时毫秒数 + private static final long HOURLY_24_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24小时毫秒数 + + public WeatherManager(Context context) { + this.mContext = context; + mGson = new Gson(); + } + + public interface WeatherNowCallback { + void onWeatherNow(WeatherNowBean.NowBaseBean nowBaseBean); + } + + public synchronized void getWeatherNow(String location, WeatherNowCallback weatherNowCallback) { + long lastTime = mMMKV.decodeLong(GET_WEATHER_NOW_LAST_TIME_KEY, 0); + Log.e(TAG, "getWeatherNow: lastTime = " + lastTime); + String jsonString = mMMKV.decodeString(CACHE_WEATHER_NOW_BEAN_KEY, ""); + long time = System.currentTimeMillis(); + + if (lastTime == 0 || TextUtils.isEmpty(jsonString) || time - lastTime > HOURLY_3_INTERVAL_MS) { + Log.e(TAG, "getWeatherNow: get data"); + QWeather.getWeatherNow(mContext, location, Lang.ZH_HANS, Unit.METRIC, new QWeather.OnResultWeatherNowListener() { + @Override + public void onError(Throwable e) { + mMMKV.encode(GET_WEATHER_NOW_LAST_TIME_KEY, System.currentTimeMillis()); + Log.e("getWeatherNow", "onError: " + e); + } + + @Override + public void onSuccess(WeatherNowBean weatherBean) { + mMMKV.encode(GET_WEATHER_NOW_LAST_TIME_KEY, System.currentTimeMillis()); + Log.e("getWeatherNow", "onSuccess: "); + + //先判断返回的status是否正确,当status正确时获取数据,若status不正确,可查看status对应的Code值找到原因 + if (Code.OK == weatherBean.getCode()) { + WeatherNowBean.NowBaseBean now = weatherBean.getNow(); + mMMKV.encode(CACHE_WEATHER_NOW_BEAN_KEY, GsonUtils.toJSONString(now)); + if (weatherNowCallback != null) weatherNowCallback.onWeatherNow(now); + } else { + //在此查看返回数据失败的原因 + Code code = weatherBean.getCode(); + Log.d("getWeatherNow", "onSuccess: failed code: " + code); + } + } + }); + } else { + Log.e(TAG, "getWeatherNow: get cache"); + try { + WeatherNowBean.NowBaseBean now = GsonUtils.toJavaObject(jsonString, WeatherNowBean.NowBaseBean.class); + if (weatherNowCallback != null) weatherNowCallback.onWeatherNow(now); + } catch (Exception e) { + Log.e(TAG, "getWeatherNow: " + e.getMessage()); + mMMKV.remove(CACHE_WEATHER_NOW_BEAN_KEY); + } + } + + } + + public interface WeatherHourlyCallback { + void onWeatherHourly(List hourlyBeanList); + } + + public synchronized void getWeather24Hourly(String location, WeatherHourlyCallback weatherHourlyCallback) { + long lastTime = mMMKV.decodeLong(GET_WEATHER_24_HOURLY_LAST_TIME_KEY, 0); + Log.e(TAG, "getWeather24Hourly: lastTime = " + lastTime); + String jsonString = mMMKV.decodeString(CACHE_WEATHER_HOURLY_BEAN_KEY, ""); + long time = System.currentTimeMillis(); + + if (lastTime == 0 || TextUtils.isEmpty(jsonString) || time - lastTime > HOURLY_8_INTERVAL_MS) { + Log.e(TAG, "getWeather24Hourly: get data"); + QWeather.getWeather24Hourly(mContext, location, new QWeather.OnResultWeatherHourlyListener() { + @Override + public void onError(Throwable throwable) { + mMMKV.encode(GET_WEATHER_24_HOURLY_LAST_TIME_KEY, System.currentTimeMillis()); + Log.e("getWeather24Hourly", "onError: " + throwable); + } + + @Override + public void onSuccess(WeatherHourlyBean weatherHourlyBean) { + mMMKV.encode(GET_WEATHER_24_HOURLY_LAST_TIME_KEY, System.currentTimeMillis()); + Log.e("getWeather24Hourly", "onSuccess: "); + + if (Code.OK == weatherHourlyBean.getCode()) { + List hourly = weatherHourlyBean.getHourly(); + mMMKV.encode(CACHE_WEATHER_HOURLY_BEAN_KEY, GsonUtils.toJSONString(hourly)); + if (weatherHourlyCallback != null) + weatherHourlyCallback.onWeatherHourly(hourly); + } else { + //在此查看返回数据失败的原因 + Code code = weatherHourlyBean.getCode(); + Log.e("getWeather24Hourly", "failed code: " + code); + } + } + }); + + } else { + Log.e(TAG, "getWeather24Hourly: get cache"); + try { + Type type = new TypeToken>() { + }.getType(); + List hourly = mGson.fromJson(jsonString, type); + if (weatherHourlyCallback != null) weatherHourlyCallback.onWeatherHourly(hourly); + } catch (Exception e) { + Log.e(TAG, "getWeather24Hourly: " + e.getMessage()); + mMMKV.remove(CACHE_WEATHER_HOURLY_BEAN_KEY); + } + } + } + + public interface WeatherDailyCallback { + void onWeatherDaily(List dailyBeanList); + } + + public synchronized void getWeather7Day(boolean refresh, String location, WeatherDailyCallback weatherDailyCallback) { + Log.e(TAG, "getWeather7Day: refresh = " + refresh); + long lastTime = mMMKV.decodeLong(GET_WEATHER_7_DAY_LAST_TIME_KEY, 0); + Log.e(TAG, "getWeather7Day: lastTime = " + lastTime); + String jsonString = mMMKV.decodeString(CACHE_WEATHER_DAILY_BEAN_KEY, ""); + long time = System.currentTimeMillis(); + + if (lastTime == 0 || TextUtils.isEmpty(jsonString) || refresh ? time - lastTime > MINUTE_1_INTERVAL_MS : time - lastTime > HOURLY_24_INTERVAL_MS) { + Log.e(TAG, "getWeather7Day: get data"); + QWeather.getWeather7D(mContext, location, new QWeather.OnResultWeatherDailyListener() { + @Override + public void onError(Throwable throwable) { + mMMKV.encode(GET_WEATHER_7_DAY_LAST_TIME_KEY, System.currentTimeMillis()); + Log.e("getWeather7Day", "onError: " + throwable.getMessage()); + } + + @Override + public void onSuccess(WeatherDailyBean weatherDailyBean) { + mMMKV.encode(GET_WEATHER_7_DAY_LAST_TIME_KEY, System.currentTimeMillis()); + Log.e("getWeather7Day", "onSuccess: "); + + if (Code.OK == weatherDailyBean.getCode()) { + List dailyBeans = weatherDailyBean.getDaily(); + mMMKV.encode(CACHE_WEATHER_DAILY_BEAN_KEY, GsonUtils.toJSONString(dailyBeans)); + if (weatherDailyCallback != null) + weatherDailyCallback.onWeatherDaily(dailyBeans); + } else { + //在此查看返回数据失败的原因 + Code code = weatherDailyBean.getCode(); + Log.e("getWeather7Day", "failed code: " + code); + } + } + }); + + } else { + Log.e(TAG, "getWeather7Day: get cache"); + try { + Type type = new TypeToken>() { + }.getType(); + List dailyBeans = mGson.fromJson(jsonString, type); + if (weatherDailyCallback != null) weatherDailyCallback.onWeatherDaily(dailyBeans); + } catch (Exception e) { + Log.e(TAG, "getWeather7Day: " + e.getMessage()); + mMMKV.remove(CACHE_WEATHER_DAILY_BEAN_KEY); + } + } + } +} diff --git a/app/src/main/java/com/xxpatx/os/utils/ContactsUtils.java b/app/src/main/java/com/xxpatx/os/utils/ContactsUtils.java index 6d82d58..88de076 100644 --- a/app/src/main/java/com/xxpatx/os/utils/ContactsUtils.java +++ b/app/src/main/java/com/xxpatx/os/utils/ContactsUtils.java @@ -18,7 +18,6 @@ import com.bumptech.glide.Glide; import com.tencent.mmkv.MMKV; import com.xxpatx.os.R; import com.xxpatx.os.bean.Contact; -import com.xxpatx.os.bean.ContactId; import com.xxpatx.os.config.CommonConfig; import java.io.ByteArrayOutputStream; @@ -26,8 +25,7 @@ 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 java.util.Objects; import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.Observable; @@ -191,26 +189,31 @@ public class ContactsUtils { } public static void updateContactPhone(Context context, Contact contact, Bitmap bitmap) { - long rawContactId = ContactsUtils.getRawContactId(context, contact.getMobile()); - Log.e(TAG, "updateContactPhone: rawContactId = " + rawContactId); - ContentResolver resolver = context.getContentResolver(); + String name = getContactNameFromNumber(context, contact.getMobile()); + Log.e(TAG, "updateContactPhone: " + name); + if (!Objects.equals(name, contact.getName())) { + long rawContactId = ContactsUtils.getRawContactId(context, contact.getMobile()); + Log.e(TAG, "updateContactPhone: rawContactId = " + rawContactId); + updateContactNameByNumber(context, contact.getMobile(), contact.getName()); - ContentValues values = new ContentValues(); - values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); - values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE); - values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, contact.getName()); - resolver.insert(ContactsContract.Data.CONTENT_URI, values); + ContentResolver resolver = context.getContentResolver(); - values.clear(); + ContentValues values = new ContentValues(); + values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); + values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE); + values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, contact.getName()); + resolver.insert(ContactsContract.Data.CONTENT_URI, values); - values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); - values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE); - values.put(ContactsContract.CommonDataKinds.Photo.PHOTO, out.toByteArray()); - resolver.insert(ContactsContract.Data.CONTENT_URI, values); + values.clear(); - values.clear(); + values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); + values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE); + values.put(ContactsContract.CommonDataKinds.Photo.PHOTO, out.toByteArray()); + resolver.insert(ContactsContract.Data.CONTENT_URI, values); + + values.clear(); // values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); // values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE); @@ -230,7 +233,85 @@ public class ContactsUtils { // values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, name); // values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone); // Log.d("updateContact", "values已经产生"); -// int result = resolver.update(ContactsContract.Data.CONTENT_URI, values, ContactsContract.Data.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(bean.getId())}); + +// int result = resolver.update(ContactsContract.Data.CONTENT_URI, values, ContactsContract.Data.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)}); +// Log.e(TAG, "updateContactPhone: " + result); + } else { + Log.e(TAG, "updateContactPhone: same contact info"); + } + } + + private static String getContactNameFromNumber(Context context, String phoneNumber) { + String contactName = null; + Uri lookupUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber)); + String[] projection = {ContactsContract.PhoneLookup.DISPLAY_NAME}; + + try (Cursor cursor = context.getContentResolver().query(lookupUri, projection, null, null, null)) { + if (cursor != null && cursor.moveToFirst()) { + contactName = cursor.getString(cursor.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME)); + } + } catch (Exception e) { + Log.e("ContactQuery", "查询失败", e); + } + return contactName != null ? contactName : "未知号码"; + } + + public static void editContactPhone(Context context, long contactId, Contact contact) { + if (contactId == -1) { + saveContactPhone(context, contact); + } else { + // 2. 获取所有关联的RawContact ID + List rawContactIds = getRawContactIds(context, contactId); + if (rawContactIds.isEmpty()) { + Log.e("UpdateContact", "rawContactIds is empty"); + return; + } + + // 3. 构建批量操作 + ArrayList ops = new ArrayList<>(); + for (Long rawId : rawContactIds) { + // 更新现有姓名(如果存在) + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection( + ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[]{String.valueOf(rawId), ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE} + ) + .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, contact.getName()) + .build()); + + // 插入新姓名(如果原数据不存在) +// ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) +// .withValue(ContactsContract.Data.RAW_CONTACT_ID, rawId) +// .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) +// .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, contact.getName()) +// .withYieldAllowed(true) +// .build()); + + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection( + ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=? AND " + + ContactsContract.CommonDataKinds.Phone.TYPE + "=?", + new String[]{ + String.valueOf(rawId), + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE, + String.valueOf(ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE) + } + ) + .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, contact.getMobile()) + .build()); + } + + // 4. 执行修改 + try { + context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); + context.getContentResolver().notifyChange(ContactsContract.Data.CONTENT_URI, null); // 通知数据变更 + Log.i("UpdateContact", "姓名已更新为: " + contact.getName()); + } catch (Exception e) { + Log.e("UpdateContact", "修改失败", e); + } + } } public static void updateContactPhone(Context context, Contact contact) { @@ -245,6 +326,90 @@ public class ContactsUtils { resolver.insert(ContactsContract.Data.CONTENT_URI, values); } + private static void updateContactNameByNumber(Context context, String phoneNumber, String newName) { + // 1. 根据号码查找联系人ID + long contactId = findContactIdByPhoneNumber(context, phoneNumber); + if (contactId == -1) { + Log.e("UpdateContact", "未找到匹配的联系人"); + return; + } + + // 2. 获取所有关联的RawContact ID + List rawContactIds = getRawContactIds(context, contactId); + if (rawContactIds.isEmpty()) { + Log.e("UpdateContact", "rawContactIds is empty"); + return; + } + + // 3. 构建批量操作 + ArrayList ops = new ArrayList<>(); + for (Long rawId : rawContactIds) { + // 更新现有姓名(如果存在) + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection( + ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[]{String.valueOf(rawId), ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE} + ) + .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, newName) + .build()); + + // 插入新姓名(如果原数据不存在) + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValue(ContactsContract.Data.RAW_CONTACT_ID, rawId) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, newName) + .withYieldAllowed(true) + .build()); + } + + // 4. 执行修改 + try { + context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); + context.getContentResolver().notifyChange(ContactsContract.Data.CONTENT_URI, null); // 通知数据变更 + Log.i("UpdateContact", "姓名已更新为: " + newName); + } catch (Exception e) { + Log.e("UpdateContact", "修改失败", e); + } + } + + // 根据号码查询联系人ID + public static long findContactIdByPhoneNumber(Context context, String phoneNumber) { + Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber)); + Cursor cursor = context.getContentResolver().query( + uri, + new String[]{ContactsContract.PhoneLookup._ID}, + null, null, null + ); + + if (cursor != null && cursor.moveToFirst()) { + long contactId = cursor.getLong(0); + cursor.close(); + return contactId; + } + return -1; + } + + // 获取联系人所有RawContact ID + private static List getRawContactIds(Context context, long contactId) { + List rawIds = new ArrayList<>(); + Cursor cursor = context.getContentResolver().query( + ContactsContract.RawContacts.CONTENT_URI, + new String[]{ContactsContract.RawContacts._ID}, + ContactsContract.RawContacts.CONTACT_ID + "=?", + new String[]{String.valueOf(contactId)}, + null + ); + + if (cursor != null) { + while (cursor.moveToNext()) { + rawIds.add(cursor.getLong(0)); + } + cursor.close(); + } + return rawIds; + } + public static void update(Context context, Contact contact, Bitmap bitmap) { long rawContactId = ContactsUtils.getRawContactId(context, contact.getMobile()); @@ -395,6 +560,32 @@ public class ContactsUtils { return -1; } + public static String getRawContactName(Context context, String phoneNum) { + ContentResolver resolver = context.getContentResolver(); + Cursor cursor = null; + try { + cursor = resolver.query(ContactsContract.Data.CONTENT_URI, + new String[]{ContactsContract.Data.RAW_CONTACT_ID, ContactsContract.CommonDataKinds.Phone.NUMBER, + ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME}, + ContactsContract.CommonDataKinds.Phone.NUMBER + "=?", new String[]{phoneNum}, null); + if (cursor.moveToFirst()) { + String displayName = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME)); + Log.e(TAG, "getRawContactName: displayName = " + displayName); + String name = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME)); + Log.e(TAG, "getRawContactName: rawName = " + name); + return name; + } + } catch (Exception e) { + e.printStackTrace(); + Log.e(TAG, "getRawContactName: " + e.getMessage()); + } finally { + if (cursor != null) { + cursor.close(); + } + } + return ""; + } + /** * 添加联系人信息 */ @@ -559,33 +750,6 @@ public class ContactsUtils { // cursor.close(); } - @Deprecated - 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; - } - - /** * 判断某个手机号是否存在 */ @@ -672,4 +836,3 @@ public class ContactsUtils { return contactList; } } -