From a4988b5b68e74c90bf7dd3c6edb22b4e66526091 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Thu, 28 Aug 2025 16:02:41 +0800 Subject: [PATCH] =?UTF-8?q?=E8=81=94=E7=B3=BB=E4=BA=BA=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E5=8F=91=E9=80=81=E7=AA=97=E5=8F=A3=E6=B7=BB=E5=8A=A0=E6=8B=BC?= =?UTF-8?q?=E9=9F=B3=E6=9F=A5=E8=AF=A2=E5=8A=9F=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mymessagemanager/build.gradle | 2 + mymessagemanager/build.properties | 4 +- .../activitys/ComposeSMSActivity.java | 115 +++++++++-- .../mymessagemanager/utils/PhoneUtil.java | 183 +++++++++++++++--- .../mymessagemanager/utils/UnitAreaUtils.java | 20 +- .../main/res/layout/activity_composesms.xml | 68 +++++-- 6 files changed, 325 insertions(+), 67 deletions(-) diff --git a/mymessagemanager/build.gradle b/mymessagemanager/build.gradle index 4dea4e5..9f82e28 100644 --- a/mymessagemanager/build.gradle +++ b/mymessagemanager/build.gradle @@ -54,6 +54,8 @@ dependencies { api 'com.jcraft:jsch:0.1.55' api 'org.jsoup:jsoup:1.13.1' api 'com.squareup.okhttp3:okhttp:4.4.1' + + api 'com.belerweb:pinyin4j:2.5.1' // 权限请求框架:https://github.com/getActivity/XXPermissions api 'com.github.getActivity:XXPermissions:18.63' diff --git a/mymessagemanager/build.properties b/mymessagemanager/build.properties index 5c89ede..539460c 100644 --- a/mymessagemanager/build.properties +++ b/mymessagemanager/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Sat Aug 23 13:40:21 HKT 2025 +#Thu Aug 28 08:00:22 GMT 2025 stageCount=3 libraryProject= baseVersion=15.3 publishVersion=15.3.2 -buildCount=0 +buildCount=22 baseBetaVersion=15.3.3 diff --git a/mymessagemanager/src/main/java/cc/winboll/studio/mymessagemanager/activitys/ComposeSMSActivity.java b/mymessagemanager/src/main/java/cc/winboll/studio/mymessagemanager/activitys/ComposeSMSActivity.java index cd995b0..4ee6488 100644 --- a/mymessagemanager/src/main/java/cc/winboll/studio/mymessagemanager/activitys/ComposeSMSActivity.java +++ b/mymessagemanager/src/main/java/cc/winboll/studio/mymessagemanager/activitys/ComposeSMSActivity.java @@ -35,6 +35,8 @@ public class ComposeSMSActivity extends BaseActivity { String mszSMSBody; String mszScheme; String mszPhoneTo; + TextView mtvTOName; + EditText metTONameSearch; EditText metTO; EditText metSMSBody; SimpleAdapter mSimpleAdapter; @@ -59,9 +61,9 @@ public class ComposeSMSActivity extends BaseActivity { // 初始化视图 initView(); // 设置适配器 - initAdapter(); + initAdapter(null); // 设置搜索到的匹配位置 - setListViewPrePosition(); + setListViewPrePositionByPhone(); } // @@ -76,8 +78,25 @@ public class ComposeSMSActivity extends BaseActivity { setActionBar(mToolbar); // 初始化联系人栏目框 + mtvTOName = findViewById(R.id.activitycomposesmsTextView2); mrlContracts = findViewById(R.id.activitycomposesmsRelativeLayout1); //mrlContracts.setBackground(drawableFrame); + metTONameSearch = findViewById(R.id.activitycomposesmsEditText2); + metTONameSearch.addTextChangedListener(new TextWatcher() { + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + metTO.setText(""); + setListViewPrePositionByName(); + } + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void afterTextChanged(Editable s) { + + } + }); // 初始化联系人列表 mlvContracts = findViewById(R.id.activitycomposesmsListView1); @@ -88,7 +107,10 @@ public class ComposeSMSActivity extends BaseActivity { metTO.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { - setListViewPrePosition(); + mtvTOName.setText(""); + //重新加载数据 + initAdapter(null); + setListViewPrePositionByPhone(); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { @@ -96,6 +118,7 @@ public class ComposeSMSActivity extends BaseActivity { @Override public void afterTextChanged(Editable s) { + } }); @@ -136,18 +159,34 @@ public class ComposeSMSActivity extends BaseActivity { } // - // 设置搜索到的匹配位置 + // 设置号码搜索到的匹配位置 // - void setListViewPrePosition() { - int nPrePosition = getContractsDataPrePosition(metTO.getText().toString()); + void setListViewPrePositionByPhone() { + int nPrePosition = getContractsDataPrePositionByPhone(metTO.getText().toString()); + + PhoneUtil phoneUtils = new PhoneUtil(this); + mtvTOName.setText(phoneUtils.getNameByPhone(metTO.getText().toString())); + mlvContracts.setSelected(false); mlvContracts.setSelection(nPrePosition); } + + // + // 设置名称搜索到的匹配位置 + // + void setListViewPrePositionByName() { + PhoneUtil phoneUtils = new PhoneUtil(this); + List newPhoneData = phoneUtils.getPhonesByName(metTONameSearch.getText().toString()); + // 重新绑定数据 + initAdapter(newPhoneData); + mlvContracts.setSelected(false); + } + // // 返回搜索到的匹配位置 // - int getContractsDataPrePosition(String szPhone) { + int getContractsDataPrePositionByPhone(String szPhone) { for (int i = 0; i < mListPhoneBeanContracts.size(); i++) { if (mListPhoneBeanContracts.get(i).getTelPhone().compareTo(szPhone) > -1) { return i; @@ -157,15 +196,65 @@ public class ComposeSMSActivity extends BaseActivity { return 0; } + + // + // 返回搜索到的匹配位置 + // + int getContractsDataPrePositionByName(String szName) { + for (int i = 0; i < mListPhoneBeanContracts.size(); i++) { + if (mListPhoneBeanContracts.get(i).getName().startsWith(szName)) { + return i; + } + + } + return 0; + } + // // 初始化适配器 // - void initAdapter() { + /*void initAdapter() { + // 初始化联系人数据适配器 + mAdapterData = new ArrayList<>(); + // 读取联系人数据 + final PhoneUtil phoneUtils = new PhoneUtil(this); + mListPhoneBeanContracts = phoneUtils.getPhoneList(); + // 映射联系人数据给适配器数据对象 + for (int i = 0;i < mListPhoneBeanContracts.size();i++) { + Map map =new HashMap<>(); + map.put(MAP_NAME, mListPhoneBeanContracts.get(i).getName()); + map.put(MAP_PHONE, mListPhoneBeanContracts.get(i).getTelPhone()); + mAdapterData.add(map); + } + // 绑定适配器与数据 + mSimpleAdapter = new SimpleAdapter(ComposeSMSActivity.this, mAdapterData, R.layout.listview_contracts + , new String[]{MAP_NAME, MAP_PHONE} + , new int[]{R.id.listviewcontractsTextView1, R.id.listviewcontractsTextView2}); + mSimpleAdapter.setDropDownViewResource(R.layout.listview_contracts); + mlvContracts.setAdapter(mSimpleAdapter); + mlvContracts.setOnItemClickListener(new ListView.OnItemClickListener() { + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + metTO.setText(mAdapterData.get(position).get(MAP_PHONE).toString()); + mListPhoneBeanContracts = phoneUtils.getPhoneList(); + mtvTOName.setText(phoneUtils.getNameByPhone(metTO.getText().toString())); + + } + }); + }*/ + + void initAdapter(List initData) { // 初始化联系人数据适配器 - mAdapterData = new ArrayList<>(); - // 读取联系人数据 - PhoneUtil phoneUtils = new PhoneUtil(this); - mListPhoneBeanContracts = phoneUtils.getPhoneList(); + mAdapterData = new ArrayList<>(); + final PhoneUtil phoneUtils = new PhoneUtil(this); + if (initData != null) { + mListPhoneBeanContracts = initData; + } else { + // 读取联系人数据 + mListPhoneBeanContracts = phoneUtils.getPhoneList(); + } + // 映射联系人数据给适配器数据对象 for (int i = 0;i < mListPhoneBeanContracts.size();i++) { Map map =new HashMap<>(); @@ -184,7 +273,7 @@ public class ComposeSMSActivity extends BaseActivity { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { metTO.setText(mAdapterData.get(position).get(MAP_PHONE).toString()); - + mtvTOName.setText(phoneUtils.getNameByPhone(metTO.getText().toString())); } }); } diff --git a/mymessagemanager/src/main/java/cc/winboll/studio/mymessagemanager/utils/PhoneUtil.java b/mymessagemanager/src/main/java/cc/winboll/studio/mymessagemanager/utils/PhoneUtil.java index 993754c..5b48db1 100644 --- a/mymessagemanager/src/main/java/cc/winboll/studio/mymessagemanager/utils/PhoneUtil.java +++ b/mymessagemanager/src/main/java/cc/winboll/studio/mymessagemanager/utils/PhoneUtil.java @@ -5,6 +5,7 @@ package cc.winboll.studio.mymessagemanager.utils; * @Date 2024/07/19 14:30:57 * @Describe 手机联系人工具类 */ + import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; @@ -12,6 +13,12 @@ import android.net.Uri; import android.provider.ContactsContract; import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.mymessagemanager.beans.PhoneBean; +import net.sourceforge.pinyin4j.PinyinHelper; +import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType; +import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat; +import net.sourceforge.pinyin4j.format.HanyuPinyinToneType; +import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination; + import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -38,28 +45,137 @@ public class PhoneUtil { } // 读取所有联系人 - // public List getPhoneList() { List listPhoneBean = new ArrayList<>(); ContentResolver cr = mContext.getContentResolver(); Cursor cursor = cr.query(mUriPhoneContent, new String[]{NUMBER, DISPLAY_NAME}, null, null, null); - while (cursor.moveToNext()) { - PhoneBean phoneBean = new PhoneBean(cursor.getString(1), cursor.getString(0).replaceAll("\\s", "")); - listPhoneBean.add(phoneBean); - + if (cursor != null) { + while (cursor.moveToNext()) { + // 去除号码中的空格 + String phone = cursor.getString(0).replaceAll("\\s", ""); + String name = cursor.getString(1); + PhoneBean phoneBean = new PhoneBean(name, phone); + listPhoneBean.add(phoneBean); + } + cursor.close(); } - cursor.close(); + // 按电话号码排序 Collections.sort(listPhoneBean, new Comparator() { - @Override - public int compare(PhoneBean o1, PhoneBean o2) { - return o1.getTelPhone().compareTo(o2.getTelPhone()); - } - }); + @Override + public int compare(PhoneBean o1, PhoneBean o2) { + return o1.getTelPhone().compareTo(o2.getTelPhone()); + } + }); return listPhoneBean; } + /** + * 根据联系人名称查询号码(兼容拼音查询) + * @param keyword 搜索关键词(支持汉字、拼音、拼音首字母) + * @return 匹配的联系人列表(包含姓名和号码) + */ + public List getPhonesByName(String keyword) { + List result = new ArrayList<>(); + if (keyword == null || keyword.trim().isEmpty()) { + return result; // 关键词为空,返回空列表 + } + + // 获取所有联系人 + List allContacts = getPhoneList(); + // 统一转为小写,忽略大小写 + String keywordLower = keyword.trim().toLowerCase(); + + for (PhoneBean contact : allContacts) { + String name = contact.getName(); + if (name == null || name.isEmpty()) { + continue; + } + + // 1. 直接匹配姓名(包含关键词) + if (name.toLowerCase().contains(keywordLower)) { + result.add(contact); + continue; + } + + // 2. 匹配姓名的全拼(包含关键词) + String namePinyin = getPinyin(name).toLowerCase(); + if (namePinyin.contains(keywordLower)) { + result.add(contact); + continue; + } + + // 3. 匹配姓名的拼音首字母(包含关键词) + String namePinyinFirstLetter = getPinyinFirstLetter(name).toLowerCase(); + if (namePinyinFirstLetter.contains(keywordLower)) { + result.add(contact); + continue; + } + } + + return result; + } + + /** + * 将汉字转为全拼(不带声调,小写) + * 例如:"张三" → "zhangsan" + */ + private String getPinyin(String chinese) { + StringBuilder pinyin = new StringBuilder(); + HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat(); + format.setCaseType(HanyuPinyinCaseType.LOWERCASE); // 小写 + format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); // 不带声调 + + char[] chars = chinese.toCharArray(); + for (char c : chars) { + // 如果是汉字,转换为拼音;否则直接拼接(如字母、数字、符号) + if (Character.toString(c).matches("[\\u4e00-\\u9fa5]")) { + try { + String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(c, format); + if (pinyinArray != null && pinyinArray.length > 0) { + pinyin.append(pinyinArray[0]); // 取第一个拼音(多音字默认取第一个) + } + } catch (BadHanyuPinyinOutputFormatCombination e) { + LogUtils.e(TAG, "拼音转换失败:" + e.getMessage()); + } + } else { + pinyin.append(c); + } + } + return pinyin.toString(); + } + + /** + * 将汉字转为拼音首字母(小写) + * 例如:"张三" → "zs" + */ + private String getPinyinFirstLetter(String chinese) { + StringBuilder firstLetters = new StringBuilder(); + HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat(); + format.setCaseType(HanyuPinyinCaseType.LOWERCASE); + format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); + + char[] chars = chinese.toCharArray(); + for (char c : chars) { + if (Character.toString(c).matches("[\\u4e00-\\u9fa5]")) { + try { + String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(c, format); + if (pinyinArray != null && pinyinArray.length > 0) { + // 取拼音首字母(如"zhang" → "z") + firstLetters.append(pinyinArray[0].charAt(0)); + } + } catch (BadHanyuPinyinOutputFormatCombination e) { + LogUtils.e(TAG, "拼音首字母转换失败:" + e.getMessage()); + } + } else { + // 非汉字直接拼接首字符(如"李3" → "l3") + firstLetters.append(c); + } + } + return firstLetters.toString(); + } + public boolean isPhoneInContacts(String szPhone) { List listPhoneDto = getPhoneList(); LogUtils.d(TAG, String.format("isPhoneInContacts(...) listPhoneDto.size() %d", listPhoneDto.size())); @@ -70,49 +186,56 @@ public class PhoneUtil { } return false; } - + + public String getNameByPhone(String szPhone) { + if (szPhone == null || szPhone.equals("")) { + return ""; + } + + List listPhoneDto = getPhoneList(); + LogUtils.d(TAG, String.format("getNameByPhone(...) listPhoneDto.size() %d", listPhoneDto.size())); + for (int i = 0; i < listPhoneDto.size(); i++) { + if (isTheSamePhoneNumber(listPhoneDto.get(i).getTelPhone(), szPhone)) { + return listPhoneDto.get(i).getName(); + } + } + return ""; + } + boolean isTheSamePhoneNumber(String szNum1, String szNum2) { - //LogUtils.d(TAG, String.format("szNum1 %s\nszNum2 %s", szNum1, szNum2)); - if(szNum1.equals(szNum2)) { + if (szNum1.equals(szNum2)) { LogUtils.d(TAG, "szNum1.equals(szNum2)"); return true; } - - if(UnitAreaUtils.getInstance(mContext).isCurrentUnitAreaNumber(szNum1)) { - if(szNum1.equals(UnitAreaUtils.getInstance(mContext).genCurrentUnitAreaNumber(szNum2))) { + + if (UnitAreaUtils.getInstance(mContext).isCurrentUnitAreaNumber(szNum1)) { + if (szNum1.equals(UnitAreaUtils.getInstance(mContext).genCurrentUnitAreaNumber(szNum2))) { LogUtils.d(TAG, "szNum1.equals(UnitAreaUtils.genCurrentUnitAreaNumber(szNum2))"); return true; } } - - if(UnitAreaUtils.getInstance(mContext).isCurrentUnitAreaNumber(szNum2)) { - if(szNum2.equals(UnitAreaUtils.getInstance(mContext).genCurrentUnitAreaNumber(szNum1))) { + + if (UnitAreaUtils.getInstance(mContext).isCurrentUnitAreaNumber(szNum2)) { + if (szNum2.equals(UnitAreaUtils.getInstance(mContext).genCurrentUnitAreaNumber(szNum1))) { LogUtils.d(TAG, "szNum2.equals(UnitAreaUtils.genCurrentUnitAreaNumber(szNum1))"); return true; } } - + LogUtils.d(TAG, "isTheSamePhoneNumber(...) return false;"); return false; } - // // 检验电话号码是否是数字 - // public static boolean isPhoneByDigit(String szPhone) { - if(!RegexPPiUtils.isPPiOK(szPhone)) { + if (!RegexPPiUtils.isPPiOK(szPhone)) { return false; } - //String text = "这里是一些任意的文本内容"; String regex = "[+]?\\d+"; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(szPhone); LogUtils.d(TAG, String.format("matcher.matches() : %s", matcher.matches())); - /*if (matcher.matches()) { - System.out.println("文本满足该正则表达式模式"); - } else { - System.out.println("文本不满足该正则表达式模式"); - }*/ return matcher.matches(); } } + diff --git a/mymessagemanager/src/main/java/cc/winboll/studio/mymessagemanager/utils/UnitAreaUtils.java b/mymessagemanager/src/main/java/cc/winboll/studio/mymessagemanager/utils/UnitAreaUtils.java index 4d106e6..8d531b8 100644 --- a/mymessagemanager/src/main/java/cc/winboll/studio/mymessagemanager/utils/UnitAreaUtils.java +++ b/mymessagemanager/src/main/java/cc/winboll/studio/mymessagemanager/utils/UnitAreaUtils.java @@ -10,9 +10,9 @@ import cc.winboll.studio.mymessagemanager.beans.AppConfigBean; import android.content.Context; public class UnitAreaUtils { - + public static final String TAG = "UnitAreaUtils"; - + static UnitAreaUtils _UnitAreaUtils; Context mContext; @@ -26,19 +26,25 @@ public class UnitAreaUtils { } return _UnitAreaUtils; } - + public boolean isCurrentUnitAreaNumber(String szPhoneNumer) { String szUnitArea = getUnitArea(); - LogUtils.d(TAG, String.format("szPhoneNumer.substring(1,3) %s", szPhoneNumer.substring(1,3))); - return szPhoneNumer.substring(1,3).equals(szUnitArea); + try { + String szPhoneNumerUnitArea = szPhoneNumer.substring(1, 3); + LogUtils.d(TAG, String.format("szPhoneNumerUnitArea %s", szPhoneNumerUnitArea)); + return szPhoneNumerUnitArea.equals(szUnitArea); + } catch (StringIndexOutOfBoundsException e) { + LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); + } + return false; } - + public String genCurrentUnitAreaNumber(String szPhoneNumer) { String szUnitArea = getUnitArea(); LogUtils.d(TAG, String.format("szUnitArea %s", szUnitArea)); return "+" + szUnitArea + szPhoneNumer; } - + String getUnitArea() { String szUnitArea = AppConfigUtil.getInstance(mContext).mAppConfigBean.getCountryCode(); LogUtils.d(TAG, String.format("szUnitArea %s", szUnitArea)); diff --git a/mymessagemanager/src/main/res/layout/activity_composesms.xml b/mymessagemanager/src/main/res/layout/activity_composesms.xml index 09f6358..fd06f8a 100644 --- a/mymessagemanager/src/main/res/layout/activity_composesms.xml +++ b/mymessagemanager/src/main/res/layout/activity_composesms.xml @@ -21,25 +21,63 @@ android:layout_height="wrap_content" android:id="@+id/activitycomposesmsRelativeLayout1"> - - - + android:layout_marginLeft="10dp" + android:layout_alignParentLeft="true"> + + + + + + + + + + + + + + + +